1use nom::{multi::count, number::complete::le_u8};
2
3use super::*;
4
5pub fn varint(mut input: &[u8]) -> IResult<&[u8], usize> {
6 let mut x = 0usize;
7 let mut shift = 0usize;
8 loop {
9 let (rest, b) = le_u8(input)?;
10 input = rest;
11
12 x |= ((b & 0x7f) as usize) << shift;
13 shift += 7;
14
15 if b & 0x80 == 0 {
16 break;
17 }
18 }
19
20 Ok((input, x))
21}
22
23pub fn string<'a>(input: &'a [u8], stable: &[Rc<ByteBuf>]) -> IResult<&'a [u8], Rc<ByteBuf>> {
24 map(varint, |i| {
25 if i > 0 {
26 stable[i - 1].clone()
27 } else {
28 Rc::new(ByteBuf::new())
29 }
30 })(input)
31}
32
33pub const LBC_CONSTANT_NIL: u8 = 0;
34pub const LBC_CONSTANT_BOOLEAN: u8 = 1;
35pub const LBC_CONSTANT_NUMBER: u8 = 2;
36pub const LBC_CONSTANT_STRING: u8 = 3;
37pub const LBC_CONSTANT_IMPORT: u8 = 4;
38pub const LBC_CONSTANT_TABLE: u8 = 5;
39pub const LBC_CONSTANT_CLOSURE: u8 = 6;
40
41pub fn table<'a>(mut input: &'a [u8], k: &[LuaConstant]) -> IResult<&'a [u8], ConstTable> {
42 let numk;
43 (input, numk) = varint(input)?;
44 let mut result = ConstTable {
45 hash: Vec::with_capacity(numk),
46 ..Default::default()
47 };
48 for _ in 0..numk {
49 let ik;
50 (input, ik) = varint(input)?;
51 result
52 .hash
53 .push((k[ik].clone(), LuaConstant::Number(LuaNumber::Integer(0))));
54 }
55 Ok((input, result))
56}
57
58pub fn constants<'a>(
59 mut input: &'a [u8],
60 stable: &[Rc<ByteBuf>],
61) -> IResult<&'a [u8], Vec<LuaConstant>> {
62 let num;
63 (input, num) = varint(input)?;
64 let mut result = Vec::with_capacity(num);
65 for _ in 0..num {
66 let ty;
67 let k;
68 (input, ty) = le_u8(input)?;
69 (input, k) = match ty {
70 LBC_CONSTANT_NIL => Ok((input, LuaConstant::Null)),
71 LBC_CONSTANT_BOOLEAN => map(le_u8, |b| LuaConstant::Bool(b != 0))(input),
72 LBC_CONSTANT_NUMBER => map(complete::f64(Endianness::Little), |n| {
73 LuaConstant::Number(LuaNumber::Float(n))
74 })(input),
75 LBC_CONSTANT_STRING => map(|i| string(i, stable), LuaConstant::String)(input),
76 LBC_CONSTANT_IMPORT => map(complete::be_u32, |_| LuaConstant::Null)(input),
78 LBC_CONSTANT_TABLE => {
79 let (input, t) = table(input, &result)?;
80 Ok((input, LuaConstant::Table(t.into())))
81 }
82 LBC_CONSTANT_CLOSURE => map(varint, LuaConstant::Proto)(input),
83 _ => unreachable!("const type: {ty}"),
85 }?;
86 result.push(k);
87 }
88 Ok((input, result))
89}
90
91pub fn bytecode(input: &[u8]) -> IResult<&[u8], LuaChunk> {
92 let (mut input, _version) = le_u8(input)?;
93 let mut types_version = 0;
94
95 if _version >= 4 {
96 let (input2, _types_version) = le_u8(input)?;
97 types_version = _types_version;
98 input = input2
99 }
100
101 let (input, stable) = length_count(
103 varint,
104 map(
105 |input| {
106 let (input, n) = varint(input)?;
107 context("string", take(n))(input)
108 },
109 |s| Rc::new(ByteBuf::from(s.to_vec())),
110 ),
111 )(input)?;
112
113 let (mut input, num) = varint(input)?;
115 let mut protos = Vec::with_capacity(num);
116
117 let string = |i| string(i, &stable);
118
119 for _ in 0..num {
120 let (mut input1, (max_stack, num_params, num_upvalues, is_vararg)) =
121 tuple((be_u8, be_u8, be_u8, be_u8))(input)?;
122
123 if _version >= 4 {
124 let (input2, _flags) = be_u8(input1)?;
125 let (mut input2, types_size) = varint(input2)?;
126
127 if types_size > 0 && types_version == 1 {
128 for _ in 0..types_size {
129 let (input3, _byte) = le_u8(input2)?;
130 input2 = input3
131 }
132 }
133
134 input1 = input2;
135 }
136
137 let (mut input1, (instructions, constants, prototypes, line_defined, name, has_lineinfo)) =
138 tuple((
139 length_count(varint, complete::u32(Endianness::Little)),
140 |i| constants(i, stable.as_slice()),
141 length_count(varint, map(varint, |i| core::mem::take(&mut protos[i]))),
142 map(varint, |n| n as u64),
143 string,
144 le_u8,
145 ))(input1)?;
146
147 if has_lineinfo > 0 {
148 let (input2, linegaplog2) = be_u8(input1)?;
149 let intervals = ((instructions.len() - 1) >> (linegaplog2 as usize)) + 1;
150 let (input2, _lineinfo) = count(be_u8, instructions.len())(input2)?;
151 let (input2, _abslineinfo) = count(complete::be_i32, intervals)(input2)?;
152 input1 = input2;
153 }
154
155 let (mut input1, has_debuginfo) = le_u8(input1)?;
156 let mut locals = vec![];
157 let mut upvalue_names = vec![];
158 if has_debuginfo > 0 {
159 (input1, (locals, upvalue_names)) = tuple((
160 length_count(
161 varint,
162 map(
163 tuple((string, varint, varint, le_u8)),
164 |(name, start, end, reg)| LuaLocal {
165 name: String::from_utf8_lossy(name.as_slice()).into(),
166 start_pc: start as _,
167 end_pc: end as _,
168 reg,
169 },
170 ),
171 ),
172 length_count(varint, map(string, |s| s.as_ref().clone().into_vec())),
173 ))(input1)?;
174 }
175
176 input = input1;
177 let proto = LuaChunk {
178 name: name.to_vec(),
179 line_defined,
180 last_line_defined: 0,
181 num_upvalues,
182 num_params,
183 max_stack,
184 prototypes,
185 is_vararg: if is_vararg > 0 {
186 Some(LuaVarArgInfo {
187 has_arg: true,
188 needs_arg: true,
189 })
190 } else {
191 None
192 },
193 instructions,
194 constants,
195 locals,
196 upvalue_names,
197 ..Default::default()
198 };
199 protos.push(proto);
200 }
201
202 let (input, mainid) = varint(input)?;
203 let main = core::mem::take(&mut protos[mainid]);
204 assert!(!main.is_empty());
205
206 Ok((input, main))
207}