mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-16 21:33:46 +03:00
1265 lines
30 KiB
Lua
1265 lines
30 KiB
Lua
--[[
|
|
| 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()
|