// ============================================================== // This file is part of MegaGlest (www.megaglest.org) // // Copyright (C) 2012 Mark Vejvoda // // You can redistribute this code and/or modify it under // the terms of the GNU General Public License as published // by the Free Software Foundation; either version 2 of the // License, or (at your option) any later version // ============================================================== #include "network_protocol.h" #include #include #include #include #include "data_types.h" #include "util.h" #include "leak_dumper.h" using namespace Shared::Platform; using namespace Shared::Util; namespace Glest { namespace Game { #pragma pack(push, 1) // macros for packing floats and doubles: #define pack754_16(f) (pack754((f), 16, 5)) #define pack754_32(f) (pack754((f), 32, 8)) #define pack754_64(f) (pack754((f), 64, 11)) #define unpack754_16(i) (unpack754((i), 16, 5)) #define unpack754_32(i) (unpack754((i), 32, 8)) #define unpack754_64(i) (unpack754((i), 64, 11)) /* ** pack754() -- pack a floating point number into IEEE-754 format */ unsigned long long int pack754(long double f, unsigned bits, unsigned expbits) { long double fnorm; int shift; long long sign, exp, significand; unsigned significandbits = bits - expbits - 1; // -1 for sign bit if (f == 0.0) return 0; // get this special case out of the way // check sign and begin normalization if (f < 0) { sign = 1; fnorm = -f; } else { sign = 0; fnorm = f; } // get the normalized form of f and track the exponent shift = 0; while (fnorm >= 2.0) { fnorm /= 2.0; shift++; } while (fnorm < 1.0) { fnorm *= 2.0; shift--; } fnorm = fnorm - 1.0; // calculate the binary form (non-float) of the significand data significand = fnorm * ((1LL << significandbits) + 0.5f); // get the biased exponent exp = (long long) shift + ((1 << (expbits - 1)) - 1); // shift + bias // return the final answer return (sign << (bits - 1)) | (exp << (bits - expbits - 1)) | significand; } /* ** unpack754() -- unpack a floating point number from IEEE-754 format */ long double unpack754(unsigned long long int i, unsigned bits, unsigned expbits) { long double result; long long shift; unsigned bias; unsigned significandbits = bits - expbits - 1; // -1 for sign bit if (i == 0) return 0.0; // pull the significand result = (i&((1LL << significandbits) - 1)); // mask result /= (1LL << significandbits); // convert back to float result += 1.0f; // add the one back on // deal with the exponent bias = (1 << (expbits - 1)) - 1; shift = ((i >> significandbits)&((1LL << expbits) - 1)) - bias; while (shift > 0) { result *= 2.0; shift--; } while (shift < 0) { result /= 2.0; shift++; } // sign it result *= ((i >> (bits - 1)) & 1) ? -1.0 : 1.0; return result; } /* ** packi16() -- store a 16-bit int into a char buffer (like htons()) */ void packi16(unsigned char *buf, uint16 i) { *buf++ = i >> 8; *buf++ = i; } /* ** packi32() -- store a 32-bit int into a char buffer (like htonl()) */ void packi32(unsigned char *buf, uint32 i) { *buf++ = i >> 24; *buf++ = i >> 16; *buf++ = i >> 8; *buf++ = i; } /* ** packi64() -- store a 64-bit int into a char buffer (like htonl()) */ void packi64(unsigned char *buf, uint64 i) { *buf++ = i >> 56; *buf++ = i >> 48; *buf++ = i >> 40; *buf++ = i >> 32; *buf++ = i >> 24; *buf++ = i >> 16; *buf++ = i >> 8; *buf++ = i; } /* ** unpacki16() -- unpack a 16-bit int from a char buffer (like ntohs()) */ int16 unpacki16(unsigned char *buf) { uint16 i2 = ((uint16) buf[0] << 8) | buf[1]; int16 i; // change unsigned numbers to signed if (i2 <= 0x7fffu) { i = i2; } else { i = -1 - (uint16) (0xffffu - i2); if (SystemFlags::VERBOSE_MODE_ENABLED) printf("IN [%s] [%d] [%d] [%d] [%u]\n", __FUNCTION__, buf[0], buf[1], i, i2); } return i; } /* ** unpacku16() -- unpack a 16-bit unsigned from a char buffer (like ntohs()) */ uint16 unpacku16(unsigned char *buf) { return ((uint16) buf[0] << 8) | buf[1]; } /* ** unpacki32() -- unpack a 32-bit int from a char buffer (like ntohl()) */ int32 unpacki32(unsigned char *buf) { uint32 i2 = ((uint32) buf[0] << 24) | ((uint32) buf[1] << 16) | ((uint32) buf[2] << 8) | buf[3]; int32 i; // change unsigned numbers to signed if (i2 <= 0x7fffffffu) { i = i2; } else { i = -1 - (int32) (0xffffffffu - i2); if (SystemFlags::VERBOSE_MODE_ENABLED) printf("IN [%s] [%d] [%d] [%d] [%d] [%d] [%u]\n", __FUNCTION__, buf[0], buf[1], buf[2], buf[3], i, i2); } return i; } /* ** unpacku32() -- unpack a 32-bit unsigned from a char buffer (like ntohl()) */ uint32 unpacku32(unsigned char *buf) { return ((uint32) buf[0] << 24) | ((uint32) buf[1] << 16) | ((uint32) buf[2] << 8) | buf[3]; } /* ** unpacki64() -- unpack a 64-bit int from a char buffer (like ntohl()) */ int64 unpacki64(unsigned char *buf) { uint64 i2 = ((uint64) buf[0] << 56) | ((uint64) buf[1] << 48) | ((uint64) buf[2] << 40) | ((uint64) buf[3] << 32) | ((uint64) buf[4] << 24) | ((uint64) buf[5] << 16) | ((uint64) buf[6] << 8) | buf[7]; int64 i; // change unsigned numbers to signed if (i2 <= 0x7fffffffffffffffull) { i = i2; } else { i = -1 - (int64) (0xffffffffffffffffull - i2); if (SystemFlags::VERBOSE_MODE_ENABLED) printf("IN [%s] [%d] [%d] [%d] [%d] [%d] [%d] [%d] [%d] [" MG_I64_SPECIFIER "] [" MG_I64U_SPECIFIER "]\n", __FUNCTION__, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], i, i2); } return i; } /* ** unpacku64() -- unpack a 64-bit unsigned from a char buffer (like ntohl()) */ uint64 unpacku64(unsigned char *buf) { return ((uint64) buf[0] << 56) | ((uint64) buf[1] << 48) | ((uint64) buf[2] << 40) | ((uint64) buf[3] << 32) | ((uint64) buf[4] << 24) | ((uint64) buf[5] << 16) | ((uint64) buf[6] << 8) | buf[7]; } /* ** pack() -- store data dictated by the format string in the buffer ** ** bits |signed unsigned float string ** -----+---------------------------------- ** 8 | c C ** 16 | h H f ** 32 | l L d ** 64 | q Q g ** - | s ** ** (16-bit unsigned length is automatically prepended to strings) */ unsigned int pack(unsigned char *buf, const char *format, ...) { va_list ap; int8 c; // 8-bit uint8 C; int16 h; // 16-bit uint16 H; int32 l; // 32-bit uint32 L; int64 q; // 64-bit uint64 Q; float f; // floats double d; long double g; unsigned long long int fhold; char *s; // strings uint16 len; unsigned int size = 0; uint16 maxstrlen = 0; unsigned char *bufStart = buf; va_start(ap, format); for (; *format != '\0'; format++) { switch (*format) { case 'c': // 8-bit size += 1; c = (int8) va_arg(ap, int); // promoted *buf++ = (unsigned char) c; if (SystemFlags::VERBOSE_MODE_ENABLED) printf("pack int8 = %d [%X] c = %d [%X] buf pos = %lu\n", *(buf - 1), *(buf - 1), c, c, (unsigned long) (buf - bufStart)); break; case 'C': // 8-bit unsigned size += 1; C = (uint8) va_arg(ap, unsigned int); // promoted *buf++ = (unsigned char) C; if (SystemFlags::VERBOSE_MODE_ENABLED) printf("pack uint8 = %u [%X] C = %u [%X] buf pos = %lu\n", *(buf - 1), *(buf - 1), C, C, (unsigned long) (buf - bufStart)); break; case 'h': // 16-bit size += 2; h = (int16) va_arg(ap, int); packi16(buf, h); buf += 2; if (SystemFlags::VERBOSE_MODE_ENABLED) printf("pack int16 = %d [%X] h = %d [%X] buf pos = %lu\n", *(buf - 2), *(buf - 2), h, h, (unsigned long) (buf - bufStart)); break; case 'H': // 16-bit unsigned size += 2; H = (uint16) va_arg(ap, unsigned int); packi16(buf, H); buf += 2; if (SystemFlags::VERBOSE_MODE_ENABLED) printf("pack uint16 = %u [%X] H = %u [%X] buf pos = %lu\n", *(buf - 2), *(buf - 2), H, H, (unsigned long) (buf - bufStart)); break; case 'l': // 32-bit size += 4; l = va_arg(ap, int32); packi32(buf, l); buf += 4; if (SystemFlags::VERBOSE_MODE_ENABLED) printf("pack int32 = %d [%X] l = %d [%X] buf pos = %lu\n", *(buf - 4), *(buf - 4), l, l, (unsigned long) (buf - bufStart)); break; case 'L': // 32-bit unsigned size += 4; L = va_arg(ap, uint32); packi32(buf, L); buf += 4; if (SystemFlags::VERBOSE_MODE_ENABLED) printf("pack uint32 = %u [%X] L = %u [%X] buf pos = %lu\n", *(buf - 4), *(buf - 4), L, L, (unsigned long) (buf - bufStart)); break; case 'q': // 64-bit size += 8; q = va_arg(ap, int64); packi64(buf, q); buf += 8; //if(SystemFlags::VERBOSE_MODE_ENABLED) printf("pack int64 = " MG_I64_SPECIFIER " [%X] q = " MG_I64_SPECIFIER " [%lX] buf pos = %lu\n",(int64)*(buf-8),*(buf-8),q,q,(unsigned long)(buf - bufStart)); if (SystemFlags::VERBOSE_MODE_ENABLED) printf("pack int64 = " MG_I64_SPECIFIER " [%X] q = " MG_I64_SPECIFIER " buf pos = %lu\n", (int64)*(buf - 8), *(buf - 8), q, (unsigned long) (buf - bufStart)); break; case 'Q': // 64-bit unsigned size += 8; Q = va_arg(ap, uint64); packi64(buf, Q); buf += 8; if (SystemFlags::VERBOSE_MODE_ENABLED) printf("pack uint64 = " MG_I64U_SPECIFIER " [%X] Q = " MG_I64U_SPECIFIER " buf pos = %lu\n", (uint64)*(buf - 8), *(buf - 8), Q, (unsigned long) (buf - bufStart)); break; case 'f': // float-16 size += 2; f = (float) va_arg(ap, double); // promoted fhold = pack754_16(f); // convert to IEEE 754 packi16(buf, fhold); buf += 2; break; case 'd': // float-32 size += 4; d = va_arg(ap, double); fhold = pack754_32(d); // convert to IEEE 754 packi32(buf, fhold); buf += 4; break; case 'g': // float-64 size += 8; g = va_arg(ap, long double); fhold = pack754_64(g); // convert to IEEE 754 packi64(buf, fhold); buf += 8; break; case 's': // string s = va_arg(ap, char*); len = (uint16) strlen(s); if (maxstrlen > 0 && len < maxstrlen) len = maxstrlen - 1; size += len + 2; packi16(buf, len); buf += 2; memcpy(buf, s, len); if (SystemFlags::VERBOSE_MODE_ENABLED) printf("pack string size = %d [%X] len = %d str [%s] buf pos = %lu\n", *(buf - 2), *(buf - 2), len, s, (unsigned long) (buf - bufStart)); buf += len; break; default: if (isdigit(*format)) { // track max str len maxstrlen = maxstrlen * 10 + (*format - '0'); } break; } if (!isdigit(*format)) maxstrlen = 0; } va_end(ap); return size; } /* ** unpack() -- unpack data dictated by the format string into the buffer ** ** bits |signed unsigned float string ** -----+---------------------------------- ** 8 | c C ** 16 | h H f ** 32 | l L d ** 64 | q Q g ** - | s ** ** (string is extracted based on its stored length, but 's' can be ** prepended with a max length) */ unsigned int unpack(unsigned char *buf, const char *format, ...) { va_list ap; int8 *c; // 8-bit uint8 *C; int16 *h; // 16-bit uint16 *H; int32 *l; // 32-bit uint32 *L; int64 *q; // 64-bit uint64 *Q; unsigned long long int fhold; char *s; uint16 len, maxstrlen = 0, count; unsigned int size = 0; unsigned char *bufStart = buf; va_start(ap, format); for (; *format != '\0'; format++) { switch (*format) { case 'c': // 8-bit c = va_arg(ap, int8*); // if (*buf <= 0x7f) { // *c = *buf++; // size += 1; // } // re-sign // else { // *c = -1 - (unsigned char)(0xffu - *buf); // } *c = (int8) *buf++; size += 1; if (SystemFlags::VERBOSE_MODE_ENABLED) printf("unpack int8 = %d [%X] c = %d [%X] buf pos = %lu\n", *(buf - 1), *(buf - 1), *c, *c, (unsigned long) (buf - bufStart)); break; case 'C': // 8-bit unsigned C = va_arg(ap, uint8*); *C = (uint8) *buf++; size += 1; if (SystemFlags::VERBOSE_MODE_ENABLED) printf("unpack uint8 = %u [%X] C = %u [%X] buf pos = %lu\n", *(buf - 1), *(buf - 1), *C, *C, (unsigned long) (buf - bufStart)); break; case 'h': // 16-bit h = va_arg(ap, int16*); *h = unpacki16(buf); buf += 2; size += 2; if (SystemFlags::VERBOSE_MODE_ENABLED) printf("unpack int16 = %d [%X] h = %d [%X] buf pos = %lu\n", *(buf - 2), *(buf - 2), *h, *h, (unsigned long) (buf - bufStart)); break; case 'H': // 16-bit unsigned H = va_arg(ap, uint16*); *H = unpacku16(buf); buf += 2; size += 2; if (SystemFlags::VERBOSE_MODE_ENABLED) printf("unpack uint16 = %u [%X] H = %u [%X] buf pos = %lu\n", *(buf - 2), *(buf - 2), *H, *H, (unsigned long) (buf - bufStart)); break; case 'l': // 32-bit l = va_arg(ap, int32*); *l = unpacki32(buf); buf += 4; size += 4; if (SystemFlags::VERBOSE_MODE_ENABLED) printf("unpack int32 = %d [%X] l = %d [%X] buf pos = %lu\n", *(buf - 4), *(buf - 4), *l, *l, (unsigned long) (buf - bufStart)); break; case 'L': // 32-bit unsigned L = va_arg(ap, uint32*); *L = unpacku32(buf); buf += 4; size += 4; if (SystemFlags::VERBOSE_MODE_ENABLED) printf("unpack uint32 = %u [%X] L = %u [%X] buf pos = %lu\n", *(buf - 4), *(buf - 4), *L, *L, (unsigned long) (buf - bufStart)); break; case 'q': // 64-bit q = va_arg(ap, int64*); *q = unpacki64(buf); buf += 8; size += 8; if (SystemFlags::VERBOSE_MODE_ENABLED) printf("unpack int64 = " MG_I64_SPECIFIER " [%X] q = " MG_I64_SPECIFIER " buf pos = %lu\n", (int64)*(buf - 8), *(buf - 8), *q, (unsigned long) (buf - bufStart)); break; case 'Q': // 64-bit unsigned Q = va_arg(ap, uint64*); *Q = unpacku64(buf); buf += 8; size += 8; if (SystemFlags::VERBOSE_MODE_ENABLED) printf("unpack uint64 = " MG_I64U_SPECIFIER " [%X] Q = " MG_I64U_SPECIFIER " buf pos = %lu\n", (uint64)*(buf - 8), *(buf - 8), *Q, (unsigned long) (buf - bufStart)); break; case 'f': // float { float *f = va_arg(ap, float*); fhold = unpacku16(buf); *f = unpack754_16(fhold); buf += 2; size += 2; } break; case 'd': // float-32 { double *d = va_arg(ap, double*); fhold = unpacku32(buf); *d = unpack754_32(fhold); buf += 4; size += 4; } break; case 'g': // float-64 { long double *g = va_arg(ap, long double*); fhold = unpacku64(buf); *g = unpack754_64(fhold); buf += 8; size += 8; } break; case 's': // string s = va_arg(ap, char*); len = unpacku16(buf); buf += 2; if (maxstrlen > 0 && len > maxstrlen) count = maxstrlen - 1; else count = len; memcpy(s, buf, count); s[count] = '\0'; if (SystemFlags::VERBOSE_MODE_ENABLED) printf("unpack string size = %d [%X] count = %d len = %d str [%s] buf pos = %lu\n", *(buf - 2), *(buf - 2), count, len, s, (unsigned long) (buf - bufStart)); buf += len; size += len; break; default: if (isdigit(*format)) { // track max str len maxstrlen = maxstrlen * 10 + (*format - '0'); } break; } if (!isdigit(*format)) maxstrlen = 0; } va_end(ap); return size; } #pragma pack(pop) } }