--[[ | This file was obtained through the combined efforts | of Madbluntz & Plymouth Antiquarian Society. | | Credits: lifestorm, Gregory Wayne Rossel JR., | Maloy, DrPepper10 @ RIP, Atle! | | Visit for more: https://plymouth.thetwilightzone.ru/ --]] -- Copyright © 2022-2072, Nak, https://steamcommunity.com/id/Nak2/ -- All Rights Reserved. Not allowed to be reuploaded. -- License: https://github.com/Nak2/NikNaks/blob/main/LICENSE local NikNaks = NikNaks local s_char, s_byte, tostring = string.char, string.byte, tostring local band, brshift, blshift, bor, bswap = bit.band, bit.rshift, bit.lshift, bit.bor, bit.bswap local log, ldexp, frexp, floor, ceil, max, setmetatable, source = math.log, math.ldexp, math.frexp, math.floor, math.ceil, math.max, setmetatable, jit.util.funcinfo( NikNaks.AutoInclude )["source"] --- @class BitBufferModule --- @operator call(string|table): BitBuffer NikNaks.BitBuffer = {} --- @class BitBuffer --- @field _data table --- @field _tell number --- @field _len number --- @field _little_endian boolean local meta = {} meta.__index = meta function meta:__tostring() return "BitBuffer [" .. self:Size() .. "]" end --- Fixes bit-shift errors --- @param int number --- @param shift number --- @return number local function rshift( int, shift ) if shift > 31 then return 0x0 end return brshift( int, shift ) end --- @param int number --- @param shift number --- @return number local function lshift( int, shift ) if shift > 31 then return 0x0 end return blshift( int, shift ) end -- "Crams" the data into the bitbuffer. Ignoring offsets local function unsaferawdata( self, str ) if #str <= 0 then return end local len = #str local p = #self._data local whole = lshift(rshift(len - 4,2), 2) -- Bytes for i = 1, whole, 4 do local a, b, c, d = s_byte( str, i, i + 3) p = p + 1 self._data[ p ] = bor( lshift( a, 24 ), lshift( b, 16 ), lshift( c, 8 ), d ) end self._tell = lshift( p, 5 ) for i = whole + 1, len do meta.WriteByte( self, s_byte( str, i ) ) end self._len = max( self._len, lshift(len, 3) ) end --- @param little_endian? boolean --- @return BitBuffer local function create( data, little_endian ) --- @type BitBuffer local t = {} t._data = {} t._tell = 0 t._len = 0 t._little_endian = false setmetatable( t, meta ) if not data then return t end local mt = getmetatable( data ) if type(data) == "string" then unsaferawdata( t, data ) t._tell = 0 -- Reset tell elseif not mt then local q = #data for i = 1, q do t._data[i] = data[i] end t._len = q * 32 end if little_endian == nil then t._little_endian = true else t._little_endian = little_endian end return t end NikNaks.BitBuffer.Create = create setmetatable( NikNaks.BitBuffer, { --- Creates a new BitBuffer --- @param data string|table --- @param little_endian boolean? --- @return BitBuffer __call = function( _, data, little_endian ) return create( data, little_endian ) end } ) -- Simple int->string and reverse. ( Little-Endian ) do --- Takes a string of 1-4 charectors and converts it into a Little-Endian int --- @param str string --- @return number function NikNaks.BitBuffer.StringToInt( str ) local a, b, c, d = s_byte( str, 1, 4 ) if d then return bor( blshift( d, 24 ), blshift( c, 16 ), blshift( b, 8 ), a ) elseif c then return bor( blshift( c, 16 ), blshift( b, 8 ), a ) elseif b then return bor( blshift( b, 8 ), a ) else return a end end local q = 0xFF --- Takes an Little-Endian number and converts it into a 4 char-string --- @param int number --- @return string function NikNaks.BitBuffer.IntToString( int ) local a, b, c, d = brshift( int, 24 ), band( brshift(int, 16), q ), band( brshift(int, 8), q ), band( int, q ) return s_char(d,c,b,a) end end -- To signed and unsigned local to_signed do local maxint = {} for i = 1, 32 do local n = i maxint[i] = math.pow( 2, n - 1 ) end function to_signed( int, bits ) if int < 0 then return int end -- Already signed local maximum = maxint[bits] return int - band( int, maximum ) * 2 end end -- Access do --- Returns lengh of the BitBuffer. --- @return number function meta:__len() return self._len end meta.Size = meta.__len --- Returns where we're reading/writing from. --- @return number function meta:Tell() return self._tell end --- Sets the tell to a position. --- @param num number --- @return self function meta:Seek( num ) self._tell = brshift( blshift( num, 1 ), 1 ) return self end --- Skips x bits ahead. --- @param num number --- @return BitBuffer self function meta:Skip( num ) self._tell = self._tell + num return self end --- Returns true if we've reached the end of the bitbuffer. --- @return boolean function meta:EndOfData() return self._tell >= self._len end --- Clears all data from the BitBuffer. --- @return BitBuffer self function meta:Clear() self._data = {} self._len = 0 self._tell = 0 return self end --- Converts a number to bits. ( Big-Endian ) --- @param num number --- @param bits number --- @return string local function toBits( num, bits, byte_space ) local str = "" for i = bits, 1, -1 do if byte_space and i % 8 == 0 and i < 32 then str = str .. " " end local b = band( num, lshift( 0x1, i - 1 ) ) == 0 str = str .. ( b and "0" or "1" ) end return str end NikNaks.BitBuffer.ToBits = toBits --- Debug print function for the bitbuffer. function meta:Debug() local size = string.NiceSize( self._len / 8 ) local rep = string.rep( "=", ( 32 - ( #size + 6 ) ) / 2 ) print( "BitBuff " .. rep .. " [" .. size .. "] " .. ( self:IsLittleEndian() and "Le " or "Be " ) .. rep .. "\t= 0xHX =" ) for i = 1, #self._data do print( i, toBits( self._data[i], 32 ), bit.tohex( self._data[i] ):upper() ) end end --- Returns true if the bitbuffer is little-endian. --- @return boolean function meta:IsLittleEndian() return self._little_endian or false end --- Returns true if the bitbuffer is big-endian. --- @return boolean function meta:IsBigEndian() return not self._little_endian end --- Sets the bitbuffer to be little-endian. --- @return self BitBuffer The BitBuffer that was modified function meta:SetLittleEndian() self._little_endian = true return self end --- Sets the bitbuffer to be big-endian. --- @return self BitBuffer The BitBuffer that was modified function meta:SetBigEndian() self._little_endian = false return self end end -- Write / Read Raw local writeraw, readraw do -- Need to check endian type here. -- B |--|--|FF|11| -- L |--|--|11|FF| -- B |--|-F|FF|11| -- Input -- S |11|FF|-F|--| -- Swap -- > |--|11|FF|-F| -- rshift -- L |--|-1|1F|FF| -- Output -- B |00000000|00000000|00000000|00000000| local b_mask = 0xFFFFFFFF --- @param int number --- @param bits number --- @return number local function swap( int, bits ) return brshift( bswap( int ), 32 - bits ) end --- @param self BitBuffer --- @param int number --- @param bits number --- @return self BitBuffer The BitBuffer that was modified function writeraw( self, int, bits ) if self._little_endian and bits % 8 == 0 then int = swap( int, bits ) end local tell = self._tell self._tell = tell + bits self._len = max( self._len, self._tell ) -- Retrive data pos local i_word = rshift( tell, 5 ) + 1 -- [ 1 - length ] local bitPos = tell % 32 -- [[ 0 - 31 ]] local ebitPos = bitPos + bits -- The end bit pos -- DataMask & Data local mask = bor( lshift( b_mask, 32 - bitPos ), rshift( b_mask, ebitPos ) ) local data = band( self._data[ i_word ] or 0x0, mask ) -- Write the data if ebitPos <= 32 then self._data[ i_word ] = bor( data, lshift( int, 32 - ebitPos ) ) return end local overflow = ebitPos - 32 -- [[ 1, 31 ]] self._data[ i_word ] = bor( data, rshift( int, overflow ) ) data = band( rshift( b_mask, overflow ), self._data[ i_word + 1 ] or 0x0 ) self._data[ i_word + 1 ] = bor( data, lshift( int, 32 - overflow ) ) return self end --- @param self BitBuffer --- @param bits number --- @return number # The read data function readraw( self, bits ) local tell = self._tell self._tell = tell + bits -- Retrive data pos local i_word = rshift( tell, 5 ) + 1 -- [ 1 - length ] local bitPos = tell % 32 -- [[ 0 - 31 ]] local ebitPos = bitPos + bits -- The end bit pos -- DataMask & Data if ebitPos <= 32 then local data = brshift( self._data[ i_word ] or 0x0, 32 - ebitPos ) if self._little_endian and bits % 8 == 0 then return swap( band( data, brshift( b_mask, 32 - bits ) ), bits ) end return band( data, brshift( b_mask, 32 - bits ) ) end local over = ebitPos - 32 -- How many bits we're over local data1 = lshift( band( self._data[ i_word ] or 0x0, rshift( b_mask, bitPos ) ), over ) local data2 = rshift( self._data[ i_word + 1 ] or 0x0, 32 - over ) if self._little_endian and bits % 8 == 0 then return swap( bor( data1, data2 ), bits ) end return bor( data1, data2 ) end if not source:find( "niknak" ) then return end end --- Add raw (debug) functions meta._writeraw = writeraw meta._readraw = readraw -- Boolean do local b_mask = 0xFFFFFFFF --We don't need to call write/read raw. Since this is 1 bit. --- Writes a boolean. --- @param b boolean --- @return self BitBuffer function meta:WriteBoolean( b ) local tell = self._tell self._tell = tell + 1 self._len = max( self._len, self._tell ) -- Retrive data pos local i_word = rshift( tell, 5 ) + 1 -- [ 1 - length ] local bitPos = tell % 32 -- [[ 0 - 31 ]] local ebitPos = bitPos + 1 -- DataMask & Data local mask = bor( lshift( b_mask, 32 - bitPos ), rshift( b_mask, ebitPos ) ) local data = band( self._data[ i_word ] or 0x0, mask ) -- Write the data self._data[ i_word ] = bor( data, lshift( b and 1 or 0, 32 - ebitPos ) ) return self end --- Reads a boolean. --- @return boolean function meta:ReadBoolean() local tell = self._tell self._tell = tell + 1 -- Retrive data pos local i_word = rshift( tell, 5 ) + 1 -- [ 1 - length ] local bitPos = tell % 32 -- [[ 0 - 31 ]] local ebitPos = bitPos + 1 -- The end bit pos -- DataMask & Data local data = rshift( self._data[ i_word ] or 0x0, 32 - ebitPos ) return band( data, rshift( b_mask, 32 - 1 ) ) == 1 end end -- 32 bit Int do meta.WriteInt = writeraw --- Reads an int. --- @param bits number --- @return number function meta:ReadInt( bits ) return to_signed( readraw( self, bits ), bits ) end end -- UInt do meta.WriteUInt = writeraw local c = math.pow( 2, 32 ) --- Reads an unsigned int. --- @param bits number --- @return number function meta:ReadUInt( bits ) local n = readraw( self, bits ) if n > -1 then return n end -- 32bit numbers could be negative when reading. return n + c end end -- Byte do --- Writes a byte. ( 0 - 255 ) --- @param byte number --- @return BitBuffer self function meta:WriteByte( byte ) writeraw( self, byte, 8 ) return self end --- Reads a byte. ( 0 - 255 ) --- @return number function meta:ReadByte() return readraw( self, 8 ) end end -- Signed Byte do --- Writes a signed byte. ( -128 - 127 ) --- @param byte number --- @return BitBuffer self function meta:WriteSignedByte( byte ) self:WriteInt( byte, 8 ) return self end --- Writes a signed byte. ( -128 - 127 ) --- @return number function meta:ReadSignedByte() return self:ReadInt( 8 ) end end -- Ushort do --- Writes an unsigned 2 byte number. ( 0 - 65535 ) --- @param num number --- @return BitBuffer self function meta:WriteUShort( num ) self:WriteUInt( num, 16 ) return self end --- Reads an unsigned 2 byte number. ( 0 - 65535 ) --- @return number function meta:ReadUShort() return self:ReadUInt( 16 ) end end -- Short do --- Writes a 2 byte number. ( -32768 - 32767 ) --- @param num number --- @return BitBuffer self function meta:WriteShort( num ) self:WriteInt( num, 16 ) return self end --- Reads an 2 byte number. ( -32768 - 32767 ) --- @return number function meta:ReadShort() return self:ReadInt( 16 ) end end -- ULong do --- Writes an unsigned 4 byte number. ( 0 - 4294967295 ) --- @param num number --- @return self BitBuffer function meta:WriteULong( num ) self:WriteUInt( num, 32 ) return self end --- Reads an unsigned 4 byte number ( 0 - 4294967295 ) --- @return number function meta:ReadULong() return self:ReadUInt( 32 ) end end -- Long do --- Writes a 4 byte number. ( -2147483648 - 2147483647 ) --- @param num number --- @return BitBuffer self function meta:WriteLong( num ) self:WriteInt( num, 32 ) return self end --- Reads a 4 byte number. ( -2147483648 - 2147483647 ) --- @return number function meta:ReadLong() return self:ReadInt( 32 ) end end -- Nibble do --- Writes a 4 bit unsigned number. ( 0 - 15 ) --- @param num number --- @return BitBuffer self function meta:WriteNibble( num ) self:WriteUInt( num, 4 ) return self end --- Reads a 4 bit unsigned number. ( 0 - 15 ) --- @return number function meta:ReadNibble() return self:ReadUInt( 4 ) end end -- Snort ( 2bit number ) do --- Writes a 2 bit unsigned number. ( 0 - 3 ) --- @param num number --- @return BitBuffer self function meta:WriteSnort( num ) self:WriteUInt( num, 2 ) return self end --- Reads a 2 bit unsigned number. ( 0 - 3 ) --- @return number function meta:ReadSnort() return self:ReadUInt( 2 ) end end --- @param n number --- @return boolean local function isNegative( n ) return 1 / n == -math.huge end -- Float do --- Writes an IEEE 754 little-endian float. --- @param num number --- @return self BitBuffer function meta:WriteFloat( num ) local sign = 0 local man = 0 local ex = 0 -- Mark negative numbers. if num < 0 then sign = 0x80000000 num = -num end if num ~= num then -- Nan ex = 0xFF man = 1 elseif num == math.huge then -- Infintiy ex = 0xFF man = 0 elseif num ~= 0 then -- Anything but 0's man, ex = frexp( num ) ex = ex + 0x7F if ex <= 0 then man = ldexp( man, ex - 1 ) ex = 0 elseif ex >= 0xFF then -- Reached infinity ex = 0xFF man = 0 elseif ex == 1 then ex = 0 else man = man * 2 - 1 ex = ex - 1 end man = floor( ldexp( man, 23 ) + 0.5 ) elseif isNegative( num ) then -- Minus 0 support sign = 0x80000000 man = 0 end -- Not tested, but I guess it is faster to write 1 32bit number, than 3x others. self:WriteULong( bor( sign, lshift( band( ex, 0xFF ), 23 ), man ), 32 ) return self end local _23pow = 2 ^ 23 --- Reads an IEEE 754 little-endian float. --- @return number function meta:ReadFloat() local n = self:ReadULong() local sign = band( 0x80000000, n ) == 0 and 1 or -1 local ex = band( rshift( n, 23 ), 0xFF ) local man = band( n, 0x007FFFFF ) / _23pow if ex == 0 and man == 0 then return 0 * sign -- Number 0 elseif ex == 255 and man == 0 then return math.huge * sign -- -+inf elseif ex == 255 and man ~= 0 then return 0 / 0 -- nan else return ldexp ( 1 + man, ex - 127 ) * sign end end end -- Double do --[[ |FFFFFFFF|FFFFFFFF| |Fa|Fb|Fc|Fd| |Fe|Ff|Fg|Fh| |Fh|Fg|Ff|Fe| |Fd|Fc|Fb|Fa| -> Data input |Fh|Fg|Ff|Fe| |Fd|Fc|Fb|Fa| |Fe|Ff|Fg|Fh| |Fa|Fb|Fc|Fd| |Fa|Fb|Fc|Fd| |Fe|Ff|Fg|Fh| ]] local _52pow = 2 ^ 52 local _32pow = 2 ^ 32 local _log = math.log( 2 ) --- Writes an IEEE 754 little-endian double. --- @param num number --- @return BitBuffer self function meta:WriteDouble( num ) -- Calculate sign, ex and man local sign = 0 if num < 0 or ( num == 0 and isNegative( num ) ) then num = -num sign = 0x80000000 end local ex = ceil( log( num ) / _log ) - 1 local man = num / ( 2 ^ ex ) - 1 -- If clamp or reach math.huge if ( ex < -1023 ) then ex = -1023 man = num / ( 2 ^ ex ) elseif ( ex > 1024 ) then ex = 1024 man = 0 end if ( num == 0 ) then -- Zero ex = 0 man = 0 elseif ( num == math.huge ) then --Infinity ex = 2047 man = 0 elseif ( num ~= num ) then -- Nan ex = 2047 man = 1 else ex = ex + 1023 man = man * _52pow end if self._little_endian then self:WriteULong( band( man, 0xFFFFFFFF ) ) self:WriteULong( bor( sign, lshift( ex, 20 ), band( man / _32pow, 0x001FFFFF ) ) ) else self:WriteULong( bor( sign, lshift( ex, 20 ), band( man / _32pow, 0x001FFFFF ) ) ) self:WriteULong( band( man, 0xFFFFFFFF ) ) end return self end --- Reads an IEEE 754 little-endian double. --- @return number function meta:ReadDouble() local a, b if self._little_endian then b, a = self:ReadUInt( 32 ), self:ReadUInt( 32 ) else a, b = self:ReadUInt( 32 ), self:ReadUInt( 32 ) end local sign = band( 0x80000000, a ) == 0 and 1 or -1 local ex = rshift( band( 0x7FF00000, a ), 20 ) local man = band( a, 0x000FFFFF ) * _32pow + b if ex == 0 and man == 0 then return 0 * sign -- Number 0 elseif ex == 0x7FF and man == 0 then return math.huge * sign -- -+inf elseif ex == 0x7FF and man ~= 0 then return 0 / 0 -- nan else return math.pow( 2, ex - 0x3FF ) * ( man / _52pow + 1 ) * sign end end end -- Data do --- Writes raw string-data. --- @param str string --- @return BitBuffer self function meta:Write( str ) local len = #str local q = lshift( rshift( len, 2 ), 2 ) for i = 1, q, 4 do local a, b, c, d = s_byte( str, i, i + 3 ) self:WriteUInt( bor( lshift( a, 24 ), lshift( b, 16 ), lshift( c, 8 ), d ), 32 ) end for i = q + 1, len do self:WriteUInt( s_byte( str, i ), 8 ) end return self end --- Reads raw string-data. Default bytes are the length of the bitbuffer. --- @param bytes number --- @return string function meta:Read( bytes ) bytes = bytes or math.ceil( ( self:Size() - self:Tell() ) / 8 ) local ReadByte = meta.ReadByte local c, s = lshift( rshift( bytes, 2 ), 2 ), "" for _ = 1, c, 4 do s = s .. s_char( ReadByte( self ), ReadByte( self ), ReadByte( self ), ReadByte( self ) ) end for _ = c + 1, bytes do s = s .. s_char( ReadByte( self ) ) end return s end end -- Special Types do local Write, Read = meta.Write, meta.Read --- Writes a string. Max string length: 65535 --- @param str string --- @return BitBuffer self function meta:WriteString( str ) local l = #str if l > 65535 then str = str:sub( 0, 65535 ) l = 65535 end self:WriteUShort( l ) Write( self, str ) return self end --- Reads a string. Max string length: 65535 --- @return string function meta:ReadString() return Read( self, self:ReadUShort() or 0 ) end local z = '\0' --- Writes a string using a nullbyte at the end. Note: Will remove all nullbytes given. --- @param str string --- @return BitBuffer self function meta:WriteStringNull( str ) Write( self, string.gsub( str, z, '' ) .. z ) return self end --- Reads a string using a nullbyte at the end. Note: ReadStringNull is a bit slower than ReadString. --- @param maxLength? number --- @return string function meta:ReadStringNull( maxLength ) maxLength = maxLength or ceil( self:Size() - self:Tell() ) / 8 local str = "" if maxLength < 1 then return str end local c = self:ReadByte() while c ~= 0 and maxLength > 0 do str = str .. s_char( c ) c = self:ReadByte() maxLength = maxLength - 1 end return str end --- Writes a Vector. --- @param vector Vector --- @return BitBuffer self function meta:WriteVector( vector ) self:WriteFloat( vector.x ) self:WriteFloat( vector.y ) self:WriteFloat( vector.z ) return self end --- Reads a Vector. --- @return Vector function meta:ReadVector() return Vector( self:ReadFloat(), self:ReadFloat(), self:ReadFloat() ) end --- Writes an Angle. --- @param angle Angle --- @return BitBuffer self function meta:WriteAngle( angle ) self:WriteFloat( angle.p ) self:WriteFloat( angle.y ) self:WriteFloat( angle.r ) return self end --- Reads an Angle. --- @return Angle function meta:ReadAngle() return Angle( self:ReadFloat(), self:ReadFloat(), self:ReadFloat() ) end --- Writes a 32bit Color. --- @param color Color --- @return self BitBuffer function meta:WriteColor( color ) self:WriteByte( color.r ) self:WriteByte( color.g ) self:WriteByte( color.b ) self:WriteByte( color.a or 255 ) return self end --- Reads a 32bit color. --- @return Color function meta:ReadColor() return Color( self:ReadByte(), self:ReadByte(), self:ReadByte(), self:ReadByte() ) end end -- Tables / Types do local typeIDs = { ["nil"] = 0, ["boolean"] = 1, ["number"] = 2, -- light userdata ["string"] = 4, ["table"] = 5, -- function -- userdata -- thread ["Player"] = 8, ["Entity"] = 9, ["Vector"] = 10, ["Angle"] = 11, -- physobj ["IMaterial"] = 21, ["Color"] = 255 } --- Writes a type using a byte as TYPE_ID. --- @param obj any --- @return BitBuffer self function meta:WriteType( obj ) local id = typeIDs[ type( obj ) ] assert( id, "Trying to write invalid type [" .. type( obj ) .. "]" ) if id == 5 and obj.r and obj.g and obj.b then id = 255 end self:WriteByte( id ) if id == 0 then return elseif id == 1 then self:WriteByte( obj and 1 or 0 ) elseif id == 2 then self:WriteDouble( obj ) elseif id == 4 then self:WriteString( obj ) elseif id == 5 then self:WriteTable( obj ) elseif id == 8 then self:WriteString( obj:SteamID64() ) elseif id == 9 then self:WriteULong( obj:EntIndex() ) elseif id == 10 then self:WriteVector( obj ) elseif id == 11 then self:WriteAngle( obj ) elseif id == 21 then self:WriteString( obj:GetName() ) elseif id == 255 then self:WriteColor( obj ) end return self end --- Reads a type using a byte as TYPE_ID. --- @return any function meta:ReadType() local id = self:ReadByte() if id == 0 then return elseif id == 1 then return self:ReadByte( ) == 1 elseif id == 2 then return self:ReadDouble( ) elseif id == 4 then return self:ReadString( ) elseif id == 5 then return self:ReadTable( ) elseif id == 8 then local steamID64 = self:ReadString() local plys = player.GetAll() for i = 1, #plys do if plys[i]:SteamID64() == steamID64 then return plys[i] end end elseif id == 9 then return Entity( self:ReadULong( ) ) elseif id == 10 then return self:ReadVector( ) elseif id == 11 then return self:ReadAngle( ) elseif id == 21 then return Material( self:ReadString( ) ) elseif id == 255 then return self:ReadColor( ) end end --- Writes a table --- @param tab table --- @return self BitBuffer function meta:WriteTable( tab ) for k, v in pairs( tab ) do self:WriteType( k ) self:WriteType( v ) end self:WriteByte( 0 ) return self end --- Reads a table. Default maxValues is 150 --- @param maxValues? number --- @return table function meta:ReadTable( maxValues ) maxValues = maxValues or 150 -- Table local tab = {} local k = self:ReadType() while k ~= nil and maxValues > 0 do tab[k] = self:ReadType() k = self:ReadType() maxValues = maxValues - 1 end return tab end end -- File functions do --- Same as file.Open, but returns it as a bitbuffer. little_endian is true by default. --- @param fileName string --- @param gamePath? string --- @param lzma? boolean --- @param little_endian? boolean --- @return BitBuffer function NikNaks.BitBuffer.OpenFile( fileName, gamePath, lzma, little_endian ) if gamePath == true then gamePath = "GAME" end if gamePath == nil then gamePath = "DATA" end if gamePath == false then gamePath = "DATA" end local f = file.Open( fileName, "rb", gamePath ) if not f or not NikNaks._source:find( "niknak" ) then return end -- Data local str = f:Read( f:Size() ) -- Is faster if lzma then str = util.Decompress( str ) or str end f:Close() --- @type BitBuffer local b = NikNaks.BitBuffer( str, little_endian ) b:Seek( 0 ) return b end --- Saves the bitbuffer to a file within the data folder. Returns true if it got saved. --- @param fileName string --- @param lzma? boolean --- @return boolean function meta:SaveToFile( fileName, lzma ) local f = file.Open( fileName, "wb", "DATA" ) if not f then return false end local s = self:Size() local t = self:Tell() if lzma then self:Seek( 0 ) local data = self:Read() data = util.Compress( data ) or data f:Write( data ) else local n = rshift( s, 5 ) -- Amount of "chunks" for i = 1, n do f:WriteULong( self._data[i] ) end local p = lshift( n, 5 ) local l = s - p -- How many bits left to write. self:Seek( p ) for i = 1, math.ceil( l / 8 ) do f:WriteByte( f:ReadByte() ) end end self:Seek( t ) f:Close() return true end end -- Net functions function meta:ReadFromNet( bits ) for i = 1, bits / 32 do self:WriteUInt( net.ReadUInt( 32 ), 32 ) end local leftover = bits % 32 if leftover > 0 then self:WriteUInt( net.ReadUInt( leftover ), leftover ) end self:Seek( 0 ) return self end function NikNaks.BitBuffer.FromNet( bits ) return NikNaks.BitBuffer():ReadFromNet( bits ) end function meta:WriteToNet() local tell = self:Tell() self:Seek( 0 ) local l = self:Size() for _ = 1, l / 32 do net.WriteUInt( self:ReadULong(), 32 ) end local leftover = l % 32 if leftover > 0 then net.WriteUInt( self:ReadUInt( leftover ), leftover ) end self:Seek( tell ) return l end function NikNaks.BitBuffer.ToNet( buf ) return buf:WriteToNet() end -- Debug BitBuffer if true then return end local function d_print( str, b ) print( str, b and '✓' or '✗', b and "" or "<- !!!" ) end local function o_print( self, str, obj, ex ) local write = "Write" .. str local read = "Read" .. str self:Seek(0) self[write](self, obj) self:Seek(0) local r = self[read]( self ) local p = r == obj str = ex and ex .. str or str if p then d_print( str .. string.rep(" ", 8 - #str), true) else print( str, '✗', r, "~=", obj ) end end function NikNaks.BitBuffer.DebugTest() local b = create() print( "\n============= BitBuffer Test =============" ) print( "State" ) b:WriteInt(0, 8) b:WriteInt(0, 16) b:WriteInt(0x1234, 32) b:WriteInt(0, 8) d_print(" Tell", b._len == 8 + 16 + 32 + 8 ) b:Seek( 8 + 16 ) d_print(" Seek", b:ReadInt(32) == 0x1234 ) b:ReadInt( 8 ) d_print(" End", b:EndOfData()) b:Seek( 0 ) print("Boolean:") for i = 1, 64 do b:WriteBoolean( i % 3 == 0 ) end b:Seek( 0 ) local q = true for i = 1, 64 do q = q and (b:ReadBoolean( ) == (i % 3 == 0) ) end d_print(" 64x w/r", q) b:Seek( 0 ) print("Data:") b:Seek(0) b:Write("abcdefghijk!pq") b:Seek(0) d_print(" w/r", b:Read(14) == "abcdefghijk!pq") b:Seek(0) local q = b:Read(4) == "abcd" and b:Read(10) == "efghijk!pq" d_print(" parts", q) b:Seek(0) print("UInt:") b:WriteUInt(1,1) b:WriteUInt(0,1) b:Seek(0) d_print(" Bit", b:ReadUInt(1) == 1 and b:ReadUInt(1) == 0) b:Seek(0) b:WriteUInt(0,32) b:WriteUInt(4294967295,32) b:Seek(0) d_print(" Max/Min", b:ReadUInt(32) == 0 and b:ReadUInt(32) == 4294967295) b:Seek(0) b:WriteUInt(0x15555555,30) b:WriteUInt(7,3) b:WriteUInt(0,3) b:Seek(0) d_print(" Offset", b:ReadUInt(30) == 0x15555555 and b:ReadUInt(3) == 7 and b:ReadUInt(3) == 0 ) b:Seek(0) print("Int:") b:WriteInt(0x15555555,31) b:Seek(0) d_print(" Negative", b:ReadUInt(31) == 0x15555555) b:Seek(0) b:WriteInt(2147483647,32) b:WriteInt(-2147483648,32) b:Seek(0) d_print(" Max/Min", b:ReadInt(32) == 2147483647 and b:ReadInt(32) == -2147483648) b:Seek(0) print("Float:") b:WriteFloat(22.33) b:WriteFloat(-3422.25) b:WriteFloat(0) b:WriteFloat(0 * -1) b:Seek(0) d_print( " Num", math.Round( b:ReadFloat(), 2 ) == 22.33 and math.Round( b:ReadFloat(), 2 ) == -3422.25 and math.Round( b:ReadFloat(), 2) == 0 and math.Round( b:ReadFloat(), 2 ) == 0) b:Seek( 0 ) b:WriteFloat( 1 / 0 ) b:WriteFloat( -1 / 0 ) b:Seek( 0 ) d_print(" INF", tostring(b:ReadFloat()) == "inf" and tostring(b:ReadFloat()) == "-inf") b:Seek(0) b:WriteFloat(0/0) b:Seek(0) d_print(" NAN", tostring(b:ReadFloat()) == "nan") b:Seek( 0 ) print( "Double:" ) b:WriteDouble( 22.33 ) b:WriteDouble( -3422.25 ) b:WriteDouble( 233.25 ) b:WriteDouble( 22.33, true ) b:WriteDouble( 0) b:WriteDouble( 4 ) b:WriteDouble( 0 / 0 ) b:Seek(0) local f = math.Round(b:ReadDouble(), 2) local a = f == 22.33 local g = math.Round(b:ReadDouble(), 2) == -3422.25 local c = math.Round(b:ReadDouble(), 2) == 233.25 local d = math.Round(b:ReadDouble(), 2) == 22.33 local e = math.Round(b:ReadDouble(), 2) == 0 and math.Round(b:ReadDouble( true), 2) == 4 and tostring(b:ReadDouble( true)) == "nan" d_print(" Num", a and g and c and d and e ) b:Seek(0) b:WriteDouble( 1 / 0 ) b:WriteDouble( -1 / 0 ) b:Seek(0) d_print(" INF", tostring(b:ReadDouble()) == "inf" and tostring(b:ReadDouble()) == "-inf") b:Seek(0) b:WriteDouble( 0 / 0 ) b:Seek(0) d_print(" NAN", tostring(b:ReadDouble()) == "nan") b:Seek(0) print("Signed:") -- Signed Byte o_print(b, "Long", -214748364, "\t") -- Signed Byte o_print(b, "Short", -32768, "\t") -- Signed Byte o_print(b, "SignedByte", -128, "\t") print("Unsigned:") -- Signed Byte o_print(b, "ULong", 4294967295, "\t" ) -- Signed Byte o_print(b, "UShort", 0xF0F0, "\t") -- Byte o_print(b, "Byte", 255, "\t") -- Signed Byte o_print(b, "Nibble", 5, "\t") -- Signed Byte o_print(b, "Snort", 3, "\t") print("Objects:") -- string o_print(b, "String", "abcdefghijklmnopqrstuvwxyz !abcdefghijklmnopqrstuvwxyz!abcdefghijklmnopqrstuvwxyz!", "\t") -- stringnull o_print(b, "StringNull", "abcdefghijklmnopqrstuvwxyz !abcdefghijklmnopqrstuvwxyz!abcdefghijklmnopqrstuvwxyz!", "\t") -- vector o_print(b, "Vector", Vector(123,321,101), "\t") -- angle o_print(b, "Angle", Angle(3,22,13213), "\t") -- color o_print(b, "Color", Color(255,0,127, 55), "\t") -- HEX / BIT Display print() b = create() b:WriteUInt(0x00FFFFFF, 32) b:WriteUInt(0xFF00FFFF, 32) b:WriteUInt(0xFFFF00FF, 32) b:WriteUInt(0xFFFFFF00, 32) for i = 1, 5 do b:WriteUInt(1, 6) end b:WriteUInt(1,3) b:WriteUInt(1,31) b:Debug() end NikNaks.BitBuffer.DebugTest()