lua_assembler/formats/luac/reader/
mod.rs1#![doc = include_str!("readme.md")]
2
3use crate::{
4 formats::luac::LuacReadConfig,
5 program::{LuaProgram, LuaVersion, LuacCodeObject, LuacHeader},
6};
7use gaia_binary::{BinaryReader, Fixed, LittleEndian};
8use gaia_types::{GaiaDiagnostics, GaiaError};
9use std::{
10 cell::RefCell,
11 io::{Read, Seek},
12 sync::OnceLock,
13};
14
15#[derive(Debug, Clone, Copy)]
17pub struct LuacInfo {
18 pub header: LuacHeader,
20 pub version: LuaVersion,
22}
23
24#[derive(Debug)]
29pub struct LuacReader<'config, R: std::io::Read> {
30 config: &'config LuacReadConfig,
31 reader: RefCell<BinaryReader<R, Fixed<LittleEndian>>>,
32 info: OnceLock<LuacInfo>,
33 program: OnceLock<LuaProgram>,
34}
35
36impl LuacReadConfig {
37 pub fn as_reader<R: Read + Seek>(&self, reader: R) -> LuacReader<'_, R> {
47 LuacReader::new(reader, self)
48 }
49}
50
51impl<'config, R: std::io::Read> LuacReader<'config, R> {
52 pub fn new(reader: R, config: &'config LuacReadConfig) -> Self {
63 Self {
64 config,
65 reader: RefCell::new(BinaryReader::<R, Fixed<LittleEndian>>::new(reader)),
66 info: Default::default(),
67 program: Default::default(),
68 }
69 }
70
71 pub fn finish(self) -> GaiaDiagnostics<LuaProgram>
77 where
78 R: std::io::Read + std::io::Seek,
79 {
80 match self.get_program() {
81 Ok(program) => {
82 let errors = self.reader.borrow_mut().take_errors();
83 GaiaDiagnostics { result: Ok(program.clone()), diagnostics: errors }
84 }
85 Err(e) => {
86 let errors = self.reader.borrow_mut().take_errors();
87 GaiaDiagnostics { result: Err(e), diagnostics: errors }
88 }
89 }
90 }
91}
92
93impl<'config, R: std::io::Read + std::io::Seek> LuacReader<'config, R> {
94 pub fn get_program(&self) -> Result<&LuaProgram, GaiaError> {
100 match self.program.get() {
101 Some(program) => Ok(program),
102 None => {
103 let program = self.read_program()?;
104 Ok(self.program.get_or_init(|| program))
105 }
106 }
107 }
108
109 pub fn get_info(&self) -> Result<&LuacInfo, GaiaError> {
115 match self.info.get() {
116 Some(info) => Ok(info),
117 None => {
118 let info = self.read_info()?;
119 Ok(self.info.get_or_init(|| info))
120 }
121 }
122 }
123
124 fn read_info(&self) -> Result<LuacInfo, GaiaError> {
125 let mut reader = self.reader.borrow_mut();
126
127 reader.seek(std::io::SeekFrom::Start(0))?;
129
130 let current_pos = reader.stream_position()?;
132 reader.seek(std::io::SeekFrom::End(0))?;
133 let file_size = reader.stream_position()?;
134 reader.seek(std::io::SeekFrom::Start(current_pos))?;
135
136 if file_size == 0 {
137 return Err(GaiaError::custom_error("File is empty".to_string()));
138 }
139
140 let header = self.read_header(&mut reader)?;
142
143 let version = if self.config.version != LuaVersion::Unknown {
145 self.config.version
146 }
147 else {
148 LuaVersion::from_byte(header.version.to_byte())
149 };
150
151 Ok(LuacInfo { header, version })
152 }
153
154 fn read_program(&self) -> Result<LuaProgram, GaiaError> {
155 let mut reader = self.reader.borrow_mut();
156
157 reader.seek(std::io::SeekFrom::Start(0))?;
159
160 let header = self.read_header(&mut reader)?;
162
163 let version = if self.config.version != LuaVersion::Unknown {
165 self.config.version
166 }
167 else {
168 LuaVersion::from_byte(header.version.to_byte())
169 };
170
171 let code_object = self.read_code_object(&mut reader)?;
173
174 let program = LuaProgram { header, code_object };
176
177 Ok(program)
178 }
179
180 fn read_header(&self, reader: &mut BinaryReader<R, Fixed<LittleEndian>>) -> Result<LuacHeader, GaiaError> {
181 let mut magic = [0u8; 4];
183 reader.read_exact(&mut magic).map_err(|e| GaiaError::custom_error(format!("Failed to read magic bytes: {}", e)))?;
184
185 if self.config.check_magic_head {
187 let expected_magic = [0x1b, b'L', b'u', b'a'];
188 if magic != expected_magic {
189 return Err(GaiaError::custom_error(format!(
190 "Invalid Lua bytecode magic: expected {:?}, got {:?}",
191 expected_magic, magic
192 )));
193 }
194 }
195
196 let version_byte =
198 reader.read_u8().map_err(|e| GaiaError::custom_error(format!("Failed to read version byte: {}", e)))?;
199 let version = LuaVersion::from_byte(version_byte);
200
201 let format_version =
203 reader.read_u8().map_err(|e| GaiaError::custom_error(format!("Failed to read format version: {}", e)))?;
204
205 let endianness = reader.read_u8().map_err(|e| GaiaError::custom_error(format!("Failed to read endianness: {}", e)))?;
207
208 let int_size = reader.read_u8().map_err(|e| GaiaError::custom_error(format!("Failed to read int_size: {}", e)))?;
210 let size_t_size =
211 reader.read_u8().map_err(|e| GaiaError::custom_error(format!("Failed to read size_t_size: {}", e)))?;
212 let instruction_size =
213 reader.read_u8().map_err(|e| GaiaError::custom_error(format!("Failed to read instruction_size: {}", e)))?;
214 let lua_number_size =
215 reader.read_u8().map_err(|e| GaiaError::custom_error(format!("Failed to read lua_number_size: {}", e)))?;
216 let integral_flag =
217 reader.read_u8().map_err(|e| GaiaError::custom_error(format!("Failed to read integral_flag: {}", e)))?;
218
219 Ok(LuacHeader {
220 magic,
221 version,
222 format_version,
223 endianness,
224 int_size,
225 size_t_size,
226 instruction_size,
227 lua_number_size,
228 integral_flag,
229 flags: 0,
230 timestamp: None,
231 size: None,
232 hash: None,
233 })
234 }
235
236 fn read_code_object(&self, _reader: &mut BinaryReader<R, Fixed<LittleEndian>>) -> Result<LuacCodeObject, GaiaError> {
237 Ok(LuacCodeObject {
240 source_name: String::new(),
241 first_line: 0,
242 last_line: 0,
243 num_params: 0,
244 is_vararg: 0,
245 max_stack_size: 0,
246 nested_functions: Vec::new(),
247 upvalues: Vec::new(),
248 local_vars: Vec::new(),
249 line_info: Vec::new(),
250 co_argcount: 0,
251 co_nlocal: 0,
252 co_stacks: 0,
253 num_upval: 0,
254 co_code: Vec::new(),
255 co_consts: Vec::new(),
256 upvalue_n: 0,
257 })
258 }
259}
260
261impl Default for LuaProgram {
262 fn default() -> Self {
263 Self { header: LuacHeader::default(), code_object: LuacCodeObject::default() }
264 }
265}
266
267impl Default for LuacHeader {
268 fn default() -> Self {
269 Self {
270 magic: [0x1b, b'L', b'u', b'a'],
271 version: LuaVersion::Unknown,
272 format_version: 0,
273 endianness: 1, int_size: 4,
275 size_t_size: 8,
276 instruction_size: 4,
277 lua_number_size: 8,
278 integral_flag: 0,
279 flags: 0,
280 timestamp: None,
281 size: None,
282 hash: None,
283 }
284 }
285}
286
287impl Default for LuacCodeObject {
288 fn default() -> Self {
289 Self {
290 source_name: String::new(),
291 first_line: 0,
292 last_line: 0,
293 num_params: 0,
294 is_vararg: 0,
295 max_stack_size: 0,
296 nested_functions: Vec::new(),
297 upvalues: Vec::new(),
298 local_vars: Vec::new(),
299 line_info: Vec::new(),
300 co_argcount: 0,
301 co_nlocal: 0,
302 co_stacks: 0,
303 num_upval: 0,
304 co_code: Vec::new(),
305 co_consts: Vec::new(),
306 upvalue_n: 0,
307 }
308 }
309}