1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3
4mod endianness;
5mod version;
6mod width;
7
8pub use endianness::Endianness;
9pub(crate) use version::LuaVersion;
10pub use width::BitWidth;
11
12use crate::serialization::{ByteStream, ByteWriter};
13use crate::{LunifyError, Settings};
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
17#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
18pub struct Format {
19 pub format: u8,
21 pub endianness: Endianness,
23 pub integer_width: BitWidth,
25 pub size_t_width: BitWidth,
27 pub instruction_width: BitWidth,
29 pub number_width: BitWidth,
31 pub is_number_integral: bool,
33}
34
35impl Default for Format {
36 fn default() -> Self {
37 let size_t_width = match cfg!(target_pointer_width = "64") {
39 true => BitWidth::Bit64,
40 false => BitWidth::Bit32,
41 };
42
43 let endianness = match unsafe { *(&1u32 as *const u32 as *const u8) } {
45 0 => Endianness::Big,
46 1 => Endianness::Little,
47 _ => unreachable!(),
48 };
49
50 Self {
51 format: 0,
52 endianness,
53 integer_width: BitWidth::Bit32,
54 size_t_width,
55 instruction_width: BitWidth::Bit32,
56 number_width: BitWidth::Bit64,
57 is_number_integral: false,
58 }
59 }
60}
61
62impl Format {
63 pub(crate) fn from_byte_stream(byte_stream: &mut ByteStream, version: LuaVersion, settings: &Settings) -> Result<Self, LunifyError> {
64 let format = match version {
65 LuaVersion::Lua51 => byte_stream.byte()?,
66 LuaVersion::Lua50 => 0,
67 };
68
69 let endianness = byte_stream.byte()?.try_into()?;
70 let integer_width = byte_stream.byte()?.try_into().map_err(LunifyError::UnsupportedIntegerWidth)?;
71 let size_t_width = byte_stream.byte()?.try_into().map_err(LunifyError::UnsupportedSizeTWidth)?;
72 let instruction_width = byte_stream.byte()?.try_into().map_err(LunifyError::UnsupportedInstructionWidth)?;
73
74 if version == LuaVersion::Lua50 {
75 let instruction_format = byte_stream.slice(4)?;
76 let expected = [
77 settings.lua50.layout.opcode.size as u8,
78 settings.lua50.layout.a.size as u8,
79 settings.lua50.layout.b.size as u8,
80 settings.lua50.layout.c.size as u8,
81 ];
82
83 if instruction_format != expected {
84 return Err(LunifyError::UnsupportedInstructionFormat(
85 instruction_format.try_into().unwrap(),
86 ));
87 }
88 }
89
90 let number_width = byte_stream.byte()?.try_into().map_err(LunifyError::UnsupportedNumberWidth)?;
91
92 let is_number_integral = match version {
93 LuaVersion::Lua51 => byte_stream.byte()? == 1,
94 LuaVersion::Lua50 => {
95 let value = from_slice!(byte_stream, number_width, endianness, f32, f64);
97 value != 31415926.535897933
98 }
99 };
100
101 #[cfg(feature = "debug")]
102 {
103 println!("format: {format}");
104 println!("endianness: {endianness:?}");
105 println!("integer_width: {integer_width}");
106 println!("size_t_width: {size_t_width}");
107 println!("instruction_width: {instruction_width}");
108 println!("number_width: {number_width}");
109 println!("is_number_integral: {is_number_integral}");
110 }
111
112 Ok(Self {
113 format,
114 endianness,
115 integer_width,
116 size_t_width,
117 instruction_width,
118 number_width,
119 is_number_integral,
120 })
121 }
122
123 pub(crate) fn write(&self, byte_writer: &mut ByteWriter) {
124 byte_writer.byte(self.format);
125 byte_writer.byte(self.endianness.into());
126 byte_writer.byte(self.integer_width.into());
127 byte_writer.byte(self.size_t_width.into());
128 byte_writer.byte(self.instruction_width.into());
129 byte_writer.byte(self.number_width.into());
130 byte_writer.byte(self.is_number_integral as u8);
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::LuaVersion;
137 use crate::serialization::{ByteStream, ByteWriter};
138 use crate::{BitWidth, Endianness, Format, LunifyError, Settings};
139
140 const EXPECTED_FORMAT: Format = Format {
141 format: 0,
142 endianness: Endianness::Little,
143 integer_width: BitWidth::Bit32,
144 size_t_width: BitWidth::Bit64,
145 instruction_width: BitWidth::Bit32,
146 number_width: BitWidth::Bit64,
147 is_number_integral: false,
148 };
149
150 fn from_test_data(version: LuaVersion, bytes: &[u8]) -> Result<Format, LunifyError> {
151 let mut byte_stream = ByteStream::new(bytes);
152 let result = Format::from_byte_stream(&mut byte_stream, version, &Settings::default());
153
154 assert!(byte_stream.is_empty());
155 result
156 }
157
158 #[test]
159 fn lua_51() -> Result<(), LunifyError> {
160 let result = from_test_data(LuaVersion::Lua51, &[0, 1, 4, 8, 4, 8, 0])?;
161 assert_eq!(result, EXPECTED_FORMAT);
162 Ok(())
163 }
164
165 #[test]
166 fn lua50() -> Result<(), LunifyError> {
167 let result = from_test_data(LuaVersion::Lua50, &[
168 1, 4, 8, 4, 6, 8, 9, 9, 8, 0xB6, 0x09, 0x93, 0x68, 0xE7, 0xF5, 0x7D, 0x41,
169 ])?;
170 assert_eq!(result, EXPECTED_FORMAT);
171 Ok(())
172 }
173
174 #[test]
175 fn write() {
176 let mut byter_writer = ByteWriter::new(&EXPECTED_FORMAT);
177 EXPECTED_FORMAT.write(&mut byter_writer);
178 assert_eq!(byter_writer.finalize(), [0, 1, 4, 8, 4, 8, 0]);
179 }
180
181 #[test]
182 fn lua50_unsupported_instruction_format() {
183 let result = from_test_data(LuaVersion::Lua50, &[1, 4, 8, 4, 6, 9, 8, 9]);
184 assert_eq!(result, Err(LunifyError::UnsupportedInstructionFormat([6, 9, 8, 9])));
185 }
186
187 #[test]
188 fn unsupported_integer_width() {
189 let result = from_test_data(LuaVersion::Lua51, &[0, 1, 6]);
190 assert_eq!(result, Err(LunifyError::UnsupportedIntegerWidth(6)));
191 }
192
193 #[test]
194 fn unsupported_size_t_width() {
195 let result = from_test_data(LuaVersion::Lua51, &[0, 1, 4, 6]);
196 assert_eq!(result, Err(LunifyError::UnsupportedSizeTWidth(6)));
197 }
198
199 #[test]
200 fn unsupported_instruction_width() {
201 let result = from_test_data(LuaVersion::Lua51, &[0, 1, 4, 8, 6]);
202 assert_eq!(result, Err(LunifyError::UnsupportedInstructionWidth(6)));
203 }
204
205 #[test]
206 fn unsupported_number_width() {
207 let result = from_test_data(LuaVersion::Lua51, &[0, 1, 4, 8, 4, 6]);
208 assert_eq!(result, Err(LunifyError::UnsupportedNumberWidth(6)));
209 }
210}