1use anyhow::{Context, Result, anyhow, bail};
2
3use crate::model::{ConstPools, Tjs2File, Tjs2Object, Variant};
4
5const FILE_TAG_LE: u32 =
6 ('T' as u32) | (('J' as u32) << 8) | (('S' as u32) << 16) | (('2' as u32) << 24);
7const VER_TAG_LE: u32 = ('1' as u32) | (('0' as u32) << 8) | (('0' as u32) << 16) | (0u32 << 24);
8
9const OBJ_TAG_LE: u32 =
10 ('O' as u32) | (('B' as u32) << 8) | (('J' as u32) << 16) | (('S' as u32) << 24);
11const DATA_TAG_LE: u32 =
12 ('D' as u32) | (('A' as u32) << 8) | (('T' as u32) << 16) | (('A' as u32) << 24);
13
14const TYPE_VOID: i16 = 0;
16const TYPE_OBJECT: i16 = 1;
17const TYPE_INTER_OBJECT: i16 = 2;
18const TYPE_STRING: i16 = 3;
19const TYPE_OCTET: i16 = 4;
20const TYPE_REAL: i16 = 5;
21const TYPE_BYTE: i16 = 6;
22const TYPE_SHORT: i16 = 7;
23const TYPE_INTEGER: i16 = 8;
24const TYPE_LONG: i16 = 9;
25const TYPE_INTER_GENERATOR: i16 = 10;
26const TYPE_UNKNOWN: i16 = -1;
27
28pub fn load_tjs2_bytecode(buf: &[u8]) -> Result<Tjs2File> {
29 if buf.len() < 12 {
30 bail!("bytecode too small: {} bytes", buf.len());
31 }
32 let mut r = Reader::new(buf);
33
34 let file_tag = r.read_u32_le().context("read file tag")?;
36 let ver_tag = r.read_u32_le().context("read version tag")?;
37 if file_tag != FILE_TAG_LE {
38 bail!(
39 "fourcc mismatch: expect {:?}, got {:?}",
40 b"TJS2",
41 u32_to_4cc(file_tag)
42 );
43 }
44 if ver_tag != VER_TAG_LE {
45 bail!(
46 "version mismatch: expect {:?}, got {:?}",
47 b"100\0",
48 u32_to_4cc(ver_tag)
49 );
50 }
51 let file_size = r.read_u32_le().context("read file size")? as usize;
52 if file_size != buf.len() {
53 bail!(
54 "file size mismatch: header={}, actual={}",
55 file_size,
56 buf.len()
57 );
58 }
59
60 let data_tag = r.read_u32_le().context("read DATA tag")?;
62 if data_tag != DATA_TAG_LE {
63 bail!(
64 "fourcc mismatch: expect {:?}, got {:?}",
65 b"DATA",
66 u32_to_4cc(data_tag)
67 );
68 }
69 let data_chunk_size = r.read_u32_le().context("read DATA chunk size")? as usize;
70 if data_chunk_size < 8 {
71 bail!("DATA chunk size too small: {}", data_chunk_size);
72 }
73 let data_payload_size = data_chunk_size - 8;
74 let data_start = r.pos();
75 let data_end = data_start
76 .checked_add(data_payload_size)
77 .ok_or_else(|| anyhow!("overflow computing DATA end"))?;
78 if data_end > buf.len() {
79 bail!(
80 "DATA payload out of range: end={}, file={}",
81 data_end,
82 buf.len()
83 );
84 }
85 let pools = read_data_area(&buf[data_start..data_end]).context("parse DATA area")?;
86 r.set_pos(data_end);
87
88 let objs_tag = r.read_u32_le().context("read OBJS tag")?;
90 if objs_tag != OBJ_TAG_LE {
91 bail!(
92 "fourcc mismatch: expect {:?}, got {:?}",
93 b"OBJS",
94 u32_to_4cc(objs_tag)
95 );
96 }
97 let objs_chunk_size = r.read_u32_le().context("read OBJS chunk size")? as usize;
98 if objs_chunk_size < 8 {
99 bail!("OBJS chunk size too small: {}", objs_chunk_size);
100 }
101 let objs_payload_size = objs_chunk_size - 8;
102 let objs_start = r.pos();
103 let objs_end = objs_start
104 .checked_add(objs_payload_size)
105 .ok_or_else(|| anyhow!("overflow computing OBJS end"))?;
106 if objs_end > buf.len() {
107 bail!(
108 "OBJS payload out of range: end={}, file={}",
109 objs_end,
110 buf.len()
111 );
112 }
113
114 let (toplevel, objects) =
115 read_objects(&buf[objs_start..objs_end], &pools).context("parse OBJS area")?;
116 r.set_pos(objs_end);
117
118 if r.pos() != buf.len() {
120 bail!(
121 "unexpected trailing bytes: pos={}, file={}",
122 r.pos(),
123 buf.len()
124 );
125 }
126
127 Ok(Tjs2File {
128 toplevel,
129 const_pools: pools,
130 objects,
131 })
132}
133
134fn read_data_area(payload: &[u8]) -> Result<ConstPools> {
135 let mut r = Reader::new(payload);
136 let mut pools = ConstPools::default();
137
138 let count = r.read_u32_le().context("DATA.bytes.count")? as usize;
140 if count > 0 {
141 let b = r.read_bytes(count).context("DATA.bytes.data")?;
142 pools.bytes = b.iter().map(|x| *x as i8).collect();
143 r.align4().context("DATA.bytes.align4")?;
144 }
145
146 let count = r.read_u32_le().context("DATA.shorts.count")? as usize;
148 if count > 0 {
149 pools.shorts.reserve(count);
150 for _ in 0..count {
151 pools
152 .shorts
153 .push(r.read_i16_le().context("DATA.shorts.elem")?);
154 }
155 if (count & 1) == 1 {
156 let _ = r.read_u16_le().context("DATA.shorts.pad")?;
158 }
159 }
160
161 let count = r.read_u32_le().context("DATA.ints.count")? as usize;
163 if count > 0 {
164 pools.ints.reserve(count);
165 for _ in 0..count {
166 pools.ints.push(r.read_i32_le().context("DATA.ints.elem")?);
167 }
168 }
169
170 let count = r.read_u32_le().context("DATA.longs.count")? as usize;
172 if count > 0 {
173 pools.longs.reserve(count);
174 for _ in 0..count {
175 pools
176 .longs
177 .push(r.read_i64_le().context("DATA.longs.elem")?);
178 }
179 }
180
181 let count = r.read_u32_le().context("DATA.doubles.count")? as usize;
183 if count > 0 {
184 pools.doubles.reserve(count);
185 for _ in 0..count {
186 let bits = r.read_u64_le().context("DATA.doubles.bits")?;
187 pools.doubles.push(f64::from_bits(bits));
188 }
189 }
190
191 let count = r.read_u32_le().context("DATA.strings.count")? as usize;
193 pools.strings.reserve(count);
194 for _ in 0..count {
195 let len = r.read_u32_le().context("DATA.strings.len")? as usize;
196 let mut units = Vec::with_capacity(len);
197 for _ in 0..len {
198 units.push(r.read_u16_le().context("DATA.strings.unit")?);
199 }
200 if (len & 1) == 1 {
201 let _ = r.read_u16_le().context("DATA.strings.pad")?;
202 }
203 pools.strings.push(String::from_utf16_lossy(&units));
204 }
205
206 let count = r.read_u32_le().context("DATA.octets.count")? as usize;
208 pools.octets.reserve(count);
209 for _ in 0..count {
210 let cap = r.read_u32_le().context("DATA.octets.len")? as usize;
211 let data = r.read_bytes(cap).context("DATA.octets.data")?.to_vec();
212 pools.octets.push(data);
213 r.align4().context("DATA.octets.align4")?;
214 }
215
216 if r.pos() != payload.len() {
217 bail!(
218 "DATA: payload not fully consumed: pos={}, payload={}",
219 r.pos(),
220 payload.len()
221 );
222 }
223
224 Ok(pools)
225}
226
227fn read_objects(payload: &[u8], pools: &ConstPools) -> Result<(i32, Vec<Tjs2Object>)> {
228 let mut r = Reader::new(payload);
229
230 let toplevel = r.read_i32_le().context("OBJS.toplevel")?;
231 let objcount = r.read_i32_le().context("OBJS.objcount")?;
232 if objcount < 0 {
233 bail!("OBJS.objcount is negative: {}", objcount);
234 }
235 let objcount = objcount as usize;
236
237 let mut objects: Vec<Tjs2Object> = Vec::with_capacity(objcount);
238
239 for o in 0..objcount {
241 let tag = r.read_u32_le().context("OBJS.obj.tag")?;
242 if tag != FILE_TAG_LE {
243 bail!(
244 "object fourcc mismatch: expect {:?}, got {:?}",
245 b"TJS2",
246 u32_to_4cc(tag)
247 );
248 }
249 let _obj_payload_size = r.read_u32_le().context("OBJS.obj.size")? as usize;
250
251 let parent = r.read_i32_le().context("obj.parent")?;
252 let name_idx = r.read_i32_le().context("obj.name_idx")?;
253 let context_type = r.read_i32_le().context("obj.context_type")?;
254 let max_variable_count = r.read_i32_le().context("obj.max_variable_count")?;
255 let variable_reserve_count = r.read_i32_le().context("obj.variable_reserve_count")?;
256 let max_frame_count = r.read_i32_le().context("obj.max_frame_count")?;
257 let func_decl_arg_count = r.read_i32_le().context("obj.func_decl_arg_count")?;
258 let func_decl_unnamed_arg_array_base = r
259 .read_i32_le()
260 .context("obj.func_decl_unnamed_arg_array_base")?;
261 let func_decl_collapse_base = r.read_i32_le().context("obj.func_decl_collapse_base")?;
262 let prop_setter = r.read_i32_le().context("obj.prop_setter")?;
263 let prop_getter = r.read_i32_le().context("obj.prop_getter")?;
264 let super_class_getter = r.read_i32_le().context("obj.super_class_getter")?;
265
266 let srcpos_count = r.read_i32_le().context("obj.srcpos.count")?;
268 if srcpos_count < 0 {
269 bail!("obj.srcpos.count is negative: {}", srcpos_count);
270 }
271 let srcpos_count = srcpos_count as usize;
272 for _ in 0..srcpos_count {
274 let _ = r.read_i32_le().context("obj.srcpos.codepos")?;
275 }
276 for _ in 0..srcpos_count {
277 let _ = r.read_i32_le().context("obj.srcpos.srcpos")?;
278 }
279
280 let code_count = r.read_i32_le().context("obj.code.count")?;
282 if code_count < 0 {
283 bail!("obj.code.count is negative: {}", code_count);
284 }
285 let code_count = code_count as usize;
286 let mut code: Vec<i32> = Vec::with_capacity(code_count);
287 for _ in 0..code_count {
288 code.push(r.read_i16_le().context("obj.code.word")? as i32);
289 }
290 if (code_count & 1) == 1 {
292 let _ = r.read_u16_le().context("obj.code.pad")?;
293 }
294
295 let data_count = r.read_i32_le().context("obj.data.count")?;
297 if data_count < 0 {
298 bail!("obj.data.count is negative: {}", data_count);
299 }
300 let data_count = data_count as usize;
301 let mut data: Vec<Variant> = Vec::with_capacity(data_count);
302 for _ in 0..data_count {
303 let ty = r.read_i16_le().context("obj.data.type")?;
304 let idx = r.read_i16_le().context("obj.data.index")? as i32;
305 let v = match ty {
306 TYPE_VOID => Variant::Void,
307 TYPE_OBJECT => Variant::NullObject,
308 TYPE_INTER_OBJECT => Variant::InterObject(idx),
309 TYPE_INTER_GENERATOR => Variant::InterGenerator(idx),
310 TYPE_STRING => Variant::String(idx),
311 TYPE_OCTET => Variant::Octet(idx),
312 TYPE_REAL => Variant::Real(idx),
313 TYPE_BYTE => Variant::Byte(idx),
314 TYPE_SHORT => Variant::Short(idx),
315 TYPE_INTEGER => Variant::Integer(idx),
316 TYPE_LONG => Variant::Long(idx),
317 TYPE_UNKNOWN | _ => Variant::Unknown,
318 };
319 data.push(v);
320 }
321
322 let scg_count = r.read_i32_le().context("obj.scgetterps.count")?;
324 if scg_count < 0 {
325 bail!("obj.scgetterps.count is negative: {}", scg_count);
326 }
327 let scg_count = scg_count as usize;
328 let mut scgetterps: Vec<i32> = Vec::with_capacity(scg_count);
329 for _ in 0..scg_count {
330 scgetterps.push(r.read_i32_le().context("obj.scgetterps.elem")?);
331 }
332
333 let prop_count = r.read_i32_le().context("obj.properties.count")?;
335 if prop_count < 0 {
336 bail!("obj.properties.count is negative: {}", prop_count);
337 }
338 let prop_count = prop_count as usize;
339 let mut properties = Vec::new();
340 if prop_count > 0 {
341 properties.reserve(prop_count);
342 for _ in 0..prop_count {
343 let pname = r.read_i32_le().context("obj.properties.name")?;
344 let pobj = r.read_i32_le().context("obj.properties.obj")?;
345 properties.push((pname, pobj));
346 }
347 }
348
349 let name = if name_idx >= 0 {
350 pools.strings.get(name_idx as usize).cloned()
351 } else {
352 None
353 };
354
355 objects.push(Tjs2Object {
356 index: o,
357 parent,
358 name_string_index: name_idx,
359 name,
360 context_type,
361 max_variable_count,
362 variable_reserve_count,
363 max_frame_count,
364 func_decl_arg_count,
365 func_decl_unnamed_arg_array_base,
366 func_decl_collapse_base,
367 prop_setter,
368 prop_getter,
369 super_class_getter,
370 code,
371 data,
372 scgetterps,
373 properties,
374 });
375 }
376
377 if r.pos() != payload.len() {
378 bail!(
379 "OBJS: payload not fully consumed: pos={}, payload={}",
380 r.pos(),
381 payload.len()
382 );
383 }
384
385 Ok((toplevel, objects))
386}
387
388fn u32_to_4cc(x: u32) -> [u8; 4] {
389 [
390 (x & 0xff) as u8,
391 ((x >> 8) & 0xff) as u8,
392 ((x >> 16) & 0xff) as u8,
393 ((x >> 24) & 0xff) as u8,
394 ]
395}
396
397struct Reader<'a> {
398 buf: &'a [u8],
399 pos: usize,
400}
401
402impl<'a> Reader<'a> {
403 fn new(buf: &'a [u8]) -> Self {
404 Self { buf, pos: 0 }
405 }
406 fn pos(&self) -> usize {
407 self.pos
408 }
409 fn set_pos(&mut self, pos: usize) {
410 self.pos = pos;
411 }
412
413 fn read_bytes(&mut self, n: usize) -> Result<&'a [u8]> {
414 let end = self.pos.checked_add(n).ok_or_else(|| anyhow!("overflow"))?;
415 if end > self.buf.len() {
416 bail!("failed to fill whole buffer");
417 }
418 let out = &self.buf[self.pos..end];
419 self.pos = end;
420 Ok(out)
421 }
422
423 fn read_u16_le(&mut self) -> Result<u16> {
424 let b = self.read_bytes(2)?;
425 Ok(u16::from_le_bytes([b[0], b[1]]))
426 }
427 fn read_i16_le(&mut self) -> Result<i16> {
428 Ok(self.read_u16_le()? as i16)
429 }
430
431 fn read_u32_le(&mut self) -> Result<u32> {
432 let b = self.read_bytes(4)?;
433 Ok(u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
434 }
435 fn read_i32_le(&mut self) -> Result<i32> {
436 Ok(self.read_u32_le()? as i32)
437 }
438
439 fn read_u64_le(&mut self) -> Result<u64> {
440 let b = self.read_bytes(8)?;
441 Ok(u64::from_le_bytes([
442 b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
443 ]))
444 }
445 fn read_i64_le(&mut self) -> Result<i64> {
446 Ok(self.read_u64_le()? as i64)
447 }
448
449 fn align4(&mut self) -> Result<()> {
450 let rem = self.pos & 3;
451 if rem != 0 {
452 let pad = 4 - rem;
453 let _ = self.read_bytes(pad)?;
454 }
455 Ok(())
456 }
457}