1use serde::{Deserialize, Serialize};
2use std::fmt::Debug;
3
4#[derive(Clone, Debug, PartialEq, Eq, Copy)]
5pub enum LuaVersion {
6 Unknown,
7 Lua51,
8 Lua52,
9 Lua53,
10 Lua54,
11 LuaJIT,
12 Luau,
13}
14
15impl LuaVersion {
16 pub fn to_byte(&self) -> u8 {
17 match self {
18 LuaVersion::Lua51 => 0x51,
19 LuaVersion::Lua52 => 0x52,
20 LuaVersion::Lua53 => 0x53,
21 LuaVersion::Lua54 => 0x54,
22 _ => 0x00, }
24 }
25
26 pub fn from_byte(byte: u8) -> Self {
27 match byte {
28 0x51 => LuaVersion::Lua51,
29 0x52 => LuaVersion::Lua52,
30 0x53 => LuaVersion::Lua53,
31 0x54 => LuaVersion::Lua54,
32 _ => LuaVersion::Unknown,
33 }
34 }
35}
36
37#[derive(Clone, PartialEq, Eq, Copy)]
38pub struct LuacHeader {
39 pub magic: [u8; 4], pub version: LuaVersion,
41 pub format_version: u8, pub endianness: u8, pub int_size: u8, pub size_t_size: u8, pub instruction_size: u8, pub lua_number_size: u8, pub integral_flag: u8, pub flags: u8, pub timestamp: Option<u32>, pub size: Option<u32>, pub hash: Option<[u8; 8]>, }
53
54impl Debug for LuacHeader {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 f.debug_struct("LuacHeader")
57 .field("version", &self.version)
58 .field("magic", &format!("0x{:08X}", u32::from_le_bytes(self.magic)))
59 .field("flags", &format!("0x{:02X}", self.flags))
60 .field("timestamp", &self.timestamp)
61 .field("size", &self.size)
62 .field("hash", &self.hash.as_ref().map(|h| format!("{:02X?}", h)))
63 .finish()
64 }
65}
66
67impl LuacHeader {
68 pub fn to_bytes(&self) -> Vec<u8> {
69 let mut bytes = Vec::new();
70 bytes.extend_from_slice(&self.magic);
71 bytes.push(self.version.to_byte());
72 bytes.push(self.format_version);
73 bytes.push(self.endianness);
74 bytes.push(self.int_size);
75 bytes.push(self.size_t_size);
76 bytes.push(self.instruction_size);
77 bytes.push(self.lua_number_size);
78 bytes.push(self.integral_flag);
79 bytes.push(self.flags);
80
81 if self.flags & 0x01 != 0 {
82 if let Some(hash_val) = self.hash {
84 bytes.extend_from_slice(&hash_val);
85 }
86 else {
87 bytes.extend_from_slice(&[0; 8]);
90 }
91 }
92 else {
93 if let Some(timestamp_val) = self.timestamp {
95 bytes.extend_from_slice(×tamp_val.to_le_bytes());
96 }
97 else {
98 bytes.extend_from_slice(&[0; 4]);
100 }
101 if let Some(size_val) = self.size {
102 bytes.extend_from_slice(&size_val.to_le_bytes());
103 }
104 else {
105 bytes.extend_from_slice(&[0; 4]);
107 }
108 }
109 bytes
110 }
111}
112
113#[derive(Debug, Clone, PartialEq, Eq)]
115pub struct Upvalue {
116 pub in_stack: u8, pub idx: u8, pub name: String, }
120
121#[derive(Debug, Clone, PartialEq, Eq)]
123pub struct LocalVar {
124 pub name: String,
125 pub start_pc: u32,
126 pub end_pc: u32,
127}
128
129#[derive(Debug, Clone, PartialEq, Eq)]
130pub enum LuaObject {
131 Str(String),
132 Int(i32),
133 Code(LuacCodeObject),
134 None,
135}
136
137#[allow(non_camel_case_types)]
139#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
140pub enum LuaOpCode {
141 MOVE,
142 LOAD_K,
143 LOAD_BOOL,
144 LOAD_NIL,
145 GET_UPVALUE,
146 GET_GLOBAL,
147 GET_TABLE,
148 SET_TABLE,
149 NEW_TABLE,
150 SET_GLOBAL,
151 SET_UPVALUE,
152 SELF,
153 ADD,
154 SUB,
155 MUL,
156 DIV,
157 MOD,
158 POW,
159 UNM,
160 NOT,
161 LEN,
162 CONCAT,
163 JMP,
164 EQ,
165 LT,
166 LE,
167 TEST,
168 TESTSET,
169 CALL,
170 TAILCALL,
171 RETURN,
172 FORLOOP,
173 TFORLOOP,
174 SETLIST,
175 CLOSE,
176 CLOSURE,
177 VARARG,
178 UNKNOWN(u8),
180}
181
182impl From<u8> for LuaOpCode {
183 fn from(byte: u8) -> Self {
184 match byte {
185 0 => LuaOpCode::MOVE,
186 1 => LuaOpCode::LOAD_K,
187 2 => LuaOpCode::LOAD_BOOL,
188 3 => LuaOpCode::LOAD_NIL,
189 4 => LuaOpCode::GET_UPVALUE,
190 5 => LuaOpCode::GET_GLOBAL,
191 6 => LuaOpCode::GET_TABLE,
192 7 => LuaOpCode::SET_GLOBAL,
193 8 => LuaOpCode::SET_UPVALUE,
194 9 => LuaOpCode::SET_TABLE,
195 10 => LuaOpCode::NEW_TABLE,
196 11 => LuaOpCode::SELF,
197 12 => LuaOpCode::ADD,
198 13 => LuaOpCode::SUB,
199 14 => LuaOpCode::MUL,
200 15 => LuaOpCode::DIV,
201 16 => LuaOpCode::MOD,
202 17 => LuaOpCode::POW,
203 18 => LuaOpCode::UNM,
204 19 => LuaOpCode::NOT,
205 20 => LuaOpCode::LEN,
206 21 => LuaOpCode::CONCAT,
207 22 => LuaOpCode::JMP,
208 23 => LuaOpCode::EQ,
209 24 => LuaOpCode::LT,
210 25 => LuaOpCode::LE,
211 26 => LuaOpCode::TEST,
212 27 => LuaOpCode::TESTSET,
213 28 => LuaOpCode::CALL,
214 29 => LuaOpCode::TAILCALL,
215 30 => LuaOpCode::RETURN,
216 31 => LuaOpCode::FORLOOP,
217 32 => LuaOpCode::TFORLOOP,
218 33 => LuaOpCode::SETLIST,
219 34 => LuaOpCode::CLOSE,
220 35 => LuaOpCode::CLOSURE,
221 36 => LuaOpCode::VARARG,
222 _ => LuaOpCode::UNKNOWN(byte),
223 }
224 }
225}
226
227#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
229pub struct LuaInstruction {
230 pub opcode: LuaOpCode,
231 pub a: u8,
232 pub b: u8,
233 pub c: u8,
234 pub bx: u16,
235 pub sbx: i16,
236 pub ax: u32,
237}
238
239#[derive(Debug, Clone, PartialEq, Eq)]
240pub struct LuacCodeObject {
241 pub source_name: String,
242 pub first_line: u32,
243 pub last_line: u32,
244 pub num_params: u8,
245 pub is_vararg: u8,
246 pub max_stack_size: u8,
247 pub nested_functions: Vec<LuacCodeObject>,
248 pub upvalues: Vec<Upvalue>,
249 pub local_vars: Vec<LocalVar>,
250 pub line_info: Vec<u8>,
251 pub co_argcount: u8,
252 pub co_nlocal: u8,
253 pub co_stacks: u8,
254 pub num_upval: u8,
255 pub co_code: Vec<u32>,
256 pub co_consts: Vec<LuaObject>,
257 pub upvalue_n: u8,
258}
259
260#[derive(Debug, Clone, PartialEq, Eq)]
262pub struct LuaProgram {
263 pub header: LuacHeader,
264 pub code_object: LuacCodeObject,
265}