/* Copyright (C) 2019 AleaJactaEst This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Build : scons platform=linux bits=64 */ // Temporary workaround until c++11 alignas() #ifdef __GNUC__ #define GCC_ALIGNED_8 __attribute__((aligned(8))) #else #define GCC_ALIGNED_8 #endif #include "modules/bitstream/bitstream.h" #include "modules/debug/debug.h" #include "modules/bitstream/convert_utf16.h" // TODO - check if on godot we have a function or variable to get if processor is little_endian or not bool little_endian = false; void BitStream::_bind_methods() { BitStream::init(); ClassDB::bind_method(D_METHOD("is_little_endian"), &BitStream::is_little_endian); ClassDB::bind_method(D_METHOD("complet_byte"), &BitStream::complet_byte); ClassDB::bind_method(D_METHOD("size"), &BitStream::size); ClassDB::bind_method(D_METHOD("size_data"), &BitStream::size_data); ClassDB::bind_method(D_METHOD("number_bit_not_read"), &BitStream::number_bit_not_read); ClassDB::bind_method(D_METHOD("put_serial", "value", "nbits"), &BitStream::put_serial); ClassDB::bind_method(D_METHOD("put_bool", "value"), &BitStream::put_bool); ClassDB::bind_method(D_METHOD("put_char", "value"), &BitStream::put_char); ClassDB::bind_method(D_METHOD("put_uint8", "value"), &BitStream::put_uint8); ClassDB::bind_method(D_METHOD("put_sint8", "value"), &BitStream::put_sint8); ClassDB::bind_method(D_METHOD("put_sint16", "value"), &BitStream::put_sint16); ClassDB::bind_method(D_METHOD("put_uint16", "value"), &BitStream::put_uint16); ClassDB::bind_method(D_METHOD("put_sint32", "value"), &BitStream::put_sint32); ClassDB::bind_method(D_METHOD("put_uint32", "value"), &BitStream::put_uint32); ClassDB::bind_method(D_METHOD("put_sint64", "value"), &BitStream::put_sint64); ClassDB::bind_method(D_METHOD("put_uint64", "value"), &BitStream::put_uint64); ClassDB::bind_method(D_METHOD("put_string", "value"), &BitStream::put_string); ClassDB::bind_method(D_METHOD("put_string_hexa32", "hexa"), &BitStream::put_string_hexa32); ClassDB::bind_method(D_METHOD("put_array_uint8", "value"), &BitStream::put_array_uint8); ClassDB::bind_method(D_METHOD("put_version", "value"), &BitStream::put_version); ClassDB::bind_method(D_METHOD("put_string_utf16", "value"), &BitStream::put_string_utf16); ClassDB::bind_method(D_METHOD("show"), &BitStream::show); ClassDB::bind_method(D_METHOD("show_detail"), &BitStream::show_detail); ClassDB::bind_method(D_METHOD("show_counter"), &BitStream::show_counter); ClassDB::bind_method(D_METHOD("get_data"), &BitStream::get_data); ClassDB::bind_method(D_METHOD("put_data", "value"), &BitStream::put_data); ClassDB::bind_method(D_METHOD("get_serial", "nbits"), &BitStream::get_serial); ClassDB::bind_method(D_METHOD("get_bool"), &BitStream::get_bool); ClassDB::bind_method(D_METHOD("get_sint8"), &BitStream::get_sint8); ClassDB::bind_method(D_METHOD("get_uint8"), &BitStream::get_uint8); ClassDB::bind_method(D_METHOD("get_sint16"), &BitStream::get_sint16); ClassDB::bind_method(D_METHOD("get_uint16"), &BitStream::get_uint16); ClassDB::bind_method(D_METHOD("get_sint32"), &BitStream::get_sint32); ClassDB::bind_method(D_METHOD("get_uint32"), &BitStream::get_uint32); ClassDB::bind_method(D_METHOD("get_sint64"), &BitStream::get_sint64); ClassDB::bind_method(D_METHOD("get_uint64"), &BitStream::get_uint64); ClassDB::bind_method(D_METHOD("get_version"), &BitStream::get_version); ClassDB::bind_method(D_METHOD("get_array_uint8", "length"), &BitStream::get_array_uint8); //ClassDB::bind_method(D_METHOD("get_bitstream", "length"), &BitStream::get_bitstream); ClassDB::bind_method(D_METHOD("get_string"), &BitStream::get_string); ClassDB::bind_method(D_METHOD("get_string_utf16"), &BitStream::get_string_utf16); ClassDB::bind_method(D_METHOD("get_string_utf8"), &BitStream::get_string_utf8); } void BitStream::clear() { this->_pos = 0; this->_read = 0; } BitStream::BitStream() { clear(); } BitStream::~BitStream() { // add your cleanup here } void BitStream::init() { uint32_t i=0x12345678; if ( (*((uint8_t*)(&i))) == 0x78 ) little_endian = true; else little_endian = false; } bool BitStream::is_little_endian() { return little_endian; } void BitStream::complet_byte() { // function to add 0 and have size multiple of 8 bit this->_pos = ((this->_pos + 7) / 8) * 8; } uint32_t BitStream::size() { return (this->_pos + 7) / 8; } uint32_t BitStream::size_data() { return this->_pos; } uint32_t BitStream::number_bit_not_read() { return this->_pos - this->_read; } void BitStream::put_serial(uint32_t value, uint32_t nbits) { uint32_t v; uint32_t mask; uint32_t freeBits; uint32_t pos; if ( nbits == 0 ) return; else if ( nbits > 32 ) throw "Out of range"; if ( this->_pos % 8 == 0 ) // Increase node this->_data.append(0); if ( nbits != 32 ) { mask = (1 << nbits) - 1; v = value & mask; } else { v = value; } freeBits = 8 - (this->_pos % 8); pos = this->_data.size() - 1; if(nbits > freeBits) { this->_data.set(pos, this->_data[pos] | (v >> (nbits - freeBits))); this->_pos += freeBits; this->put_serial(v, nbits - freeBits); } else { this->_data.set(pos, this->_data[pos] | (v << (freeBits - nbits))); this->_pos += nbits; } } void BitStream::put_bool(bool value) { this->put_serial(value, 1); } void BitStream::put_sint8(int8_t value) { this->put_serial(value, 8); } void BitStream::put_uint8(uint8_t value) { this->put_serial(value, 8); } void BitStream::put_sint16(int16_t value) { this->put_serial(value, 16); } void BitStream::put_uint16(uint16_t value) { this->put_serial(value, 16); } void BitStream::put_sint32(int32_t value) { this->put_serial(value, 32); } void BitStream::put_uint32(uint32_t value) { this->put_serial(value, 32); } void BitStream::put_uint64(uint64_t value) { // khanat-opennel-code/code/nel/include/nel/misc/stream_inline.h inline void IStream::serial(uint64 &b) // khanat-opennel-code/code/nel/include/nel/misc/stream.h #define NLMISC_BSWAP64(src) (src) = (((src)>>56)&0xFF) | ((((src)>>48)&0xFF)<<8) | ((((src)>>40)&0xFF)<<16) | ((((src)>>32)&0xFF)<<24) | ((((src)>>24)&0xFF)<<32) | ((((src)>>16)&0xFF)<<40) | ((((src)>>8)&0xFF)<<48) | (((src)&0xFF)<<56) union { uint64_t full; uint8_t key[8]; } v GCC_ALIGNED_8; v.full = value; if(little_endian) { this->put_uint8(v.key[3]); this->put_uint8(v.key[2]); this->put_uint8(v.key[1]); this->put_uint8(v.key[0]); this->put_uint8(v.key[7]); this->put_uint8(v.key[6]); this->put_uint8(v.key[5]); this->put_uint8(v.key[4]); } else { this->put_uint8(v.key[0]); this->put_uint8(v.key[1]); this->put_uint8(v.key[2]); this->put_uint8(v.key[3]); this->put_uint8(v.key[4]); this->put_uint8(v.key[5]); this->put_uint8(v.key[6]); this->put_uint8(v.key[7]); } } void BitStream::put_sint64(int64_t value) { put_uint64((uint64_t) value); } void BitStream::put_string_hexa32(String hexa) { uint32_t res = 0; CharString tmp = hexa.ascii(); for(const char * c = tmp.get_data(); *c != 0; c++) { res <<= 4; if ( *c >= '0' && *c <= '9') res += *c - '0'; else if ( *c >= 'A' && *c <= 'F') res += *c - 'A' + 10; } this->put_serial(res,32); } void BitStream::put_char(String value) { if ( value.length() >= 1) this->put_uint8( value[0] ); else this->put_uint8( 0 ); } void BitStream::put_string(String value) { CharString data = value.ascii(); int lenvalue = data.size() - 1; this->put_uint32(lenvalue); for(int i = 0; i < lenvalue; ++i) { this->put_uint8(data[i]); } } void BitStream::put_array_uint8(PoolByteArray value) { for(int i = 0; i < value.size() ; ++i) this->put_serial(value[i], 8); } void BitStream::put_bitstream(BitStream & value) { uint32_t last_read; uint32_t nb; last_read = value._read; value._read = 0; while ( value.number_bit_not_read() >= 32 ) put_uint32(value.get_uint32()); while ( value.number_bit_not_read() >= 16 ) put_uint16(value.get_uint16()); while ( value.number_bit_not_read() >= 8 ) put_uint8(value.get_uint8()); nb = value.number_bit_not_read(); if( nb > 0 ) put_serial(value.get_serial(nb), nb); // restore pos read value._read = last_read; } void BitStream::put_bitstream(BitStream * value) { uint32_t last_read; uint32_t nb; last_read = value->_read; value->_read = 0; while ( value->number_bit_not_read() >= 32 ) put_uint32(value->get_uint32()); while ( value->number_bit_not_read() >= 16 ) put_uint16(value->get_uint16()); while ( value->number_bit_not_read() >= 8 ) put_uint8(value->get_uint8()); nb = value->number_bit_not_read(); if( nb > 0 ) put_serial(value->get_serial(nb), nb); DBG_PRINT("put_bitstream: " + itos(nb) + " / " + itos(_pos)); // restore pos read value->_read = last_read; } void BitStream::put_version(uint32_t value) { if ( value < 255 ) // 0xff { this->put_uint8(value); } else { this->put_uint8(255); this->put_uint32(value); } } void BitStream::put_float(float value) { union { float f; uint32_t u; } data GCC_ALIGNED_8; data.f = value; put_uint32(data.u); } void BitStream::put_string_utf16(String value) { uint16_t * u16_buf = NULL; size_t u16_len = 0; convert_utf8_to_utf16(value, &u16_buf, &u16_len); this->put_uint32(u16_len); if ( u16_len == 0 ) return; for(uint32_t i=0;iput_uint16(u16_buf[i]); } delete [] u16_buf; } String BitStream::show() { String ret; uint32_t pos = 0; for(int i = 0; i < this->_data.size(); ++i) { uint8_t c = this->_data[i]; uint8_t mask = 0x80; while(mask) { if ( (c & mask) != 0 ) ret += String("1"); else ret += String("0"); mask >>= 1; pos += 1; if (pos >= this->_pos) break; } if (pos >= this->_pos) break; } return ret ; } String BitStream::show_detail() { String ret; uint32_t pos = 0; for(int i = 0; i < this->_data.size(); ++i) { uint8_t c = this->_data[i]; uint8_t mask = 0x80; while(mask) { if ( (c & mask) != 0 ) ret += String("1"); else ret += String("0"); mask >>= 1; pos += 1; if (pos >= this->_pos) break; } if (pos >= this->_pos) break; } String strsize; uint32_t t = this->_data.size(); if ( t == 0) strsize = "0"; while ( t > 0 ) { const char c = '0' + ( t % 10 ); CharString tmp ; tmp += c; strsize = tmp + strsize; t /= 10; } String strpos; t = this->_pos; if ( t == 0) strpos = "0"; while ( t > 0 ) { const char c = '0' + ( t % 10 ); CharString tmp ; tmp += c; strpos = tmp + strpos; t /= 10; } return "[size:" + String(strsize) + ", pos:" + String(strpos) + "]" + ret ; } String BitStream::show_counter() { String ret = "[" + String::num_int64(this->_read) + " / " + String::num_int64(this->_pos) + "]"; return ret; } PoolByteArray BitStream::get_data() { return this->_data; } void BitStream::put_data(PoolByteArray value) { this->_data = value; this->_pos = value.size() * 8; } void BitStream::copy_ref_bitstream(Ref value) { this->_data = value->_data; //*value.ptr(); this->_pos = value->_pos; // ->size() * 8; this->_read = value->_read; } void BitStream::copy_bitstream(BitStream & value) { this->_data = value._data; //*value.ptr(); this->_pos = value._pos; // ->size() * 8; this->_read = value._read; } uint32_t BitStream::get_serial(uint32_t nbits) { uint32_t value; uint32_t pos; uint32_t freeBits; uint32_t v; if ( nbits == 0 ) return 0; else if ( nbits > 32 ) { ERR_PRINT("Out of range (BitStream::get_serial)"); throw "Out of range"; } if (this->_read + nbits > this->_pos) { ERR_PRINT("Out of range (BitStream::get_serial)"); throw "Out of range"; } value = 0; pos = this->_read / 8; freeBits = 8 - (this->_read % 8); v = this->_data[pos] & ((1 << freeBits) - 1); if(nbits > freeBits) { value |= (v << (nbits-freeBits)); this->_read += freeBits; value |= this->get_serial(nbits - freeBits); } else { value |= (v >> (freeBits-nbits)); this->_read += nbits; } return value; } bool BitStream::get_bool() { if(this->get_serial(1) == 0) return false; return true; } int8_t BitStream::get_sint8() { return this->get_serial(8); } uint8_t BitStream::get_uint8() { return this->get_serial(8); } int16_t BitStream::get_sint16() { return this->get_serial(16); } uint16_t BitStream::get_uint16() { return this->get_serial(16); } int32_t BitStream::get_sint32() { return this->get_serial(32); } uint32_t BitStream::get_uint32() { return this->get_serial(32); } uint64_t BitStream::get_uint64() { // khanat-opennel-code/code/nel/include/nel/misc/stream_inline.h inline void IStream::serial(uint64 &b) // khanat-opennel-code/code/nel/include/nel/misc/stream.h #define NLMISC_BSWAP64(src) (src) = (((src)>>56)&0xFF) | ((((src)>>48)&0xFF)<<8) | ((((src)>>40)&0xFF)<<16) | ((((src)>>32)&0xFF)<<24) | ((((src)>>24)&0xFF)<<32) | ((((src)>>16)&0xFF)<<40) | ((((src)>>8)&0xFF)<<48) | (((src)&0xFF)<<56) union S { uint64_t full; uint8_t key[8]; } v GCC_ALIGNED_8; if(little_endian) { v.key[3] = this->get_uint8(); v.key[2] = this->get_uint8(); v.key[1] = this->get_uint8(); v.key[0] = this->get_uint8(); v.key[7] = this->get_uint8(); v.key[6] = this->get_uint8(); v.key[5] = this->get_uint8(); v.key[4] = this->get_uint8(); } else { v.key[0] = this->get_uint8(); v.key[1] = this->get_uint8(); v.key[2] = this->get_uint8(); v.key[3] = this->get_uint8(); v.key[4] = this->get_uint8(); v.key[5] = this->get_uint8(); v.key[6] = this->get_uint8(); v.key[7] = this->get_uint8(); } return v.full; } int64_t BitStream::get_sint64() { return (int64_t) get_uint64(); } uint32_t BitStream::get_version() { uint32_t ret; ret = this->get_uint8(); if ( ret == 255 ) // 0xff { ret = this->get_uint32(); } return ret; } float BitStream::get_float() { union { float f; uint32_t u; } data GCC_ALIGNED_8; data.u = get_uint32(); return data.f; } PoolByteArray BitStream::get_array_uint8(uint32_t length) { PoolByteArray ret; while(length > 0) { ret.append(this->get_serial(8)); --length; } /* if (length == 1) { int nb = this->number_bit_not_read(); ret.append(this->get_serial(nb)); } */ return ret; } BitStream BitStream::get_bitstream(uint32_t length) { BitStream a; while(length != 0) { a.put_uint8(this->get_serial(8)); --length; } return a; } String BitStream::get_string() { String ret; uint32_t size = this->get_uint32(); while(size > 0) { union { uint8_t v; char c; } x GCC_ALIGNED_8; x.v = this->get_uint8(); CharString cs; cs += x.c; ret += cs; --size; } return ret; } String BitStream::get_string_utf16() { // https://en.wikipedia.org/wiki/UTF-16 // https://gist.github.com/rsms/716794 String ret; uint32_t size = this->get_uint32(); uint32_t i = 0; uint8_t tmp[size]; for(i=0;iget_uint16(); } ret = convert_utf16_to_utf8(tmp, i); return ret; } String BitStream::get_string_utf8() { // https://en.wikipedia.org/wiki/UTF-8 String ret; uint32_t i; uint32_t size = this->get_uint32(); char * src = new char[size+1]; src[size] = 0; for(i=0;iget_uint8(); src[i] = x.c; } ret = String::utf8(src); delete [] src; return ret; }