1 /////////////////////////////////////////
2 //
3 // OpenLieroX
4 //
5 // Auxiliary Software class library
6 //
7 // based on the work of JasonB
8 // enhanced by Dark Charlie and Albert Zeyer
9 //
10 // code under LGPL
11 //
12 /////////////////////////////////////////
13
14
15 // Byte stream class
16 // Created 13/10/01
17 // Jason Boettcher
18
19
20 #include <cassert>
21 #include <stdarg.h>
22 #include <iomanip>
23
24 #include "CBytestream.h"
25 #include "EndianSwap.h"
26 #include "StringUtils.h"
27 #include "MathLib.h"
28 #include "CScriptableVars.h"
29 #include "Debug.h"
30 #include "Iterator.h"
31 #include "Utils.h"
32
33
Test()34 void CBytestream::Test()
35 {
36 notes << endl;
37 notes << "Running a Bytestream debug test:" << endl;
38 notes << "Tested function / Original / Write / Read / Warning" << endl;
39
40 // Byte
41 uchar b = 125;
42 notes << "Byte: (" << b << ") ";
43 writeByte(b);
44 ResetPosToBegin();
45 notes << "(" << Data << ") ";
46 uchar b2 = readByte();
47 notes << "(" << b2 << ") ";
48 if (b2 != b)
49 notes << "NOT SAME!";
50 notes <<endl;
51 Clear();
52
53 // Bool
54 bool boo = true;
55 notes << "Bool: (" << boo << ") ";
56 writeByte(boo);
57 ResetPosToBegin();
58 notes << "(" << Data << ") ";
59 bool boo2 = readBool();
60 notes << "(" << boo2 << ") ";
61 if (boo2 != boo)
62 notes << "NOT SAME!";
63 notes << endl;
64 Clear();
65
66 {
67 // Integer
68 int i = 125;
69 notes << "Int: (" << i << ") ";
70 writeInt(i, 4);
71 ResetPosToBegin();
72 notes << "(" << Data << ") ";
73 int i2 = readInt(4);
74 notes << "(" << itoa(i2) << ") ";
75 if (i2 != i)
76 notes << "NOT SAME!";
77 notes <<endl;
78 Clear();
79 }
80
81 {
82 // Integer
83 Sint16 i = -126;
84 notes << "Int: (" << i << ") ";
85 writeInt(i, 2);
86 ResetPosToBegin();
87 notes << "(" << Data << ") ";
88 Sint16 i2 = readInt(2);
89 notes << "(" << itoa(i2) << ") ";
90 if (i2 != i)
91 notes << "NOT SAME!";
92 notes <<endl;
93 Clear();
94 }
95
96 // Short
97 short s = 125;
98 notes << "Short: (" << s << ") ";
99 writeInt16(s);
100 ResetPosToBegin();
101 notes << "(" << Data << ") ";
102 short s2 = readInt16();
103 notes << "(" << s2 << ") ";
104 if (s2 != s)
105 notes << "NOT SAME!";
106 notes <<endl;
107 Clear();
108
109 // Float
110 float f = 125.125f;
111 notes << "Float: (" << f << ") ";
112 writeFloat(f);
113 ResetPosToBegin();
114 notes << "(" << Data << ") ";
115 float f2 = readFloat();
116 notes << "(" << f2 << ") ";
117 if (f2 != f)
118 notes << "NOT SAME!";
119 notes <<endl;
120 Clear();
121
122 // String
123 std::string str = "Test";
124 notes << "String: (" << str << ") ";
125 writeString(str);
126 ResetPosToBegin();
127 notes << "(" << Data << ") ";
128 std::string str2 = readString();
129 notes << "(" << str2 << ") ";
130 if (str2 != str)
131 notes << "NOT SAME!";
132 notes <<endl;
133 Clear();
134
135 // 2Int12
136 short x = 125;
137 short y = 521;
138 notes << "2Int12: (" << x << "/" << y << ") ";
139 write2Int12(x, y);
140 ResetPosToBegin();
141 notes << "(" << Data << ") ";
142 short x2, y2;
143 read2Int12(x2, y2);
144 notes << "(" << x2 << "/" << y2 << ") ";
145 if (x2 != x || y2 != y)
146 notes << "NOT SAME!";
147 notes <<endl;
148 Clear();
149
150 // 2Int4
151 short u = 10;
152 short v = 12;
153 notes << "2Int4: (" << u << "/" << v << ") ";
154 write2Int4(u, v);
155 ResetPosToBegin();
156 notes << "(" << Data << ") ";
157 short u2, v2;
158 read2Int4(u2, v2);
159 notes << "(" << u2 << "/" << v2 << ") ";
160 if (u2 != u || v2 != v)
161 notes << "NOT SAME!";
162 notes <<endl;
163 Clear();
164
165 // Bits
166 writeBit(1);
167 writeBit(1);
168 writeBit(0);
169 writeBit(1);
170 writeBit(1);
171 writeBit(1);
172 writeBit(0);
173 writeBit(0);
174 writeBit(1);
175 notes << "Data.size() = " << Data.size() << " ";
176 notes << "Bits: (" << (unsigned)Data[0] << ", " << (unsigned)Data[1] << ") ";
177 ResetPosToBegin();
178 if(
179 readBit() != 1 ||
180 readBit() != 1 ||
181 readBit() != 0 ||
182 readBit() != 1 ||
183 readBit() != 1 ||
184 readBit() != 1 ||
185 readBit() != 0 ||
186 readBit() != 0 ||
187 readBit() != 1
188 )
189 notes << "NOT SAME!";
190 notes <<endl;
191 Clear();
192
193 std::string str1( "Lala\0_1\0!2345", 12 );
194 notes << "Data: " << str1.size() << " ";
195 writeData(str1);
196 //ResetPosToBegin();
197 readByte();
198 if( readData(1) != "a" )
199 notes << "NOT SAME!";
200 str2 = readData();
201 if( str2.size() <= 4 || str2[5] != 0 )
202 notes << "\"" << str2 << "\" size " << str2.size() << " NOT SAME!";
203 notes <<endl;
204 Clear();
205
206 // Bit iterator
207 char bitData[10];
208 bitData[0] = 0;
209 bitData[1] = 0;
210 CBytestreamBitIterator bitIter(bitData);
211 bitIter.setBit(); ++bitIter;
212 bitIter.setBit(); ++bitIter;
213 ++bitIter;
214 bitIter.setBit(); ++bitIter;
215 bitIter.setBit(); ++bitIter;
216 bitIter.setBit(); ++bitIter;
217 ++bitIter;
218 ++bitIter;
219 bitIter.setBit(); ++bitIter;
220 bitIter.resetPos();
221 notes << "Bits iterator: (" << (unsigned)bitData[0] << ", " << (unsigned)bitData[1] << ") ";
222 if(
223 bitIter.readBit() != 1 ||
224 bitIter.readBit() != 1 ||
225 bitIter.readBit() != 0 ||
226 bitIter.readBit() != 1 ||
227 bitIter.readBit() != 1 ||
228 bitIter.readBit() != 1 ||
229 bitIter.readBit() != 0 ||
230 bitIter.readBit() != 0 ||
231 bitIter.readBit() != 1
232 )
233 notes << "NOT SAME!";
234 notes <<endl;
235
236 }
237
Clear()238 void CBytestream::Clear() {
239 Data = "";
240 pos = 0;
241 bitPos = 0;
242 }
243
244
245 ///////////////////
246 // Append another bytestream onto this one
Append(CBytestream * bs)247 void CBytestream::Append(CBytestream *bs) {
248 Data += bs->Data;
249 }
250
251
252 ///////////////////
253 // Dump the data out
Dump(const PrintOutFct & printer,const std::set<size_t> & marks,size_t start,size_t count)254 void CBytestream::Dump(const PrintOutFct& printer, const std::set<size_t>& marks, size_t start, size_t count) {
255 Iterator<char>::Ref it = GetConstIterator(Data);
256 if(start > 0) it->nextn(start);
257 HexDump(it, printer, marks, count);
258 }
259
Dump()260 void CBytestream::Dump() {
261 Dump(PrintOnLogger(notes), Set(pos));
262 }
263
264
265 // Writes
266
267
268 ///////////////////
269 // Writes a single byte
writeByte(uchar byte)270 bool CBytestream::writeByte(uchar byte)
271 {
272 Data += byte;
273 return true;
274 }
275
276
277 ///////////////////
278 // Writes a boolean value to the stream
writeBool(bool value)279 bool CBytestream::writeBool(bool value)
280 {
281 return writeByte((uchar)value);
282 }
283
284
285 ///////////////////
286 // Writes an integer to the stream
writeInt(int value,uchar numbytes)287 bool CBytestream::writeInt(int value, uchar numbytes)
288 {
289 // Numbytes cannot be more then 4
290 assert(numbytes > 0 && numbytes < 5);
291
292 // HINT: we send always in little endian
293 Uint32 val = (Uint32)value;
294 EndianSwap(val);
295
296 for(short n = 0; n < numbytes; n++)
297 writeByte( ((uchar *)&val)[n] );
298
299 return true;
300 }
301
writeUInt64(Uint64 val)302 bool CBytestream::writeUInt64(Uint64 val) {
303 EndianSwap(val);
304
305 for(short n = 0; n < 8; n++)
306 writeByte( ((uchar *)&val)[n] );
307
308 return true;
309 }
310
311 ///////////////////
312 // Write a short to the stream
writeInt16(Sint16 value)313 bool CBytestream::writeInt16(Sint16 value)
314 {
315 // HINT: this time, the value is stored in big endian
316 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
317 ByteSwap5(value);
318 #endif
319
320 writeByte( ((uchar *)&value)[0]);
321 writeByte( ((uchar *)&value)[1]);
322
323 return true;
324 }
325
326
327 ///////////////////
328 // Writes a float to the stream
writeFloat(float value)329 bool CBytestream::writeFloat(float value)
330 {
331 union {
332 uchar bin[4];
333 float val;
334 } tmp;
335 tmp.val = value;
336
337 // HINT: original LX uses little endian floats over network
338 EndianSwap(tmp.bin);
339
340 for(short i = 0; i < 4; i++)
341 writeByte(tmp.bin[i]);
342
343 return true;
344 }
345
346
writeString(const std::string & value)347 bool CBytestream::writeString(const std::string& value) {
348 Data += value.c_str(); // convert it to a C-string because we don't want null-bytes in it
349 Data += (char)'\0';
350
351 return true;
352 }
353
354 // cast 2 int12 to 3 bytes
write2Int12(short x,short y)355 bool CBytestream::write2Int12(short x, short y) {
356 writeByte((ushort)x & 0xff);
357 writeByte((((ushort)x & 0xf00) >> 8) + (((ushort)y & 0xf) << 4));
358 writeByte(((ushort)y & 0xff0) >> 4);
359 return true;
360 }
361
362 // cast 2 int4 to 1 byte
write2Int4(short x,short y)363 bool CBytestream::write2Int4(short x, short y) {
364 return writeByte(((ushort)x & 0xf) + (((ushort)y & 0xf) << 4));
365 }
366
367
writeBit(bool bit)368 bool CBytestream::writeBit(bool bit)
369 {
370 if( bitPos == 0 )
371 writeByte( 0 );
372 uchar byte = Data[ Data.size() - 1 ];
373 byte = byte | ( ( bit ? 1 : 0 ) << bitPos );
374 Data[ Data.size() - 1 ] = byte;
375 bitPos ++; bitPos %= 8;
376 return true;
377 }
378
writeData(const std::string & value)379 bool CBytestream::writeData(const std::string& value)
380 {
381 Data.append( value );
382 return true;
383 }
384
writeVar(const ScriptVar_t & var)385 bool CBytestream::writeVar(const ScriptVar_t& var) {
386 assert( var.type >= 0 && var.type <= 4 );
387 if(!writeByte( var.type )) return false;
388 switch( var.type ) {
389 case SVT_BOOL: return writeBool(var.b);
390 case SVT_INT: return writeInt(var.i, 4);
391 case SVT_FLOAT: return writeFloat(var.f);
392 case SVT_STRING: return writeString(var.s);
393 case SVT_COLOR: {
394 writeByte(var.c.r);
395 writeByte(var.c.g);
396 writeByte(var.c.b);
397 writeByte(var.c.a);
398 return true;
399 }
400 default: assert(false); return false;
401 }
402 }
403
404
405
406 // Reads
407
408
409
410
411 ///////////////////
412 // Reads a single byte
readByte()413 uchar CBytestream::readByte() {
414 if(!isPosAtEnd())
415 return Data[pos++];
416 else {
417 #ifndef FUZZY_ERROR_TESTING
418 errors <<"reading from stream behind end" << endl;
419 #endif
420 return 0;
421 }
422 }
423
424
425 ///////////////////
426 // Reads a boolean value from the stream
readBool()427 bool CBytestream::readBool()
428 {
429 return readByte() != 0;
430 }
431
432
433 ///////////////////
434 // Reads an interger value from the stream
readInt(uchar numbytes)435 int CBytestream::readInt(uchar numbytes)
436 {
437 // Numbytes cannot be more than 4
438 if(numbytes <= 0 || numbytes >= 5)
439 return 0;
440
441 Uint32 ret = 0;
442 for(short n=0; n<numbytes; n++)
443 ret += (Uint32)readByte() << (n * 8);
444
445 return (unsigned)ret;
446 }
447
readUInt64()448 Uint64 CBytestream::readUInt64()
449 {
450
451 Uint64 ret = 0;
452 for(short n=0; n<8; n++)
453 ret += (Uint64)readByte() << (n * 8);
454
455 return ret;
456 }
457
458
459
460
461 ///////////////////
462 // Read a short from the stream
readInt16()463 Sint16 CBytestream::readInt16()
464 {
465 // HINT: this time, the value is stored in big endian
466 uchar dat[2];
467 dat[1] = readByte();
468 dat[0] = readByte();
469
470 Uint16 value;
471 value = (Uint16)dat[0];
472 value += (Uint16)dat[1] << 8;
473
474 return (Sint16)value;
475 }
476
477
478
479 ///////////////////
480 // Read a float value from the stream
readFloat()481 float CBytestream::readFloat()
482 {
483 union {
484 uchar bin[4];
485 float val;
486 } tmp;
487
488 tmp.val = 0;
489
490 for(short i = 0; i < 4; i++)
491 tmp.bin[i] = readByte();
492
493 // HINT: original LX uses little endian floats over network
494 EndianSwap(tmp.bin);
495
496 return tmp.val;
497 }
498
499
readString()500 std::string CBytestream::readString() {
501 std::string result = "";
502 uchar b;
503 while((b = readByte()) != 0) result += (char)b;
504 return result;
505 }
506
readString(size_t maxlen)507 std::string CBytestream::readString(size_t maxlen) {
508 std::string result = "";
509 size_t i = 0;
510 uchar b = 0;
511 while(i < maxlen && (b = readByte()) != 0) {
512 result += b;
513 ++i;
514 }
515 if(b != 0)
516 warnings("WARNING: CBytestream: stop reading string at no real ending\n");
517 return result;
518 }
519
520 ////////////////////
521 // cast 3 bytes to 2 int12
read2Int12(short & x,short & y)522 void CBytestream::read2Int12(short& x, short& y) {
523 ushort dat[3];
524 dat[0] = readByte();
525 dat[1] = readByte();
526 dat[2] = readByte();
527
528 x = dat[0] + ((dat[1] & 0xf) << 8);
529 y = (short)(((dat[1] & 0xf0) >> 4) + (dat[2] << 4));
530 }
531
532 ///////////////////
533 // cast 1 byte to 2 int4
read2Int4(short & x,short & y)534 void CBytestream::read2Int4(short& x, short& y) {
535 uchar tmp = readByte();
536 x = tmp & 0xf;
537 y = (short)((tmp & 0xf0) >> 4);
538 }
539
540
541 ////////////////////
542 // Read one bit from the bytestream
readBit()543 bool CBytestream::readBit()
544 {
545 if( isPosAtEnd() )
546 {
547 errors << "reading from stream behind end" << endl;
548 return false;
549 }
550 bool ret = (Data[pos] & ( 1 << bitPos )) != 0;
551 bitPos ++;
552 if( bitPos >= 8 )
553 {
554 bitPos = 0;
555 pos ++;
556 }
557 return ret;
558 }
559
560 /////////////////////
561 // Get data from the bytestream
readData(size_t size)562 std::string CBytestream::readData( size_t size )
563 {
564 size = MIN( size, GetLength() - pos );
565 size_t oldpos = pos;
566 pos += size;
567 return Data.substr( oldpos, size );
568 }
569
readVar(ScriptVar_t & var)570 bool CBytestream::readVar(ScriptVar_t& var) {
571 assert( var.type >= 0 && var.type <= 4 );
572 var.type = (ScriptVarType_t)readByte();
573 switch( var.type ) {
574 case SVT_BOOL: var.b = readBool(); break;
575 case SVT_INT: var.i = readInt(4); break;
576 case SVT_FLOAT: var.f = readFloat(); break;
577 case SVT_STRING: var.s = readString(); break;
578 case SVT_COLOR: var.c.r = readInt(1); var.c.g = readInt(1); var.c.b = readInt(1); var.c.a = readInt(1); break;
579 default:
580 warnings << "read var has invalid type" << endl;
581 var = ScriptVar_t();
582 }
583 return true;
584 }
585
586
587
588 /////////////////////
589 // Read a byte but don't change the position
peekByte() const590 uchar CBytestream::peekByte() const
591 {
592 if (!isPosAtEnd())
593 return Data[GetPos()];
594 errors << "CBytestream::peekByte(): reading from stream beyond end" << endl;
595 return 0;
596 }
597
598 ///////////////////////
599 // Peek data from the bytestream
peekData(size_t len) const600 std::string CBytestream::peekData(size_t len) const
601 {
602 if (GetPos() + len <= GetLength())
603 return Data.substr(GetPos(), len);
604 return "";
605 }
606
607
608 // Skips a string, including the terminating character
609 // Returns true if we're at the end of the stream after the skip
SkipString()610 bool CBytestream::SkipString() {
611 readString();
612 return isPosAtEnd();
613 }
614
Skip(size_t num)615 bool CBytestream::Skip(size_t num) {
616 pos += num;
617 return isPosAtEnd();
618 }
619
620 ////////////////
621 // Read from network
622 // WARNING: overrides any previous data
Read(NetworkSocket * sock)623 size_t CBytestream::Read(NetworkSocket* sock) {
624 Clear();
625 char buf[4096];
626 size_t len = 0;
627 int res; // MUST be signed, else an overflow can occur (ReadScoket can return -1!)
628 while(true) {
629 res = sock->Read(buf, sizeof(buf));
630 if(res <= 0) break;
631 Data.append(buf, res);
632 len += res;
633 if((size_t)res < sizeof(buf)) break;
634 }
635
636 #ifdef DEBUG
637 // DEBUG: randomly drop packets to test network stability
638 /* if (GetRandomInt(128) > 110) {
639 warnings("DEBUG: packet ignored\n");
640 Dump();
641 warnings("\n");
642 Clear();
643 return 0;
644 }*/
645 #endif
646
647 return len;
648 }
649
Send(NetworkSocket * sock)650 bool CBytestream::Send(NetworkSocket* sock) {
651 return (size_t)sock->Write(Data) == Data.size();
652 }
653
654