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 byteorder::{LittleEndian, ReadBytesExt};
8use gaia_types::{BinaryReader, 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> {
30 config: &'config LuacReadConfig,
31 reader: RefCell<BinaryReader<R, 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> LuacReader<'config, R> {
52 pub fn new(reader: R, config: &'config LuacReadConfig) -> Self {
63 Self { config, reader: RefCell::new(BinaryReader::new(reader)), info: Default::default(), program: Default::default() }
64 }
65
66 pub fn finish(self) -> GaiaDiagnostics<LuaProgram>
72 where
73 R: Read + Seek,
74 {
75 match self.get_program() {
76 Ok(program) => {
77 let errors = self.reader.borrow_mut().take_errors();
78 GaiaDiagnostics { result: Ok(program.clone()), diagnostics: errors }
79 }
80 Err(e) => {
81 let errors = self.reader.borrow_mut().take_errors();
82 GaiaDiagnostics { result: Err(e), diagnostics: errors }
83 }
84 }
85 }
86}
87
88impl<'config, R: Read + Seek> LuacReader<'config, R> {
89 pub fn get_program(&self) -> Result<&LuaProgram, GaiaError> {
95 match self.program.get() {
96 Some(program) => Ok(program),
97 None => {
98 let program = self.read_program()?;
99 Ok(self.program.get_or_init(|| program))
100 }
101 }
102 }
103
104 pub fn get_info(&self) -> Result<&LuacInfo, GaiaError> {
110 match self.info.get() {
111 Some(info) => Ok(info),
112 None => {
113 let info = self.read_info()?;
114 Ok(self.info.get_or_init(|| info))
115 }
116 }
117 }
118
119 fn read_info(&self) -> Result<LuacInfo, GaiaError> {
120 let mut reader = self.reader.borrow_mut();
121
122 reader.seek(std::io::SeekFrom::Start(0))?;
124
125 let current_pos = reader.stream_position()?;
127 reader.seek(std::io::SeekFrom::End(0))?;
128 let file_size = reader.stream_position()?;
129 reader.seek(std::io::SeekFrom::Start(current_pos))?;
130
131 if file_size == 0 {
132 return Err(GaiaError::custom_error("File is empty".to_string()));
133 }
134
135 let header = self.read_header(&mut reader)?;
137
138 let version = if self.config.version != LuaVersion::Unknown {
140 self.config.version
141 }
142 else {
143 LuaVersion::from_byte(header.version.to_byte())
144 };
145
146 Ok(LuacInfo { header, version })
147 }
148
149 fn read_program(&self) -> Result<LuaProgram, GaiaError> {
150 let mut reader = self.reader.borrow_mut();
151
152 reader.seek(std::io::SeekFrom::Start(0))?;
154
155 let header = self.read_header(&mut reader)?;
157
158 let version = if self.config.version != LuaVersion::Unknown {
160 self.config.version
161 }
162 else {
163 LuaVersion::from_byte(header.version.to_byte())
164 };
165
166 let code_object = self.read_code_object(&mut reader)?;
168
169 let program = LuaProgram { header, code_object };
171
172 Ok(program)
173 }
174
175 fn read_header(&self, reader: &mut BinaryReader<R, LittleEndian>) -> Result<LuacHeader, GaiaError> {
176 let mut magic = [0u8; 4];
178 reader.read_exact(&mut magic).map_err(|e| GaiaError::custom_error(format!("Failed to read magic bytes: {}", e)))?;
179
180 if self.config.check_magic_head {
182 let expected_magic = [0x1b, b'L', b'u', b'a'];
183 if magic != expected_magic {
184 return Err(GaiaError::custom_error(format!(
185 "Invalid Lua bytecode magic: expected {:?}, got {:?}",
186 expected_magic, magic
187 )));
188 }
189 }
190
191 let version_byte =
193 reader.read_u8().map_err(|e| GaiaError::custom_error(format!("Failed to read version byte: {}", e)))?;
194 let version = LuaVersion::from_byte(version_byte);
195
196 let format_version =
198 reader.read_u8().map_err(|e| GaiaError::custom_error(format!("Failed to read format version: {}", e)))?;
199
200 let endianness = reader.read_u8().map_err(|e| GaiaError::custom_error(format!("Failed to read endianness: {}", e)))?;
202
203 let int_size = reader.read_u8().map_err(|e| GaiaError::custom_error(format!("Failed to read int_size: {}", e)))?;
205 let size_t_size =
206 reader.read_u8().map_err(|e| GaiaError::custom_error(format!("Failed to read size_t_size: {}", e)))?;
207 let instruction_size =
208 reader.read_u8().map_err(|e| GaiaError::custom_error(format!("Failed to read instruction_size: {}", e)))?;
209 let lua_number_size =
210 reader.read_u8().map_err(|e| GaiaError::custom_error(format!("Failed to read lua_number_size: {}", e)))?;
211 let integral_flag =
212 reader.read_u8().map_err(|e| GaiaError::custom_error(format!("Failed to read integral_flag: {}", e)))?;
213
214 Ok(LuacHeader {
215 magic,
216 version,
217 format_version,
218 endianness,
219 int_size,
220 size_t_size,
221 instruction_size,
222 lua_number_size,
223 integral_flag,
224 flags: 0,
225 timestamp: None,
226 size: None,
227 hash: None,
228 })
229 }
230
231 fn read_code_object(&self, _reader: &mut BinaryReader<R, LittleEndian>) -> Result<LuacCodeObject, GaiaError> {
232 Ok(LuacCodeObject {
235 source_name: String::new(),
236 first_line: 0,
237 last_line: 0,
238 num_params: 0,
239 is_vararg: 0,
240 max_stack_size: 0,
241 nested_functions: Vec::new(),
242 upvalues: Vec::new(),
243 local_vars: Vec::new(),
244 line_info: Vec::new(),
245 co_argcount: 0,
246 co_nlocal: 0,
247 co_stacks: 0,
248 num_upval: 0,
249 co_code: Vec::new(),
250 co_consts: Vec::new(),
251 upvalue_n: 0,
252 })
253 }
254}
255
256impl Default for LuaProgram {
257 fn default() -> Self {
258 Self { header: LuacHeader::default(), code_object: LuacCodeObject::default() }
259 }
260}
261
262impl Default for LuacHeader {
263 fn default() -> Self {
264 Self {
265 magic: [0x1b, b'L', b'u', b'a'],
266 version: LuaVersion::Unknown,
267 format_version: 0,
268 endianness: 1, int_size: 4,
270 size_t_size: 8,
271 instruction_size: 4,
272 lua_number_size: 8,
273 integral_flag: 0,
274 flags: 0,
275 timestamp: None,
276 size: None,
277 hash: None,
278 }
279 }
280}
281
282impl Default for LuacCodeObject {
283 fn default() -> Self {
284 Self {
285 source_name: String::new(),
286 first_line: 0,
287 last_line: 0,
288 num_params: 0,
289 is_vararg: 0,
290 max_stack_size: 0,
291 nested_functions: Vec::new(),
292 upvalues: Vec::new(),
293 local_vars: Vec::new(),
294 line_info: Vec::new(),
295 co_argcount: 0,
296 co_nlocal: 0,
297 co_stacks: 0,
298 num_upval: 0,
299 co_code: Vec::new(),
300 co_consts: Vec::new(),
301 upvalue_n: 0,
302 }
303 }
304}