-
Notifications
You must be signed in to change notification settings - Fork 51
/
dataview.lua
191 lines (172 loc) · 6.78 KB
/
dataview.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
--[[
Default, and assumed, LUAI_MAXSHORTLEN is 40. To create a non internalized
string always force the buffer to be greater than that value.
--]]
local _strblob = string.blob or function(length)
return string.rep("\0", math.max(40 + 1, length))
end
--[[
API:
DataView::{Get | Set}Int8
DataView::{Get | Set}Uint8
DataView::{Get | Set}Int16
DataView::{Get | Set}Uint16
DataView::{Get | Set}Int32
DataView::{Get | Set}Uint32
DataView::{Get | Set}Int64
DataView::{Get | Set}Uint64
DataView::{Get | Set}LuaInt
DataView::{Get | Set}UluaInt
DataView::{Get | Set}LuaNum
DataView::{Get | Set}Float32
DataView::{Get | Set}Float64
DataView::{Get | Set}String
Parameters:
Get: self, offset, endian (optional)
Set: self, offset, value, endian (optional)
DataView::{GetFixed | SetFixed}::Int
DataView::{GetFixed | SetFixed}::Uint
DataView::{GetFixed | SetFixed}::String
Parameters:
Get: offset, typelen, endian (optional)
Set: offset, typelen, value, endian (optional)
NOTES:
(1) Endianness changed from JS API, defaults to little endian.
(2) {Get|Set|Next} offsets are zero-based.
EXAMPLES:
local view = DataView.ArrayBuffer(512)
if Citizen.InvokeNative(0x79923CD21BECE14E, 1, view:Buffer(), Citizen.ReturnResultAnyway()) then
local dlc = {
validCheck = view:GetInt64(0),
weaponHash = view:GetInt32(8),
val3 = view:GetInt64(16),
weaponCost = view:GetInt64(24),
ammoCost = view:GetInt64(32),
ammoType = view:GetInt64(40),
defaultClipSize = view:GetInt64(48),
nameLabel = view:GetFixedString(56, 64),
descLabel = view:GetFixedString(120, 64),
simpleDesc = view:GetFixedString(184, 64),
upperCaseName = view:GetFixedString(248, 64),
}
end
--]]
DataView = {
EndBig = ">",
EndLittle = "<",
Types = {
Int8 = { code = "i1", size = 1 },
Uint8 = { code = "I1", size = 1 },
Int16 = { code = "i2", size = 2 },
Uint16 = { code = "I2", size = 2 },
Int32 = { code = "i4", size = 4 },
Uint32 = { code = "I4", size = 4 },
Int64 = { code = "i8", size = 8 },
Uint64 = { code = "I8", size = 8 },
LuaInt = { code = "j", size = 8 }, -- a lua_Integer
UluaInt = { code = "J", size = 8 }, -- a lua_Unsigned
LuaNum = { code = "n", size = 8}, -- a lua_Number
Float32 = { code = "f", size = 4 }, -- a float (native size)
Float64 = { code = "d", size = 8 }, -- a double (native size)
String = { code = "z", size = -1, }, -- zero terminated string
},
FixedTypes = {
String = { code = "c", size = -1, }, -- a fixed-sized string with n bytes
Int = { code = "i", size = -1, }, -- a signed int with n bytes
Uint = { code = "I", size = -1, }, -- an unsigned int with n bytes
},
}
DataView.__index = DataView
--[[ Is a dataview type at a specific offset still within buffer length --]]
local function _ib(o, l, t) return ((t.size < 0 and true) or (o + (t.size - 1) <= l)) end
local function _ef(big) return (big and DataView.EndBig) or DataView.EndLittle end
--[[ Helper function for setting fixed datatypes within a buffer --]]
local SetFixed = nil
--[[ Create an ArrayBuffer with a size in bytes --]]
function DataView.ArrayBuffer(length)
return setmetatable({
offset = 1, length = length, blob = _strblob(length)
}, DataView)
end
--[[ Wrap a non-internalized string --]]
function DataView.Wrap(blob)
return setmetatable({
offset = 1, blob = blob, length = blob:len(),
}, DataView)
end
function DataView:Buffer() return self.blob end
function DataView:ByteLength() return self.length end
function DataView:ByteOffset() return self.offset end
function DataView:SubView(offset)
return setmetatable({
offset = offset, blob = self.blob, length = self.length,
}, DataView)
end
--[[ Create the API by using DataView.Types. --]]
for label,datatype in pairs(DataView.Types) do
DataView["Get" .. label] = function(self, offset, endian)
local o = self.offset + offset
if _ib(o, self.length, datatype) then
local v,_ = string.unpack(_ef(endian) .. datatype.code, self.blob, o)
return v
end
return nil -- Out of bounds
end
DataView["Set" .. label] = function(self, offset, value, endian)
local o = self.offset + offset
if _ib(o, self.length, datatype) then
return SetFixed(self, o, value, _ef(endian) .. datatype.code)
end
return self -- Out of bounds
end
-- Ensure cache is correct.
if datatype.size >= 0 and string.packsize(datatype.code) ~= datatype.size then
local msg = "Pack size of %s (%d) does not match cached length: (%d)"
error(msg:format(label, string.packsize(fmt[#fmt]), datatype.size))
return nil
end
end
for label,datatype in pairs(DataView.FixedTypes) do
DataView["GetFixed" .. label] = function(self, offset, typelen, endian)
local o = self.offset + offset
if o + (typelen - 1) <= self.length then
local code = _ef(endian) .. "c" .. tostring(typelen)
local v,_ = string.unpack(code, self.blob, o)
return v
end
return nil -- Out of bounds
end
DataView["SetFixed" .. label] = function(self, offset, typelen, value, endian)
local o = self.offset + offset
if o + (typelen - 1) <= self.length then
local code = _ef(endian) .. "c" .. tostring(typelen)
return SetFixed(self, o, value, code)
end
return self
end
end
--[[ Helper function for setting fixed datatypes within a buffer --]]
SetFixed = function(self, offset, value, code)
local fmt = { }
local values = { }
-- All bytes prior to the offset
if self.offset < offset then
local size = offset - self.offset
fmt[#fmt + 1] = "c" .. tostring(size)
values[#values + 1] = self.blob:sub(self.offset, size)
end
fmt[#fmt + 1] = code
values[#values + 1] = value
-- All bytes after the value (offset + size) to the end of the buffer
-- growing the buffer if needed.
local ps = string.packsize(fmt[#fmt])
if (offset + ps) <= self.length then
local newoff = offset + ps
local size = self.length - newoff + 1
fmt[#fmt + 1] = "c" .. tostring(size)
values[#values + 1] = self.blob:sub(newoff, self.length)
end
self.blob = string.pack(table.concat(fmt, ""), table.unpack(values))
self.length = self.blob:len()
return self
end