inside_baseball/script/
decode.rs

1use crate::{
2    script::{
3        cursor::{read_i16, read_i32, read_string, read_u16, read_u8, read_var},
4        ins::{GenericArg, GenericIns, Ins, ItemSize, Operand},
5    },
6    utils::subslice_offset,
7};
8use arrayvec::ArrayVec;
9use std::{cell::Cell, fmt::Write};
10
11pub fn disasm_to_string(code: &[u8]) -> String {
12    let mut output = String::new();
13    let decoder = Decoder::new(code);
14    while let Some((pos, ins)) = decoder.next() {
15        writeln!(output, "0x{:04x}  {}", pos, ins).unwrap();
16    }
17    // Anything left was not decoded; dump the raw bytes
18    for pos in decoder.pos()..decoder.code.len() {
19        writeln!(output, "0x{:04x}  .db 0x{:02x}", pos, decoder.code[pos]).unwrap();
20    }
21    output
22}
23
24pub struct Decoder<'a> {
25    code: &'a [u8],
26    pub pos: Cell<usize>,
27}
28
29impl<'a> Decoder<'a> {
30    pub fn new(code: &'a [u8]) -> Self {
31        Self {
32            code,
33            pos: Cell::new(0),
34        }
35    }
36
37    pub fn next(&self) -> Option<(usize, Ins<'a>)> {
38        let pos = self.pos.get();
39        let code = &self.code[pos..];
40        let mut cur = code;
41        let ins = decode_ins(&mut cur)?;
42        self.pos.set(pos + subslice_offset(code, cur));
43        Some((pos, ins))
44    }
45
46    pub fn pos(&self) -> usize {
47        self.pos.get()
48    }
49
50    pub fn set_pos(&self, value: usize) {
51        self.pos.set(value);
52    }
53}
54
55/// I regret everything.
56macro_rules! ins {
57    ////////////////////////////////////////////////////////////////////////////
58    // Parse
59    ////////////////////////////////////////////////////////////////////////////
60
61    // Parse bytecode
62    (
63        @parse,
64        bytecode = {},
65        name = $name:tt,
66        ops = $ops:tt,
67        args = $args:tt,
68        retval = $retval:tt,
69        rest = {[$($byte:expr),+] $(, $($rest:tt)*)?},
70    ) => {
71        ins!(
72            @parse,
73            bytecode = {bytearray![$($byte),+]},
74            name = $name,
75            ops = $ops,
76            args = $args,
77            retval = $retval,
78            rest = {$($($rest)*)?},
79        )
80    };
81    // Parse name
82    (
83        @parse,
84        bytecode = $bytecode:tt,
85        name = {},
86        ops = $ops:tt,
87        args = $args:tt,
88        retval = $retval:tt,
89        rest = {name = $name:expr $(, $($rest:tt)*)?},
90    ) => {
91        ins!(
92            @parse,
93            bytecode = $bytecode,
94            name = {Some($name)},
95            ops = $ops,
96            args = $args,
97            retval = $retval,
98            rest = {$($($rest)*)?},
99        )
100    };
101    // Parse ops
102    (
103        @parse,
104        bytecode = $bytecode:tt,
105        name = $name:tt,
106        ops = {},
107        args = $args:tt,
108        retval = $retval:tt,
109        rest = {ops = [$($ops:tt)*] $(, $($rest:tt)*)?},
110    ) => {
111        ins!(
112            @parse,
113            bytecode = $bytecode,
114            name = $name,
115            ops = {ins!(@ops, begin = {$($ops)*})},
116            args = $args,
117            retval = $retval,
118            rest = {$($($rest)*)?},
119        )
120    };
121    // Parse args
122    (
123        @parse,
124        bytecode = $bytecode:tt,
125        name = $name:tt,
126        ops = $ops:tt,
127        args = {},
128        retval = $retval:tt,
129        rest = {args = [$($args:tt)*] $(, $($rest:tt)*)?},
130    ) => {
131        ins!(
132            @parse,
133            bytecode = $bytecode,
134            name = $name,
135            ops = $ops,
136            args = {ins!(@args, begin = {$($args)*})},
137            retval = $retval,
138            rest = {$($($rest)*)?},
139        )
140    };
141    // Parse retval
142    (
143        @parse,
144        bytecode = $bytecode:tt,
145        name = $name:tt,
146        ops = $ops:tt,
147        args = $args:tt,
148        retval = {},
149        rest = {retval $(, $($rest:tt)*)?},
150    ) => {
151        ins!(
152            @parse,
153            bytecode = $bytecode,
154            name = $name,
155            ops = $ops,
156            args = $args,
157            retval = {true},
158            rest = {$($($rest)*)?},
159        )
160    };
161    // Done parsing. Transfer control to @set_defaults
162    (
163        @parse,
164        bytecode = $bytecode:tt,
165        name = $name:tt,
166        ops = $ops:tt,
167        args = $args:tt,
168        retval = $retval:tt,
169        rest = {},
170    ) => {
171        ins!(
172            @set_defaults,
173            bytecode = $bytecode,
174            name = $name,
175            ops = $ops,
176            args = $args,
177            retval = $retval,
178        )
179    };
180
181    ////////////////////////////////////////////////////////////////////////////
182    // Defaults
183    ////////////////////////////////////////////////////////////////////////////
184
185    // Set default name
186    (
187        @set_defaults,
188        bytecode = $bytecode:tt,
189        name = {},
190        ops = $ops:tt,
191        args = $args:tt,
192        retval = $retval:tt,
193    ) => {
194        ins!(
195            @set_defaults,
196            bytecode = $bytecode,
197            name = {None},
198            ops = $ops,
199            args = $args,
200            retval = $retval,
201        )
202    };
203    // Set default ops
204    (
205        @set_defaults,
206        bytecode = $bytecode:tt,
207        name = $name:tt,
208        ops = {},
209        args = $args:tt,
210        retval = $retval:tt,
211    ) => {
212        ins!(
213            @set_defaults,
214            bytecode = $bytecode,
215            name = $name,
216            ops = {arrayvec![]},
217            args = $args,
218            retval = $retval,
219        )
220    };
221    // Set default args
222    (
223        @set_defaults,
224        bytecode = $bytecode:tt,
225        name = $name:tt,
226        ops = $ops:tt,
227        args = {},
228        retval = $retval:tt,
229    ) => {
230        ins!(
231            @set_defaults,
232            bytecode = $bytecode,
233            name = $name,
234            ops = $ops,
235            args = {&[]},
236            retval = $retval,
237        )
238    };
239    // Set default retval
240    (
241        @set_defaults,
242        bytecode = $bytecode:tt,
243        name = $name:tt,
244        ops = $ops:tt,
245        args = $args:tt,
246        retval = {},
247    ) => {
248        ins!(
249            @set_defaults,
250            bytecode = $bytecode,
251            name = $name,
252            ops = $ops,
253            args = $args,
254            retval = {false},
255        )
256    };
257    // All defaults set. Emit.
258    (
259        @set_defaults,
260        bytecode = {$($bytecode:tt)*},
261        name = {$($name:tt)*},
262        ops = {$($ops:tt)*},
263        args = {$($args:tt)*},
264        retval = {$retval:tt},
265    ) => {
266        Some(Ins::Generic({$($bytecode)*}, {$($ops)*}, &GenericIns {
267            name: {$($name)*},
268            args: {$($args)*},
269            returns_value: $retval,
270        }))
271    };
272
273    ////////////////////////////////////////////////////////////////////////////
274    // Ops
275    ////////////////////////////////////////////////////////////////////////////
276
277    // Entry point
278    (
279        @ops,
280        begin = {$($in:tt)*}
281    ) => {
282        ins!(
283            @ops,
284            in = {$($in)*},
285            out = {,},
286        )
287    };
288    // Parse one item
289    (
290        @ops,
291        in = {$type:ident: $value:expr $(, $($tail:tt)*)?},
292        out = {$($out:tt)*},
293    ) => {
294        ins!(
295            @ops,
296            in = {$($($tail)*)?},
297            out = {$($out)* ins!(@op_kind $type)($value),},
298        )
299    };
300    (@op_kind u8) => {
301        Operand::Byte
302    };
303    (@op_kind i16) => {
304        Operand::I16
305    };
306    (@op_kind var) => {
307        Operand::Var
308    };
309    (@op_kind string) => {
310        Operand::String
311    };
312    // Done. Return them.
313    (
314        @ops,
315        in = {},
316        out = {, $($out:tt)*},
317    ) => {
318        arrayvec![$($out)*]
319    };
320
321    ////////////////////////////////////////////////////////////////////////////
322    // Args
323    ////////////////////////////////////////////////////////////////////////////
324
325    // Entry point
326    (
327        @args,
328        begin = {$($in:tt)*}
329    ) => {
330        ins!(
331            @args,
332            in = {$($in)*},
333            out = {,},
334        )
335    };
336    // Parse one item
337    (
338        @args,
339        in = {$arg:ident $(, $($rest:tt)*)?},
340        out = {$($out:tt)*},
341    ) => {
342        ins!(
343            @args,
344            in = {$($($rest)*)?},
345            out = {$($out)* ins!(@one_arg $arg),},
346        )
347    };
348    (@one_arg int) => {
349        GenericArg::Int
350    };
351    (@one_arg string) => {
352        GenericArg::String
353    };
354    (@one_arg list) => {
355        GenericArg::List
356    };
357    (@one_arg script) => {
358        GenericArg::IntScript
359    };
360    // Done. Return a slice.
361    (
362        @args,
363        in = {},
364        out = {, $($out:tt)*},
365    ) => {
366        &[$($out)*]
367    };
368
369    ////////////////////////////////////////////////////////////////////////////
370    // Public matchers
371    ////////////////////////////////////////////////////////////////////////////
372
373    // Fallback error
374    (@ $($rest:tt)*) => {
375        compile_error!("macro has failed")
376    };
377
378    // Main entrypoint. Transfer control to @parse
379    ($($rest:tt)*) => {
380        ins!(
381            @parse,
382            bytecode = {},
383            name = {},
384            ops = {},
385            args = {},
386            retval = {},
387            rest = {$($rest)*},
388        )
389    };
390}
391
392#[allow(clippy::too_many_lines)]
393fn decode_ins<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
394    let opcode = read_u8(code)?;
395    #[allow(clippy::match_same_arms)]
396    match opcode {
397        0x00 => op_00_push_byte(code),
398        0x01 => op_01_push_i16(code),
399        0x02 => op_02_push_i32(code),
400        0x03 => op_03_push_var(code),
401        0x04 => op_04_push_str(code),
402        0x07 => op_07_get_array_item(code),
403        0x0a => Some(Ins::StackDupN(read_u16(code)?)),
404        0x0b => op_0b_get_array_item_2d(code),
405        0x0c => Some(Ins::StackDup),
406        0x0d => Some(Ins::Not),
407        0x0e => Some(Ins::Equal),
408        0x0f => Some(Ins::NotEqual),
409        0x10 => Some(Ins::Greater),
410        0x11 => Some(Ins::Less),
411        0x12 => Some(Ins::LessOrEqual),
412        0x13 => Some(Ins::GreaterOrEqual),
413        0x14 => Some(Ins::Add),
414        0x15 => Some(Ins::Sub),
415        0x16 => Some(Ins::Mul),
416        0x17 => Some(Ins::Div),
417        0x18 => Some(Ins::LogicalAnd),
418        0x19 => Some(Ins::LogicalOr),
419        0x1a => Some(Ins::PopDiscard),
420        0x1b => Some(Ins::In),
421        0x1c => op_1c_image(code),
422        0x1d => ins!([0x1d], name = "min", args = [int, int], retval),
423        0x1e => ins!([0x1e], name = "max", args = [int, int], retval),
424        0x1f => ins!([0x1f], name = "sin", args = [int], retval),
425        0x20 => ins!([0x20], name = "cos", args = [int], retval),
426        0x21 => ins!([0x21], args = [int], retval),
427        0x22 => ins!([0x22], name = "atan2", args = [int, int], retval),
428        0x23 => ins!([0x22], name = "atan4", args = [int, int, int, int], retval),
429        0x24 => op_24(code),
430        0x25 => op_25_sprite_retval(code),
431        0x26 => op_26_sprite(code),
432        0x27 => op_27_sprite_group_retval(code),
433        0x28 => op_28_sprite_group(code),
434        0x29 => op_29_image_retval(code),
435        0x2a => {
436            ins!(
437                [0x2a],
438                name = "actor-get-property",
439                args = [int, int, int],
440                retval,
441            )
442        }
443        0x2b => op_2b_begin_script(code),
444        0x2c => op_2c_become_script(code),
445        0x30 => ins!([0x30], name = "mod", args = [int, int], retval),
446        0x31 => ins!([0x31], name = "shl", args = [int, int], retval),
447        0x32 => ins!([0x32], name = "shr", args = [int, int], retval),
448        0x34 => {
449            ins!(
450                [0x34],
451                name = "find-all-objects-of-class",
452                args = [int, list],
453                retval,
454            )
455        }
456        0x36 => ins!([0x36], name = "iif", args = [int, int, int], retval),
457        0x37 => op_37_dim_array(code),
458        0x38 => op_38_redim_array(code),
459        0x3a => op_3a_array_sort(code),
460        0x43 => op_43_set(code),
461        0x46 => ins!([0x46], name = "file-get-size", args = [string], retval),
462        0x47 => op_47_set_array_item(code),
463        0x48 => ins!([0x48], name = "atoi", args = [int], retval),
464        0x4b => op_4b_set_array_item_2d(code),
465        0x4d => op_4d_read_ini(code),
466        0x4e => op_4e_write_ini(code),
467        0x4f => op_4f_inc(code),
468        0x50 => ins!([0x50]),
469        0x53 => ins!([0x53], name = "inc-array-item", ops = [var: read_var(code)?], args = [int]),
470        0x57 => op_57_dec(code),
471        0x58 => op_58(code),
472        0x5a => {
473            ins!(
474                [0x5a],
475                name = "sound-samples-remaining",
476                args = [int],
477                retval,
478            )
479        }
480        0x5b => ins!([0x5b], name = "dec-array-item", ops = [var: read_var(code)?], args = [int]),
481        0x5c => op_5c_jump_if(code),
482        0x5d => op_5d_jump_unless(code),
483        0x5e => op_5e_run_script(code),
484        0x60 => op_60_start_script(code),
485        0x61 => op_61_draw_object(code),
486        0x62 => ins!([0x62], name = "draw-x62", args = [int]),
487        0x63 => op_63_array_sizes(code),
488        0x64 => ins!([0x64], name = "get-free-arrays", retval),
489        0x65 => ins!([0x65], name = "finish-script"),
490        0x66 => ins!([0x66], name = "free-script"),
491        0x69 => op_69_window(code),
492        0x6a => ins!([0x6a], args = [int]),
493        0x6b => op_6b_cursor(code),
494        0x6c => ins!([0x6c], name = "stop-script"),
495        0x6d => ins!([0x6d], name = "get-class", args = [int, list], retval),
496        0x6e => ins!([0x6e], name = "put-class", args = [int, list]),
497        0x6f => ins!([0x6f], name = "object-get-state", args = [int], retval),
498        0x70 => ins!([0x70], name = "object-put-state", args = [int, int]),
499        0x73 => op_73_jump(code),
500        0x74 => op_74_sound(code),
501        0x75 => ins!([0x75], name = "stop-sound", args = [int]),
502        0x7b => ins!([0x7b], name = "go-to-room", args = [int]),
503        0x7c => ins!([0x7c], name = "free-running-script", args = [script]),
504        0x7f => ins!([0x7f], name = "put-actor", args = [int, int, int, int]),
505        0x82 => ins!([0x82], name = "actor-do-anim", args = [int, int]),
506        0x87 => ins!([0x87], name = "random", args = [int], retval),
507        0x88 => ins!([0x88], name = "random2", args = [int, int], retval),
508        0x8b => ins!([0x8b], name = "is-script-running", args = [script], retval),
509        0x8c => ins!([0x8c], name = "get-room", args = [int], retval),
510        0x8d => ins!([0x8d], name = "get-x", args = [int], retval),
511        0x8e => ins!([0x8e], name = "get-y", args = [int], retval),
512        0x91 => ins!([0x91], name = "actor-get-costume", args = [int], retval),
513        0x94 => op_94(code),
514        0x95 => op_95_cutscene_start(code),
515        0x96 => ins!([0x96], name = "cutscene-end"),
516        0x98 => ins!([0x98], name = "is-sound-playing", args = [int], retval),
517        0x9b => op_9b(code),
518        0x9c => op_9c(code),
519        0x9d => op_9d_actor(code),
520        0x9e => op_9e_palette(code),
521        0x9f => ins!([0x9f], args = [int, int], retval),
522        0xa0 => ins!([0xa0], args = [int, int], retval),
523        0xa2 => ins!([0xa2], args = [int], retval),
524        0xa3 => ins!([0xa3], args = [int, int], retval),
525        0xa4 => op_a4_array(code),
526        0xa6 => ins!([0xa6], args = [int, int, int, int, int]),
527        0xa7 => ins!([0xa7], name = "pop-discard", args = [int]),
528        0xa9 => op_a9(code),
529        0xaa => ins!([0xaa], name = "actor-get-xaa", args = [int], retval),
530        0xad => Some(Ins::In), // same as 0x1b except iterates in reverse order?
531        0xae => op_ae(code),
532        0xb0 => ins!([0xb0], name = "sleep-frames", args = [int]),
533        0xb1 => ins!([0xb1], name = "sleep-seconds", args = [int]),
534        0xb3 => ins!([0xb3], name = "stop-script-34"),
535        0xb5 => op_b5(code),
536        0xb6 => op_b6(code),
537        0xb7 => op_b7(code),
538        0xb8 => op_b8(code),
539        0xb9 => op_b9(code),
540        0xba => ins!([0xba], ops = [string: read_string(code)?], args = [int]),
541        0xbb => ins!([0xbb], ops = [string: read_string(code)?]),
542        0xbc => op_bc_array(code),
543        0xbd => ins!([0xbd], name = "return", args = [int]),
544        0xbf => ins!([0xbf], name = "call-script", args = [script, list], retval),
545        0xc0 => op_c0_dim_array(code),
546        0xc1 => ins!([0xc1], name = "pop-discard-2", args = [int, string]),
547        0xc4 => ins!([0xc4], name = "abs", args = [int], retval),
548        0xc8 => ins!([0xc8], name = "kludge-retval", args = [list], retval),
549        0xc9 => ins!([0xc9], name = "kludge", args = [list]),
550        0xca => ins!([0xca], name = "sleep", args = [int]),
551        0xcb => ins!([0xcb], name = "pick", args = [int, list], retval),
552        0xcf => ins!([0xcf], name = "input-dialog", args = [string], retval),
553        0xd0 => ins!([0xd0], name = "now"),
554        0xd1 => ins!([0xd1]),
555        0xd2 => ins!([0xd2], name = "actor-get-var", args = [int, int], retval),
556        0xd4 => {
557            ins!([0xd4], name = "shuffle-array", ops = [var: read_var(code)?], args = [int, int])
558        }
559        0xd5 => op_d5_exec_script(code),
560        0xd6 => Some(Ins::BitwiseAnd),
561        0xd7 => Some(Ins::BitwiseOr),
562        0xd9 => ins!([0xd9], name = "file-close", args = [int]),
563        0xda => ins!([0xda], name = "file-open", args = [string, int], retval),
564        0xdb => op_db_file_read(code),
565        0xdc => op_dc_file_write(code),
566        0xde => ins!([0xde], name = "file-delete", args = [string]),
567        0xe0 => op_e0(code),
568        0xe2 => ins!([0xe2], args = [int]),
569        0xe3 => ins!([0xe3], ops = [var: read_var(code)?], args = [list], retval),
570        0xe9 => ins!([0xe9], name = "file-seek", args = [int, int, int]),
571        0xea => {
572            ins!(
573                [0xea],
574                name = "redim",
575                ops = [u8: read_u8(code)?, var: read_var(code)?],
576                args = [int, int],
577            )
578        }
579        0xeb => ins!([0xeb], name = "file-get-pos", args = [int], retval),
580        0xec => ins!([0xec], name = "strcpy", args = [int], retval),
581        0xed => ins!([0xed], args = [int, int, int], retval),
582        0xee => ins!([0xee], name = "strlen", args = [int], retval),
583        0xef => ins!([0xef], name = "substr", args = [int, int, int], retval),
584        0xf1 => ins!([0xf1], name = "strcmp", args = [int, int], retval),
585        0xf2 => op_f2_percent_loaded(code),
586        0xf3 => op_f3_read_ini(code),
587        0xf4 => op_f4_write_ini(code),
588        0xf5 => ins!([0xf5], args = [int, int, int], retval),
589        0xf6 => {
590            ins!(
591                [0xf6],
592                name = "array-find",
593                args = [int, int, int, int],
594                retval,
595            )
596        }
597        0xf8 => op_f8_get_size(code),
598        0xf9 => ins!([0xf9], name = "create-directory", args = [string]),
599        0xfa => op_fa_window_title(code),
600        0xfb => op_fb(code),
601        0xfc => ins!([0xfc], args = [int, int], retval),
602        _ => None,
603    }
604}
605
606fn op_00_push_byte<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
607    Some(Ins::Push(Operand::Byte(read_u8(code)?)))
608}
609
610fn op_01_push_i16<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
611    Some(Ins::Push(Operand::I16(read_i16(code)?)))
612}
613
614fn op_02_push_i32<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
615    Some(Ins::Push(Operand::I32(read_i32(code)?)))
616}
617
618fn op_03_push_var<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
619    Some(Ins::Push(Operand::Var(read_var(code)?)))
620}
621
622fn op_04_push_str<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
623    Some(Ins::Push(Operand::String(read_string(code)?)))
624}
625
626fn op_07_get_array_item<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
627    Some(Ins::GetArrayItem(read_var(code)?))
628}
629
630fn op_0b_get_array_item_2d<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
631    Some(Ins::GetArrayItem2D(read_var(code)?))
632}
633
634fn op_1c_image<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
635    match read_u8(code)? {
636        0x20 => ins!([0x1c, 0x20], name = "image-x20", args = [int]),
637        0x21 => ins!([0x1c, 0x21], name = "image-x21", args = [int]),
638        0x30 => ins!([0x1c, 0x30], name = "image-x30"),
639        0x31 => ins!([0x1c, 0x31], name = "image-path", args = [string]),
640        0x33 => {
641            ins!(
642                [0x1c, 0x33],
643                name = "image-x33",
644                args = [int, int, int, int, int],
645            )
646        }
647        0x34 => ins!([0x1c, 0x34], name = "image-x34", args = [int]),
648        0x36 => ins!([0x1c, 0x36], name = "image-x36", args = [int]),
649        0x38 => {
650            ins!(
651                [0x1c, 0x38],
652                name = "image-x38",
653                args = [int, int, int, int, int],
654            )
655        }
656        0x39 => ins!([0x1c, 0x39], name = "image-select", args = [int]),
657        0x41 => ins!([0x1c, 0x41], name = "image-pos", args = [int, int]),
658        0x56 => ins!([0x1c, 0x56], name = "image-palette", args = [int]),
659        0x62 => ins!([0x1c, 0x62], name = "image-x62", args = [int]),
660        0x85 => {
661            ins!(
662                [0x1c, 0x85],
663                name = "image-x85",
664                args = [int, int, int, int, int],
665            )
666        }
667        0x89 => ins!([0x1c, 0x89], name = "image-render-into", args = [int]),
668        0x9a => ins!([0x1c, 0x9a], name = "image-x9a", args = [int, int]),
669        0xd9 => ins!([0x1c, 0xd9], name = "image-op-create"),
670        0xf6 => ins!([0x1c, 0xf6], name = "image-xf6", args = [int]),
671        0xff => ins!([0x1c, 0xff], name = "image-xff"),
672        _ => None,
673    }
674}
675
676fn op_24<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
677    match read_u8(code)? {
678        0x1c => {
679            ins!(
680                [0x24, 0x1c],
681                name = "distance-2d",
682                args = [int, int, int, int],
683                retval,
684            )
685        }
686        0x1d => {
687            ins!(
688                [0x24, 0x1d],
689                name = "distance-3d",
690                args = [int, int, int, int, int, int],
691                retval,
692            )
693        }
694        _ => None,
695    }
696}
697
698fn op_25_sprite_retval<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
699    match read_u8(code)? {
700        0x1e => {
701            ins!(
702                [0x25, 0x1e],
703                name = "sprite-get-hotspot-x",
704                args = [int],
705                retval,
706            )
707        }
708        0x1f => {
709            ins!(
710                [0x25, 0x1f],
711                name = "sprite-get-hotspot-y",
712                args = [int],
713                retval,
714            )
715        }
716        0x24 => ins!([0x25, 0x24], name = "sprite-get-x24", args = [int], retval),
717        0x25 => {
718            ins!(
719                [0x25, 0x25],
720                name = "sprite-get-group",
721                args = [int],
722                retval,
723            )
724        }
725        0x26 => {
726            ins!(
727                [0x25, 0x26],
728                name = "sprite-get-translated-hotspot-x",
729                args = [int],
730                retval,
731            )
732        }
733        0x27 => {
734            ins!(
735                [0x25, 0x27],
736                name = "sprite-get-translated-hotspot-y",
737                args = [int],
738                retval,
739            )
740        }
741        0x2b => ins!([0x25, 0x2b], name = "sprite-get-x2b", args = [int], retval),
742        0x2d => {
743            ins!(
744                [0x25, 0x2d],
745                name = "sprite-get-x2d",
746                args = [int, int, int, int, list],
747                retval,
748            )
749        }
750        0x34 => ins!([0x25, 0x34], name = "sprite-get-wiz", args = [int], retval),
751        0x3f => {
752            ins!(
753                [0x25, 0x3f],
754                name = "sprite-get-image",
755                args = [int],
756                retval,
757            )
758        }
759        0x56 => ins!([0x25, 0x56], name = "sprite-get-x56", args = [int], retval),
760        0x7c => ins!([0x25, 0x7c], name = "sprite-get-x7c", args = [int], retval),
761        0x7d => {
762            ins!(
763                [0x25, 0x7d],
764                name = "sprite-has-class",
765                args = [int, list],
766                retval,
767            )
768        }
769        0xc6 => {
770            ins!(
771                [0x25, 0xc6],
772                name = "sprite-get-xc6",
773                args = [int, int],
774                retval,
775            )
776        }
777        _ => None,
778    }
779}
780
781fn op_26_sprite<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
782    match read_u8(code)? {
783        0x25 => ins!([0x26, 0x25], name = "sprite-x25", args = [int]),
784        0x2a => ins!([0x26, 0x2a], name = "sprite-x2a", args = [int, int]),
785        0x2b => ins!([0x26, 0x2b], name = "sprite-x2b", args = [int]),
786        0x2c => {
787            ins!(
788                [0x26, 0x2c],
789                name = "sprite-move-hotspot",
790                args = [int, int],
791            )
792        }
793        0x34 => ins!([0x26, 0x34], name = "sprite-x34", args = [int]),
794        0x39 => ins!([0x26, 0x39], name = "sprite-set-range", args = [int, int]),
795        0x3f => ins!([0x26, 0x3f], name = "sprite-set-image", args = [int]),
796        0x41 => ins!([0x26, 0x41], name = "sprite-set-hotspot", args = [int, int]),
797        0x4d => ins!([0x26, 0x4d], name = "sprite-x4d", args = [int, int]),
798        0x52 => ins!([0x26, 0x52], name = "sprite-x52", args = [int]),
799        0x56 => ins!([0x26, 0x56], name = "sprite-palette", args = [int]),
800        0x61 => ins!([0x26, 0x61], name = "sprite-x61", args = [int]),
801        0x62 => ins!([0x26, 0x62], name = "sprite-x62", args = [int]),
802        0x7c => ins!([0x26, 0x7c], name = "sprite-x7c", args = [int]),
803        0x7d => ins!([0x26, 0x7d], name = "sprite-x7d", args = [list]),
804        0x8c => ins!([0x26, 0x8c], name = "sprite-x8c", args = [int]),
805        0x9e => ins!([0x26, 0x9e], name = "sprite-x9e"),
806        0xc6 => ins!([0x26, 0xc6], name = "sprite-xc6", args = [int, int]),
807        0xd9 => ins!([0x26, 0xd9], name = "sprite-clear"),
808        _ => None,
809    }
810}
811
812fn op_27_sprite_group_retval<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
813    match read_u8(code)? {
814        0x08 => {
815            ins!(
816                [0x27, 0x08],
817                name = "sprite-group-get-x08",
818                args = [int],
819                retval,
820            )
821        }
822        0x1e => {
823            ins!(
824                [0x27, 0x1e],
825                name = "sprite-group-get-x",
826                args = [int],
827                retval,
828            )
829        }
830        0x1f => {
831            ins!(
832                [0x27, 0x1f],
833                name = "sprite-group-get-y",
834                args = [int],
835                retval,
836            )
837        }
838        _ => None,
839    }
840}
841
842fn op_28_sprite_group<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
843    match read_u8(code)? {
844        0x2c => ins!([0x28, 0x2c], name = "sprite-group-x2c", args = [int, int]),
845        0x39 => ins!([0x28, 0x39], name = "sprite-group-select", args = [int]),
846        0x41 => ins!([0x28, 0x41], name = "sprite-group-x41", args = [int, int]),
847        0x43 => {
848            ins!(
849                [0x28, 0x43],
850                name = "sprite-group-x43",
851                args = [int, int, int, int],
852            )
853        }
854        0xd9 => ins!([0x28, 0xd9], name = "sprite-group-clear"),
855        _ => None,
856    }
857}
858
859fn op_29_image_retval<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
860    match read_u8(code)? {
861        0x1e => {
862            ins!(
863                [0x29, 0x1e],
864                name = "image-get-hotspot-x",
865                args = [int, int],
866                retval,
867            )
868        }
869        0x1f => {
870            ins!(
871                [0x29, 0x1f],
872                name = "image-get-hotspot-y",
873                args = [int, int],
874                retval,
875            )
876        }
877        0x20 => {
878            ins!(
879                [0x29, 0x20],
880                name = "image-get-width",
881                args = [int, int],
882                retval,
883            )
884        }
885        0x21 => {
886            ins!(
887                [0x29, 0x21],
888                name = "image-get-height",
889                args = [int, int],
890                retval,
891            )
892        }
893        0x24 => ins!([0x29, 0x24], name = "image-get-x24", args = [int], retval),
894        0x42 => {
895            ins!(
896                [0x29, 0x42],
897                name = "image-get-x42",
898                args = [int, int, int, int],
899                retval,
900            )
901        }
902        _ => None,
903    }
904}
905
906fn op_2b_begin_script<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
907    match read_u8(code)? {
908        0x01 => {
909            ins!(
910                [0x2b, 0x01],
911                name = "begin-script",
912                args = [script, int, list],
913            )
914        }
915        _ => None,
916    }
917}
918
919fn op_2c_become_script<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
920    match read_u8(code)? {
921        0x01 => {
922            ins!(
923                [0x2c, 0x01],
924                name = "become-script",
925                args = [script, int, list],
926            )
927        }
928        _ => None,
929    }
930}
931
932fn op_37_dim_array<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
933    let item_size = to_item_size(read_u8(code)?)?;
934    let var = read_var(code)?;
935    Some(Ins::DimArray2D(item_size, var))
936}
937
938fn op_38_redim_array<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
939    let item_size = to_item_size(read_u8(code)?)?;
940    let var = read_var(code)?;
941    Some(Ins::RedimArray2D(item_size, var))
942}
943
944fn to_item_size(n: u8) -> Option<ItemSize> {
945    match n {
946        2 => Some(ItemSize::Bit),
947        4 => Some(ItemSize::Byte),
948        5 => Some(ItemSize::I16),
949        6 => Some(ItemSize::I32),
950        7 => Some(ItemSize::Char),
951        _ => None,
952    }
953}
954
955fn op_3a_array_sort<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
956    match read_u8(code)? {
957        0x81 => {
958            ins!(
959                [0x3a, 0x81],
960                name = "array-sort",
961                ops = [var: read_var(code)?],
962                args = [int, int, int, int, int],
963            )
964        }
965        _ => None,
966    }
967}
968
969fn op_43_set<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
970    Some(Ins::Set(read_var(code)?))
971}
972
973fn op_47_set_array_item<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
974    Some(Ins::SetArrayItem(read_var(code)?))
975}
976
977fn op_4b_set_array_item_2d<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
978    Some(Ins::SetArrayItem2D(read_var(code)?))
979}
980
981fn op_4d_read_ini<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
982    match read_u8(code)? {
983        0x06 => {
984            ins!(
985                [0x4d, 0x06],
986                name = "read-ini-int",
987                args = [string, string, string],
988                retval,
989            )
990        }
991        0x07 => {
992            ins!(
993                [0x4d, 0x07],
994                name = "read-ini-string",
995                args = [string, string, string],
996                retval,
997            )
998        }
999        _ => None,
1000    }
1001}
1002
1003fn op_4e_write_ini<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1004    match read_u8(code)? {
1005        0x06 => {
1006            ins!(
1007                [0x4e, 0x06],
1008                name = "write-ini-int",
1009                args = [string, string, string, int],
1010            )
1011        }
1012        0x07 => {
1013            ins!(
1014                [0x4e, 0x07],
1015                name = "write-ini-string",
1016                args = [string, string, string, string],
1017            )
1018        }
1019        _ => None,
1020    }
1021}
1022
1023fn op_4f_inc<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1024    Some(Ins::Inc(read_var(code)?))
1025}
1026
1027fn op_57_dec<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1028    Some(Ins::Dec(read_var(code)?))
1029}
1030
1031fn op_58<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1032    match read_u8(code)? {
1033        0x0a => ins!([0x58, 0x0a], name = "timer-x0a", args = [int], retval),
1034        _ => None,
1035    }
1036}
1037
1038fn op_5c_jump_if<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1039    Some(Ins::JumpIf(read_i16(code)?))
1040}
1041
1042fn op_5d_jump_unless<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1043    Some(Ins::JumpUnless(read_i16(code)?))
1044}
1045
1046fn op_5e_run_script<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1047    match read_u8(code)? {
1048        0x01 => ins!([0x5e, 0x01], name = "run-script", args = [script, list]),
1049        0xc3 => ins!([0x5e, 0xc3], name = "run-script-xc3", args = [script, list]),
1050        _ => None,
1051    }
1052}
1053
1054fn op_60_start_script<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1055    match read_u8(code)? {
1056        0x01 => {
1057            ins!(
1058                [0x60, 0x01],
1059                name = "start-script",
1060                args = [script, int, list],
1061            )
1062        }
1063        0xc3 => {
1064            ins!(
1065                [0x60, 0xc3],
1066                name = "start-script-xc3",
1067                args = [script, int, list],
1068            )
1069        }
1070        _ => None,
1071    }
1072}
1073
1074fn op_61_draw_object<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1075    match read_u8(code)? {
1076        0x3f => ins!([0x61, 0x3f], name = "draw-object-x3f", args = [int, int]),
1077        _ => None,
1078    }
1079}
1080
1081fn op_63_array_sizes<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1082    match read_u8(code)? {
1083        0x01 => ins!([0x63, 0x01], name = "array-width", ops = [var: read_var(code)?], retval),
1084        0x03 => ins!([0x63, 0x03], name = "array-width", ops = [var: read_var(code)?], retval),
1085        0x02 => ins!([0x63, 0x02], name = "array-height", ops = [var: read_var(code)?], retval),
1086        _ => None,
1087    }
1088}
1089
1090fn op_69_window<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1091    match read_u8(code)? {
1092        0x39 => ins!([0x69, 0x39], name = "window-select", args = [int]),
1093        0x3a => ins!([0x69, 0x3a], name = "window-x3a", args = [int]),
1094        0x3f => ins!([0x69, 0x3f], name = "window-set-image", args = [int]),
1095        0xd9 => ins!([0x69, 0xd9], name = "window-destroy"),
1096        0xf3 => ins!([0x69, 0xf3], name = "window-set-text", args = [string]),
1097        0xff => ins!([0x69, 0xff], name = "window-xff"),
1098        _ => None,
1099    }
1100}
1101
1102fn op_6b_cursor<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1103    match read_u8(code)? {
1104        0x13 => ins!([0x6b, 0x13], name = "cursor-set-image-bw", args = [int]),
1105        0x14 => ins!([0x6b, 0x14], name = "cursor-set-image-color", args = [int]),
1106        0x90 => ins!([0x6b, 0x90], name = "cursor-show"),
1107        0x91 => ins!([0x6b, 0x91], name = "cursor-hide"),
1108        0x92 => ins!([0x6b, 0x92], name = "cursor-input-enable"),
1109        0x93 => ins!([0x6b, 0x93], name = "cursor-input-disable"),
1110        0x94 => ins!([0x6b, 0x94], name = "cursor-visible-increment"),
1111        0x95 => ins!([0x6b, 0x95], name = "cursor-visible-decrement"),
1112        0x9c => ins!([0x6b, 0x9c], name = "cursor-charset", args = [int]),
1113        _ => None,
1114    }
1115}
1116
1117fn op_73_jump<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1118    Some(Ins::Jump(read_i16(code)?))
1119}
1120
1121fn op_74_sound<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1122    match read_u8(code)? {
1123        0x09 => ins!([0x74, 0x09], name = "sound-x09"),
1124        0xe6 => ins!([0x74, 0xe6], name = "sound-channel", args = [int]),
1125        0xe7 => ins!([0x74, 0xe7], name = "sound-offset", args = [int]),
1126        0xe8 => ins!([0x74, 0xe8], name = "sound-select", args = [int]),
1127        0xf5 => ins!([0x74, 0xf5], name = "sound-xf5"),
1128        0xff => ins!([0x74, 0xff], name = "sound-play"),
1129        _ => None,
1130    }
1131}
1132
1133fn op_94<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1134    match read_u8(code)? {
1135        0x42 => {
1136            ins!(
1137                [0x94, 0x42],
1138                name = "palette-x42",
1139                args = [int, int],
1140                retval,
1141            )
1142        }
1143        0xd9 => {
1144            ins!(
1145                [0x94, 0xd9],
1146                name = "palette-xd9",
1147                args = [int, int, int],
1148                retval,
1149            )
1150        }
1151        _ => None,
1152    }
1153}
1154
1155fn op_95_cutscene_start<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1156    ins!([0x95], name = "cutscene-start", ops = [u8: read_u8(code)?, i16: read_i16(code)?])
1157}
1158
1159fn op_9b<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1160    match read_u8(code)? {
1161        0x64 => ins!([0x9b, 0x64], name = "load-script", args = [script]),
1162        0x65 => ins!([0x9b, 0x65], name = "load-sound", args = [int]),
1163        0x66 => ins!([0x9b, 0x66], name = "load-costume", args = [int]),
1164        0x69 => ins!([0x9b, 0x69], name = "free-sound", args = [int]),
1165        0x6a => ins!([0x9b, 0x6a], name = "free-costume", args = [int]),
1166        0x6c => ins!([0x9b, 0x6c], name = "lock-script", args = [script]),
1167        0x6e => ins!([0x9b, 0x6e], name = "lock-costume", args = [int]),
1168        0x72 => ins!([0x9b, 0x72], name = "unlock-costume", args = [int]),
1169        0x75 => ins!([0x9b, 0x75], name = "load-charset", args = [int]),
1170        0x79 => ins!([0x9b, 0x79], name = "queue-sound", args = [int]),
1171        0x7a => ins!([0x9b, 0x7a], name = "queue-costume", args = [int]),
1172        0x7b => ins!([0x9b, 0x7b], name = "queue-room", args = [int]),
1173        0x9f => ins!([0x9b, 0x9f], name = "unlock-image", args = [int]),
1174        0xc0 => ins!([0x9b, 0xc0], name = "free-image", args = [int]),
1175        0xc9 => ins!([0x9b, 0xc9], name = "load-image", args = [int]),
1176        0xca => ins!([0x9b, 0xca], name = "lock-image", args = [int]),
1177        0xcb => ins!([0x9b, 0xcb], name = "queue-image", args = [int]),
1178        _ => None,
1179    }
1180}
1181
1182fn op_9c<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1183    match read_u8(code)? {
1184        0xb5 => ins!([0x9c, 0xb5], args = [int]),
1185        0xd5 => ins!([0x9c, 0xd5], args = [int]),
1186        0xdd => ins!([0x9c, 0xdd], name = "save-load-game", args = [int, string]),
1187        _ => None,
1188    }
1189}
1190
1191fn op_9d_actor<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1192    match read_u8(code)? {
1193        0x15 => ins!([0x9d, 0x15], name = "actor-set-conditons", args = [list]),
1194        0x2b => ins!([0x9d, 0x2b], name = "actor-x2b", args = [int]),
1195        0x40 => {
1196            ins!(
1197                [0x9d, 0x40],
1198                name = "actor-default-clip-rect",
1199                args = [int, int, int, int],
1200            )
1201        }
1202        0x41 => ins!([0x9d, 0x41], name = "actor-move", args = [int, int]),
1203        0x43 => {
1204            ins!(
1205                [0x9d, 0x43],
1206                name = "actor-clip-rect",
1207                args = [int, int, int, int],
1208            )
1209        }
1210        0x4c => ins!([0x9d, 0x4c], name = "actor-set-costume", args = [int]),
1211        0x4e => ins!([0x9d, 0x4e], name = "actor-set-sounds", args = [list]),
1212        0x50 => ins!([0x9d, 0x50], name = "actor-x50", args = [int, int]),
1213        0x54 => ins!([0x9d, 0x54], name = "actor-x54", args = [int]),
1214        0x56 => ins!([0x9d, 0x56], name = "actor-x56", args = [int, int]),
1215        0x57 => ins!([0x9d, 0x57], name = "actor-x57", args = [int]),
1216        0x5c => ins!([0x9d, 0x5c], name = "actor-set-scale", args = [int]),
1217        0x5d => ins!([0x9d, 0x5d], name = "actor-x5d"),
1218        0x5e => ins!([0x9d, 0x5e], name = "actor-x5e", args = [int]),
1219        0x5f => ins!([0x9d, 0x5f], name = "actor-x5f"),
1220        0x61 => ins!([0x9d, 0x61], name = "actor-x61", args = [int]),
1221        0x62 => ins!([0x9d, 0x62], name = "actor-x62", args = [int]),
1222        0x63 => ins!([0x9d, 0x63], name = "actor-x63", args = [int, int]),
1223        0xc5 => ins!([0x9d, 0xc5], name = "actor-select", args = [int]),
1224        0xc6 => ins!([0x9d, 0xc6], name = "actor-set-var", args = [int, int]),
1225        0xd9 => ins!([0x9d, 0xd9], name = "actor-xd9"),
1226        0xda => ins!([0x9d, 0xda], name = "actor-xda"),
1227        _ => None,
1228    }
1229}
1230
1231fn op_9e_palette<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1232    match read_u8(code)? {
1233        0x39 => ins!([0x9e, 0x39], name = "palette-select", args = [int]),
1234        0x3f => ins!([0x9e, 0x3f], name = "palette-load", args = [int, int]),
1235        0x42 => {
1236            ins!(
1237                [0x9e, 0x42],
1238                name = "palette-set-rgb",
1239                args = [int, int, int, int, int],
1240            )
1241        }
1242        0x46 => {
1243            ins!(
1244                [0x9e, 0x46],
1245                name = "palette-set-16bit",
1246                args = [int, int, int],
1247            )
1248        }
1249        0x56 => ins!([0x9e, 0x56], name = "palette-copy", args = [int]),
1250        0xd9 => ins!([0x9e, 0xd9], name = "palette-reset"),
1251        0xff => ins!([0x9e, 0xff], name = "palette-unselect"),
1252        _ => None,
1253    }
1254}
1255
1256fn op_a4_array<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1257    match read_u8(code)? {
1258        0x07 => Some(Ins::AssignString(read_var(code)?)),
1259        0x7e => {
1260            ins!(
1261                [0xa4, 0x7e],
1262                name = "array-fill-list",
1263                ops = [var: read_var(code)?],
1264                args = [int, int, int, int, list],
1265            )
1266        }
1267        0x7f => {
1268            ins!(
1269                [0xa4, 0x7f],
1270                name = "array-copy-range",
1271                ops = [var: read_var(code)?, var: read_var(code)?],
1272                args = [int, int, int, int, int, int, int, int],
1273            )
1274        }
1275        0x80 => {
1276            ins!(
1277                [0xa4, 0x80],
1278                name = "array-fill-values",
1279                ops = [var: read_var(code)?],
1280                args = [int, int, int, int, int, int],
1281            )
1282        }
1283        0xc2 => Some(Ins::Sprintf(read_var(code)?)),
1284        0xd0 => {
1285            ins!(
1286                [0xa4, 0xd0],
1287                name = "array-set",
1288                ops = [var: read_var(code)?],
1289                args = [list, int],
1290            )
1291        }
1292        0xd4 => {
1293            ins!(
1294                [0xa4, 0xd4],
1295                name = "array-set-row",
1296                ops = [var: read_var(code)?],
1297                args = [int, list],
1298            )
1299        }
1300        _ => None,
1301    }
1302}
1303
1304fn op_a9<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1305    match read_u8(code)? {
1306        0xa9 => ins!([0xa9, 0xa9]),
1307        _ => None,
1308    }
1309}
1310
1311fn op_ae<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1312    match read_u8(code)? {
1313        0x1a => ins!([0xae, 0x1a], name = "paint"),
1314        0xa0 => ins!([0xae, 0xa0], name = "quit-without-prompt"),
1315        0xf4 => ins!([0xae, 0xf4], name = "prompt-quit"),
1316        _ => None,
1317    }
1318}
1319
1320fn op_b5<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1321    op_b4_thru_b9(0xb5, false, code)
1322}
1323
1324fn op_b6<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1325    op_b4_thru_b9(0xb6, false, code)
1326}
1327
1328fn op_b7<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1329    op_b4_thru_b9(0xb7, false, code)
1330}
1331
1332fn op_b8<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1333    op_b4_thru_b9(0xb8, true, code)
1334}
1335
1336fn op_b9<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1337    op_b4_thru_b9(0xb9, true, code)
1338}
1339
1340fn op_b4_thru_b9<'a>(opcode: u8, flag: bool, code: &mut &'a [u8]) -> Option<Ins<'a>> {
1341    let sub = read_u8(code)?;
1342    let stack_hack = opcode == 0xb9;
1343    if stack_hack && sub != 0xfe {
1344        return None;
1345    }
1346    match sub {
1347        0x41 => ins!([opcode, 0x41], args = [int, int]),
1348        0x45 => ins!([opcode, 0x45]),
1349        0x4b => ins!([opcode, 0x4b], ops = [string: read_string(code)?]),
1350        0xc2 => ins!([opcode, 0xc2], ops = [string: read_string(code)?], args = [int, list]),
1351        0xe1 => ins!([opcode, 0xe1], args = [int]),
1352        0xf9 => ins!([opcode, 0xf9], args = [list]),
1353        0xfe => {
1354            Some(Ins::Generic(
1355                bytearray![opcode, 0xfe],
1356                ArrayVec::new(),
1357                if flag && !stack_hack {
1358                    &GenericIns {
1359                        name: None,
1360                        args: &[GenericArg::Int],
1361                        returns_value: false,
1362                    }
1363                } else {
1364                    &GenericIns {
1365                        name: None,
1366                        args: &[],
1367                        returns_value: false,
1368                    }
1369                },
1370            ))
1371        }
1372        0xff => ins!([opcode, 0xff]),
1373        _ => None,
1374    }
1375}
1376
1377fn op_bc_array<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1378    let n = read_u8(code)?;
1379    if let Some(item_size) = to_item_size(n) {
1380        return Some(Ins::DimArray1DSimple(item_size, read_var(code)?));
1381    }
1382    match n {
1383        0xcc => ins!([0xbc, 0xcc], name = "free-array", ops = [var: read_var(code)?]),
1384        _ => None,
1385    }
1386}
1387
1388fn op_c0_dim_array<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1389    let item_size = to_item_size(read_u8(code)?)?;
1390    let var = read_var(code)?;
1391    Some(Ins::DimArray2DSimple(item_size, var))
1392}
1393
1394fn op_d5_exec_script<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1395    match read_u8(code)? {
1396        0x01 => {
1397            ins!([0xd5, 0x01], name = "exec-script", args = [script, list])
1398        }
1399        0xc3 => {
1400            ins!(
1401                [0xd5, 0xc3],
1402                name = "exec-script-xc3",
1403                args = [script, list],
1404            )
1405        }
1406        _ => None,
1407    }
1408}
1409
1410fn op_db_file_read<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1411    match read_u8(code)? {
1412        0x05 => ins!([0xdb, 0x05], name = "file-read-i16", args = [int], retval),
1413        0x08 => {
1414            ins!(
1415                [0xdb, 0x08],
1416                name = "file-read-array",
1417                ops = [u8: read_u8(code)?],
1418                args = [int, int],
1419                retval,
1420            )
1421        }
1422        _ => None,
1423    }
1424}
1425
1426fn op_dc_file_write<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1427    match read_u8(code)? {
1428        0x05 => ins!([0xdc, 0x05], name = "file-write-i16", args = [int, int]),
1429        0x08 => {
1430            ins!(
1431                [0xdc, 0x08],
1432                name = "file-write-array",
1433                ops = [u8: read_u8(code)?],
1434                args = [int, int],
1435            )
1436        }
1437        _ => None,
1438    }
1439}
1440
1441fn op_e0<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1442    match read_u8(code)? {
1443        0x42 => ins!([0xe0, 0x42], args = [int, int, int, int, int, int]),
1444        _ => None,
1445    }
1446}
1447
1448fn op_f2_percent_loaded<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1449    match read_u8(code)? {
1450        0xe3 => {
1451            ins!(
1452                [0xf2, 0xe3],
1453                name = "percent-loaded-costume",
1454                args = [int],
1455                retval,
1456            )
1457        }
1458        _ => None,
1459    }
1460}
1461
1462fn op_f3_read_ini<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1463    match read_u8(code)? {
1464        0x06 => ins!([0xf3, 0x06], name = "read-ini-int", args = [string], retval),
1465        0x07 => {
1466            ins!(
1467                [0xf3, 0x07],
1468                name = "read-ini-string",
1469                args = [string],
1470                retval,
1471            )
1472        }
1473        _ => None,
1474    }
1475}
1476
1477fn op_f4_write_ini<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1478    match read_u8(code)? {
1479        0x06 => ins!([0xf4, 0x06], name = "write-ini-int", args = [string, int]),
1480        0x07 => {
1481            ins!(
1482                [0xf4, 0x07],
1483                name = "write-ini-string",
1484                args = [string, string],
1485            )
1486        }
1487        _ => None,
1488    }
1489}
1490
1491fn op_f8_get_size<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1492    match read_u8(code)? {
1493        0x0d => ins!([0xf8, 0x0d], name = "get-sound-size", args = [int], retval),
1494        _ => None,
1495    }
1496}
1497
1498fn op_fa_window_title<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1499    match read_u8(code)? {
1500        0xf3 => ins!([0xfa, 0xf3], name = "set-window-title", args = [string]),
1501        _ => None,
1502    }
1503}
1504
1505fn op_fb<'a>(code: &mut &'a [u8]) -> Option<Ins<'a>> {
1506    match read_u8(code)? {
1507        0xf7 => ins!([0xfb, 0xf7], name = "polygon-xf7", args = [int, int]),
1508        0xf8 => {
1509            ins!(
1510                [0xfb, 0xf8],
1511                name = "polygon-quadrilateral",
1512                args = [int, int, int, int, int, int, int, int, int],
1513            )
1514        }
1515        _ => None,
1516    }
1517}