diff --git a/src/ast/meta.rs b/src/ast/meta.rs index a9ede23..3959461 100644 --- a/src/ast/meta.rs +++ b/src/ast/meta.rs @@ -576,7 +576,7 @@ impl ToMeta for f32 { fn to_meta(&self) -> Meta { Meta::Scalar(sp!((*self).into())) } } impl ToMeta for bool { - fn to_meta(&self) -> Meta { Meta::Scalar(sp!(ast::Expr::LitInt { value: *self as i32, radix: ast::IntRadix::Bool })) } + fn to_meta(&self) -> Meta { Meta::Scalar(sp!(ast::Expr::LitInt { value: *self as i32, format: ast::IntFormat { unsigned: true, radix: ast::IntRadix::Bool } })) } } impl ToMeta for String { fn to_meta(&self) -> Meta { Meta::Scalar(sp!(self.to_owned().into())) } diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 6b4ea18..0eb0c4a 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -455,7 +455,7 @@ pub enum Expr { value: raw::LangInt, /// A hint to the formatter on how it should write the integer. /// (may not necessarily represent the original radix of a parsed token) - radix: IntRadix, + format: IntFormat, }, LitFloat { value: raw::LangFloat }, LitString(LitString), @@ -469,14 +469,18 @@ pub enum Expr { }, } -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct IntFormat { + pub unsigned: bool, + pub radix: IntRadix, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum IntRadix { /// Display as decimal. Dec, /// Display as hexadecimal, with an `0x` prefix. Hex, - /// Display as potentially negative hexadecimal, with an `0x` prefix. - SignedHex, /// Display as binary, with an `0b` prefix. Bin, /// Use `true` and `false` if the value is `1` or `0`. Otherwise, fall back to decimal. @@ -695,6 +699,9 @@ string_enum! { #[strum(serialize = "~")] BitNot, #[strum(serialize = "sin")] Sin, #[strum(serialize = "cos")] Cos, + #[strum(serialize = "tan")] Tan, + #[strum(serialize = "acos")] Acos, + #[strum(serialize = "atan")] Atan, #[strum(serialize = "sqrt")] Sqrt, #[strum(serialize = "$")] EncodeI, #[strum(serialize = "%")] EncodeF, @@ -711,6 +718,9 @@ impl UnOpKind { UnOpKind::BitNot => OpClass::Bitwise, UnOpKind::Sin => OpClass::FloatMath, UnOpKind::Cos => OpClass::FloatMath, + UnOpKind::Tan => OpClass::FloatMath, + UnOpKind::Acos => OpClass::FloatMath, + UnOpKind::Atan => OpClass::FloatMath, UnOpKind::Sqrt => OpClass::FloatMath, UnOpKind::CastI => OpClass::Cast, UnOpKind::CastF => OpClass::Cast, @@ -842,7 +852,7 @@ string_enum! { } impl From for Expr { - fn from(value: raw::LangInt) -> Expr { Expr::LitInt { value, radix: IntRadix::Dec } } + fn from(value: raw::LangInt) -> Expr { Expr::LitInt { value, format: IntFormat { unsigned: false, radix: IntRadix::Dec } } } } impl From for Expr { fn from(value: raw::LangFloat) -> Expr { Expr::LitFloat { value } } @@ -1257,7 +1267,7 @@ macro_rules! generate_visitor_stuff { Expr::XcrementOp { op: _, order: _, var } => { v.visit_var(var); }, - Expr::LitInt { value: _, radix: _ } => {}, + Expr::LitInt { value: _, format: _ } => {}, Expr::LitFloat { value: _ } => {}, Expr::LitString(_s) => {}, Expr::LabelProperty { .. } => {}, diff --git a/src/cli_def.rs b/src/cli_def.rs index 690bee4..408905b 100644 --- a/src/cli_def.rs +++ b/src/cli_def.rs @@ -576,7 +576,10 @@ pub mod msg_compile { truth.load_mapfiles_from_pragmas(game, &ast)?; }, MsgMode::Mission => {}, - MsgMode::Ending => return Err(truth.emit(error!("--ending is not yet implemented"))), + MsgMode::Ending => { + load_mapfiles(truth, game, &[LanguageKey::End], mapfile_options)?; + truth.load_mapfiles_from_pragmas(game, &ast)?; + }, } let mut truth = truth.validate_defs()?; @@ -590,7 +593,10 @@ pub mod msg_compile { let msg = truth.compile_mission(game, &ast)?; truth.write_mission(game, out_path, &msg)?; }, - MsgMode::Ending => unreachable!(), + MsgMode::Ending => { + let msg = truth.compile_msg(game, LanguageKey::End, &ast)?; + truth.write_msg(game, LanguageKey::End, out_path, &msg)?; + }, } if let Some(debug_info_path) = debug_info_path { truth.prepare_and_write_debug_info(debug_info_path)?; @@ -636,7 +642,14 @@ pub mod msg_decompile { let msg = truth.read_mission(game, in_path)?; truth.decompile_mission(game, &msg) }, - MsgMode::Ending => return Err(truth.emit(error!("--ending is not yet implemented"))), + MsgMode::Ending => { + let mapfile_options = add_env_mapfile_for_decomp(mapfile_options, ".endm"); + load_mapfiles(truth, game, &[LanguageKey::End], &mapfile_options)?; + + let mut truth = truth.validate_defs()?; + let msg = truth.read_msg(game, LanguageKey::End, in_path)?; + truth.decompile_msg(game, LanguageKey::End, &msg, decompile_options) + }, } } } diff --git a/src/context/defs.rs b/src/context/defs.rs index e4a1bed..da9af0e 100644 --- a/src/context/defs.rs +++ b/src/context/defs.rs @@ -1114,34 +1114,18 @@ impl Signature { signature_from_func_ast(ty_keyword, params) } - pub(crate) fn validate(&self, ctx: &CompilerContext) -> Result<(), ErrorReported> { - self._check_non_optional_after_optional(ctx) - } - - fn _check_non_optional_after_optional(&self, ctx: &CompilerContext) -> Result<(), ErrorReported> { - let mut first_optional = None; - for param in self.params.iter() { - if param.default.is_some() { - first_optional = Some(param); - } else if let Some(optional) = first_optional { - return Err(ctx.emitter.emit(error!( - message("invalid function signature"), - secondary(optional.useful_span, "optional parameter"), - primary(param.useful_span, "non-optional parameter after optional"), - ))); - } - } + pub(crate) fn validate(&self, _ctx: &CompilerContext) -> Result<(), ErrorReported> { Ok(()) } /// Minimum number of arguments accepted. pub fn min_args(&self) -> usize { - self.params.iter().take_while(|param| param.default.is_none()).count() + self.params.iter().fold(0, |count, param| count + param.default.is_none() as usize) } /// Maximum number of arguments accepted. pub fn max_args(&self) -> usize { - self.params.len() + self.min_args() } /// Matches arguments at a call site to their corresponding parameters. diff --git a/src/core_mapfiles/anm.rs b/src/core_mapfiles/anm.rs index ce56c27..aa017a1 100644 --- a/src/core_mapfiles/anm.rs +++ b/src/core_mapfiles/anm.rs @@ -32,9 +32,9 @@ pub(super) fn core_signatures(game: Game) -> &'static CoreSignatures { OUT }, - Th13 | Th14 | Th143 | Th15 | Th16 | Th165 | Th17 | Th18 => { + Th13 | Th14 | Th143 | Th15 | Th16 | Th165 | Th17 | Th18 | Th185 | Th19 => { static OUT: &CoreSignatures = &CoreSignatures { - inherit: &[ANM_INS_13_18, ANM_VAR], + inherit: &[ANM_INS_13_19, ANM_VAR], ins: &[], var: &[], }; OUT @@ -49,8 +49,8 @@ static ANM_INS_06: &'static CoreSignatures = &CoreSignatures { (Th06, 0, Some(("", None))), (Th06, 1, Some(("n", None))), (Th06, 2, Some(("ff", None))), - (Th06, 3, Some(("S", None))), - (Th06, 4, Some(("C", None))), + (Th06, 3, Some(("b(hex)---", None))), + (Th06, 4, Some(("b(hex)b(hex)b(hex)-", None))), (Th06, 5, Some(("o", Some(IKind::Jmp)))), (Th06, 6, Some(("", None))), (Th06, 7, Some(("", None))), @@ -58,26 +58,26 @@ static ANM_INS_06: &'static CoreSignatures = &CoreSignatures { (Th06, 9, Some(("fff", None))), (Th06, 10, Some(("fff", None))), (Th06, 11, Some(("ff", None))), - (Th06, 12, Some(("SS", None))), + (Th06, 12, Some(("b(hex)---s--", None))), (Th06, 13, Some(("", None))), (Th06, 14, Some(("", None))), (Th06, 15, Some(("", None))), - (Th06, 16, Some(("nS", None))), + (Th06, 16, Some(("nu--", None))), (Th06, 17, Some(("fff", None))), - (Th06, 18, Some(("fffS", None))), - (Th06, 19, Some(("fffS", None))), - (Th06, 20, Some(("fffS", None))), + (Th06, 18, Some(("fffs--", None))), + (Th06, 19, Some(("fffs--", None))), + (Th06, 20, Some(("fffs--", None))), (Th06, 21, Some(("", None))), (Th06, 22, Some(("S", Some(IKind::InterruptLabel)))), (Th06, 23, Some(("", None))), (Th06, 24, Some(("", None))), - (Th06, 25, Some(("S", None))), - (Th06, 26, Some(("S", None))), + (Th06, 25, Some((r#"U(enum="bool")"#, None))), // zero: U(enum="BitBool") + (Th06, 26, Some(("s--", None))), (Th06, 27, Some(("f", None))), (Th06, 28, Some(("f", None))), - (Th06, 29, Some(("S", None))), - (Th06, 30, Some(("ffS", None))), - (Th06, 31, Some(("S", None))), + (Th06, 29, Some((r#"U(enum="bool")"#, None))), // zero: U(enum="BitBool") + (Th06, 30, Some(("ffs--", None))), + (Th06, 31, Some((r#"U(enum="bool")"#, None))), // zero: U(enum="BitBool") ], var: &[], }; @@ -86,44 +86,44 @@ static ANM_INS_06: &'static CoreSignatures = &CoreSignatures { static ANM_INS_07_09: &'static CoreSignatures = &CoreSignatures { inherit: &[], ins: &[ - // v2 + // v2 (PCB) (Th07, 0, Some(("", None))), (Th07, 1, Some(("", None))), (Th07, 2, Some(("", None))), (Th07, 3, Some(("n", None))), (Th07, 4, Some(("ot", Some(IKind::Jmp)))), - (Th07, 5, Some(("Sot", Some(IKind::CountJmp(B::Ne))))), + (Th07, 5, Some(("Sot", Some(IKind::CountJmp(B::Gt))))), (Th07, 6, Some(("fff", None))), (Th07, 7, Some(("ff", None))), - (Th07, 8, Some(("S", None))), - (Th07, 9, Some(("S", None))), + (Th07, 8, Some(("b(imm;hex)---", None))), + (Th07, 9, Some(("b(imm;hex)b(imm;hex)b(imm;hex)-", None))), (Th07, 10, Some(("", None))), (Th07, 11, Some(("", None))), (Th07, 12, Some(("fff", None))), (Th07, 13, Some(("fff", None))), (Th07, 14, Some(("ff", None))), - (Th07, 15, Some(("SS", None))), - (Th07, 16, Some(("S", None))), + (Th07, 15, Some(("b(imm;hex)---S", None))), + (Th07, 16, Some(("U(imm)", None))), (Th07, 17, Some(("fffS", None))), (Th07, 18, Some(("fffS", None))), (Th07, 19, Some(("fffS", None))), (Th07, 20, Some(("", None))), - (Th07, 21, Some(("S", Some(IKind::InterruptLabel)))), + (Th07, 21, Some(("S(imm)", Some(IKind::InterruptLabel)))), (Th07, 22, Some(("", None))), (Th07, 23, Some(("", None))), - (Th07, 24, Some(("S", None))), - (Th07, 25, Some(("S", None))), + (Th07, 24, Some((r#"U(imm;enum="bool")"#, None))), // zero: U(imm;enum="BitBool") + (Th07, 25, Some(("s(imm)--", None))), (Th07, 26, Some(("f", None))), (Th07, 27, Some(("f", None))), - (Th07, 28, Some(("S", None))), + (Th07, 28, Some((r#"U(imm;enum="bool")"#, None))), // zero: U(imm;enum="BitBool") (Th07, 29, Some(("ffS", None))), - (Th07, 30, Some(("S", None))), - (Th07, 31, Some(("S", None))), - (Th07, 32, Some(("SSfff", None))), - (Th07, 33, Some(("SSS", None))), - (Th07, 34, Some(("SSS", None))), - (Th07, 35, Some(("SSfff", None))), - (Th07, 36, Some(("SSff", None))), + (Th07, 30, Some((r#"U(imm;enum="bool")"#, None))), // zero: U(imm;enum="BitBool") + (Th07, 31, Some((r#"U(imm;enum="bool")"#, None))), // zero: U(imm;enum="BitBool") + (Th07, 32, Some(("Sb(imm)---fff", None))), + (Th07, 33, Some(("Sb(imm)---b(imm;hex)b(imm;hex)b(imm;hex)-", None))), + (Th07, 34, Some(("Sb(imm)---b(imm;hex)---", None))), + (Th07, 35, Some(("Sb(imm)---fff", None))), + (Th07, 36, Some(("Sb(imm)---ff", None))), (Th07, 37, Some(("SS", Some(IKind::AssignOp(A::Assign, Ty::Int))))), (Th07, 38, Some(("ff", Some(IKind::AssignOp(A::Assign, Ty::Float))))), (Th07, 39, Some(("SS", Some(IKind::AssignOp(A::Add, Ty::Int))))), @@ -146,13 +146,13 @@ static ANM_INS_07_09: &'static CoreSignatures = &CoreSignatures { (Th07, 56, Some(("fff", Some(IKind::BinOp(B::Div, Ty::Float))))), (Th07, 57, Some(("SSS", Some(IKind::BinOp(B::Rem, Ty::Int))))), (Th07, 58, Some(("fff", Some(IKind::BinOp(B::Rem, Ty::Float))))), - (Th07, 59, Some(("SS", None))), + (Th07, 59, Some(("SU", None))), (Th07, 60, Some(("ff", None))), (Th07, 61, Some(("ff", Some(IKind::UnOp(U::Sin, Ty::Float))))), (Th07, 62, Some(("ff", Some(IKind::UnOp(U::Cos, Ty::Float))))), - (Th07, 63, Some(("ff", None))), - (Th07, 64, Some(("ff", None))), - (Th07, 65, Some(("ff", None))), + (Th07, 63, Some(("ff", Some(IKind::UnOp(U::Tan, Ty::Float))))), + (Th07, 64, Some(("ff", Some(IKind::UnOp(U::Acos, Ty::Float))))), + (Th07, 65, Some(("ff", Some(IKind::UnOp(U::Atan, Ty::Float))))), (Th07, 66, Some(("f", None))), (Th07, 67, Some(("SSot", Some(IKind::CondJmp(B::Eq, Ty::Int))))), (Th07, 68, Some(("ffot", Some(IKind::CondJmp(B::Eq, Ty::Float))))), @@ -170,18 +170,22 @@ static ANM_INS_07_09: &'static CoreSignatures = &CoreSignatures { (Th07, 80, Some(("f", None))), (Th07, 81, Some(("f", None))), - // v3 - // color instructions changed to take 3 dwords - (Th08, 9, Some(("SSS", None))), - (Th08, 33, Some(("SSSSS", None))), + // v3, v3b (IN, PoFV) + // alpha/color/alphaTime/colorTime instructions changed to take dword variables + // alphaTimeLinear not updated + (Th08, 8, Some(("C", None))), + (Th08, 9, Some(("CCC", None))), + (Th08, 16, Some((r#"U(imm;enum="bool")"#, None))), + (Th08, 33, Some(("Sb(imm)---CCC", None))), + (Th08, 34, Some(("Sb(imm)---C", None))), // new instructions - (Th08, 82, Some(("S", None))), - (Th08, 83, Some(("S", None))), - (Th08, 84, Some(("SSS", None))), - (Th08, 85, Some(("S", None))), - (Th08, 86, Some(("SSSSS", None))), - (Th08, 87, Some(("SSS", None))), - (Th08, 88, Some(("S", None))), + (Th08, 82, Some(("U(imm)", None))), + (Th08, 83, Some(("S(imm)", None))), + (Th08, 84, Some(("CCC", None))), + (Th08, 85, Some(("C", None))), + (Th08, 86, Some(("Sb(imm)---CCC", None))), + (Th08, 87, Some(("Sb(imm)---C", None))), + (Th08, 88, Some(("-b(imm)--", None))), (Th08, 89, Some(("", None))), ], var: &[], @@ -191,6 +195,7 @@ static ANM_INS_07_09: &'static CoreSignatures = &CoreSignatures { static ANM_INS_095_128: &'static CoreSignatures = &CoreSignatures { inherit: &[], ins: &[ + // v4 (StB) (Th095, 0, Some(("", None))), (Th095, 1, Some(("", None))), (Th095, 2, Some(("", None))), @@ -235,73 +240,80 @@ static ANM_INS_095_128: &'static CoreSignatures = &CoreSignatures { (Th095, 41, Some(("ff", None))), (Th095, 42, Some(("ff", Some(IKind::UnOp(U::Sin, Ty::Float))))), (Th095, 43, Some(("ff", Some(IKind::UnOp(U::Cos, Ty::Float))))), - (Th095, 44, Some(("ff", None))), - (Th095, 45, Some(("ff", None))), - (Th095, 46, Some(("ff", None))), + (Th095, 44, Some(("ff", Some(IKind::UnOp(U::Tan, Ty::Float))))), + (Th095, 45, Some(("ff", Some(IKind::UnOp(U::Acos, Ty::Float))))), + (Th095, 46, Some(("ff", Some(IKind::UnOp(U::Atan, Ty::Float))))), (Th095, 47, Some(("f", None))), (Th095, 48, Some(("fff", None))), (Th095, 49, Some(("fff", None))), (Th095, 50, Some(("ff", None))), - (Th095, 51, Some(("S", None))), - (Th095, 52, Some(("SSS", None))), + (Th095, 51, Some(("C", None))), + (Th095, 52, Some(("CCC", None))), (Th095, 53, Some(("fff", None))), (Th095, 54, Some(("ff", None))), - (Th095, 55, Some(("SS", None))), - (Th095, 56, Some(("SSfff", None))), - (Th095, 57, Some(("SSSSS", None))), - (Th095, 58, Some(("SSS", None))), - (Th095, 59, Some(("SSfff", None))), - (Th095, 60, Some(("SSff", None))), + (Th095, 55, Some(("b(imm;hex)---S", None))), + (Th095, 56, Some(("Sb(imm)---fff", None))), + (Th095, 57, Some(("Sb(imm)---CCC", None))), + (Th095, 58, Some(("Sb(imm)---C", None))), + (Th095, 59, Some(("Sb(imm)---fff", None))), + (Th095, 60, Some(("Sb(imm)---ff", None))), (Th095, 61, Some(("", None))), (Th095, 62, Some(("", None))), (Th095, 63, Some(("", None))), - (Th095, 64, Some(("S", Some(IKind::InterruptLabel)))), - (Th095, 65, Some(("ss", None))), - (Th095, 66, Some(("S", None))), - (Th095, 67, Some(("S", None))), - (Th095, 68, Some(("S", None))), + (Th095, 64, Some(("S(imm)", Some(IKind::InterruptLabel)))), + (Th095, 65, Some(("u(imm)u(imm)", None))), + (Th095, 66, Some(("U(imm)", None))), + (Th095, 67, Some(("U(imm)", None))), + (Th095, 68, Some(("b(imm)---", None))), (Th095, 69, Some(("", None))), (Th095, 70, Some(("f", None))), (Th095, 71, Some(("f", None))), - (Th095, 72, Some(("S", None))), - (Th095, 73, Some(("S", None))), - (Th095, 74, Some(("S", None))), + (Th095, 72, Some((r#"U(imm;enum="bool")"#, None))), // zero: U(imm;enum="BitBool") + (Th095, 73, Some((r#"U(imm;enum="bool")"#, None))), // zero: U(imm;enum="BitBool") + (Th095, 74, Some((r#"U(imm;enum="bool")"#, None))), // zero: U(imm;enum="BitBool") (Th095, 75, Some(("S", None))), - (Th095, 76, Some(("SSS", None))), - (Th095, 77, Some(("S", None))), - (Th095, 78, Some(("SSSSS", None))), - (Th095, 79, Some(("SSS", None))), - (Th095, 80, Some(("S", None))), + (Th095, 76, Some(("CCC", None))), + (Th095, 77, Some(("C", None))), + (Th095, 78, Some(("Sb(imm)---CCC", None))), + (Th095, 79, Some(("Sb(imm)---C", None))), + (Th095, 80, Some(("b(imm)---", None))), (Th095, 81, Some(("", None))), - (Th095, 82, Some(("S", None))), + (Th095, 82, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- (Th095, 83, Some(("", None))), (Th095, 84, Some(("S", None))), - (Th095, 85, Some(("S", None))), - (Th095, 86, Some(("S", None))), - (Th095, 87, Some(("S", None))), + (Th095, 85, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- + (Th095, 86, Some((r#"U(enum="bool")"#, None))), // zero: U(enum="BitBool") + (Th095, 87, Some(("b(imm)---", None))), + // v4b (MoF, alcostg) + // For some reason only a few of the interp instructions + // were updated to use a 32 bit mode + (Th10, 56, Some(("SU(imm)fff", None))), + (Th10, 59, Some(("SU(imm)fff", None))), (Th10, 88, Some(("N", None))), - (Th10, 89, Some(("S", None))), + (Th10, 89, Some((r#"U(imm;enum="bool")"#, None))), // zero: U(imm;enum="BitBool") (Th10, 90, Some(("N", None))), (Th10, 91, Some(("N", None))), (Th10, 92, Some(("N", None))), - (Th11, 93, Some(("SSf", None))), - (Th11, 94, Some(("SSf", None))), + // v4c (SA) + (Th11, 93, Some(("SU(imm)f", None))), + (Th11, 94, Some(("SU(imm)f", None))), (Th11, 95, Some(("N", None))), (Th11, 96, Some(("Nff", None))), (Th11, 97, Some(("Nff", None))), (Th11, 98, Some(("", None))), - (Th11, 99, Some(("S", None))), + (Th11, 99, Some((r#"U(enum="bool")"#, None))), // zero: U(enum="BitBool") (Th11, 100, Some(("Sfffffffff", None))), (Th11, 101, Some(("S", None))), - (Th11, 102, Some(("nS", None))), - (Th11, 103, Some(("ff", None))), + (Th11, 102, Some(("nU", None))), + // v4d (UFO) + (Th12, 103, Some(("ff", None))), (Th12, 104, Some(("fS", None))), (Th12, 105, Some(("fS", None))), (Th12, 106, Some(("ff", None))), - (Th12, 107, Some(("SSff", None))), + (Th12, 107, Some(("Sb(imm)---ff", None))), (Th12, 108, Some(("ff", None))), (Th12, 109, Some(("ff", None))), (Th12, 110, Some(("ff", None))), @@ -316,9 +328,10 @@ static ANM_INS_095_128: &'static CoreSignatures = &CoreSignatures { }; // v8 -static ANM_INS_13_18: &CoreSignatures = &CoreSignatures { +static ANM_INS_13_19: &CoreSignatures = &CoreSignatures { inherit: &[], ins: &[ + // Section A (Th13, 0, Some(("", None))), (Th13, 1, Some(("", None))), (Th13, 2, Some(("", None))), @@ -327,6 +340,8 @@ static ANM_INS_13_18: &CoreSignatures = &CoreSignatures { (Th13, 5, Some(("S", Some(IKind::InterruptLabel)))), (Th13, 6, Some(("S", None))), (Th13, 7, Some(("", None))), + + // Section B (Th13, 100, Some(("SS", Some(IKind::AssignOp(A::Assign, Ty::Int))))), (Th13, 101, Some(("ff", Some(IKind::AssignOp(A::Assign, Ty::Float))))), (Th13, 102, Some(("SS", Some(IKind::AssignOp(A::Add, Ty::Int))))), @@ -353,12 +368,14 @@ static ANM_INS_13_18: &CoreSignatures = &CoreSignatures { (Th13, 123, Some(("ff", None))), (Th13, 124, Some(("ff", Some(IKind::UnOp(U::Sin, Ty::Float))))), (Th13, 125, Some(("ff", Some(IKind::UnOp(U::Cos, Ty::Float))))), - (Th13, 126, Some(("ff", None))), - (Th13, 127, Some(("ff", None))), - (Th13, 128, Some(("ff", None))), + (Th13, 126, Some(("ff", Some(IKind::UnOp(U::Tan, Ty::Float))))), + (Th13, 127, Some(("ff", Some(IKind::UnOp(U::Acos, Ty::Float))))), + (Th13, 128, Some(("ff", Some(IKind::UnOp(U::Atan, Ty::Float))))), (Th13, 129, Some(("f", None))), (Th13, 130, Some(("ffff", None))), (Th13, 131, Some(("ffff", None))), + + // Section C (Th13, 200, Some(("ot", Some(IKind::Jmp)))), (Th13, 201, Some(("Sot", Some(IKind::CountJmp(B::Ne))))), (Th13, 202, Some(("SSot", Some(IKind::CondJmp(B::Eq, Ty::Int))))), @@ -373,6 +390,8 @@ static ANM_INS_13_18: &CoreSignatures = &CoreSignatures { (Th13, 211, Some(("ffot", Some(IKind::CondJmp(B::Gt, Ty::Float))))), (Th13, 212, Some(("SSot", Some(IKind::CondJmp(B::Ge, Ty::Int))))), (Th13, 213, Some(("ffot", Some(IKind::CondJmp(B::Ge, Ty::Float))))), + + // Section D (Th13, 300, Some(("n", None))), (Th13, 301, Some(("nS", None))), (Th13, 302, Some(("S", None))), @@ -386,6 +405,18 @@ static ANM_INS_13_18: &CoreSignatures = &CoreSignatures { (Th13, 310, Some(("S", None))), (Th13, 311, Some(("S", None))), (Th13, 312, Some(("SS", None))), + + (Th14, 313, Some(("S", None))), + (Th14, 314, Some(("S", None))), + (Th14, 315, Some(("S", None))), + + (Th143, 316, Some(("", None))), + (Th143, 317, Some(("", None))), + + (Th19, 318, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- + (Th19, 319, Some(("nnnn", None))), + + // Section E (Th13, 400, Some(("fff", None))), (Th13, 401, Some(("fff", None))), (Th13, 402, Some(("ff", None))), @@ -425,6 +456,16 @@ static ANM_INS_13_18: &CoreSignatures = &CoreSignatures { (Th13, 436, Some(("ff", None))), (Th13, 437, Some(("S", None))), (Th13, 438, Some(("S", None))), + + (Th165, 439, Some(("S", None))), // files use this, but it's not in the jumptable! + + (Th17, 439, None), // ... TH17 doesn't use it ... + + (Th18, 439, Some(("Sff", None))), // ...and TH18 demo reused its ID for something else! + + (Th185, 440, Some(("", None))), + + // Section F (Th13, 500, Some(("N", None))), (Th13, 501, Some(("N", None))), (Th13, 502, Some(("N", None))), @@ -434,6 +475,12 @@ static ANM_INS_13_18: &CoreSignatures = &CoreSignatures { (Th13, 506, Some(("Nff", None))), (Th13, 507, Some(("S", None))), (Th13, 508, Some(("S", None))), + + (Th14, 509, Some(("", None))), + + (Th19, 510, Some(("Nff", None))), + + // Section G (Th13, 600, Some(("S", None))), (Th13, 601, Some(("S", None))), (Th13, 602, Some(("S", None))), @@ -444,26 +491,34 @@ static ANM_INS_13_18: &CoreSignatures = &CoreSignatures { (Th13, 607, Some(("ff", None))), (Th13, 608, Some(("ff", None))), - (Th14, 313, Some(("S", None))), - (Th14, 314, Some(("S", None))), - (Th14, 315, Some(("S", None))), - (Th14, 509, Some(("", None))), (Th14, 609, Some(("S", None))), (Th14, 610, Some(("S", None))), - (Th143, 316, Some(("", None))), - (Th143, 317, Some(("", None))), (Th143, 611, Some(("ffS", None))), (Th16, 612, Some(("ff", None))), (Th16, 613, Some(("ff", None))), - (Th165, 439, Some(("S", None))), // files use this, but it's not in the jumptable! - - (Th17, 439, None), // ... TH17 doesn't use it ... - - (Th18, 439, Some(("Sff", None))), // ...and TH18 demo reused its ID for something else! (Th18, 614, Some(("ff", None))), + + (Th19, 615, Some(("ffS", None))), + (Th19, 616, Some(("ffS", None))), + (Th19, 617, Some(("fS", None))), + (Th19, 618, Some(("", None))), + (Th19, 619, Some(("fS", None))), + (Th19, 620, Some(("ffS", None))), + (Th19, 621, Some(("ffS", None))), + (Th19, 622, Some(("ffS", None))), + (Th19, 623, Some(("fffS", None))), + (Th19, 624, Some(("fffS", None))), + (Th19, 625, Some(("ffffS", None))), + (Th19, 626, Some(("ffffS", None))), + (Th19, 627, Some(("ffffS", None))), + (Th19, 628, Some(("fS", None))), + (Th19, 629, Some(("fS", None))), + (Th19, 630, Some(("ffS", None))), + (Th19, 631, Some(("ffS", None))), + (Th19, 632, Some(("ffS", None))), ], var: &[], }; @@ -473,6 +528,7 @@ static ANM_VAR: &'static CoreSignatures = &CoreSignatures { inherit: &[], ins: &[], var: &[ + // v2, v3 (PCB, IN) (Th07, 10000, Some("$")), (Th07, 10001, Some("$")), (Th07, 10002, Some("$")), @@ -484,13 +540,17 @@ static ANM_VAR: &'static CoreSignatures = &CoreSignatures { (Th07, 10008, Some("$")), (Th07, 10009, Some("$")), - (Th095, 10010, Some("%")), - (Th095, 10011, Some("%")), - (Th095, 10012, Some("%")), + // v3b (PoFV) + (Th09, 10010, Some("%")), + (Th09, 10011, Some("%")), + (Th09, 10012, Some("%")), + + // v4 (StB) (Th095, 10013, Some("%")), (Th095, 10014, Some("%")), (Th095, 10015, Some("%")), + // v4b (MoF, alcostg) (Th10, 10016, Some("%")), (Th10, 10017, Some("%")), (Th10, 10018, Some("%")), @@ -498,8 +558,10 @@ static ANM_VAR: &'static CoreSignatures = &CoreSignatures { (Th10, 10020, Some("%")), (Th10, 10021, Some("%")), + // v4c (SA) (Th11, 10022, Some("$")), + // v4d (UFO) (Th12, 10023, Some("%")), (Th12, 10024, Some("%")), (Th12, 10025, Some("%")), diff --git a/src/core_mapfiles/ecl.rs b/src/core_mapfiles/ecl.rs index 0d8d550..793d045 100644 --- a/src/core_mapfiles/ecl.rs +++ b/src/core_mapfiles/ecl.rs @@ -12,9 +12,11 @@ pub(super) fn core_signatures(game: Game) -> &'static CoreSignatures { Th07 => ECL_07, Th08 | Th09 => ECL_08_09, Th095 => ECL_095, + Th10 | Alcostg | Th11 => ECL_10_11, + Th12 | Th125 => ECL_12, - Th10 | Alcostg | Th11 | Th12 | Th125 | Th128 | - Th13 | Th14 | Th143 | Th15 | Th16 | Th165 | Th17 | Th18 => CoreSignatures::EMPTY, + Th128 | + Th13 | Th14 | Th143 | Th15 | Th16 | Th165 | Th17 | Th18 | Th185 | Th19 => CoreSignatures::EMPTY, } } @@ -23,7 +25,7 @@ pub(super) fn timeline_core_signatures(game: Game) -> &'static CoreSignatures { Th06 | Th07 | Th08 | Th09 | Th095 => TIMELINE, Th10 | Alcostg | Th11 | Th12 | Th125 | Th128 | - Th13 | Th14 | Th143 | Th15 | Th16 | Th165 | Th17 | Th18 => CoreSignatures::EMPTY, + Th13 | Th14 | Th143 | Th15 | Th16 | Th165 | Th17 | Th18 | Th185 | Th19 => CoreSignatures::EMPTY, } } @@ -41,13 +43,14 @@ static TIMELINE: &'static CoreSignatures = &CoreSignatures { (Th06, 8, Some((r#"s(arg0;enum="MsgScript")"#, None))), (Th06, 9, Some(("", None))), (Th06, 10, Some(("SS", None))), - (Th06, 11, Some(("s(arg0)", None))), + (Th06, 11, Some(("u(arg0)", None))), (Th06, 12, Some(("s(arg0)", None))), (Th07, 0, Some((r#"s(arg0;enum="EclSub")fffSSS"#, None))), (Th07, 2, Some((r#"s(arg0;enum="EclSub")fffSSS"#, None))), (Th07, 4, Some((r#"s(arg0;enum="EclSub")fffSSS"#, None))), (Th07, 6, Some((r#"s(arg0;enum="EclSub")fffSSS"#, None))), + (Th07, 11, Some(("s(arg0)", None))), (Th08, 0, Some(("EffSSS", None))), (Th08, 1, Some(("EffSSS", None))), @@ -57,7 +60,7 @@ static TIMELINE: &'static CoreSignatures = &CoreSignatures { (Th08, 5, Some(("EfSSS", None))), (Th08, 6, Some((r#"S(enum="MsgScript")"#, None))), (Th08, 7, Some(("", None))), // Not implemented in PoFV, but present in the files anyway - (Th08, 8, Some(("SS", None))), + (Th08, 8, Some(("Ss--", None))), (Th08, 9, Some(("S", None))), (Th08, 10, Some(("S", None))), (Th08, 11, Some(("EffSSSS", None))), @@ -71,6 +74,7 @@ static TIMELINE: &'static CoreSignatures = &CoreSignatures { (Th09, 9, None), (Th09, 17, Some(("EffSSS", None))), + (Th095, 7, None), (Th095, 13, None), (Th095, 14, None), (Th095, 16, None), @@ -83,13 +87,13 @@ static ECL_06: &'static CoreSignatures = &CoreSignatures { inherit: &[], ins: &[ (Th06, 0, Some(("", None))), - (Th06, 1, Some(("S", None))), + (Th06, 1, Some(("_", None))), // Bytes are never read (Th06, 2, Some(("to", Some(IKind::Jmp)))), (Th06, 3, Some(("toS", Some(IKind::CountJmp(B::Gt))))), (Th06, 4, Some(("SS", Some(IKind::AssignOp(A::Assign, Ty::Int))))), (Th06, 5, Some(("Sf", Some(IKind::AssignOp(A::Assign, Ty::Float))))), - (Th06, 6, Some(("SS", None))), - (Th06, 7, Some(("SSS", None))), + (Th06, 6, Some(("SU", None))), // The division for both of these uses DIV instead of IDIV + (Th06, 7, Some(("SUS", None))), (Th06, 8, Some(("Sf", None))), (Th06, 9, Some(("Sff", None))), (Th06, 10, Some(("S", None))), @@ -97,7 +101,7 @@ static ECL_06: &'static CoreSignatures = &CoreSignatures { (Th06, 12, Some(("S", None))), (Th06, 13, Some(("SSS", Some(IKind::BinOp(B::Add, Ty::Int))))), (Th06, 14, Some(("SSS", Some(IKind::BinOp(B::Sub, Ty::Int))))), - (Th06, 15, Some(("SSS", Some(IKind::BinOp(B::Mul, Ty::Int))))), + (Th06, 15, Some(("SSS", Some(IKind::BinOp(B::Mul, Ty::Int))))), // EoSD reads args 2/3 an extra time for multiplication compared to the other ops, so don't prefer multiplication based fallback sequences (Th06, 16, Some(("SSS", Some(IKind::BinOp(B::Div, Ty::Int))))), (Th06, 17, Some(("SSS", Some(IKind::BinOp(B::Rem, Ty::Int))))), (Th06, 18, Some(("S", None))), // Some(IKind::UnOp(U::Inc, Ty::Int)) @@ -117,108 +121,109 @@ static ECL_06: &'static CoreSignatures = &CoreSignatures { (Th06, 32, Some(("to", Some(IKind::CondJmp2B(B::Gt))))), (Th06, 33, Some(("to", Some(IKind::CondJmp2B(B::Ge))))), (Th06, 34, Some(("to", Some(IKind::CondJmp2B(B::Ne))))), - (Th06, 35, Some(("ESf", Some(IKind::CallEosd)))), + (Th06, 35, Some(("E(imm)S(imm)f(imm)", Some(IKind::CallEosd)))), (Th06, 36, Some(("", None))), // Some(IKind::Return) - (Th06, 37, Some(("ESfSS", None))), // Some(IKind::CallEosdCond(B::Lt)) - (Th06, 38, Some(("ESfSS", None))), // Some(IKind::CallEosdCond(B::Le)) - (Th06, 39, Some(("ESfSS", None))), // Some(IKind::CallEosdCond(B::Eq)) - (Th06, 40, Some(("ESfSS", None))), // Some(IKind::CallEosdCond(B::Gt)) - (Th06, 41, Some(("ESfSS", None))), // Some(IKind::CallEosdCond(B::Ge)) - (Th06, 42, Some(("ESfSS", None))), // Some(IKind::CallEosdCond(B::Ne)) + (Th06, 37, Some(("E(imm)S(imm)f(imm)SS(imm)", None))), // Some(IKind::CallEosdCond(B::Lt)) + (Th06, 38, Some(("E(imm)S(imm)f(imm)SS(imm)", None))), // Some(IKind::CallEosdCond(B::Le)) + (Th06, 39, Some(("E(imm)S(imm)f(imm)SS(imm)", None))), // Some(IKind::CallEosdCond(B::Eq)) + (Th06, 40, Some(("E(imm)S(imm)f(imm)SS(imm)", None))), // Some(IKind::CallEosdCond(B::Gt)) + (Th06, 41, Some(("E(imm)S(imm)f(imm)SS(imm)", None))), // Some(IKind::CallEosdCond(B::Ge)) + (Th06, 42, Some(("E(imm)S(imm)f(imm)SS(imm)", None))), // Some(IKind::CallEosdCond(B::Ne)) (Th06, 43, Some(("fff", None))), (Th06, 44, Some(("fff", None))), (Th06, 45, Some(("ff", None))), (Th06, 46, Some(("f", None))), (Th06, 47, Some(("f", None))), (Th06, 48, Some(("f", None))), - (Th06, 49, Some(("ff", None))), - (Th06, 50, Some(("ff", None))), - (Th06, 51, Some(("ff", None))), - (Th06, 52, Some(("Sff", None))), - (Th06, 53, Some(("Sff", None))), - (Th06, 54, Some(("Sff", None))), - (Th06, 55, Some(("Sff", None))), - (Th06, 56, Some(("Sfff", None))), - (Th06, 57, Some(("Sfff", None))), - (Th06, 58, Some(("Sfff", None))), - (Th06, 59, Some(("Sfff", None))), - (Th06, 60, Some(("Sfff", None))), - (Th06, 61, Some(("S", None))), - (Th06, 62, Some(("S", None))), - (Th06, 63, Some(("S", None))), - (Th06, 64, Some(("S", None))), - (Th06, 65, Some(("ffff", None))), + (Th06, 49, Some(("f(imm)f(imm)", None))), + (Th06, 50, Some(("f(imm)f(imm)", None))), + (Th06, 51, Some(("f(imm)f", None))), + (Th06, 52, Some(("S(imm)ff(imm)", None))), + (Th06, 53, Some(("S(imm)ff(imm)", None))), + (Th06, 54, Some(("S(imm)ff(imm)", None))), + (Th06, 55, Some(("S(imm)ff(imm)", None))), + (Th06, 56, Some(("S(imm)fff", None))), + (Th06, 57, Some(("S(imm)fff", None))), + (Th06, 58, Some(("S(imm)fff", None))), + (Th06, 59, Some(("S(imm)fff", None))), + (Th06, 60, Some(("S(imm)fff", None))), + (Th06, 61, Some(("S(imm)", None))), // + (Th06, 62, Some(("S(imm)", None))), // These read the current value of SELF_ANGLE as a variable + (Th06, 63, Some(("S(imm)", None))), // + (Th06, 64, Some(("S(imm)", None))), // + (Th06, 65, Some(("f(imm)f(imm)f(imm)f(imm)", None))), (Th06, 66, Some(("", None))), - (Th06, 67, Some(("ssSSffffS", None))), - (Th06, 68, Some(("ssSSffffS", None))), - (Th06, 69, Some(("ssSSffffS", None))), - (Th06, 70, Some(("ssSSffffS", None))), - (Th06, 71, Some(("ssSSffffS", None))), - (Th06, 72, Some(("ssSSffffS", None))), - (Th06, 73, Some(("ssSSffffS", None))), - (Th06, 74, Some(("ssSSffffS", None))), - (Th06, 75, Some(("ssSSffffS", None))), - (Th06, 76, Some(("S", None))), - (Th06, 77, Some(("S", None))), + // Flags marked as hex + (Th06, 67, Some(("s(imm)sSSffffU(imm;hex)", None))), + (Th06, 68, Some(("s(imm)sSSffffU(imm;hex)", None))), + (Th06, 69, Some(("s(imm)sSSffffU(imm;hex)", None))), + (Th06, 70, Some(("s(imm)sSSffffU(imm;hex)", None))), + (Th06, 71, Some(("s(imm)sSSffffU(imm;hex)", None))), + (Th06, 72, Some(("s(imm)sSSffffU(imm;hex)", None))), + (Th06, 73, Some(("s(imm)sSSffffU(imm;hex)", None))), + (Th06, 74, Some(("s(imm)sSSffffU(imm;hex)", None))), + (Th06, 75, Some(("s(imm)sSSffffU(imm;hex)", None))), + (Th06, 76, Some(("S(imm)", None))), + (Th06, 77, Some(("S(imm)", None))), // This value is used with both IDIV and DIV...? (Th06, 78, Some(("", None))), (Th06, 79, Some(("", None))), (Th06, 80, Some(("", None))), (Th06, 81, Some(("fff", None))), (Th06, 82, Some(("SSSSffff", None))), (Th06, 83, Some(("", None))), - (Th06, 84, Some(("S", None))), - (Th06, 85, Some(("ssffffffSSSSSS", None))), - (Th06, 86, Some(("ssffffffSSSSSS", None))), + (Th06, 84, Some(("S(imm)", None))), + (Th06, 85, Some(("s(imm)s(imm)ffffff(imm)S(imm)S(imm)S(imm)S(imm)S(imm)U(imm;hex)", None))), + (Th06, 86, Some(("s(imm)s(imm)ffffff(imm)S(imm)S(imm)S(imm)S(imm)S(imm)U(imm;hex)", None))), (Th06, 87, Some(("S", None))), - (Th06, 88, Some(("Sf", None))), - (Th06, 89, Some(("Sf", None))), - (Th06, 90, Some(("Sfff", None))), - (Th06, 91, Some(("S", None))), - (Th06, 92, Some(("S", None))), + (Th06, 88, Some(("S(imm)f", None))), + (Th06, 89, Some(("S(imm)f", None))), + (Th06, 90, Some(("S(imm)f(imm)f(imm)f(imm)", None))), + (Th06, 91, Some(("S(imm)", None))), + (Th06, 92, Some(("S(imm)", None))), // 34. Yes. This makes every spell name instruction not a multiple of 4 bytes. - (Th06, 93, Some(("ssz(len=34)", None))), + (Th06, 93, Some(("s(imm)s(imm)z(len=34)", None))), (Th06, 94, Some(("", None))), - (Th06, 95, Some(("EfffssS", None))), + (Th06, 95, Some(("E(imm)fffs(imm)s(imm)S(imm)", None))), (Th06, 96, Some(("", None))), - (Th06, 97, Some(("N", None))), - (Th06, 98, Some((r#"s(enum="AnmScript")s(enum="AnmScript")s(enum="AnmScript")s(enum="AnmScript")N"#, None))), // zero: s(enum="AnmScript")s(enum="AnmScript")s(enum="AnmScript")s(enum="AnmScript")s(enum="AnmScript")-- - (Th06, 99, Some(("SN", None))), - (Th06, 100, Some(("U", None))), // zero: bbb- - (Th06, 101, Some(("S", None))), - (Th06, 102, Some(("Sffff", None))), - (Th06, 103, Some(("fff", None))), - (Th06, 104, Some(("U", None))), // zero: b--- - (Th06, 105, Some(("U", None))), // zero: b--- - (Th06, 106, Some(("S", None))), - (Th06, 107, Some(("U", None))), // zero: b--- - (Th06, 108, Some(("E", None))), - (Th06, 109, Some(("ES", None))), - (Th06, 110, Some(("S", None))), - (Th06, 111, Some(("S", None))), - (Th06, 112, Some(("S", None))), - (Th06, 113, Some(("S", None))), - (Th06, 114, Some(("E", None))), - (Th06, 115, Some(("S", None))), - (Th06, 116, Some(("E", None))), - (Th06, 117, Some(("U", None))), // zero: b--- - (Th06, 118, Some(("SUC", None))), - (Th06, 119, Some(("S", None))), - (Th06, 120, Some(("U", None))), // zero: b--- - (Th06, 121, Some(("SS", None))), - (Th06, 122, Some(("S", None))), - (Th06, 123, Some(("S", None))), - (Th06, 124, Some(("S", None))), + (Th06, 97, Some(("N(imm)", None))), + (Th06, 98, Some((r#"s(imm;enum="AnmScript")s(imm;enum="AnmScript")s(imm;enum="AnmScript")s(imm;enum="AnmScript")s(imm;enum="AnmScript")--"#, None))), + (Th06, 99, Some(("S(imm)N(imm)", None))), + (Th06, 100, Some(("b(imm)b(imm)b(imm)-", None))), + (Th06, 101, Some(("S(imm)", None))), + (Th06, 102, Some(("S(imm)f(imm)f(imm)f(imm)f(imm)", None))), + (Th06, 103, Some(("f(imm)f(imm)f(imm)", None))), + (Th06, 104, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- + (Th06, 105, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- + (Th06, 106, Some(("S(imm)", None))), + (Th06, 107, Some(("b(imm)---", None))), + (Th06, 108, Some(("E(imm)", None))), + (Th06, 109, Some(("E(imm)S(imm)", None))), + (Th06, 110, Some(("S(imm)", None))), + (Th06, 111, Some(("S(imm)", None))), + (Th06, 112, Some(("S(imm)", None))), + (Th06, 113, Some(("S(imm)", None))), + (Th06, 114, Some(("E(imm)", None))), + (Th06, 115, Some(("S(imm)", None))), + (Th06, 116, Some(("E(imm)", None))), + (Th06, 117, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- + (Th06, 118, Some(("S(imm)U(imm)C(imm)", None))), + (Th06, 119, Some(("S(imm)", None))), + (Th06, 120, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- + (Th06, 121, Some(("S(imm)S(imm)", None))), // zero: S(imm)v + (Th06, 122, Some(("S(imm)", None))), + (Th06, 123, Some(("S", None))), // Lack of (imm) here is not a typo + (Th06, 124, Some(("S(imm)", None))), (Th06, 125, Some(("", None))), - (Th06, 126, Some(("S", None))), + (Th06, 126, Some(("S(imm)", None))), (Th06, 127, Some(("S", None))), - (Th06, 128, Some(("S", None))), // zero: s-- - (Th06, 129, Some(("SS", None))), // zero: Ss-- - (Th06, 130, Some(("U", None))), // zero: b--- - (Th06, 131, Some(("ffSSSS", None))), - (Th06, 132, Some(("U", None))), // zero: b--- + (Th06, 128, Some(("s(imm)--", None))), + (Th06, 129, Some(("S(imm)s(imm)--", None))), + (Th06, 130, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- + (Th06, 131, Some(("f(imm)f(imm)S(imm)S(imm)S(imm)S(imm)", None))), + (Th06, 132, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- (Th06, 133, Some(("", None))), (Th06, 134, Some(("", None))), - (Th06, 135, Some(("U", None))), // zero: b--- + (Th06, 135, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- ], var: &[ (Th06, -10001, Some("$")), @@ -258,8 +263,8 @@ static ECL_07: &'static CoreSignatures = &CoreSignatures { (Th07, 3, Some(("toS", Some(IKind::CountJmp(B::Gt))))), (Th07, 4, Some(("SS", Some(IKind::AssignOp(A::Assign, Ty::Int))))), (Th07, 5, Some(("ff", Some(IKind::AssignOp(A::Assign, Ty::Float))))), - (Th07, 6, Some(("SS", None))), - (Th07, 7, Some(("SSS", None))), + (Th07, 6, Some(("SU", None))), + (Th07, 7, Some(("SUS", None))), (Th07, 8, Some(("ff", None))), (Th07, 9, Some(("fff", None))), (Th07, 10, Some(("SS", None))), @@ -293,7 +298,7 @@ static ECL_07: &'static CoreSignatures = &CoreSignatures { (Th07, 38, Some(("SSto", Some(IKind::CondJmp(B::Ge, Ty::Int))))), (Th07, 39, Some(("ffto", Some(IKind::CondJmp(B::Ge, Ty::Float))))), (Th07, 40, Some(("f", None))), - (Th07, 41, Some(("E", Some(IKind::CallReg)))), + (Th07, 41, Some(("E(imm)", Some(IKind::CallReg)))), (Th07, 42, Some(("", None))), // Some(IKind::Return) (Th07, 43, Some(("SSS", None))), (Th07, 44, Some(("ffS", None))), @@ -304,7 +309,7 @@ static ECL_07: &'static CoreSignatures = &CoreSignatures { (Th07, 49, Some(("f", None))), (Th07, 50, Some(("f", None))), (Th07, 51, Some(("fff", None))), - (Th07, 52, Some(("fff", None))), + (Th07, 52, Some(("fff", None))), // Arguments 2 and 3 are never read (Th07, 53, Some(("ff", None))), (Th07, 54, Some(("SSff", None))), (Th07, 55, Some(("SSfff", None))), @@ -316,51 +321,51 @@ static ECL_07: &'static CoreSignatures = &CoreSignatures { (Th07, 61, Some(("S", None))), (Th07, 62, Some(("ffff", None))), (Th07, 63, Some(("", None))), - (Th07, 64, Some(("ssSSffffS", None))), - (Th07, 65, Some(("ssSSffffS", None))), - (Th07, 66, Some(("ssSSffffS", None))), - (Th07, 67, Some(("ssSSffffS", None))), - (Th07, 68, Some(("ssSSffffS", None))), - (Th07, 69, Some(("ssSSffffS", None))), - (Th07, 70, Some(("ssSSffffS", None))), - (Th07, 71, Some(("ssSSffffS", None))), - (Th07, 72, Some(("ssSSffffS", None))), + (Th07, 64, Some(("ssSSffffU(imm;hex)", None))), + (Th07, 65, Some(("ssSSffffU(imm;hex)", None))), + (Th07, 66, Some(("ssSSffffU(imm;hex)", None))), + (Th07, 67, Some(("ssSSffffU(imm;hex)", None))), + (Th07, 68, Some(("ssSSffffU(imm;hex)", None))), + (Th07, 69, Some(("ssSSffffU(imm;hex)", None))), + (Th07, 70, Some(("ssSSffffU(imm;hex)", None))), + (Th07, 71, Some(("ssSSffffU(imm;hex)", None))), + (Th07, 72, Some(("ssSSffffU(imm;hex)", None))), (Th07, 73, Some(("S", None))), (Th07, 74, Some(("S", None))), (Th07, 75, Some(("", None))), (Th07, 76, Some(("", None))), (Th07, 77, Some(("", None))), (Th07, 78, Some(("fff", None))), - (Th07, 79, Some(("SSUSSff", None))), + (Th07, 79, Some((r#"SU(hex)U(enum="bool")SSff"#, None))), (Th07, 80, Some(("", None))), (Th07, 81, Some(("SS", None))), - (Th07, 82, Some(("ssffffffSSSSSS", None))), - (Th07, 83, Some(("ssffffffSSSSSS", None))), + (Th07, 82, Some(("s(imm)sffffff(imm)S(imm)S(imm)S(imm)S(imm)S(imm)U(imm;hex)", None))), + (Th07, 83, Some(("s(imm)sffffff(imm)S(imm)S(imm)S(imm)S(imm)S(imm)U(imm;hex)", None))), (Th07, 84, Some(("S", None))), (Th07, 85, Some(("Sf", None))), (Th07, 86, Some(("Sf", None))), (Th07, 87, Some(("Sfff", None))), (Th07, 88, Some(("S", None))), (Th07, 89, Some(("S", None))), - (Th07, 90, Some(("sum(len=48;mask=0xaa,0,0)", None))), + (Th07, 90, Some(("s(imm)u(imm)m(len=48;mask=0xaa,0,0)", None))), (Th07, 91, Some(("", None))), - (Th07, 92, Some(("EfffSSS", None))), - (Th07, 93, Some(("EfffSSS", None))), + (Th07, 92, Some(("E(imm)fffSSS", None))), + (Th07, 93, Some(("E(imm)fffSSS", None))), (Th07, 94, Some(("", None))), (Th07, 95, Some(("N", None))), - (Th07, 96, Some((r#"s(enum="AnmScript")s(enum="AnmScript")s(enum="AnmScript")s(enum="AnmScript")N"#, None))), // zero: s(enum="AnmScript")s(enum="AnmScript")s(enum="AnmScript")s(enum="AnmScript")s(enum="AnmScript")-- + (Th07, 96, Some((r#"s(imm;enum="AnmScript")s(imm;enum="AnmScript")s(imm;enum="AnmScript")s(imm;enum="AnmScript")s(imm;enum="AnmScript")--"#, None))), (Th07, 97, Some(("SN", None))), - (Th07, 98, Some(("S", None))), // zero: bbb- + (Th07, 98, Some(("c(imm)b(imm)b(imm)-", None))), (Th07, 99, Some(("S", None))), - (Th07, 100, Some(("Sffff", None))), + (Th07, 100, Some(("S(imm)f(imm)f(imm)f(imm)f(imm)", None))), (Th07, 101, Some(("fff", None))), - (Th07, 102, Some(("U", None))), // zero: b--- - (Th07, 103, Some(("U", None))), // zero: b--- - (Th07, 104, Some(("U", None))), // zero: b--- + (Th07, 102, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- + (Th07, 103, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- + (Th07, 104, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- (Th07, 105, Some(("S", None))), - (Th07, 106, Some(("S", None))), - (Th07, 107, Some(("E", None))), // zero: b(enum="EclSub")--- - (Th07, 108, Some(("ES", None))), + (Th07, 106, Some(("b(imm)---", None))), + (Th07, 107, Some((r#"b(imm;enum="EclSub")---"#, None))), + (Th07, 108, Some(("ES", None))), // Yes, this really is a non-immediate sub id. Any fields marked as such are intentional! (Th07, 109, Some(("S", None))), (Th07, 110, Some(("S", None))), (Th07, 111, Some(("S", None))), @@ -368,29 +373,29 @@ static ECL_07: &'static CoreSignatures = &CoreSignatures { (Th07, 113, Some(("E", None))), (Th07, 114, Some(("S", None))), (Th07, 115, Some(("E", None))), - (Th07, 116, Some(("U", None))), // zero: b--- - (Th07, 117, Some(("SUC", None))), - (Th07, 118, Some(("SUCfff", None))), + (Th07, 116, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- + (Th07, 117, Some(("SUC", None))), // Third argument is read from a pointer for some reason... + (Th07, 118, Some(("SUCfff", None))), // Also read as a pointer (Th07, 119, Some(("S", None))), - (Th07, 120, Some(("U", None))), // zero: b--- - (Th07, 121, Some(("SS", None))), - (Th07, 122, Some(("SS", None))), + (Th07, 120, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- + (Th07, 121, Some(("SS(imm)", None))), // zero: Sv + (Th07, 122, Some(("SS(imm)", None))), // zero: Sv (Th07, 123, Some(("S", None))), (Th07, 124, Some(("S", None))), (Th07, 125, Some(("S", None))), (Th07, 126, Some(("S", None))), - (Th07, 127, Some(("", None))), + (Th07, 127, Some(("S", None))), (Th07, 128, Some(("S", None))), - (Th07, 129, Some(("SS", None))), - (Th07, 130, Some(("U", None))), // zero: b--- + (Th07, 129, Some(("S(imm)s(imm)--", None))), + (Th07, 130, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- (Th07, 131, Some(("ffSSSS", None))), - (Th07, 132, Some(("U", None))), // zero: b--- + (Th07, 132, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- (Th07, 133, Some(("", None))), (Th07, 134, Some(("", None))), - (Th07, 135, Some(("U", None))), // zero: b--- - (Th07, 136, Some(("U", None))), // zero: b--- - (Th07, 137, Some(("U", None))), // zero: b--- - (Th07, 138, Some(("USSS", None))), // zero: b---SSS + (Th07, 135, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- + (Th07, 136, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- + (Th07, 137, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- + (Th07, 138, Some(("b(imm;hex)---SSS", None))), (Th07, 139, Some(("SSSC", None))), (Th07, 140, Some(("ffff", None))), (Th07, 141, Some(("S", None))), // Not implemented @@ -401,19 +406,19 @@ static ECL_07: &'static CoreSignatures = &CoreSignatures { (Th07, 146, Some(("", None))), (Th07, 147, Some(("S", None))), (Th07, 148, Some(("SSE", None))), - (Th07, 149, Some(("Ufff", None))), + (Th07, 149, Some((r#"U(enum="bool")fff"#, None))), // zero: U(enum="BitBool")fff (Th07, 150, Some(("f", None))), (Th07, 151, Some(("ffff", None))), (Th07, 152, Some(("Sf", None))), (Th07, 153, Some(("fff", None))), (Th07, 154, Some(("S", None))), (Th07, 155, Some(("f", None))), - (Th07, 156, Some(("SU", None))), + (Th07, 156, Some((r#"SU(enum="bool")"#, None))), (Th07, 157, Some(("Sf", None))), (Th07, 158, Some(("Sff", None))), (Th07, 159, Some(("ffff", None))), (Th07, 160, Some(("S", None))), - (Th07, 161, Some(("U", None))), + (Th07, 161, Some((r#"U(enum="bool")"#, None))), // zero: U(enum="BitBool") ], var: &[ (Th07, 10000, Some("$")), @@ -548,7 +553,7 @@ static ECL_08_09: &'static CoreSignatures = &CoreSignatures { (Th08, 49, Some(("ffto", Some(IKind::CondJmp(B::Gt, Ty::Float))))), (Th08, 50, Some(("SSto", Some(IKind::CondJmp(B::Ge, Ty::Int))))), (Th08, 51, Some(("ffto", Some(IKind::CondJmp(B::Ge, Ty::Float))))), - (Th08, 52, Some(("E", Some(IKind::CallReg)))), + (Th08, 52, Some(("E(imm)", Some(IKind::CallReg)))), (Th08, 53, Some(("", None))), // Some(IKind::Return) (Th08, 54, Some(("N", None))), (Th08, 55, Some(("N", None))), @@ -575,110 +580,110 @@ static ECL_08_09: &'static CoreSignatures = &CoreSignatures { (Th08, 76, Some(("", None))), (Th08, 77, Some(("ff", None))), (Th08, 78, Some(("ff", None))), - (Th08, 79, Some(("U", None))), - (Th08, 80, Some(("U", None))), - (Th08, 81, Some(("U", None))), + (Th08, 79, Some(("U(hex)", None))), + (Th08, 80, Some(("U(hex)", None))), + (Th08, 81, Some(("U(hex)", None))), (Th08, 82, Some(("f", None))), - (Th08, 83, Some(("U", None))), + (Th08, 83, Some((r#"U(enum="bool")"#, None))), // zero: U(enum="BitBool") (Th08, 86, Some(("SSS", None))), (Th08, 87, Some(("ffS", None))), - (Th08, 88, Some(("SE", None))), + (Th08, 88, Some(("SE(imm)", None))), (Th08, 89, Some(("SS", None))), - (Th08, 90, Some(("EffSSS", None))), - (Th08, 91, Some(("EffSSS", None))), - (Th08, 92, Some(("EffSSS", None))), - (Th08, 93, Some(("EfffSSS", None))), - (Th08, 94, Some(("EfffSSS", None))), + (Th08, 90, Some(("E(imm)ffSSS", None))), + (Th08, 91, Some(("E(imm)ffSSS", None))), + (Th08, 92, Some(("E(imm)ffSSS", None))), + (Th08, 93, Some(("E(imm)fffSSS", None))), + (Th08, 94, Some(("E(imm)fffSSS", None))), (Th08, 95, Some(("", None))), - (Th08, 96, Some(("ssSSffffS", None))), - (Th08, 97, Some(("ssSSffffS", None))), - (Th08, 98, Some(("ssSSffffS", None))), - (Th08, 99, Some(("ssSSffffS", None))), - (Th08, 100, Some(("ssSSffffS", None))), - (Th08, 101, Some(("ssSSffffS", None))), - (Th08, 102, Some(("ssSSffffS", None))), - (Th08, 103, Some(("ssSSffffS", None))), - (Th08, 104, Some(("ssSSffffS", None))), + (Th08, 96, Some(("ssSSffffU(imm;hex)", None))), + (Th08, 97, Some(("ssSSffffU(imm;hex)", None))), + (Th08, 98, Some(("ssSSffffU(imm;hex)", None))), + (Th08, 99, Some(("ssSSffffU(imm;hex)", None))), + (Th08, 100, Some(("ssSSffffU(imm;hex)", None))), + (Th08, 101, Some(("ssSSffffU(imm;hex)", None))), + (Th08, 102, Some(("ssSSffffU(imm;hex)", None))), + (Th08, 103, Some(("ssSSffffU(imm;hex)", None))), + (Th08, 104, Some(("ssSSffffU(imm;hex)", None))), (Th08, 105, Some(("S", None))), (Th08, 106, Some(("S", None))), (Th08, 107, Some(("", None))), (Th08, 108, Some(("", None))), (Th08, 109, Some(("", None))), (Th08, 110, Some(("ff", None))), - (Th08, 111, Some(("SSUSSff", None))), + (Th08, 111, Some((r#"SU(hex)U(enum="bool")SSff"#, None))), (Th08, 112, Some(("", None))), (Th08, 113, Some(("SS", None))), - (Th08, 114, Some(("ssffffffSSSSSS", None))), - (Th08, 115, Some(("ssffffffSSSSSS", None))), + (Th08, 114, Some(("s(imm)sffffffSSSS(imm)S(imm)U(imm;hex)", None))), + (Th08, 115, Some(("s(imm)sffffffSSSS(imm)S(imm)U(imm;hex)", None))), (Th08, 116, Some(("S", None))), (Th08, 117, Some(("Sf", None))), (Th08, 118, Some(("Sf", None))), (Th08, 119, Some(("Sfff", None))), (Th08, 120, Some(("S", None))), (Th08, 121, Some(("S", None))), - (Th08, 122, Some(("suSm(len=48;mask=0xaa,0,0)m(len=48;mask=0xbb,0,0)m(len=64;nulless;mask=0xdd,0,0)m(len=64;nulless;mask=0xee,0,0)", None))), + (Th08, 122, Some(("s(imm)u(imm)S(imm)m(len=48;mask=0xaa,0,0)m(len=48;mask=0xbb,0,0)m(len=64;nulless;mask=0xdd,0,0)m(len=64;nulless;mask=0xee,0,0)", None))), (Th08, 123, Some(("", None))), (Th08, 124, Some(("S", None))), (Th08, 125, Some(("S", None))), (Th08, 126, Some(("ES", None))), (Th08, 127, Some(("S", None))), - (Th08, 128, Some(("Sffff", None))), - (Th08, 129, Some(("U", None))), // zero: b--- - (Th08, 130, Some(("E", None))), // zero: s(enum="EclSub")-- + (Th08, 128, Some(("S(imm)f(imm)f(imm)f(imm)f(imm)", None))), // Argument 1 is unread + (Th08, 129, Some(("b(imm)---", None))), + (Th08, 130, Some((r#"s(imm;extend;enum="EclSub")--"#, None))), (Th08, 131, Some(("S", None))), (Th08, 132, Some(("S", None))), (Th08, 133, Some(("SSE", None))), (Th08, 134, Some(("SE", None))), (Th08, 135, Some(("SE", None))), // Some(IKind::CallRegAsync) - (Th08, 136, Some(("SS", None))), - (Th08, 137, Some(("SS", None))), - (Th08, 138, Some(("U", None))), // zero: bbb- - (Th08, 139, Some(("SUC", None))), - (Th08, 140, Some(("SUCfff", None))), + (Th08, 136, Some(("SS(imm)", None))), // zero: Sv + (Th08, 137, Some(("SS(imm)", None))), // zero: Sv + (Th08, 138, Some(("c(imm)b(imm)b(imm)-", None))), + (Th08, 139, Some(("SUC", None))), // Third argument is read from a pointer for some reason... + (Th08, 140, Some(("SUCfff", None))), // Also read as a pointer (Th08, 141, Some(("S", None))), (Th08, 142, Some(("S", None))), (Th08, 143, Some(("S", None))), (Th08, 144, Some(("SS", None))), - (Th08, 145, Some(("U", None))), // zero: b--- + (Th08, 145, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- (Th08, 146, Some(("S", None))), (Th08, 147, Some(("S", None))), (Th08, 148, Some(("S", None))), (Th08, 149, Some(("S", None))), - (Th08, 150, Some(("SS", None))), - (Th08, 151, Some(("U", None))), // zero: b--- + (Th08, 150, Some(("S(imm)s(imm)--", None))), + (Th08, 151, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- (Th08, 152, Some(("ffSSSS", None))), (Th08, 153, Some(("", None))), (Th08, 154, Some(("", None))), - (Th08, 155, Some(("U", None))), // zero: b--- - (Th08, 156, Some(("U", None))), // zero: b--- - (Th08, 157, Some(("USSS", None))), // zero: b---SSS + (Th08, 155, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- + (Th08, 156, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- + (Th08, 157, Some(("b(imm;hex)---SSS", None))), (Th08, 158, Some(("SSSC", None))), (Th08, 159, Some(("U", None))), (Th08, 160, Some(("S", None))), (Th08, 161, Some(("f", None))), (Th08, 162, Some(("", None))), (Th08, 163, Some(("S", None))), - (Th08, 164, Some(("Ufff", None))), + (Th08, 164, Some((r#"U(enum="bool")fff"#, None))), // zero: U(enum="BitBool")fff (Th08, 165, Some(("f", None))), (Th08, 166, Some(("ffff", None))), (Th08, 167, Some(("Sf", None))), (Th08, 168, Some(("S", None))), (Th08, 169, Some(("f", None))), - (Th08, 170, Some(("SU", None))), + (Th08, 170, Some((r#"SU(enum="bool")"#, None))), (Th08, 171, Some(("Sf", None))), (Th08, 172, Some(("Sff", None))), - (Th08, 173, Some(("U", None))), + (Th08, 173, Some((r#"U(enum="bool")"#, None))), // zero: U(enum="BitBool") (Th08, 174, Some(("S", None))), - (Th08, 175, Some(("U", None))), - (Th08, 176, Some(("S", None))), + (Th08, 175, Some((r#"U(enum="bool")"#, None))), + (Th08, 176, Some((r#"b(imm;enum="bool")---"#, None))), // Argument is never read, so this is just to try and make it look better in the output (Th08, 177, Some(("S", None))), (Th08, 178, Some(("SSf", None))), (Th08, 179, Some(("", None))), (Th08, 180, Some(("", None))), (Th08, 181, Some(("", None))), - (Th08, 182, Some(("U", None))), - (Th08, 183, Some(("U", None))), - (Th08, 184, Some(("U", None))), + (Th08, 182, Some((r#"U(enum="bool")"#, None))), // zero: U(enum="BitBool") + (Th08, 183, Some((r#"U(enum="bool")"#, None))), // zero: U(enum="BitBool") + (Th08, 184, Some((r#"U(enum="bool")"#, None))), // zero: U(enum="BitBool") (Th09, 83, None), (Th09, 90, None), @@ -697,9 +702,9 @@ static ECL_08_09: &'static CoreSignatures = &CoreSignatures { (Th09, 180, None), (Th09, 181, None), (Th09, 184, None), - (Th09, 185, Some(("U", None))), + (Th09, 185, Some((r#"U(enum="bool")"#, None))), // zero: U(enum="BitBool") (Th09, 186, Some(("", None))), - (Th09, 187, Some(("U", None))), + (Th09, 187, Some((r#"U(enum="bool")"#, None))), // zero: U(enum="BitBool") ], var: &[ (Th08, 10000, Some("$")), @@ -868,7 +873,7 @@ static ECL_095: &'static CoreSignatures = &CoreSignatures { (Th095, 49, Some(("ffto", Some(IKind::CondJmp(B::Gt, Ty::Float))))), (Th095, 50, Some(("SSto", Some(IKind::CondJmp(B::Ge, Ty::Int))))), (Th095, 51, Some(("ffto", Some(IKind::CondJmp(B::Ge, Ty::Float))))), - (Th095, 52, Some(("E", Some(IKind::CallReg)))), + (Th095, 52, Some(("E(imm)", Some(IKind::CallReg)))), (Th095, 53, Some(("", None))), // Some(IKind::Return) (Th095, 54, Some(("N", None))), (Th095, 55, Some(("N", None))), @@ -893,29 +898,29 @@ static ECL_095: &'static CoreSignatures = &CoreSignatures { (Th095, 76, Some(("", None))), (Th095, 77, Some(("ff", None))), (Th095, 78, Some(("ff", None))), - (Th095, 79, Some(("U", None))), - (Th095, 80, Some(("U", None))), - (Th095, 81, Some(("U", None))), + (Th095, 79, Some(("U(hex)", None))), + (Th095, 80, Some(("U(hex)", None))), + (Th095, 81, Some(("U(hex)", None))), (Th095, 82, Some(("f", None))), - (Th095, 83, Some(("E", None))), - (Th095, 84, Some(("Eff", None))), + (Th095, 83, Some(("E(imm)", None))), + (Th095, 84, Some(("E(imm)ff", None))), // Game engine reads E(imm)fff, but the files are cursed trash (Th095, 85, Some(("", None))), - (Th095, 86, Some(("ssSSffffS", None))), - (Th095, 87, Some(("ssSSffffS", None))), - (Th095, 88, Some(("ssSSffffS", None))), - (Th095, 89, Some(("ssSSffffS", None))), - (Th095, 90, Some(("ssSSffffS", None))), - (Th095, 91, Some(("ssSSffffS", None))), - (Th095, 92, Some(("ssSSffffS", None))), - (Th095, 93, Some(("ssSSffffS", None))), - (Th095, 94, Some(("ssSSffffS", None))), + (Th095, 86, Some(("ssSSffffU(imm;hex)", None))), + (Th095, 87, Some(("ssSSffffU(imm;hex)", None))), + (Th095, 88, Some(("ssSSffffU(imm;hex)", None))), + (Th095, 89, Some(("ssSSffffU(imm;hex)", None))), + (Th095, 90, Some(("ssSSffffU(imm;hex)", None))), + (Th095, 91, Some(("ssSSffffU(imm;hex)", None))), + (Th095, 92, Some(("ssSSffffU(imm;hex)", None))), + (Th095, 93, Some(("ssSSffffU(imm;hex)", None))), + (Th095, 94, Some(("ssSSffffU(imm;hex)", None))), (Th095, 95, Some(("S", None))), (Th095, 96, Some(("S", None))), (Th095, 97, Some(("", None))), (Th095, 98, Some(("", None))), (Th095, 99, Some(("", None))), (Th095, 100, Some(("ff", None))), - (Th095, 101, Some(("SSUSSff", None))), + (Th095, 101, Some((r#"SU(hex)U(enum="bool")SSff"#, None))), (Th095, 102, Some(("", None))), (Th095, 103, Some(("SS", None))), (Th095, 104, Some(("m(len=48;mask=0xaa,0,0)", None))), @@ -924,47 +929,47 @@ static ECL_095: &'static CoreSignatures = &CoreSignatures { (Th095, 107, Some(("S", None))), (Th095, 108, Some(("ES", None))), (Th095, 109, Some(("S", None))), - (Th095, 112, Some(("E", None))), // zero: s(enum="EclSub")-- + (Th095, 112, Some((r#"s(imm;enum="EclSub")--"#, None))), (Th095, 113, Some(("S", None))), (Th095, 114, Some(("S", None))), (Th095, 115, Some(("SSE", None))), (Th095, 116, Some(("SE", None))), (Th095, 117, Some(("SE", None))), // Some(IKind::CallRegAsync) - (Th095, 118, Some(("S", None))), - (Th095, 119, Some(("S", None))), - (Th095, 120, Some(("U", None))), // zero: b--- + (Th095, 118, Some(("S", None))), // zero: Sv + (Th095, 119, Some(("S", None))), // zero: Sv + (Th095, 120, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- (Th095, 121, Some(("S", None))), (Th095, 124, Some(("S", None))), - (Th095, 126, Some(("U", None))), // zero: b--- + (Th095, 126, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- (Th095, 128, Some(("", None))), - (Th095, 130, Some(("U", None))), // zero: b--- - (Th095, 131, Some(("USSS", None))), // zero: b---SSS - (Th095, 132, Some(("S", None))), + (Th095, 130, Some((r#"b(imm;enum="bool")---"#, None))), // zero: b(imm;enum="BitBool")--- + (Th095, 131, Some(("b(imm;hex)---SSS", None))), + (Th095, 132, Some(("U", None))), (Th095, 133, Some(("S", None))), (Th095, 135, Some(("f", None))), (Th095, 136, Some(("ffff", None))), (Th095, 137, Some(("f", None))), - (Th095, 138, Some(("U", None))), // zero: b--- + (Th095, 138, Some((r#"U(enum="bool")"#, None))), // zero: U(enum="BitBool") (Th095, 139, Some(("S", None))), - (Th095, 140, Some(("U", None))), // zero: b--- + (Th095, 140, Some((r#"U(enum="bool")"#, None))), // zero: U(enum="BitBool") (Th095, 141, Some(("S", None))), (Th095, 142, Some(("", None))), - (Th095, 143, Some(("US", None))), + (Th095, 143, Some((r#"U(enum="bool")S"#, None))), // zero: U(enum="BitBool")S (Th095, 144, Some(("S", None))), (Th095, 145, Some(("SSffff", None))), (Th095, 146, Some(("SSffff", None))), - (Th095, 147, Some(("SSfffSSSSfU", None))), - (Th095, 148, Some(("SSfffSSSSfU", None))), + (Th095, 147, Some(("SSfffSSSSfU(imm;hex)", None))), + (Th095, 148, Some(("SSfffSSSSfU(imm;hex)", None))), (Th095, 149, Some(("f", None))), (Th095, 150, Some(("N", None))), (Th095, 151, Some(("SN", None))), (Th095, 152, Some(("SS", None))), - (Th095, 153, Some(("SSfffSSSSfU", None))), - (Th095, 154, Some(("SSfffSSSSfU", None))), - (Th095, 155, Some(("SSfffSSSSfUff", None))), - (Th095, 156, Some(("SSfffSSSSfUff", None))), - (Th095, 157, Some(("SSfffSSSSfUff", None))), - (Th095, 158, Some(("U", None))), + (Th095, 153, Some(("SSfffSSSSfU(imm;hex)", None))), + (Th095, 154, Some(("SSfffSSSSfU(imm;hex)", None))), + (Th095, 155, Some(("SSfffSSSSfU(imm;hex)ff", None))), + (Th095, 156, Some(("SSfffSSSSfU(imm;hex)ff", None))), + (Th095, 157, Some(("SSfffSSSSfU(imm;hex)ff", None))), + (Th095, 158, Some((r#"U(enum="bool")"#, None))), // zero: U(enum="BitBool") ], var: &[ (Th095, 10000, Some("$")), @@ -1053,3 +1058,591 @@ static ECL_095: &'static CoreSignatures = &CoreSignatures { (Th095, 10085, Some("%")), ], }; + + +static ECL_10_11: &'static CoreSignatures = &CoreSignatures { + inherit: &[ECL_10_19_COMMON], + ins: &[ + // Section B + (Th10, 256, Some(("P(bs=4)ffSSS", None))), + (Th10, 257, Some(("P(bs=4)ffSSS", None))), + (Th10, 258, Some(("S", None))), + (Th10, 259, Some(("SN", None))), + (Th10, 260, Some(("P(bs=4)ffSSS", None))), + (Th10, 261, Some(("P(bs=4)ffSSS", None))), + (Th10, 262, Some(("SN", None))), + (Th10, 263, Some(("SN", None))), + (Th10, 264, Some(("SN", None))), + (Th10, 265, Some(("P(bs=4)ffSSS", None))), + (Th10, 266, Some(("P(bs=4)ffSSS", None))), + (Th10, 267, Some(("P(bs=4)ffSSS", None))), + (Th10, 268, Some(("P(bs=4)ffSSS", None))), + (Th10, 269, Some(("S", None))), + (Th10, 270, Some(("P(bs=4)fffSSS", None))), + (Th10, 271, Some(("P(bs=4)fffSSS", None))), + (Th10, 272, Some(("SN", None))), + (Th10, 273, Some(("SNf", None))), + + (Alcostg, 274, Some(("SN", None))), + (Alcostg, 275, Some(("SS", None))), + + (Th11, 276, Some(("", None))), + (Th11, 277, Some(("Sf", None))), + (Th11, 278, Some(("S", None))), // Not implemented + + // Section C + (Th10, 280, Some(("ff", None))), + (Th10, 281, Some(("SSff", None))), + (Th10, 282, Some(("ff", None))), + (Th10, 283, Some(("SSff", None))), + (Th10, 284, Some(("ff", None))), + (Th10, 285, Some(("SSff", None))), + (Th10, 286, Some(("ff", None))), + (Th10, 287, Some(("SSff", None))), + (Th10, 288, Some(("ffff", None))), + (Th10, 289, Some(("SSffff", None))), + (Th10, 290, Some(("ffff", None))), + (Th10, 291, Some(("SSffff", None))), + (Th10, 292, Some(("SSf", None))), + (Th10, 293, Some(("SSf", None))), + (Th10, 294, Some(("", None))), + (Th10, 295, Some(("", None))), + (Th10, 296, Some(("fff", None))), + (Th10, 297, Some(("fff", None))), + (Th10, 298, Some(("ff", None))), + (Th10, 299, Some(("ff", None))), + + (Alcostg, 300, Some(("ffffff", None))), + (Alcostg, 301, Some(("SSffffff", None))), + (Alcostg, 302, Some(("ffffff", None))), + (Alcostg, 303, Some(("SSffffff", None))), + + (Th11, 289, Some(("SSfff", None))), // Why is there 1 fewer arg after this? + (Th11, 291, Some(("SSfff", None))), + (Th11, 304, Some((r#"S(enum="bool")"#, None))), // zero: S(enum="BitBool") + (Th11, 305, Some(("Sffffff", None))), + (Th11, 306, Some(("Sffffff", None))), + (Th11, 307, Some(("", None))), + + // Section D + (Th10, 320, Some(("ff", None))), + (Th10, 321, Some(("ff", None))), + (Th10, 322, Some(("S(hex)", None))), + (Th10, 323, Some(("S(hex)", None))), + (Th10, 324, Some(("ffff", None))), + (Th10, 325, Some(("", None))), + (Th10, 326, Some(("", None))), + (Th10, 327, Some(("SS", None))), + (Th10, 328, Some(("ff", None))), + (Th10, 329, Some(("", None))), + (Th10, 330, Some(("S", None))), + (Th10, 331, Some(("S", None))), + (Th10, 332, Some(("S", None))), + (Th10, 333, Some(("", None))), + (Th10, 334, Some(("SSSP(bs=4)", None))), + (Th10, 335, Some(("S", None))), + (Th10, 336, Some(("S", None))), + (Th10, 337, Some(("SSS", None))), + (Th10, 338, Some((r#"S(enum="MsgScript")"#, None))), + (Th10, 339, Some(("", None))), + (Th10, 340, Some(("", None))), + (Th10, 341, Some(("SP(bs=4)", None))), + (Th10, 342, Some(("SSSp(bs=4;mask=0x77,7,16)", None))), // Third arg read but not used + (Th10, 343, Some(("", None))), + (Th10, 344, Some(("S", None))), + (Th10, 345, Some(("", None))), + (Th10, 346, Some(("f", None))), + (Th10, 347, Some(("SfC", None))), + (Th10, 348, Some(("SSSp(bs=4;mask=0x77,7,16)", None))), // Third arg read but not used + (Th10, 349, Some(("fff", None))), + (Th10, 350, Some(("fffff", None))), + (Th10, 351, Some(("fff", None))), + (Th10, 352, Some(("SSS", None))), + (Th10, 353, Some(("SSSSS", None))), + (Th10, 354, Some(("SSS", None))), + (Th10, 355, Some(("SSSSS", None))), // alcostg: SS___ + (Th10, 356, Some(("fffff", None))), // alcostg: ff___ + (Th10, 357, Some(("SSSp(bs=4;mask=0x77,7,16)", None))), // Third arg read but not used + (Th10, 358, Some(("SSSp(bs=4;mask=0x77,7,16)", None))), // Third arg read but not used + (Th10, 359, Some(("SSSp(bs=4;mask=0x77,7,16)", None))), // Third arg read but not used + (Th10, 360, Some(("S", None))), + (Th10, 361, Some(("S", None))), + (Th10, 362, Some(("", None))), + (Th10, 363, Some(("", None))), + (Th10, 364, Some((r#"S(enum="bool")"#, None))), // zero: S(enum="BitBool") + (Th10, 365, Some(("", None))), + (Th10, 366, Some((r#"S(enum="bool")N"#, None))), // zero: S(enum="BitBool")N + (Th10, 367, Some(("f", None))), + (Th10, 368, Some(("SSSS", None))), // alcostg: S___ + + (Alcostg, 369, Some(("SSSS", None))), + + (Th11, 369, Some((r#"S(enum="bool")"#, None))), // zero: S(enum="BitBool") + (Th11, 370, Some(("S", None))), + (Th11, 371, Some(("S", None))), + (Th11, 372, Some(("S", None))), + + // Section E + (Th10, 400, Some(("S", None))), + (Th10, 401, Some(("S", None))), + (Th10, 402, Some(("SSS", None))), + (Th10, 403, Some(("Sff", None))), + (Th10, 404, Some(("Sff", None))), + (Th10, 405, Some(("Sff", None))), + (Th10, 406, Some(("SSS", None))), + (Th10, 407, Some(("SS", None))), + (Th10, 408, Some(("SSS", None))), + (Th10, 409, Some((r#"SSS(enum="bool")SSSff"#, None))), + (Th10, 410, Some(("", None))), + (Th10, 411, Some(("SS", None))), + (Th10, 412, Some(("SSffffff", None))), + (Th10, 413, Some(("SSSfffSSSSfS(hex)", None))), + (Th10, 414, Some(("Sff", None))), + (Th10, 415, Some(("Sff", None))), + (Th10, 416, Some(("Sf", None))), + (Th10, 417, Some(("Sf", None))), + (Th10, 418, Some(("Sf", None))), + (Th10, 419, Some(("Sf", None))), + (Th10, 420, Some(("f", None))), + (Th10, 421, Some(("f", None))), + (Th10, 422, Some(("Sffffff", None))), + (Th10, 423, Some(("Sffffffffff", None))), + (Th10, 424, Some(("Sffff", None))), + (Th10, 425, Some(("SSSSSSS", None))), + (Th10, 426, Some(("SSSSSSSSSSS", None))), + (Th10, 427, Some(("SSSSS", None))), + (Th10, 428, Some(("SSffffff", None))), + (Th10, 429, Some(("SSSfffSSSSfS(hex)", None))), + (Th10, 430, Some(("fff", None))), + (Th10, 431, Some(("SSffffff", None))), + (Th10, 432, Some(("SSSfffSSSSfS(hex)", None))), + (Th10, 433, Some(("SSffffff", None))), + (Th10, 434, Some(("SSSfffSSSSfS(hex)", None))), + (Th10, 435, Some(("Sffffffff", None))), // alcostg: Sf___f___ + (Th10, 436, Some(("SSSSSSSSS", None))), // alcostg: SS___S___ + + (Alcostg, 437, Some(("S", None))), + (Alcostg, 438, Some(("SS", None))), + (Alcostg, 439, Some(("Sf", None))), + (Alcostg, 440, Some(("", None))), + (Alcostg, 441, Some((r#"S(enum="bool")"#, None))), // zero: S(enum="BitBool") + (Alcostg, 442, Some(("S", None))), + (Alcostg, 443, Some(("S", None))), // S(enum="StdInterrupt") + (Alcostg, 444, Some(("Sf", None))), + + (Th11, 437, Some(("Sff", None))), + (Th11, 438, Some(("Sf", None))), + (Th11, 439, Some(("Sff", None))), + (Th11, 440, Some(("fC", None))), + (Th11, 441, Some(("S", None))), // S(enum="StdInterrupt") + (Th11, 442, Some((r#"S(enum="bool")"#, None))), // zero: S(enum="BitBool") + (Th11, 443, Some(("S", None))), + (Th11, 444, Some(("S", None))), + (Th11, 445, Some(("S", None))), + (Th11, 446, Some(("f", None))), + (Th11, 447, Some(("f", None))), + (Th11, 448, Some(("S", None))), + (Th11, 449, Some(("S", None))), + (Th11, 450, Some(("S", None))), + + // Section F (Didn't exist yet) + + // Section G (Didn't exist yet) + + // Section H + (Th11, 500, Some(("S", None))), // Not implemented + + // Section I (Didn't exist yet) + ], + var: &[ + // This is placed here to avoid putting game-specific + // stuff into the "common" definition block + (Alcostg, -9959, None), // Didn't have difficulty + (Th11, -9959, Some("$")), // Readded difficulty + ], +}; + + +static ECL_12: &'static CoreSignatures = &CoreSignatures { + inherit: &[ECL_10_19_COMMON], + ins: &[ + // Section B + (Th12, 256, Some(("P(bs=4)ffSSS", None))), + (Th12, 257, Some(("P(bs=4)ffSSS", None))), + (Th12, 258, Some(("S", None))), + (Th12, 259, Some(("SN", None))), + (Th12, 260, Some(("P(bs=4)ffSSS", None))), + (Th12, 261, Some(("P(bs=4)ffSSS", None))), + (Th12, 262, Some(("SN", None))), + (Th12, 263, Some(("SN", None))), + (Th12, 264, Some(("SN", None))), + (Th12, 265, Some(("P(bs=4)ffSSS", None))), + (Th12, 266, Some(("P(bs=4)ffSSS", None))), + (Th12, 267, Some(("P(bs=4)ffSSS", None))), + (Th12, 268, Some(("P(bs=4)ffSSS", None))), + (Th12, 269, Some(("S", None))), + (Th12, 270, Some(("P(bs=4)fffSSS", None))), + (Th12, 271, Some(("P(bs=4)fffSSS", None))), + (Th12, 272, Some(("SN", None))), + (Th12, 273, Some(("SNf", None))), + (Th12, 274, Some(("SN", None))), + (Th12, 275, Some(("SS", None))), + (Th12, 276, Some(("", None))), + (Th12, 277, Some(("Sf", None))), + (Th12, 278, Some(("Sff", None))), + (Th12, 279, Some(("Sff", None))), + (Th12, 280, Some(("P(bs=4)ffSSS", None))), + (Th12, 281, Some(("SS", None))), + (Th125, 282, Some(("SS", None))), + + // Section C + (Th12, 300, Some(("ff", None))), + (Th12, 301, Some(("SSff", None))), + (Th12, 302, Some(("ff", None))), + (Th12, 303, Some(("SSff", None))), + (Th12, 304, Some(("ff", None))), + (Th12, 305, Some(("SSff", None))), + (Th12, 306, Some(("ff", None))), + (Th12, 307, Some(("SSff", None))), + (Th12, 308, Some(("ffff", None))), + (Th12, 309, Some(("SSfff", None))), + (Th12, 310, Some(("ffff", None))), + (Th12, 311, Some(("SSfff", None))), + (Th12, 312, Some(("SSf", None))), + (Th12, 313, Some(("SSf", None))), + (Th12, 314, Some(("", None))), + (Th12, 315, Some(("", None))), + (Th12, 316, Some(("fff", None))), + (Th12, 317, Some(("fff", None))), + (Th12, 318, Some(("ff", None))), + (Th12, 319, Some(("ff", None))), + (Th12, 320, Some(("ffffff", None))), + (Th12, 321, Some(("SSfffff", None))), // Again, another pair of removed floats...? + (Th12, 322, Some(("ffffff", None))), + (Th12, 323, Some(("SSfffff", None))), + (Th12, 324, Some((r#"S(enum="bool")"#, None))), // zero: S(enum="BitBool") + (Th12, 325, Some(("Sffffff", None))), + (Th12, 326, Some(("Sffffff", None))), + (Th12, 327, Some(("", None))), + (Th12, 328, Some(("ff", None))), + (Th12, 329, Some(("SSff", None))), + (Th12, 330, Some(("ff", None))), + (Th12, 331, Some(("SSff", None))), + (Th125, 332, Some(("S", None))), + (Th125, 333, Some(("S", None))), + + // Section D + (Th12, 400, Some(("ff", None))), + (Th12, 401, Some(("ff", None))), + (Th12, 402, Some(("S(hex)", None))), + (Th12, 403, Some(("S(hex)", None))), + (Th12, 404, Some(("ffff", None))), + (Th12, 405, Some(("", None))), + (Th12, 406, Some(("", None))), + (Th12, 407, Some(("SS", None))), + (Th12, 408, Some(("ff", None))), + (Th12, 409, Some(("", None))), + (Th12, 410, Some(("S", None))), + (Th12, 411, Some(("S", None))), + (Th12, 412, Some(("S", None))), + (Th12, 413, Some(("", None))), + (Th12, 414, Some(("SSSP(bs=4)", None))), + (Th12, 415, Some(("S", None))), + (Th12, 416, Some(("S", None))), + (Th12, 417, Some(("SSS", None))), + (Th12, 418, Some((r#"S(enum="MsgScript")"#, None))), + (Th12, 419, Some(("", None))), + (Th12, 420, Some(("", None))), + (Th12, 421, Some(("SP(bs=4)", None))), + (Th12, 422, Some(("SSSp(bs=4;mask=0x77,7,16)", None))), // Third arg read but not used + (Th12, 423, Some(("", None))), + (Th12, 424, Some(("S", None))), + (Th12, 425, Some(("", None))), + (Th12, 426, Some(("f", None))), + (Th12, 427, Some(("SfC", None))), + (Th12, 428, Some(("SSSp(bs=4;mask=0x77,7,16)", None))), // Third arg read but not used + (Th12, 429, Some(("fff", None))), + (Th12, 430, Some(("fffff", None))), + (Th12, 431, Some(("fff", None))), + (Th12, 432, Some(("SSS", None))), + (Th12, 433, Some(("SSSSS", None))), + (Th12, 434, Some(("SSS", None))), + (Th12, 435, Some(("SSSSS", None))), + (Th12, 436, Some(("fffff", None))), + (Th12, 437, Some(("SSSp(bs=4;mask=0x77,7,16)", None))), // Third arg read but not used + (Th12, 438, Some(("SSSp(bs=4;mask=0x77,7,16)", None))), // Third arg read but not used + (Th12, 439, Some(("SSSp(bs=4;mask=0x77,7,16)", None))), // Third arg read but not used + (Th12, 440, Some(("S", None))), + (Th12, 441, Some(("S", None))), + (Th12, 442, Some(("", None))), + (Th12, 443, Some(("", None))), + (Th12, 444, Some((r#"S(enum="bool")"#, None))), // zero: S(enum="BitBool") + (Th12, 445, Some(("", None))), + (Th12, 446, Some((r#"S(enum="bool")N"#, None))), // zero: S(enum="BitBool")N + (Th12, 447, Some(("f", None))), + (Th12, 448, Some(("SSSS", None))), + (Th12, 449, Some((r#"S(enum="bool")"#, None))), // zero: S(enum="BitBool") + (Th12, 450, Some(("S", None))), + (Th12, 451, Some(("S", None))), + (Th12, 452, Some(("S", None))), + (Th12, 453, Some(("S", None))), + (Th12, 454, Some(("", None))), + (Th12, 455, Some(("SS", None))), + (Th12, 456, Some(("ffS", None))), + + (Th125, 427, None), + (Th125, 440, None), + (Th125, 454, None), + (Th125, 457, Some(("", None))), + (Th125, 458, Some(("S", None))), + (Th125, 459, Some(("S", None))), + (Th125, 460, Some(("f", None))), + (Th125, 461, Some(("f", None))), + (Th125, 462, Some(("S", None))), + // (Th125, 463, Some(("P(bs=4)", None))), + + // Section E + (Th12, 500, Some(("S", None))), + (Th12, 501, Some(("S", None))), + (Th12, 502, Some(("SSS", None))), + (Th12, 503, Some(("Sff", None))), + (Th12, 504, Some(("Sff", None))), + (Th12, 505, Some(("Sff", None))), + (Th12, 506, Some(("SSS", None))), + (Th12, 507, Some(("SS", None))), + (Th12, 508, Some(("SSS", None))), + (Th12, 509, Some((r#"SSS(enum="bool")SSSff"#, None))), + (Th12, 510, Some(("", None))), + (Th12, 511, Some(("SS", None))), + // Laser instructions moved, everything else shifted up + (Th12, 512, Some(("f", None))), + (Th12, 513, Some(("f", None))), + (Th12, 514, Some(("Sffffff", None))), + (Th12, 515, Some(("Sffffffffff", None))), + (Th12, 516, Some(("Sffff", None))), + (Th12, 517, Some(("SSSSSSS", None))), + (Th12, 518, Some(("SSSSSSSSSSS", None))), + (Th12, 519, Some(("SSSSS", None))), + // More lasers moved + (Th12, 520, Some(("fff", None))), + // Even more lasers moved + (Th12, 521, Some(("Sffffffff", None))), + (Th12, 522, Some(("SSSSSSSSS", None))), + (Th12, 523, Some(("Sff", None))), + (Th12, 524, Some(("Sf", None))), + (Th12, 525, Some(("Sff", None))), + (Th12, 526, Some(("fC", None))), + (Th12, 527, Some(("S", None))), // S(enum="StdInterrupt") + (Th12, 528, Some((r#"S(enum="bool")"#, None))), // zero: S(enum="BitBool") + (Th12, 529, Some(("S", None))), + (Th12, 530, Some(("S", None))), + (Th12, 531, Some(("S", None))), + (Th12, 532, Some(("f", None))), + (Th12, 533, Some(("f", None))), + // Final laser instruction moved + (Th12, 534, Some(("S", None))), + (Th12, 535, Some(("S", None))), + (Th125, 536, Some(("S", None))), + + // Section F + (Th12, 600, Some(("Sffff", None))), + (Th12, 601, Some(("SSSSSS(hex)", None))), + (Th12, 602, Some(("S", None))), + (Th12, 603, Some(("SS", None))), + (Th12, 604, Some(("Sff", None))), + (Th12, 605, Some(("Sff", None))), + (Th12, 606, Some(("Sf", None))), + (Th12, 607, Some(("Sf", None))), + (Th12, 608, Some(("Sf", None))), + (Th12, 609, Some(("Sf", None))), + (Th12, 610, Some(("S", None))), + (Th12, 611, Some(("S", None))), + (Th125, 612, Some(("ff", None))), + + // Section G (Didn't exist yet) + + // Section H + (Th12, 700, Some(("S", None))), // Not implemented + + // Section I (Didn't exist yet) + ], + var: &[], +}; + + +static ECL_10_19_COMMON: &'static CoreSignatures = &CoreSignatures { + inherit: &[], + ins: &[ + (Th10, 0, Some(("", None))), + (Th10, 1, Some(("", None))), + (Th10, 10, Some(("", None))), // + // (Th10, 11, Some((r#"P(bs=4)v(rep="G")"#, None))), // Some(IKind::Return) + (Th10, 12, Some(("ot", Some(IKind::Jmp)))), + (Th10, 13, Some(("ot", None))), // Some(IKind::PopJmp(B::Eq)) + (Th10, 14, Some(("ot", None))), // Some(IKind::PopJmp(B::Ne)) + // (Th10, 15, Some((r#"P(bs=4)v(rep="G")"#, None))), // Some(IKind::CallStackAsync) + // (Th10, 16, Some((r#"P(bs=4)Sv(rep="G")"#, None))), // Some(IKind::CallStackAsyncId) + (Th10, 17, Some(("S", None))), + (Th10, 18, Some(("S", None))), + (Th10, 19, Some(("S", None))), + (Th10, 20, Some(("SS", None))), + (Th10, 21, Some(("", None))), + // (Th10, 30, Some((r#"p(bs=4)v(rep="g")"#, None))), // Implementation removed as of DS + (Th10, 40, Some(("S", None))), // Some(IKind::FrameEnter) + (Th10, 41, Some(("", None))), // Some(IKind::FrameLeave) + (Th10, 42, Some(("S", None))), // Some(IKind::Push(Ty::Int)) + (Th10, 43, Some(("S", None))), // Some(IKind::Pop(Ty::Int)) + (Th10, 44, Some(("f", None))), // Some(IKind::Push(Ty::Float)) + (Th10, 45, Some(("f", None))), // Some(IKind::Pop(Ty::Float)) + (Th10, 50, Some(("", None))), // Some(IKind::StackBinOp(B::Add, Ty::Int)) + (Th10, 51, Some(("", None))), // Some(IKind::StackBinOp(B::Add, Ty::Float)) + (Th10, 52, Some(("", None))), // Some(IKind::StackBinOp(B::Sub, Ty::Int)) + (Th10, 53, Some(("", None))), // Some(IKind::StackBinOp(B::Sub, Ty::Float)) + (Th10, 54, Some(("", None))), // Some(IKind::StackBinOp(B::Mul, Ty::Int)) + (Th10, 55, Some(("", None))), // Some(IKind::StackBinOp(B::Mul, Ty::Float)) + (Th10, 56, Some(("", None))), // Some(IKind::StackBinOp(B::Div, Ty::Int)) + (Th10, 57, Some(("", None))), // Some(IKind::StackBinOp(B::Div, Ty::Float)) + (Th10, 58, Some(("", None))), // Some(IKind::StackBinOp(B::Mod, Ty::Int)) + (Th10, 59, Some(("", None))), // Some(IKind::StackBinCmp(B::Eq, Ty::Int)) + (Th10, 60, Some(("", None))), // Some(IKind::StackBinCmp(B::Eq, Ty::Float)) + (Th10, 61, Some(("", None))), // Some(IKind::StackBinCmp(B::Ne, Ty::Int)) + (Th10, 62, Some(("", None))), // Some(IKind::StackBinCmp(B::Ne, Ty::Float)) + (Th10, 63, Some(("", None))), // Some(IKind::StackBinCmp(B::Lt, Ty::Int)) + (Th10, 64, Some(("", None))), // Some(IKind::StackBinCmp(B::Lt, Ty::Float)) + (Th10, 65, Some(("", None))), // Some(IKind::StackBinCmp(B::Le, Ty::Int)) + (Th10, 66, Some(("", None))), // Some(IKind::StackBinCmp(B::Le, Ty::Float)) + (Th10, 67, Some(("", None))), // Some(IKind::StackBinCmp(B::Gt, Ty::Int)) + (Th10, 68, Some(("", None))), // Some(IKind::StackBinCmp(B::Gt, Ty::Float)) + (Th10, 69, Some(("", None))), // Some(IKind::StackBinCmp(B::Ge, Ty::Int)) + (Th10, 70, Some(("", None))), // Some(IKind::StackBinCmp(B::Ge, Ty::Float)) + (Th10, 71, Some(("", None))), // Some(IKind::StackUnCmp(U::Not, Ty::Int)) + (Th10, 72, Some(("", None))), // Some(IKind::StackUnCmp(U::Not, Ty::Float)) + (Th10, 73, Some(("", None))), // Some(IKind::StackBinOp(B::IllogicOr, Ty::Int)) + (Th10, 74, Some(("", None))), // Some(IKind::StackBinOp(B::IllogicAnd, Ty::Int)) + (Th10, 75, Some(("", None))), // Some(IKind::StackBinCmp(B::BitXor, Ty::Int)) + (Th10, 76, Some(("", None))), // Some(IKind::StackBinCmp(B::BitOr, Ty::Int)) + (Th10, 77, Some(("", None))), // Some(IKind::StackBinCmp(B::BitAnd, Ty::Int)) + (Th10, 78, Some(("S", None))), // + (Th10, 79, Some(("", None))), // Some(IKind::StackUnOp(U::Sin, Ty::Float)) + (Th10, 80, Some(("", None))), // Some(IKind::StackUnOp(U::Cos, Ty::Float)) + (Th10, 81, Some(("ffff", None))), + (Th10, 82, Some(("f", None))), + (Th10, 83, Some(("S", None))), + (Th10, 84, Some(("", None))), // Some(IKind::StackUnOp(U::Neg, Ty::Int)) + (Th10, 85, Some(("", None))), // NOT an intrinsic, since it's broken + (Th10, 86, Some(("fff", None))), + (Th10, 87, Some(("ffff", None))), + + (Th12, 87, Some(("fffff", None))), // Changed to not use first arg as input and output + + (Alcostg, 88, Some(("", None))), // Some(IKind::StackUnOp(U::Sqrt, Ty::Float)) + + (Th12, 89, Some(("fff", None))), + + // Finally not broken + (Th125, 85, Some(("", None))), // Some(IKind::StackUnOp(U::Neg, Ty::Float)) + (Th125, 90, Some(("fffff", None))), + (Th125, 91, Some(("SfSSff", None))), + (Th125, 92, Some(("SfSSffff", None))), + + (Th128, 22, Some((r#"SP(bs=4)"#, None))), + ], + var: &[ + (Th10, -10000, Some("$")), + (Th10, -9999, Some("%")), + (Th10, -9998, Some("%")), + (Th10, -9997, Some("%")), + (Th10, -9996, Some("%")), + (Th10, -9995, Some("%")), + (Th10, -9994, Some("%")), + (Th10, -9993, Some("%")), + (Th10, -9992, Some("%")), + (Th10, -9991, Some("%")), + (Th10, -9990, Some("%")), + (Th10, -9989, Some("%")), + (Th10, -9988, Some("$")), + (Th10, -9987, Some("%")), + (Th10, -9986, Some("$")), + (Th10, -9985, Some("$")), // Writable since MoF + (Th10, -9984, Some("$")), // Writable since MoF + (Th10, -9983, Some("$")), // Writable since MoF + (Th10, -9982, Some("$")), // Writable since MoF + (Th10, -9981, Some("%")), // Writable since MoF + (Th10, -9980, Some("%")), // Writable since MoF + (Th10, -9979, Some("%")), // Writable since MoF + (Th10, -9978, Some("%")), // Writable since MoF + (Th10, -9977, Some("%")), + (Th10, -9976, Some("%")), + (Th10, -9975, Some("%")), + (Th10, -9974, Some("%")), + (Th10, -9973, Some("%")), + (Th10, -9972, Some("%")), + (Th10, -9971, Some("%")), + (Th10, -9970, Some("%")), + (Th10, -9969, Some("%")), + (Th10, -9968, Some("%")), + (Th10, -9967, Some("%")), + (Th10, -9966, Some("%")), + (Th10, -9965, Some("%")), + (Th10, -9964, Some("%")), + (Th10, -9963, Some("%")), + (Th10, -9962, Some("%")), + (Th10, -9961, Some("$")), + (Th10, -9960, Some("$")), + (Th10, -9959, Some("$")), + (Th10, -9958, Some("%")), + (Th10, -9957, Some("$")), + (Th10, -9956, Some("%")), + (Th10, -9955, Some("%")), + (Th10, -9954, Some("$")), + (Th10, -9953, Some("$")), + (Th10, -9952, Some("$")), + (Th10, -9951, Some("$")), + (Th10, -9950, Some("$")), + + (Alcostg, -9949, Some("$")), // Writable since SA + (Alcostg, -9948, Some("$")), // Writable since SA + + (Th11, -9947, Some("$")), // Writable since SA + (Th11, -9946, Some("$")), + (Th11, -9945, Some("$")), + (Th11, -9944, Some("%")), + (Th11, -9943, Some("$")), // Writable since SA + (Th11, -9942, Some("$")), // Writable since SA + (Th11, -9941, Some("$")), // Writable since SA + (Th11, -9940, Some("$")), // Writable since SA + (Th11, -9939, Some("%")), // Writable since SA + (Th11, -9938, Some("%")), // Writable since SA + (Th11, -9937, Some("%")), // Writable since SA + (Th11, -9936, Some("%")), // Writable since SA + (Th11, -9935, Some("%")), // Writable since SA + (Th11, -9934, Some("%")), // Writable since SA + (Th11, -9933, Some("%")), // Writable since SA + (Th11, -9932, Some("%")), // Writable since SA + + (Th12, -9931, Some("$")), + (Th12, -9930, Some("$")), + + (Th125, -9930, Some("$")), + (Th125, -9929, Some("$")), + (Th125, -9928, Some("$")), + (Th125, -9927, Some("$")), + (Th125, -9926, Some("$")), + (Th125, -9925, Some("$")), + (Th125, -9924, Some("$")), + (Th125, -9923, Some("$")), + (Th125, -9922, Some("%")), + (Th125, -9921, Some("%")), + (Th125, -9920, Some("%")), + (Th125, -9919, Some("%")), + (Th125, -9918, Some("%")), + (Th125, -9917, Some("%")), + (Th125, -9916, Some("%")), + (Th125, -9915, Some("%")), + (Th125, -9914, Some("$")), + (Th125, -9913, Some("$")), + (Th125, -9912, Some("$")), + (Th125, -9911, Some("%")), + (Th125, -9910, Some("%")), + ], +}; diff --git a/src/core_mapfiles/end.rs b/src/core_mapfiles/end.rs new file mode 100644 index 0000000..889675f --- /dev/null +++ b/src/core_mapfiles/end.rs @@ -0,0 +1,55 @@ +use super::CoreSignatures; +use crate::Game::{self, *}; + +pub(super) fn core_signatures(game: Game) -> &'static CoreSignatures { + match game { + | Th095 | Alcostg | Th125 | Th143 | Th165 | Th185 + => EMPTY, + + | Th06 | Th07 | Th08 | Th09 + => END_06_09, + + | Th10 | Th11 | Th12 | Th128 | Th13 + | Th14 | Th15 | Th16 | Th17 | Th18 | Th19 + => END_10_19, + } +} + +// Bunkachou titles have no true END scripts. +static EMPTY: &CoreSignatures = &CoreSignatures { + inherit: &[], + ins: &[], + var: &[], +}; + +static END_06_09: &CoreSignatures = &CoreSignatures { + inherit: &[], + // TODO + ins: &[], + var: &[], +}; + +static END_10_19: &CoreSignatures = &CoreSignatures { + inherit: &[], + ins: &[ + (Th10, 0, Some(("", None))), + (Th10, 1, None), + (Th10, 2, None), + (Th10, 3, Some(("m(bs=4;mask=0x77,7,16)", None))), + (Th10, 4, Some(("", None))), + (Th10, 5, Some(("S", None))), + (Th10, 6, Some(("S", None))), + (Th10, 7, Some(("Sz(bs=4)", None))), + (Th10, 8, Some(("SSN", None))), + (Th10, 9, Some(("C", None))), + (Th10, 10, Some(("z(bs=4)", None))), + (Th10, 11, Some(("", None))), + (Th10, 12, Some(("z(bs=4)", None))), + (Th10, 13, Some(("S", None))), + (Th10, 14, Some(("S", None))), + (Th10, 15, Some(("SSN", None))), + (Th10, 16, Some(("SSN", None))), + (Th10, 17, Some(("SSN", None))), + ], + var: &[], +}; diff --git a/src/core_mapfiles/mod.rs b/src/core_mapfiles/mod.rs index 21de548..34ae1d9 100644 --- a/src/core_mapfiles/mod.rs +++ b/src/core_mapfiles/mod.rs @@ -8,6 +8,7 @@ use crate::llir::IntrinsicInstrKind; mod anm; mod ecl; +mod end; mod msg; mod std; @@ -20,7 +21,7 @@ pub fn core_mapfile(emitter: &RootEmitter, game: Game, language: LanguageKey) -> LanguageKey::Msg => self::msg::core_signatures(game), LanguageKey::Ecl => self::ecl::core_signatures(game), LanguageKey::Timeline => self::ecl::timeline_core_signatures(game), - LanguageKey::End => CoreSignatures::EMPTY, // TODO + LanguageKey::End => self::end::core_signatures(game), LanguageKey::Dummy => CoreSignatures::EMPTY, }; diff --git a/src/core_mapfiles/msg.rs b/src/core_mapfiles/msg.rs index fb529dd..a166cd5 100644 --- a/src/core_mapfiles/msg.rs +++ b/src/core_mapfiles/msg.rs @@ -3,15 +3,15 @@ use crate::Game::{self, *}; pub(super) fn core_signatures(game: Game) -> &'static CoreSignatures { match game { - | Th095 | Th125 + | Th095 | Alcostg => EMPTY, | Th06 | Th07 | Th08 | Th09 => MSG_06_09, - | Th10 | Alcostg | Th11 | Th12 | Th128 | Th13 - | Th14 | Th143 | Th15 | Th16 | Th165 | Th17 | Th18 - => MSG_10_18, + | Th10 | Th11 | Th12 | Th125 | Th128 | Th13 + | Th14 | Th143 | Th15 | Th16 | Th165 | Th17 | Th18 | Th185 | Th19 + => MSG_10_19, } } @@ -26,37 +26,40 @@ static MSG_06_09: &CoreSignatures = &CoreSignatures { inherit: &[], ins: &[ (Th06, 0, Some(("", None))), - (Th06, 1, Some(("ss", None))), - (Th06, 2, Some(("ss", None))), // note: 2nd word is technically an anm sprite + (Th06, 1, Some((r#"ss(enum="AnmScript")"#, None))), + (Th06, 2, Some((r#"ss(enum="AnmSprite")"#, None))), (Th06, 3, Some(("ssz(bs=4)", None))), (Th06, 4, Some(("S", None))), - (Th06, 5, Some(("ss", None))), + (Th06, 5, Some(("sb-", None))), (Th06, 6, Some(("", None))), (Th06, 7, Some(("S", None))), (Th06, 8, Some(("ssz(bs=4)", None))), - (Th06, 9, Some(("S", None))), // arg looks unused + (Th06, 9, Some(("_", None))), (Th06, 10, Some(("", None))), (Th06, 11, Some(("", None))), (Th06, 12, Some(("", None))), - (Th06, 13, Some(("S", None))), + (Th06, 13, Some((r#"b(enum="bool")---"#, None))), (Th07, 14, Some(("", None))), (Th08, 3, Some(("ssm(bs=4;mask=0x77,0,0)", None))), (Th08, 8, Some(("ssm(bs=4;mask=0x77,0,0)", None))), - (Th08, 15, Some(("SSSSS", None))), // Snnnn + (Th08, 15, Some(("Unnnn", None))), (Th08, 16, Some(("m(bs=4;mask=0x77,0,0)", None))), - (Th08, 17, Some(("SS", None))), // Sn - (Th08, 18, Some(("S", None))), + (Th08, 17, Some(("Un", None))), + (Th08, 18, Some((r#"b(enum="bool")---"#, None))), (Th08, 19, Some(("m(bs=4;mask=0x77,0,0)", None))), (Th08, 20, Some(("m(bs=4;mask=0x77,0,0)", None))), (Th08, 21, Some(("S", None))), (Th08, 22, Some(("", None))), + (Th09, 1, Some(("s--", None))), (Th09, 3, Some(("ssm(bs=4;mask=0x77,7,16)", None))), (Th09, 8, Some(("", None))), - (Th09, 15, Some(("SSS", None))), + (Th09, 9, Some(("S", None))), // Can't be marked as padding since unused value is non-zero + (Th09, 15, Some(("Snn", None))), (Th09, 16, Some(("m(bs=4;mask=0x77,7,16)", None))), + (Th09, 17, Some(("Sn", None))), (Th09, 19, None), // removed from jumptable (Th09, 20, None), (Th09, 21, None), @@ -64,29 +67,29 @@ static MSG_06_09: &CoreSignatures = &CoreSignatures { (Th09, 23, Some(("S", None))), (Th09, 24, Some(("", None))), (Th09, 25, Some(("", None))), - (Th09, 26, Some(("S", None))), + (Th09, 26, Some(("b---", None))), // 27 is not in the jumptable; could be a nop, but it's never used (Th09, 28, Some(("S", None))), ], var: &[], }; -static MSG_10_18: &CoreSignatures = &CoreSignatures { +static MSG_10_19: &CoreSignatures = &CoreSignatures { inherit: &[], ins: &[ (Th10, 0, Some(("", None))), - (Th10, 1, Some(("S", None))), // arg is unused - (Th10, 2, Some(("S", None))), // arg is unused + (Th10, 1, Some(("_", None))), + (Th10, 2, Some(("_", None))), (Th10, 3, Some(("", None))), (Th10, 4, Some(("", None))), (Th10, 5, Some(("", None))), (Th10, 6, Some(("", None))), (Th10, 7, Some(("", None))), (Th10, 8, Some(("", None))), - (Th10, 9, Some(("S", None))), + (Th10, 9, Some((r#"b(enum="bool")---"#, None))), // zero: b(enum="BitBool")--- (Th10, 10, Some(("S", None))), (Th10, 11, Some(("", None))), - (Th10, 12, Some(("S", None))), - (Th10, 13, Some(("S", None))), + (Th10, 12, Some(("N", None))), + (Th10, 13, Some(("N", None))), (Th10, 14, Some(("m(bs=4;mask=0x77,7,16)", None))), (Th10, 15, Some(("m(bs=4;mask=0x77,7,16)", None))), (Th10, 16, Some(("m(bs=4;mask=0x77,7,16)", None))), @@ -100,7 +103,8 @@ static MSG_10_18: &CoreSignatures = &CoreSignatures { // th11 inserts one in the middle :( (Th11, 9, Some(("", None))), // new - (Th11, 10, Some(("S", None))), // 10...24 are TH10's 9...23 + // 10...24 are TH10's 9...23 + (Th11, 10, Some((r#"b(enum="bool")---"#, None))), // zero: b(enum="BitBool")--- (Th11, 11, Some(("S", None))), (Th11, 12, Some(("", None))), (Th11, 13, Some(("S", None))), @@ -123,6 +127,7 @@ static MSG_10_18: &CoreSignatures = &CoreSignatures { (Th12, 17, Some(("m(bs=4;mask=0x77,7,16;furibug)", None))), (Th12, 27, Some(("f", None))), // new + (Th128, 2, Some(("S", None))), // Argument started being non-zero (Th128, 28, Some(("ff", None))), (Th128, 29, Some(("S", None))), (Th128, 30, Some(("", None))), @@ -139,6 +144,7 @@ static MSG_10_18: &CoreSignatures = &CoreSignatures { (Th15, 33, None), // removed + (Th16, 1, Some(("S", None))), // Argument started being non-zero (Th16, 33, Some(("SS", None))), // replaced with something totally different (but unused) (Th16, 34, Some(("SS", None))), (Th16, 35, Some(("", None))), @@ -155,6 +161,32 @@ static MSG_10_18: &CoreSignatures = &CoreSignatures { (Th18, 7, Some(("S", None))), (Th18, 13, Some(("SS", None))), (Th18, 36, Some(("", None))), + + (Th185, 19, Some(("S", None))), + (Th185, 37, Some(("", None))), + (Th185, 38, Some(("", None))), + (Th185, 39, Some(("", None))), + (Th185, 40, Some(("m(bs=4;mask=0x77,7,16;furibug)", None))), // Just hoping these string types are correct + (Th185, 41, Some(("m(bs=4;mask=0x77,7,16;furibug)", None))), + + // Th19 Notes: + // 20 doesn't read an argument, check size + (Th19, 31, None), + (Th19, 42, Some(("S", None))), + (Th19, 43, Some(("S", None))), + (Th19, 44, Some(("ff", None))), + (Th19, 45, Some(("ff", None))), + (Th19, 46, Some(("SS", None))), + (Th19, 47, Some(("SS", None))), + (Th19, 48, Some(("S", None))), + (Th19, 49, Some(("S", None))), + (Th19, 50, Some(("S", None))), + (Th19, 51, Some(("S", None))), + (Th19, 52, Some(("", None))), + (Th19, 53, Some(("", None))), + (Th19, 54, Some(("", None))), + (Th19, 55, Some(("", None))), + (Th19, 56, Some(("", None))), ], var: &[], }; diff --git a/src/core_mapfiles/std.rs b/src/core_mapfiles/std.rs index 1036711..2127811 100644 --- a/src/core_mapfiles/std.rs +++ b/src/core_mapfiles/std.rs @@ -11,8 +11,8 @@ pub(super) fn core_signatures(game: Game) -> &'static CoreSignatures { => STD_07_09, | Th095 | Th10 | Alcostg | Th11 | Th12 | Th125 | Th128 - | Th13 | Th14 | Th143 | Th15 | Th16 | Th165 | Th17 | Th18 - => STD_095_18 + | Th13 | Th14 | Th143 | Th15 | Th16 | Th165 | Th17 | Th18 | Th185 | Th19 + => STD_095_19 } } @@ -61,44 +61,49 @@ static STD_07_09: &CoreSignatures = &CoreSignatures { (Th07, 26, Some(("fff", None))), (Th07, 27, Some(("fff", None))), (Th07, 28, Some(("S__", None))), - (Th07, 29, Some(("S__", None))), // anm script - (Th07, 30, Some(("S__", None))), // anm script + (Th07, 29, Some(("N__", None))), + (Th07, 30, Some(("N__", None))), (Th07, 31, Some(("S__", Some(IKind::InterruptLabel)))), (Th08, 32, Some(("fff", None))), - (Th08, 33, Some(("S__", None))), - (Th08, 34, Some(("S__", None))), // anm script + (Th08, 33, Some(("b---__", None))), + (Th08, 34, Some(("N__", None))), ], var: &[], }; -static STD_095_18: &CoreSignatures = &CoreSignatures { +static STD_095_19: &CoreSignatures = &CoreSignatures { inherit: &[], ins: &[ (Th095, 0, Some(("", None))), (Th095, 1, Some(("ot", Some(IKind::Jmp)))), (Th095, 2, Some(("fff", None))), - (Th095, 3, Some(("SSfff", None))), + (Th095, 3, Some(("Sbb--fff", None))), (Th095, 4, Some(("fff", None))), - (Th095, 5, Some(("SSfff", None))), + (Th095, 5, Some(("Sbb--fff", None))), (Th095, 6, Some(("fff", None))), (Th095, 7, Some(("f", None))), (Th095, 8, Some(("Cff", None))), - (Th095, 9, Some(("SSCff", None))), - (Th095, 10, Some(("SSfffffffff", None))), - (Th095, 11, Some(("SSfffffffff", None))), - (Th095, 12, Some(("S", None))), + (Th095, 9, Some(("Sbb--Cff", None))), + (Th095, 10, Some(("SUfffffffff", None))), // Technically S_fffffffff since MoF + (Th095, 11, Some(("SUfffffffff", None))), // Technically S_fffffffff since MoF + (Th095, 12, Some(("b---", None))), (Th095, 13, Some(("C", None))), - (Th095, 14, Some(("SS", None))), // SN + (Th095, 14, Some(("SN", None))), // 15 appears to be a nop (i.e. it's not in the jumptable). // However, no game ever uses it - (Th11, 16, Some(("S", Some(IKind::InterruptLabel)))), + (Th10, 3, Some(("SUfff", None))), + (Th10, 5, Some(("SUfff", None))), + (Th10, 9, Some(("SUCff", None))), // Technically the C arg is split into 4 individual byte reads. But why tho + + (Alcostg, 16, Some(("S(imm)", Some(IKind::InterruptLabel)))), + (Th11, 17, Some(("S", None))), - (Th12, 18, Some(("SSfff", None))), + (Th12, 18, Some(("SUfff", None))), - (Th14, 14, Some(("SSS", None))), // SNS. 'layer' argument added + (Th14, 14, Some(("SNS", None))), // 'layer' argument added (Th14, 19, Some(("S", None))), (Th14, 20, Some(("f", None))), diff --git a/src/fmt.rs b/src/fmt.rs index c462995..d87a0fe 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -892,19 +892,21 @@ impl Format for ast::Expr { token![unop $] | token![unop %] | token![unop int] | token![unop float] | - token![sin] | token![cos] | token![sqrt] + token![sin] | token![cos] | token![tan] | token![acos] | token![atan] | token![sqrt] => out.fmt((op, "(", SuppressParens(x), ")")), }, ast::Expr::XcrementOp { order: ast::XcrementOpOrder::Pre, op, var } => out.fmt((op, var)), ast::Expr::XcrementOp { order: ast::XcrementOpOrder::Post, op, var } => out.fmt((var, op)), ast::Expr::EnumConst { enum_name, ident } => out.fmt((enum_name, ".", ident)), - ast::Expr::LitInt { value: 0, radix: ast::IntRadix::Bool } => out.fmt("false"), - ast::Expr::LitInt { value: 1, radix: ast::IntRadix::Bool } => out.fmt("true"), - ast::Expr::LitInt { value, radix: ast::IntRadix::Bool } => out.fmt(value), - ast::Expr::LitInt { value, radix: ast::IntRadix::Dec } => out.fmt(value), - ast::Expr::LitInt { value, radix: ast::IntRadix::Hex } => out.fmt(format_args!("{:#x}", value)), - ast::Expr::LitInt { value, radix: ast::IntRadix::SignedHex } => out.fmt(format_args!("{:#x}", SignedRadix(*value))), - ast::Expr::LitInt { value, radix: ast::IntRadix::Bin } => out.fmt(format_args!("{:#b}", value)), + ast::Expr::LitInt { value: 0, format: ast::IntFormat { unsigned: _, radix: ast::IntRadix::Bool } } => out.fmt("false"), + ast::Expr::LitInt { value: 1, format: ast::IntFormat { unsigned: _, radix: ast::IntRadix::Bool } } => out.fmt("true"), + ast::Expr::LitInt { value, format: ast::IntFormat { unsigned: false, radix: ast::IntRadix::Bool } } => out.fmt(value), + ast::Expr::LitInt { value, format: ast::IntFormat { unsigned: true, radix: ast::IntRadix::Bool } } => out.fmt(format_args!("{}", *value as u32)), + ast::Expr::LitInt { value, format: ast::IntFormat { unsigned: false, radix: ast::IntRadix::Dec } } => out.fmt(value), + ast::Expr::LitInt { value, format: ast::IntFormat { unsigned: true, radix: ast::IntRadix::Dec } } => out.fmt(format_args!("{}", *value as u32)), + ast::Expr::LitInt { value, format: ast::IntFormat { unsigned: true, radix: ast::IntRadix::Hex } } => out.fmt(format_args!("{:#x}", value)), + ast::Expr::LitInt { value, format: ast::IntFormat { unsigned: false, radix: ast::IntRadix::Hex } } => out.fmt(format_args!("{:#x}", SignedRadix(*value))), + ast::Expr::LitInt { value, format: ast::IntFormat { unsigned: _, radix: ast::IntRadix::Bin } } => out.fmt(format_args!("{:#b}", value)), ast::Expr::LitFloat { value } => out.fmt(value), ast::Expr::LitString(x) => out.fmt(x), ast::Expr::LabelProperty { label, keyword } => out.fmt((keyword, "(", label, ")")), diff --git a/src/formats/anm/mod.rs b/src/formats/anm/mod.rs index 79c837f..6783265 100644 --- a/src/formats/anm/mod.rs +++ b/src/formats/anm/mod.rs @@ -225,6 +225,9 @@ pub struct EntrySpecs { /// e.g. the border around the game. (not used for things like `ascii.anm` which have dedicated files /// for each resolution) pub low_res_scale: bool, + pub jpeg_quality: u32, + pub full_width: u32, + pub full_height: u32, } #[derive(Debug, Clone, Default)] @@ -244,6 +247,9 @@ struct WorkingEntrySpecs { offset_y: SoftOption, memory_priority: SoftOption, low_res_scale: SoftOption, + jpeg_quality: SoftOption, + full_width: SoftOption, + full_height: SoftOption, } fn default_memory_priority(version: Version) -> u32 { @@ -267,6 +273,9 @@ fn finalize_entry(fs: &Fs, entry: WorkingEntry, game: Game, emitter: &impl Emitt specs.memory_priority.set_soft_if_missing(default_memory_priority(version)); specs.low_res_scale.set_soft_if_missing(DEFAULT_LOW_RES_SCALE); specs.has_data.set_soft_if_missing(DEFAULT_HAS_DATA); + specs.jpeg_quality.set_soft_if_missing(0); + specs.full_width.set_soft_if_missing(0); + specs.full_height.set_soft_if_missing(0); // Do this now. For missing images that are also missing metadata, // this tends to produce the nicest error message. @@ -314,6 +323,9 @@ fn finalize_entry(fs: &Fs, entry: WorkingEntry, game: Game, emitter: &impl Emitt colorkey: specs.colorkey.into_option().expect("was filled by default"), memory_priority: specs.memory_priority.into_option().expect("was filled by default"), low_res_scale: specs.low_res_scale.into_option().expect("was filled by default"), + jpeg_quality: specs.jpeg_quality.into_option().expect("was filled by default"), + full_width: specs.full_width.into_option().expect("was filled by default"), + full_height: specs.full_height.into_option().expect("was filled by default"), }, texture_metadata: texture_data.as_ref().map(|_| TextureMetadata { width: specs.img_width.into_option().expect("was filled by default"), @@ -534,6 +546,7 @@ impl Entry { let EntrySpecs { rt_width, rt_height, rt_format, colorkey, offset_x, offset_y, memory_priority, low_res_scale, + jpeg_quality, full_width, full_height, } = self.specs; // suppress defaults @@ -562,6 +575,9 @@ impl Entry { .field_opt("rt_format", Some(rt_format).filter(|&x| x != img_format).map(format_to_meta)) .field_opt("memory_priority", Some(memory_priority).filter(|&x| x != default_memory_priority(version))) .field_opt("low_res_scale", Some(low_res_scale).filter(|&x| x != DEFAULT_LOW_RES_SCALE)) + .field_opt("jpeg_quality", Some(jpeg_quality).filter(|&x| x != 0)) + .field_opt("full_width", Some(full_width).filter(|&x| x != 0)) + .field_opt("full_height", Some(full_height).filter(|&x| x != 0)) .field("sprites", &self.sprites) .build_fields() } @@ -641,6 +657,9 @@ impl WorkingEntry { let memory_priority = make_explicit(m.get_field("memory_priority")?); let low_res_scale = make_explicit(m.get_field("low_res_scale")?); let has_data = make_explicit(m.get_field("has_data")?); + let jpeg_quality = make_explicit(m.get_field("jpeg_quality")?); + let full_width = make_explicit(m.get_field("full_width")?); + let full_height = make_explicit(m.get_field("full_height")?); let path: Sp = m.expect_field("path")?; let path_2 = m.get_field("path_2")?; let sprites = m.get_field("sprites")?.unwrap_or_default(); @@ -664,6 +683,7 @@ impl WorkingEntry { img_width, img_height, img_format, colorkey, offset_x, offset_y, memory_priority, has_data, low_res_scale, + jpeg_quality, full_width, full_height, }; let loaded_texture = None; let scripts = Default::default(); @@ -686,7 +706,7 @@ fn format_to_meta(format_num: u32) -> Meta { } fn colorkey_to_meta(colorkey: u32) -> impl ToMeta { - ast::Expr::LitInt { value: colorkey as i32, radix: ast::IntRadix::Hex } + ast::Expr::LitInt { value: colorkey as i32, format: ast::IntFormat { unsigned: true, radix: ast::IntRadix::Hex } } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] @@ -768,17 +788,19 @@ pub struct Sprite { pub id: Option, pub offset: [f32; 2], pub size: [f32; 2], + pub unknown: Option<[f32; 5]>, } impl ToMeta for Sprite { fn to_meta(&self) -> Meta { - let &Sprite { id, offset, size } = self; + let &Sprite { id, offset, size, unknown, } = self; Meta::make_object() .field("x", &offset[0]) .field("y", &offset[1]) .field("w", &size[0]) .field("h", &size[1]) .field_opt("id", id.as_ref()) + .field_opt("unknown", unknown.as_ref()) .build() } } @@ -789,6 +811,7 @@ impl FromMeta<'_> for Sprite { id: m.get_field("id")?, offset: [m.expect_field("x")?, m.expect_field("y")?], size: [m.expect_field("w")?, m.expect_field("h")?], + unknown: m.get_field("unknown")? })) } } @@ -887,6 +910,9 @@ fn update_entry_from_anm_image_source(dest_file: &mut WorkingEntry, src_file: En colorkey: src_colorkey, offset_x: src_offset_x, offset_y: src_offset_y, memory_priority: src_memory_priority, low_res_scale: src_low_res_scale, + jpeg_quality: src_jpeg_quality, + full_width: src_full_width, + full_height: src_full_height, } = src_specs; dest_file.specs.has_data.set_soft(HasData::from(src_texture_data.is_some())); @@ -910,6 +936,9 @@ fn update_entry_from_anm_image_source(dest_file: &mut WorkingEntry, src_file: En dest_file.specs.offset_y.set_soft(src_offset_y); dest_file.specs.memory_priority.set_soft(src_memory_priority); dest_file.specs.low_res_scale.set_soft(src_low_res_scale); + dest_file.specs.jpeg_quality.set_soft(src_jpeg_quality); + dest_file.specs.full_width.set_soft(src_full_width); + dest_file.specs.full_height.set_soft(src_full_height); Ok(()) } @@ -1154,7 +1183,7 @@ fn strip_unnecessary_sprite_ids<'a>(entry_sprites: impl IntoIterator(entry_sprites: impl IntoIterator Version::V3, Th095 | Th10 | Alcostg => Version::V4, Th11 | Th12 | Th125 | Th128 => Version::V7, - Th13 | Th14 | Th143 | Th15 | Th16 | Th165 | Th17 | Th18 => Version::V8, + Th13 | Th14 | Th143 | Th15 | Th16 | Th165 | Th17 | Th18 | Th185 | Th19 => Version::V8, } } diff --git a/src/formats/anm/read_write.rs b/src/formats/anm/read_write.rs index 3ec5070..4ee520d 100644 --- a/src/formats/anm/read_write.rs +++ b/src/formats/anm/read_write.rs @@ -29,7 +29,10 @@ struct EntryHeaderData { thtx_offset: Option, has_data: u32, low_res_scale: u32, + jpeg_quality: u32, next_offset: u64, + full_width: u32, + full_height: u32, } pub fn read_anm( @@ -107,7 +110,7 @@ fn read_entry( let mut sprites_seen_in_entry = IndexSet::new(); let sprites = sprite_offsets.iter().map(|&offset| { reader.seek_to(entry_pos + offset as u64)?; - let sprite = read_sprite(reader)?; + let sprite = read_sprite(reader, format.has_extended_sprites())?; let sprite_id = sprite.id.expect("(bug!) sprite read from binary must always have id"); // Note: Duplicate IDs do happen between different entries, so we don't check that. @@ -164,6 +167,9 @@ fn read_entry( offset_x: header_data.offset_x, offset_y: header_data.offset_y, memory_priority: header_data.memory_priority, low_res_scale: header_data.low_res_scale != 0, + jpeg_quality: header_data.jpeg_quality, + full_width: header_data.full_width, + full_height: header_data.full_height, }; let entry = Entry { @@ -226,7 +232,8 @@ fn write_entry( let EntrySpecs { rt_width, rt_height, rt_format, colorkey, offset_x, offset_y, memory_priority, - low_res_scale, + low_res_scale, jpeg_quality, + full_width, full_height } = entry.specs; file_format.write_header(w, &EntryHeaderData { @@ -241,6 +248,9 @@ fn write_entry( // we will overwrite these later name_offset: 0, secondary_name_offset: None, next_offset: 0, thtx_offset: None, + jpeg_quality: jpeg_quality as u32, + full_width: full_width as u32, + full_height: full_height as u32, })?; let sprite_offsets_pos = w.pos()?; @@ -261,9 +271,9 @@ fn write_entry( let sprite_offset = w.pos()? - entry_pos; let sprite_id = sprite.id.unwrap_or(*next_auto_sprite_id); - *next_auto_sprite_id = sprite_id + 1; + *next_auto_sprite_id = sprite_id.wrapping_add(1); - write_sprite(w, sprite_id, sprite)?; + write_sprite(w, sprite_id, sprite, file_format.has_extended_sprites())?; Ok(sprite_offset) }).collect::>>()?; @@ -325,11 +335,16 @@ fn write_entry( Ok(()) } -fn read_sprite(f: &mut BinReader) -> ReadResult { +fn read_sprite(f: &mut BinReader, extended: bool) -> ReadResult { Ok(Sprite { id: Some(f.read_u32()?), offset: f.read_f32s_2()?, size: f.read_f32s_2()?, + unknown: if extended { + Some(f.read_f32s_5()?) + } else { + None + }, }) } @@ -337,10 +352,15 @@ fn write_sprite( f: &mut BinWriter, sprite_id: u32, // we ignore sprite.id because that can be None sprite: &Sprite, + extended: bool ) -> WriteResult { f.write_u32(sprite_id)?; f.write_f32s(&sprite.offset)?; - f.write_f32s(&sprite.size) + f.write_f32s(&sprite.size)?; + if extended { + f.write_f32s(&sprite.unknown.unwrap_or_else(|| [0.0, 0.0, 1.0, 1.0, 0.0]))?; + } + Ok(()) } #[inline(never)] @@ -390,6 +410,7 @@ fn write_texture(f: &mut BinWriter, data: &TextureData, metadata: &TextureMetada /// Type responsible for dealing with version differences in the container format. struct FileFormat { version: Version, + game: Game, instr_format: Box, } @@ -400,7 +421,11 @@ impl FileFormat { fn from_game(game: Game) -> Self { let version = Version::from_game(game); let instr_format = get_instr_format(version); - FileFormat { version, instr_format } + FileFormat { version, game, instr_format } + } + + fn has_extended_sprites(&self) -> bool { + self.game == Game::Th19 } fn read_header(&self, f: &mut BinReader, emitter: &dyn Emitter) -> ReadResult { @@ -430,10 +455,11 @@ impl FileFormat { let version = f.read_u32()? as _; let memory_priority = f.read_u32()? as _; let thtx_offset = NonZeroU64::new(f.read_u32()? as _) as _; - let has_data = f.read_u16()? as _; - warn_if_nonzero!("unused_2", f.read_u16()?); + let has_data = f.read_u8()? as _; + warn_if_nonzero!("unused_2", f.read_u8()?); + warn_if_nonzero!("unused_3", f.read_u16()?); let next_offset = f.read_u32()? as _; - warn_if_nonzero!("unused_3", f.read_u32()?); + warn_if_nonzero!("unused_4", f.read_u32()?); Ok(EntryHeaderData { version, num_sprites, num_scripts, @@ -443,6 +469,7 @@ impl FileFormat { next_offset, secondary_name_offset, colorkey, memory_priority, thtx_offset, has_data, offset_x: 0, offset_y: 0, low_res_scale: 0, + jpeg_quality: 0, full_width: 0, full_height: 0, }) } else { @@ -459,13 +486,20 @@ impl FileFormat { let offset_y = f.read_u16()? as _; let memory_priority = f.read_u32()? as _; let thtx_offset = NonZeroU64::new(f.read_u32()? as _); - let has_data = f.read_u16()? as _; - let low_res_scale = f.read_u16()? as _; + let has_data = f.read_u8()? as _; + warn_if_nonzero!("unused_1", f.read_u8()?); + let low_res_scale = f.read_u8()? as _; + let jpeg_quality = f.read_u8()? as _; let next_offset = f.read_u32()? as _; + let full_width = f.read_u16()? as _; + let full_height = f.read_u16()? as _; + // header gets padded to 16 dwords + warn_if_nonzero!("header_padding1", f.read_u32()?); + warn_if_nonzero!("header_padding2", f.read_u32()?); + warn_if_nonzero!("header_padding3", f.read_u32()?); + warn_if_nonzero!("header_padding4", f.read_u32()?); + warn_if_nonzero!("header_padding5", f.read_u32()?); - for _ in 0..6 { // header gets padded to 16 dwords - warn_if_nonzero!("header padding", f.read_u32()?); - } Ok(EntryHeaderData { version, num_sprites, num_scripts, rt_width: width, @@ -475,6 +509,7 @@ impl FileFormat { memory_priority, thtx_offset, has_data, secondary_name_offset: None, colorkey: 0, + jpeg_quality, full_width, full_height, }) } } @@ -514,10 +549,14 @@ impl FileFormat { f.write_u16(header.offset_y as _)?; f.write_u32(header.memory_priority as _)?; f.write_u32(header.thtx_offset.map(NonZeroU64::get).unwrap_or(0) as _)?; - f.write_u16(header.has_data as _)?; - f.write_u16(header.low_res_scale as _)?; + f.write_u8(header.has_data as _)?; + f.write_u8(0)?; + f.write_u8(header.low_res_scale as _)?; + f.write_u8(header.jpeg_quality as _)?; f.write_u32(header.next_offset as _)?; - f.write_u32s(&[0; 6])?; + f.write_u16(header.full_width as _)?; + f.write_u16(header.full_height as _)?; + f.write_u32s(&[0; 5])?; } Ok(()) } diff --git a/src/formats/ecl/ecl_06.rs b/src/formats/ecl/ecl_06.rs index cb88790..8311edc 100644 --- a/src/formats/ecl/ecl_06.rs +++ b/src/formats/ecl/ecl_06.rs @@ -1003,7 +1003,7 @@ impl LanguageHooks for OldeEclHooks { Game::Th06 => enum_map::enum_map!{ ScalarType::Int => vec![ R(-10001), R(-10002), R(-10003), R(-10004), // I0-I3 - R(-10009), R(-10010), R(-10011), R(-10012), // I4-I7 + R(-10009), R(-10010), R(-10011), R(-10012), // IC0-IC3 ], ScalarType::Float => vec![ R(-10005), R(-10006), R(-10007), R(-10008), // F0-F3 @@ -1013,23 +1013,44 @@ impl LanguageHooks for OldeEclHooks { Game::Th07 => enum_map::enum_map!{ ScalarType::Int => vec![ R(10000), R(10001), R(10002), R(10003), // I0-I3 - R(10012), R(10013), R(10014), R(10015), // I4-I7 + R(10012), R(10013), R(10014), R(10015), // IC0-IC3 + //R(10029), R(10030), R(10031), R(10032), // PARAM_A-PARAM_D ], ScalarType::Float => vec![ R(10004), R(10005), R(10006), R(10007), // F0-F3 R(10008), R(10009), R(10010), R(10011), // F4-F7 R(10072), R(10074), // F8-F9 + //R(10033), R(10034), R(10035), R(10036), // PARAM_R-PARAM_N ], ScalarType::String => vec![], }, - Game::Th08 => enum_map::enum_map!{ + Game::Th08 | Game::Th09 => enum_map::enum_map!{ ScalarType::Int => vec![ - R(10000), R(10001), R(10002), R(10003), - R(10012), R(10013), R(10014), R(10015), + R(10000), R(10001), R(10002), R(10003), // I0-I3 + R(10004), R(10005), R(10006), R(10007), // I4-I7 + R(10036), R(10037), R(10038), R(10039), // IC0-IC3 + //R(10053), R(10054), R(10055), R(10056), // PARAM_A-PARAM_D ], ScalarType::Float => vec![ - R(10004), R(10005), R(10006), R(10007), - R(10008), R(10009), R(10010), R(10011), + R(10016), R(10017), R(10018), R(10019), // F0-F3 + R(10020), R(10021), R(10022), R(10023), // F4-F7 + R(10094), R(10095), // F8-F9 + //R(10057), R(10058), R(10059), R(10060), // PARAM_R-PARAM_N + ], + ScalarType::String => vec![], + }, + Game::Th095 => enum_map::enum_map!{ + ScalarType::Int => vec![ + R(10000), R(10001), R(10002), R(10003), // I0-I3 + R(10004), R(10005), R(10006), R(10007), // I4-I7 + R(10020), R(10021), R(10022), R(10023), // IC0-IC3 + //R(10036), R(10037), R(10038), R(10039), // PARAM_A-PARAM_D + ], + ScalarType::Float => vec![ + R(10008), R(10009), R(10010), R(10011), // F0-F3 + R(10012), R(10013), R(10014), R(10015), // F4-F7 + R(10077), R(10078), R(10079), R(10080), // F8-F11 + //R(10040), R(10041), R(10042), R(10043), // PARAM_R-PARAM_N ], ScalarType::String => vec![], }, @@ -1076,7 +1097,7 @@ impl InstrFormat for OldeEclHooks { fn read_instr(&self, f: &mut BinReader, emitter: &dyn Emitter) -> ReadResult { let time = f.read_i32()?; let opcode = f.read_u16()?; - let size = f.read_u16()? as usize; + let size = f.read_i16()? as usize; let before_difficulty = f.read_u8()?; // according to zero, not referenced in any game let difficulty = f.read_u8()?; let param_mask = f.read_u16()?; @@ -1110,7 +1131,7 @@ impl InstrFormat for OldeEclHooks { fn write_instr(&self, f: &mut BinWriter, _: &dyn Emitter, instr: &RawInstr) -> WriteResult { f.write_i32(instr.time)?; f.write_u16(instr.opcode)?; - f.write_u16(self.instr_size(instr) as _)?; + f.write_i16(self.instr_size(instr) as _)?; f.write_u8(0)?; f.write_u8(instr.difficulty)?; @@ -1126,7 +1147,7 @@ impl InstrFormat for OldeEclHooks { fn write_terminal_instr(&self, f: &mut BinWriter, _: &dyn Emitter) -> WriteResult { f.write_i32(-1)?; // time f.write_i16(-1)?; // opcode - f.write_u16(self.instr_header_size() as _)?; // size + f.write_i16(self.instr_header_size() as _)?; // size f.write_u16(0xff00)?; // difficulty f.write_u16(0x00ff)?; // param_mask Ok(()) @@ -1162,7 +1183,7 @@ impl InstrFormat for TimelineFormat06 { } let opcode = f.read_u16()?; - let size = f.read_u16()? as usize; + let size = f.read_i16()? as usize; let args_size = size.checked_sub(self.instr_header_size()).ok_or_else(|| { emitter.as_sized().emit(error!("bad instruction size ({} < {})", size, self.instr_header_size())) diff --git a/src/formats/msg.rs b/src/formats/msg.rs index b85aa4c..e07fb2f 100644 --- a/src/formats/msg.rs +++ b/src/formats/msg.rs @@ -22,6 +22,7 @@ use indexmap::IndexMap; pub struct MsgFile { pub dense_table: Vec, pub scripts: IndexMap>, + pub extended_header: Option>, /// Filename of a read binary file, for display purposes only. binary_filename: Option, } @@ -75,15 +76,16 @@ impl Default for ScriptTableEntry { /// An alternative structure closer to the Meta representation. #[derive(Debug, Clone, PartialEq)] -struct SparseScriptTable { +struct MsgMeta { /// The script table is sparsely filled and could potentially have empty entries after the /// last full one, so we must store its true length. table_len: Sp, table: IndexMap, ScriptTableEntry>, + extended_header: Option>, default: ScriptTableEntry, } -impl SparseScriptTable { +impl MsgMeta { fn make_meta(&self) -> meta::Fields { let mut builder = Meta::make_object(); @@ -102,6 +104,10 @@ impl SparseScriptTable { inner.field_default("default", &self.default, &Default::default()); inner.build() }); + + if let Some(extended_header) = &self.extended_header { + builder.field("header", extended_header); + } builder.build_fields() } @@ -130,11 +136,16 @@ impl SparseScriptTable { let table_len = m.get_field("table_len")?.unwrap_or_else(|| { sp!(fields.span => sparse_table_implicit_len(&int_map)) }); - Ok(SparseScriptTable { table_len, table: int_map, default }) + Ok(MsgMeta { + table_len, + table: int_map, + extended_header: m.get_field("header")?, + default + }) }) } - fn densify(&self) -> Vec { + fn make_dense_script_table(&self) -> Vec { (0..self.table_len.value) .map(|index| { self.table.get(&index).unwrap_or_else(|| &self.default).clone() @@ -192,13 +203,13 @@ fn decompile( ) -> Result { let hooks = &*format.language_hooks(); - let sparse_script_table = sparsify_script_table(&msg.dense_table); + let msg_meta = generate_msg_meta(&msg.dense_table, msg.extended_header.clone()); let const_proof = crate::passes::evaluate_const_vars::run(ctx)?; let mut raiser = llir::Raiser::new(hooks, ctx.emitter, ctx, decompile_options, const_proof)?; let mut items = vec![sp!(ast::Item::Meta { keyword: sp!(token![meta]), - fields: sp!(sparse_script_table.make_meta()), + fields: sp!(msg_meta.make_meta()), })]; items.extend(msg.scripts.iter().map(|(ident, instrs)| { let code = raiser.raise_instrs_to_sub_ast(emitter, instrs, ctx)?; @@ -220,7 +231,7 @@ fn decompile( Ok(script) } -fn sparsify_script_table(dense_table: &[ScriptTableEntry]) -> SparseScriptTable { +fn generate_msg_meta(dense_table: &[ScriptTableEntry], extended_header: Option>) -> MsgMeta { let counts = get_counts(dense_table.iter()); // get first index of all nonzero entries @@ -253,7 +264,12 @@ fn sparsify_script_table(dense_table: &[ScriptTableEntry]) -> SparseScriptTable }; let table_len = sp!(dense_table.len() as u32); - SparseScriptTable { table_len, table, default } + MsgMeta { + table_len, + table, + extended_header, + default + } } fn get_counts(items: impl IntoIterator) -> BTreeMap { @@ -328,10 +344,10 @@ fn compile( None => return Err(emit(error!("missing 'meta' section"))), } }; - let sparse_table: SparseScriptTable = { - SparseScriptTable::from_fields(meta).map_err(|e| ctx.emitter.emit(e))? + let msg_meta: MsgMeta = { + MsgMeta::from_fields(meta).map_err(|e| ctx.emitter.emit(e))? }; - let dense_table = sparse_table.densify(); + let dense_table = msg_meta.make_dense_script_table(); let script_table_indices_by_name = get_script_table_indices_by_name(&dense_table); let mut errors = ErrorFlag::new(); @@ -361,13 +377,13 @@ fn compile( errors.into_result(())?; let unused_table_keys = { - sparse_table.table.keys().copied() - .filter(|&key| key >= sparse_table.table_len).collect::>() + msg_meta.table.keys().copied() + .filter(|&key| key >= msg_meta.table_len).collect::>() }; if !unused_table_keys.is_empty() { let mut diag = warning!( message("unused script table entry"), - secondary(sparse_table.table_len, "unused due to this length"), + secondary(msg_meta.table_len, "unused due to this length"), ); for key in unused_table_keys { diag.primary(key.span, format!("unused table entry")); @@ -376,8 +392,8 @@ fn compile( } let used_scripts = { - std::iter::once(sparse_table.default.clone()) - .chain(sparse_table.table.values().cloned()) + std::iter::once(msg_meta.default.clone()) + .chain(msg_meta.table.values().cloned()) .filter_map(|entry| match entry.script.value { ScriptTableOffset::Zero => None, ScriptTableOffset::Name(ref ident) => Some(ident.clone()), @@ -395,6 +411,7 @@ fn compile( Ok(MsgFile { dense_table, scripts, + extended_header: msg_meta.extended_header, /// Filename of a read binary file, for display purposes only. binary_filename: None, }) @@ -436,6 +453,13 @@ fn read_msg( let start_pos = reader.pos()?; let script_table_len = reader.read_u32()?; + + let extended_header = if format.extended_header() { + Some(reader.read_u32s(20)?) + } else { + None + }; + let script_table = { (0..script_table_len).map(|_| { Ok(RawScriptTableEntry { @@ -486,7 +510,7 @@ fn read_msg( }).collect(); let binary_filename = Some(reader.display_filename().to_owned()); - Ok(MsgFile { dense_table, scripts, binary_filename }) + Ok(MsgFile { dense_table, scripts, extended_header, binary_filename }) } #[derive(Debug)] @@ -533,6 +557,10 @@ fn write_msg( let start_pos = w.pos()?; w.write_u32(msg.dense_table.len() as _)?; + + if msg.extended_header.is_some() { + w.write_u32s(&msg.extended_header.as_ref().unwrap())?; + } let script_offsets_pos = w.pos()?; for _ in 0..msg.dense_table.len() { @@ -588,8 +616,24 @@ fn write_msg( fn game_format(game: Game, language: LanguageKey, emitter: &RootEmitter) -> Result { match (game, language) { | (Game::Th095, LanguageKey::Msg) - | (Game::Th125, LanguageKey::Msg) - => Err(emitter.emit(error!("{} does not have stage MSG files; maybe try 'trumsg --mission'?", game))), + | (Game::Th095, LanguageKey::End) + => Err(emitter.emit(error!("{} does not have MSG files; maybe try 'trumsg --mission'?", game))), + + | (Game::Alcostg, LanguageKey::Msg) + | (Game::Alcostg, LanguageKey::End) + => Err(emitter.emit(error!("{} does not have MSG files", game))), + + | (Game::Th125, LanguageKey::End) + | (Game::Th143, LanguageKey::End) + | (Game::Th165, LanguageKey::End) + | (Game::Th185, LanguageKey::End) + => Err(emitter.emit(error!("{} does not have ending MSG files", game))), + + | (Game::Th06, LanguageKey::End) + | (Game::Th07, LanguageKey::End) + | (Game::Th08, LanguageKey::End) + | (Game::Th09, LanguageKey::End) + => Err(emitter.emit(error!("--ending is not yet implemented for {}", game))), _ => Ok(FileFormat { game, language }) } @@ -606,18 +650,23 @@ impl FileFormat { fn table_has_flags(&self) -> bool { self.game >= Game::Th09 } + + fn extended_header(&self) -> bool { + self.language == LanguageKey::Msg && self.game == Game::Th19 + } fn language_hooks(&self) -> Box { match self.game { | Game::Th06 | Game::Th07 | Game::Th08 - | Game::Th09 | Game::Th10 | Game::Alcostg | Game::Th11 - | Game::Th12 | Game::Th128 | Game::Th13 - | Game::Th14 | Game::Th143 | Game::Th15 - | Game::Th16 | Game::Th165 | Game::Th17 - | Game::Th18 + | Game::Th09 | Game::Th10 | Game::Th11 + | Game::Th12 | Game::Th125 | Game::Th128 + | Game::Th13 | Game::Th14 | Game::Th143 + | Game::Th15 | Game::Th16 | Game::Th165 + | Game::Th17 | Game::Th18 | Game::Th185 + | Game::Th19 => Box::new(MsgHooks { language: self.language }), - | Game::Th095 | Game::Th125 + | Game::Th095 | Game::Alcostg => unreachable!(), } } diff --git a/src/formats/std.rs b/src/formats/std.rs index 8714d4c..55e9ff8 100644 --- a/src/formats/std.rs +++ b/src/formats/std.rs @@ -113,6 +113,7 @@ pub struct Object { /// This field determines when objects are drawn relative to 2D sprites, /// as if their polygon scripts had the given `layer(n)` command. pub layer: u16, + pub id: Option, pub pos: [f32; 3], pub size: [f32; 3], pub quads: Vec, @@ -122,6 +123,7 @@ impl FromMeta<'_> for Object { fn from_meta(meta: &Sp) -> Result> { meta.parse_object(|m| Ok(Object { layer: m.expect_renamed_field::("unknown", "layer")? as u16, + id: m.get_field("id")?, pos: m.expect_field("pos")?, size: m.expect_field("size")?, quads: m.expect_field("quads")?, @@ -131,12 +133,16 @@ impl FromMeta<'_> for Object { impl ToMeta for Object { fn to_meta(&self) -> Meta { - Meta::make_object() - .field("layer", &(self.layer as i32)) - .field("pos", &self.pos) - .field("size", &self.size) - .field("quads", &self.quads) - .build() + let mut builder = Meta::make_object(); + if let Some(id) = &self.id { + builder.field("id", id); + } + builder.field("layer", &(self.layer as i32)); + builder.field("pos", &self.pos); + builder.field("size", &self.size); + builder.field("quads", &self.quads); + + builder.build() } } @@ -494,11 +500,21 @@ fn read_object(f: &mut BinReader, emitter: &impl Emitter, expected_id: usize) -> while let Some(quad) = read_quad(f, emitter)? { quads.push(quad); } - Ok(Object { layer, pos, size, quads }) + Ok(Object { + layer, + id: if id as usize == expected_id { + None + } else { + Some(id as u32) + }, + pos, + size, + quads + }) } fn write_object(f: &mut BinWriter, emitter: &impl Emitter, format: &dyn FileFormat, id: usize, x: &Object) -> WriteResult { - f.write_u16(id as u16)?; + f.write_u16(x.id.unwrap_or_else(|| id as u32) as u16)?; f.write_u16(x.layer)?; f.write_f32s(&x.pos)?; f.write_f32s(&x.size)?; diff --git a/src/game.rs b/src/game.rs index 0301beb..b3d91be 100644 --- a/src/game.rs +++ b/src/game.rs @@ -6,8 +6,9 @@ use crate::diagnostic::Diagnostic; pub enum Game { Th06, Th07, Th08, Th09, Th095, Th10, Alcostg, Th11, Th12, Th125, Th128, Th13, Th14, Th143, Th15, Th16, Th165, Th17, Th18, + Th185, Th19 } -macro_rules! max_game_str { () => { "th18" }; } +macro_rules! max_game_str { () => { "th19" }; } impl std::str::FromStr for Game { type Err = Diagnostic; @@ -46,6 +47,8 @@ impl std::str::FromStr for Game { 165 => Ok(Game::Th165), 17 => Ok(Game::Th17), 18 => Ok(Game::Th18), + 185 => Ok(Game::Th185), + 19 => Ok(Game::Th19), _ => Err(unknown_game()), } } @@ -73,6 +76,8 @@ impl Game { Game::Th165 => "VD", Game::Th17 => "WBaWC", Game::Th18 => "UM", + Game::Th185 => "HBM", + Game::Th19 => "UDoALG", } } @@ -97,6 +102,9 @@ impl Game { Game::Th165 => "th165", Game::Th17 => "th17", Game::Th18 => "th18", + Game::Th185 => "th185", + Game::Th19 => "th19", + } } @@ -121,6 +129,8 @@ impl Game { Game::Th165 => 165, Game::Th17 => 17, Game::Th18 => 18, + Game::Th185 => 185, + Game::Th19 => 19, } } } diff --git a/src/image/color.rs b/src/image/color.rs index 075f93b..58043dd 100644 --- a/src/image/color.rs +++ b/src/image/color.rs @@ -4,70 +4,102 @@ use std::rc::Rc; use crate::io::{BinRead, BinWrite}; const FORMAT_ARGB_8888: u32 = 1; +const FORMAT_ARGB_1555: u32 = 2; const FORMAT_RGB_565: u32 = 3; +const FORMAT_RGB_888: u32 = 4; const FORMAT_ARGB_4444: u32 = 5; +const FORMAT_ARGB_8332: u32 = 6; const FORMAT_GRAY_8: u32 = 7; +const FORMAT_RGB_332: u32 = 8; #[repr(u32)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ColorFormat { Argb8888 = FORMAT_ARGB_8888, + Argb1555 = FORMAT_ARGB_1555, Rgb565 = FORMAT_RGB_565, + Rgb888 = FORMAT_RGB_888, Argb4444 = FORMAT_ARGB_4444, + Argb8332 = FORMAT_ARGB_8332, Gray8 = FORMAT_GRAY_8, + Rgb332 = FORMAT_RGB_332, } impl ColorFormat { pub fn from_format_num(num: u32) -> Option { match num { FORMAT_ARGB_8888 => Some(Self::Argb8888), + FORMAT_ARGB_1555 => Some(Self::Argb1555), FORMAT_RGB_565 => Some(Self::Rgb565), + FORMAT_RGB_888 => Some(Self::Rgb888), FORMAT_ARGB_4444 => Some(Self::Argb4444), + FORMAT_ARGB_8332 => Some(Self::Argb8332), FORMAT_GRAY_8 => Some(Self::Gray8), + FORMAT_RGB_332 => Some(Self::Rgb332), _ => None, } } pub fn get_all() -> Vec { vec![ ColorFormat::Argb8888, + ColorFormat::Argb1555, ColorFormat::Rgb565, + ColorFormat::Rgb888, ColorFormat::Argb4444, + ColorFormat::Argb8332, ColorFormat::Gray8, + ColorFormat::Rgb332, ]} pub fn const_name(&self) -> &'static str { match self { ColorFormat::Argb8888 => "FORMAT_ARGB_8888", + ColorFormat::Argb1555 => "FORMAT_ARGB_1555", ColorFormat::Rgb565 => "FORMAT_RGB_565", + ColorFormat::Rgb888 => "FORMAT_RGB_888", ColorFormat::Argb4444 => "FORMAT_ARGB_4444", + ColorFormat::Argb8332 => "FORMAT_ARGB_8332", ColorFormat::Gray8 => "FORMAT_GRAY_8", + ColorFormat::Rgb332 => "FORMAT_RGB_332", } } pub fn bytes_per_pixel(&self) -> usize { match self { ColorFormat::Argb8888 => Argb8888::BYTES_PER_PIXEL, + ColorFormat::Argb1555 => Argb1555::BYTES_PER_PIXEL, ColorFormat::Rgb565 => Rgb565::BYTES_PER_PIXEL, + ColorFormat::Rgb888 => Rgb888::BYTES_PER_PIXEL, ColorFormat::Argb4444 => Argb4444::BYTES_PER_PIXEL, + ColorFormat::Argb8332 => Argb8332::BYTES_PER_PIXEL, ColorFormat::Gray8 => Gray8::BYTES_PER_PIXEL, + ColorFormat::Rgb332 => Rgb332::BYTES_PER_PIXEL, } } pub fn transcode_to_argb_8888(&self, bytes: &Rc>) -> Rc> { match self { ColorFormat::Argb8888 => Rc::clone(bytes), + ColorFormat::Argb1555 => Rc::new(Argb8888::encode(&Argb1555::decode(bytes))), ColorFormat::Rgb565 => Rc::new(Argb8888::encode(&Rgb565::decode(bytes))), + ColorFormat::Rgb888 => Rc::new(Argb8888::encode(&Rgb888::decode(bytes))), ColorFormat::Argb4444 => Rc::new(Argb8888::encode(&Argb4444::decode(bytes))), + ColorFormat::Argb8332 => Rc::new(Argb8888::encode(&Argb8332::decode(bytes))), ColorFormat::Gray8 => Rc::new(Argb8888::encode(&Gray8::decode(bytes))), + ColorFormat::Rgb332 => Rc::new(Rgb332::encode(&Rgb332::decode(bytes))), } } pub fn transcode_from_argb_8888(&self, bytes: &Rc>) -> Rc> { match self { ColorFormat::Argb8888 => Rc::clone(bytes), + ColorFormat::Argb1555 => Rc::new(Argb1555::encode(&Argb8888::decode(bytes))), ColorFormat::Rgb565 => Rc::new(Rgb565::encode(&Argb8888::decode(bytes))), + ColorFormat::Rgb888 => Rc::new(Rgb888::encode(&Argb8888::decode(bytes))), ColorFormat::Argb4444 => Rc::new(Argb4444::encode(&Argb8888::decode(bytes))), + ColorFormat::Argb8332 => Rc::new(Argb8332::encode(&Argb8888::decode(bytes))), ColorFormat::Gray8 => Rc::new(Gray8::encode(&Argb8888::decode(bytes))), + ColorFormat::Rgb332 => Rc::new(Rgb332::encode(&Argb8888::decode(bytes))), } } @@ -76,9 +108,13 @@ impl ColorFormat { // Slightly transparent magenta where possible // (if fully opaque, thcrap would overlay loaded images instead of overwriting the alpha channel) ColorFormat::Argb8888 => &[0xFF, 0xFF, 0x00, 0xFE], - ColorFormat::Argb4444 => &[0x0F, 0xEF], + ColorFormat::Argb1555 => &[0b000_11111, 0b1_11111_00], ColorFormat::Rgb565 => &[0b000_11111, 0b11111_000], + ColorFormat::Rgb888 => &[0xFF, 0x00, 0xFF], + ColorFormat::Argb4444 => &[0x0F, 0xEF], + ColorFormat::Argb8332 => &[0b111_000_11, 0xFE], ColorFormat::Gray8 => &[0x80], + ColorFormat::Rgb332 => &[0b111_000_11], } } } @@ -96,18 +132,34 @@ impl Components { #[derive(Debug, Copy, Clone, PartialEq, Eq)] struct Argb8888(u32); +/// Format which is the little-endian encoding of a 16-bit integer `0bA_RRRRR_GGGGG_BBBBB`. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +struct Argb1555(u16); + /// Format which is the little-endian encoding of a 16-bit integer `0bRRRRR_GGGGGG_BBBBB`. #[derive(Debug, Copy, Clone, PartialEq, Eq)] struct Rgb565(u16); +/// Format which is the little-endian encoding of a 24-bit integer `0xRGB`. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +struct Rgb888(u32); + /// Format which is the little-endian encoding of a 16-bit integer `0xARGB`. #[derive(Debug, Copy, Clone, PartialEq, Eq)] struct Argb4444(u16); +/// Format which is the little-endian encoding of a 16-bit integer `0bAAAAAAAA_RRR_GGG_BB`. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +struct Argb8332(u16); + /// Format which is a single-byte luminosity channel. #[derive(Debug, Copy, Clone, PartialEq, Eq)] struct Gray8(u8); +/// Format which is single byte encoding of a 8-bit integer `0bRRR_GGG_BB`. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +struct Rgb332(u8); + trait ColorBytes: Sized where @@ -157,6 +209,39 @@ impl ColorBytes for Argb8888 { fn write_color_bytes(&self, mut w: W) -> Result<(), W::Err> { w.write_u32(self.0) } } +impl From for Components { + fn from(color: Argb1555) -> Components { + let blue = (color.0 & 0x1F) as u8; + let green = ((color.0 >> 5) & 0x1F) as u8; + let red = ((color.0 >> 10) & 0x1F) as u8; + let alpha = ((color.0 >> 15) & 1) as u8; + Components { + blue: change_bit_depth::<5, 8>(blue), + green: change_bit_depth::<5, 8>(green), + red: change_bit_depth::<5, 8>(red), + alpha: change_bit_depth::<1, 8>(alpha), + } + } +} + +impl From for Argb1555 { + fn from(components: Components) -> Self { + let blue = (components.blue >> 3) as u16; + let green = (components.green >> 3) as u16; + let red = (components.red >> 3) as u16; + let alpha = (components.alpha >> 7) as u16; + + Argb1555((alpha << 15) + (red << 10) + (green << 5) + blue) + } +} + +impl ColorBytes for Argb1555 { + const BYTES_PER_PIXEL: usize = 2; + + fn read_color_bytes(mut r: R) -> Result { r.read_u16().map(Argb1555) } + fn write_color_bytes(&self, mut w: W) -> Result<(), W::Err> { w.write_u16(self.0) } +} + impl From for Components { fn from(color: Rgb565) -> Components { let blue = (color.0 & 0x1F) as u8; @@ -188,6 +273,50 @@ impl ColorBytes for Rgb565 { fn write_color_bytes(&self, mut w: W) -> Result<(), W::Err> { w.write_u16(self.0) } } +impl From for Components { + fn from(color: Rgb888) -> Components { + let blue = (color.0 & 0xFF) as u8; + let green = ((color.0 >> 8) & 0xFF) as u8; + let red = ((color.0 >> 16) & 0xFF) as u8; + Components { + blue, + green, + red, + alpha: 0xFF + } + } +} + +impl From for Rgb888 { + fn from(components: Components) -> Self { + let blue = components.blue as u32; + let green = components.green as u32; + let red = components.red as u32; + + Rgb888((red << 16) + (green << 8) + blue) + } +} + +impl ColorBytes for Rgb888 { + const BYTES_PER_PIXEL: usize = 3; + + fn read_color_bytes(mut r: R) -> Result { + let mut color = r.read_u8()? as u32; + color |= (r.read_u8()? as u32) << 8; + color |= (r.read_u8()? as u32) << 16; + Ok(Rgb888(color)) + } + fn write_color_bytes(&self, mut w: W) -> Result<(), W::Err> { + let blue = (self.0 & 0xFF) as u8; + let green = ((self.0 >> 8) & 0xFF) as u8; + let red = ((self.0 >> 16) & 0xFF) as u8; + w.write_u8(blue)?; + w.write_u8(green)?; + w.write_u8(red)?; + Ok(()) + } +} + impl From for Components { fn from(color: Argb4444) -> Components { let blue = (color.0 & 0xF) as u8; @@ -221,6 +350,39 @@ impl ColorBytes for Argb4444 { fn write_color_bytes(&self, mut w: W) -> Result<(), W::Err> { w.write_u16(self.0) } } +impl From for Components { + fn from(color: Argb8332) -> Components { + let blue = (color.0 & 0x3) as u8; + let green = ((color.0 >> 2) & 0x7) as u8; + let red = ((color.0 >> 5) & 0x7) as u8; + let alpha = ((color.0 >> 8) & 0xFF) as u8; + Components { + blue: change_bit_depth::<2, 8>(blue), + green: change_bit_depth::<3, 8>(green), + red: change_bit_depth::<3, 8>(red), + alpha + } + } +} + +impl From for Argb8332 { + fn from(components: Components) -> Self { + let blue = (components.blue >> 6) as u16; + let green = (components.green >> 5) as u16; + let red = (components.red >> 5) as u16; + let alpha = components.alpha as u16; + + Argb8332((alpha << 8) + (red << 5) + (green << 2) + blue) + } +} + +impl ColorBytes for Argb8332 { + const BYTES_PER_PIXEL: usize = 2; + + fn read_color_bytes(mut r: R) -> Result { r.read_u16().map(Argb8332) } + fn write_color_bytes(&self, mut w: W) -> Result<(), W::Err> { w.write_u16(self.0) } +} + impl From for Components { fn from(Gray8(value): Gray8) -> Components { Components { blue: value, green: value, red: value, alpha: 0xFF } @@ -249,6 +411,37 @@ impl ColorBytes for Gray8 { fn write_color_bytes(&self, mut w: W) -> Result<(), W::Err> { w.write_u8(self.0) } } +impl From for Components { + fn from(color: Rgb332) -> Components { + let blue = (color.0 & 0x3) as u8; + let green = ((color.0 >> 2) & 0x7) as u8; + let red = ((color.0 >> 5) & 0x7) as u8; + Components { + blue: change_bit_depth::<2, 8>(blue), + green: change_bit_depth::<3, 8>(green), + red: change_bit_depth::<3, 8>(red), + alpha: 0xFF + } + } +} + +impl From for Rgb332 { + fn from(components: Components) -> Self { + let blue = (components.blue >> 6) as u8; + let green = (components.green >> 5) as u8; + let red = (components.red >> 5) as u8; + + Rgb332((red << 5) + (green << 2) + blue) + } +} + +impl ColorBytes for Rgb332 { + const BYTES_PER_PIXEL: usize = 1; + + fn read_color_bytes(mut r: R) -> Result { r.read_u8().map(Rgb332) } + fn write_color_bytes(&self, mut w: W) -> Result<(), W::Err> { w.write_u8(self.0) } +} + // take a color value that is N bits large and rescale it to M bits. #[inline(always)] fn change_bit_depth(x: u8) -> u8 { diff --git a/src/io.rs b/src/io.rs index 5f3b111..a8d238d 100644 --- a/src/io.rs +++ b/src/io.rs @@ -317,7 +317,10 @@ pub trait BinRead { fn read_u16(&mut self) -> Result { ReadBytesExt::read_u16::(self._bin_read_reader()).map_err(|e| self._bin_read_io_error(e)) } fn read_i32(&mut self) -> Result { ReadBytesExt::read_i32::(self._bin_read_reader()).map_err(|e| self._bin_read_io_error(e)) } fn read_u32(&mut self) -> Result { ReadBytesExt::read_u32::(self._bin_read_reader()).map_err(|e| self._bin_read_io_error(e)) } + //fn read_i64(&mut self) -> Result { ReadBytesExt::read_i64::(self._bin_read_reader()).map_err(|e| self._bin_read_io_error(e)) } + //fn read_u64(&mut self) -> Result { ReadBytesExt::read_u64::(self._bin_read_reader()).map_err(|e| self._bin_read_io_error(e)) } fn read_f32(&mut self) -> Result { ReadBytesExt::read_f32::(self._bin_read_reader()).map_err(|e| self._bin_read_io_error(e)) } + //fn read_f64(&mut self) -> Result { ReadBytesExt::read_f64::(self._bin_read_reader()).map_err(|e| self._bin_read_io_error(e)) } fn read_u32s(&mut self, count: usize) -> Result, Self::Err> { (0..count).map(|_| self.read_u32()).collect() } @@ -340,6 +343,9 @@ pub trait BinRead { fn read_f32s_3(&mut self) -> Result<[f32; 3], Self::Err> { Ok([self.read_f32()?, self.read_f32()?, self.read_f32()?]) } + fn read_f32s_5(&mut self) -> Result<[f32; 5], Self::Err> { + Ok([self.read_f32()?, self.read_f32()?, self.read_f32()?, self.read_f32()?, self.read_f32()?]) + } fn read_byte_vec(&mut self, len: usize) -> Result, Self::Err> { let mut buf = vec![0; len]; diff --git a/src/llir/abi.rs b/src/llir/abi.rs index db721c5..0ef2562 100644 --- a/src/llir/abi.rs +++ b/src/llir/abi.rs @@ -18,7 +18,7 @@ use ArgEncoding as Enc; /// and how to present them in a decompiled file (e.g. hexadecimal for colors). /// /// Like in thtk, signatures are derived from strings. Parse a signature using [`std::str::FromStr`]. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq)] pub struct InstrAbi { encodings: Vec, } @@ -31,29 +31,35 @@ pub struct InstrAbi { /// /// By this notion, [`ArgEncoding`] tends to be more relevant for immediate/literal arguments, while /// [`ScalarType`] tends to be more relevant for variables. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq)] pub enum ArgEncoding { - /// `S` or `s` in mapfile. 4-byte or 2-byte integer immediate or register. Displayed as signed. + /// `S`, `s`, or `c` in mapfile. Integer immediate or register. Displayed as signed. + /// `U`, `u`, or `b` in mapfile. Integer immediate or register. Displayed as unsigned. + /// `C` in mapfile. 4-byte integer immediate or register, printed as hex when immediate. /// /// May be decompiled as an enum or const based on its value. /// /// The first argument may have `arg0` if it is two bytes large. This indicates that the argument is /// stored in the arg0 header field of the instruction in EoSD and PCB ECL. (which is mapped to the /// `@arg0` pseudo-argument in raw instruction syntax) - Integer { size: u8, ty_color: Option, arg0: bool }, + Integer { + size: u8, + ty_color: Option, + arg0: bool, + immediate: bool, + extend: bool, + format: ast::IntFormat + }, /// `o` in mapfile. Max of one per instruction. Is decoded to a label. JumpOffset, /// `t` in mapfile. Max of one per instruction, and requires an accompanying `o` arg. JumpTime, - /// `_` in mapfile. Unused 4-byte space after script arguments, optionally displayed as integer in text. - /// - /// Only exists in pre-StB STD where instructions have fixed sizes. - Padding, - /// `C` in mapfile. 4-byte integer immediate or register, printed as hex when immediate. - Color, + /// `_` in mapfile. Unused 4-byte space. + /// `-` in mapfile. Unused 1-byte space. + Padding { size: u8 }, /// `f` in mapfile. Single-precision float. - Float, - /// `z(bs=)`, `m(bs=;mask=,,)`, or `m(len=;mask=,,)` in mapfile. + Float { immediate: bool }, + /// `z(bs=)`, `m(bs=;mask=,,)`, or `m(len=;mask=,,)` in mapfile. /// /// See [`StringArgSize`] about the size args. /// @@ -66,9 +72,20 @@ pub enum ArgEncoding { mask: AcceleratingByteMask, furibug: bool, }, + /* + /// `g` in mapfile. Single typed argument + /// 'G' in mapfile. Double typed argument + TypedArg { + is_double: bool, + }, + /// `v` in mapfile. + Variadic { + pattern: Option> + }, + */ } -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] pub enum StringArgSize { /// A string arg that uses `len=`. /// @@ -80,24 +97,36 @@ pub enum StringArgSize { /// A string arg that uses `bs=`. /// /// A null-terminated string argument which **can only be the final argument**, and - /// consists of all remaining bytes. When written, it is padded to a multiple of `bs` bytes. - Block { block_size: usize }, + /// consists of all remaining bytes. When written, a null terminator is appended and it is padded + /// to a multiple of `bs` bytes. + ToBlobEnd { block_size: usize }, + /// A string arg that uses the `p` type to be length-prefixed. + /// + /// This is a Pascal-type string which stores the total length (including null + padding) followed + /// by the data. When written, a null terminator is appended and it is padded to a multiple of + /// `bs` bytes. + Pascal { block_size: usize }, } impl ArgEncoding { - pub fn dword() -> Self { ArgEncoding::Integer { size: 4, ty_color: None, arg0: false } } + pub fn dword() -> Self { ArgEncoding::Integer { size: 4, ty_color: None, arg0: false, immediate: false, extend: false, format: ast::IntFormat { unsigned: false, radix: ast::IntRadix::Dec } } } pub fn static_descr(&self) -> &'static str { match self { + Self::Integer { size: 1, .. } => "byte-sized integer", Self::Integer { size: 2, .. } => "word-sized integer", Self::Integer { size: 4, .. } => "dword integer", + //Self::Integer { size: 8, .. } => "qword integer", Self::Integer { size: _, .. } => "integer", Self::JumpOffset => "jump offset", Self::JumpTime => "jump time", - Self::Padding => "padding", - Self::Color => "hex integer", - Self::Float => "float", + Self::Padding { size: 4 } => "dword padding", + Self::Padding { size: 1 } => "byte padding", + Self::Padding { size: _ } => "padding", + Self::Float { .. } => "float", Self::String { .. } => "string", + //Self::TypedArg { .. } => "type cast argument", + //Self::Variadic { .. } => "variadic", } } @@ -106,16 +135,20 @@ impl ArgEncoding { impl fmt::Display for Impl<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match &self.0 { - Enc::Integer { arg0: true, ty_color, size } => write!( - f, - "{} (in timeline arg0)", - Enc::Integer { arg0: false, ty_color: ty_color.clone(), size: *size }.descr(), - ), + match self.0 { + Enc::Integer { arg0: true, .. } => { + let mut temp = self.0.clone(); + write!(f, "{} (in timeline arg0)", match &mut temp { + Enc::Integer { arg0, .. } => { *arg0 = false; temp }, + _ => unreachable!(), + }.descr()) + }, Enc::Integer { ty_color: Some(en), size: 4, .. } => write!(f, "{}", en.descr()), Enc::Integer { ty_color: Some(en), size, .. } => write!(f, "{size}-byte {}", en.descr()), + Enc::Integer { ty_color: None, size: 1, .. } => write!(f, "byte-sized integer"), Enc::Integer { ty_color: None, size: 2, .. } => write!(f, "word-sized integer"), Enc::Integer { ty_color: None, size: 4, .. } => write!(f, "dword integer"), + //Enc::Integer { ty_color: None, size: 8, .. } => write!(f, "qword integer"), Enc::Integer { ty_color: None, size, .. } => write!(f, "{size}-byte integer"), enc => write!(f, "{}", enc.static_descr()), } @@ -124,6 +157,28 @@ impl ArgEncoding { Impl(self) } + + pub fn contributes_to_param_mask(&self) -> bool { + !matches!(self, Self::Padding { .. }) + } + + pub fn is_always_immediate(&self) -> bool { + match self { + | Self::String { .. } + | Self::JumpOffset + | Self::JumpTime + | Self::Padding { .. } + | Self::Integer { immediate: true, .. } + | Self::Float { immediate: true, .. } + => true, + + | Self::Integer { immediate: false, .. } + | Self::Float { immediate: false, .. } + //| Self::TypedArg { .. } + //| Self::Variadic { .. } + => false, + } + } } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -193,12 +248,11 @@ impl ArgEncoding { match self { | ArgEncoding::JumpOffset | ArgEncoding::JumpTime - | ArgEncoding::Padding - | ArgEncoding::Color + | ArgEncoding::Padding { .. } | ArgEncoding::Integer { .. } => ScalarType::Int, - | ArgEncoding::Float + | ArgEncoding::Float { .. } => ScalarType::Float, | ArgEncoding::String { .. } @@ -212,6 +266,8 @@ impl ArgEncoding { fn arg_encoding_from_attrs(param: &abi_ast::Param, emitter: &dyn Emitter) -> Result { if let Some(encoding) = int_from_attrs(param, emitter)? { Ok(encoding) + } else if let Some(encoding) = float_from_attrs(param, emitter)? { + Ok(encoding) } else if let Some(encoding) = string_from_attrs(param, emitter)? { Ok(encoding) } else if let Some(encoding) = other_from_attrs(param, emitter)? { @@ -225,43 +281,80 @@ fn arg_encoding_from_attrs(param: &abi_ast::Param, emitter: &dyn Emitter) -> Res } fn int_from_attrs(param: &abi_ast::Param, emitter: &dyn Emitter) -> Result, ErrorReported> { - let (size, default_ty_color) = match param.format_char.value { + let (size, unsigned, mut hex_radix, default_ty_color) = match param.format_char.value { // FIXME: Uu should be unsigned but I'm not sure yet if I want `i(signed)`, `i(unsigned)`, or `i(sign=1)` - 'S' => (4u8, None), - 's' => (2, None), - 'U' => (4, None), - 'u' => (2, None), - 'n' => (4, Some(TypeColor::Enum(auto_enum_names::anm_sprite()))), - 'N' => (4, Some(TypeColor::Enum(auto_enum_names::anm_script()))), - 'E' => (4, Some(TypeColor::Enum(auto_enum_names::ecl_sub()))), + 'S' => (4u8, false, false, None), + 's' => (2u8, false, false, None), + 'c' => (1u8, false, false, None), + 'U' => (4u8, true, false, None), + 'u' => (2u8, true, false, None), + 'b' => (1u8, true, false, None), + 'n' => (4u8, false, false, Some(TypeColor::Enum(auto_enum_names::anm_sprite()))), + 'N' => (4u8, false, false, Some(TypeColor::Enum(auto_enum_names::anm_script()))), + 'E' => (4u8, false, false, Some(TypeColor::Enum(auto_enum_names::ecl_sub()))), + 'C' => (4u8, true, true, None), _ => return Ok(None), // not an integer }; param.clone().deserialize_attributes(emitter, |de| { let user_ty_color = de.accept_value("enum")?.map(|ident| TypeColor::Enum(ident.value)); let arg0 = de.accept_flag("arg0")?; + let imm = de.accept_flag("imm")?; + let is_hex = de.accept_flag("hex")?; + let extend = de.accept_flag("extend")?; if let Some(arg0_flag) = arg0 { - if size != 2 { + if size.wrapping_sub(1) >= 2 { return Err(emitter.as_sized().emit(error!( - message("timeline arg0 must be word-sized ('s' or 'u')"), + message("timeline arg0 must be word-sized or less ('s', 'u', 'c', or 'b')"), primary(arg0_flag, ""), ))); } } + + if is_hex.is_some() { + hex_radix = true; + } + + let radix = match hex_radix { + false => ast::IntRadix::Dec, + true => ast::IntRadix::Hex, + }; Ok(Some(ArgEncoding::Integer { size, ty_color: user_ty_color.or(default_ty_color), arg0: arg0.is_some(), + immediate: imm.is_some(), + extend: extend.is_some(), + format: ast::IntFormat { unsigned, radix }, })) }) } +fn float_from_attrs(param: &abi_ast::Param, emitter: &dyn Emitter) -> Result, ErrorReported> { + match param.format_char.value { + 'f' => param.clone().deserialize_attributes(emitter, |de| { + //let user_ty_color = de.accept_value("enum")?.map(|ident| TypeColor::Enum(ident.value)); + let imm = de.accept_flag("imm")?; + + Ok(Some(ArgEncoding::Float { + //ty_color: user_ty_color.or(default_ty_color), + immediate: imm.is_some(), + })) + }), + _ => Ok(None) + } +} + fn string_from_attrs(param: &abi_ast::Param, emitter: &dyn Emitter) -> Result, ErrorReported> { - let default_mask = match param.format_char.value { - 'z' => Some([0,0,0]), - 'm' => None, + struct LenPrefixed(bool); // self-documenting bool + + let (default_mask, is_len_prefixed) = match param.format_char.value { + 'z' => (Some([0,0,0]), LenPrefixed(false)), + 'm' => (None, LenPrefixed(false)), + 'p' => (Some([0,0,0]), LenPrefixed(true)), + 'P' => (Some([0,0,0]), LenPrefixed(true)), _ => return Ok(None), // not a string }; @@ -271,19 +364,26 @@ fn string_from_attrs(param: &abi_ast::Param, emitter: &dyn Emitter) -> Result("len")?; let user_bs = de.accept_value::("bs")?; - match (user_len, user_bs) { - (None, Some(bs)) => StringArgSize::Block { + match (user_len, user_bs, is_len_prefixed) { + (None, Some(bs), LenPrefixed(false)) => StringArgSize::ToBlobEnd { block_size: bs.value as _, }, - (Some(len), None) => StringArgSize::Fixed { + (None, Some(bs), LenPrefixed(true)) => StringArgSize::Pascal { + block_size: bs.value as _, + }, + (Some(len), None, LenPrefixed(false)) => StringArgSize::Fixed { len: len.value as _, nulless: de.accept_flag("nulless")?.is_some(), }, - (None, None) => return Err(emitter.as_sized().emit(error!( + (Some(len), None, LenPrefixed(true)) => return Err(emitter.as_sized().emit(error!( + message("'len' attribute is not supported by '{}'", param.format_char), + primary(len, ""), + ))), + (None, None, _) => return Err(emitter.as_sized().emit(error!( message("missing length attribute ('len' or 'bs') for '{}'", param.format_char), primary(param.format_char, ""), ))), - (Some(len), Some(bs)) => return Err(emitter.as_sized().emit(error!( + (Some(len), Some(bs), _) => return Err(emitter.as_sized().emit(error!( message("mutually exclusive attributes 'len' and 'bs' in '{}' format", param.format_char), primary(len, ""), primary(bs, ""), @@ -305,11 +405,13 @@ fn string_from_attrs(param: &abi_ast::Param, emitter: &dyn Emitter) -> Result Result, ErrorReported> { match param.format_char.value { - 'C' => Ok(Some(ArgEncoding::Color)), 'o' => Ok(Some(ArgEncoding::JumpOffset)), 't' => Ok(Some(ArgEncoding::JumpTime)), - 'f' => Ok(Some(ArgEncoding::Float)), - '_' => Ok(Some(ArgEncoding::Padding)), + '_' => Ok(Some(ArgEncoding::Padding { size: 4 })), + '-' => Ok(Some(ArgEncoding::Padding { size: 1 })), + //'g' => Ok(Some(ArgEncoding::TypedArg { is_double: false })), + //'G' => Ok(Some(ArgEncoding::TypedArg { is_double: true })), + //'v' => _ => Ok(None), } } @@ -332,19 +434,12 @@ fn validate(abi_span: Span, encodings: &[ArgEncoding]) -> Result<(), Diagnostic> } if encodings.iter().skip(1).any(|c| matches!(c, Enc::Integer { arg0: true, .. })) { - return err(format!("'T()' arguments may only appear at the beginning of a signature")); + return err(format!("'(arg0)' arguments may only appear at the beginning of a signature")); } - if encodings.iter().rev().skip(1).any(|c| matches!(c, Enc::String { size: StringArgSize::Block { .. }, .. })) { + if encodings.iter().rev().skip(1).any(|c| matches!(c, Enc::String { size: StringArgSize::ToBlobEnd { .. }, .. })) { return err(format!("'z' or 'm' arguments with 'bs=' can only appear at the very end")); } - - let trailing_pad_count = encodings.iter().rev().take_while(|c| matches!(c, Enc::Padding)).count(); - let total_pad_count = encodings.iter().filter(|c| matches!(c, Enc::Padding)).count(); - if total_pad_count != trailing_pad_count { - // this restriction is required because Padding produces signatures with optional args. - return err(format!("non-'_' arguments cannot come after '_' arguments")); - } Ok(()) } @@ -360,11 +455,8 @@ fn abi_to_signature(abi: &InstrAbi, abi_span: Span, ctx: &mut CompilerContext<'_ defs::Signature { return_ty: sp!(value::ExprType::Void), - params: abi.encodings.iter().enumerate().flat_map(|(index, enc)| { + params: abi.encodings.iter().filter(|enc| !matches!(*enc, ArgEncoding::Padding { .. })).enumerate().flat_map(|(index, enc)| { let Info { ty, default, reg_ok, ty_color } = match *enc { - | ArgEncoding::Color - => Info { ty: ScalarType::Int, default: None, reg_ok: true, ty_color: None }, - | ArgEncoding::Integer { arg0: false, ref ty_color, .. } => Info { ty: ScalarType::Int, default: None, reg_ok: true, ty_color: ty_color.clone() }, @@ -375,10 +467,10 @@ fn abi_to_signature(abi: &InstrAbi, abi_span: Span, ctx: &mut CompilerContext<'_ | ArgEncoding::JumpTime => Info { ty: ScalarType::Int, default: None, reg_ok: false, ty_color: None }, - | ArgEncoding::Padding - => Info { ty: ScalarType::Int, default: Some(sp!(0.into())), reg_ok: true, ty_color: None }, + | ArgEncoding::Padding { .. } + => Info { ty: ScalarType::Int, default: Some(sp!(0.into())), reg_ok: false, ty_color: None }, - | ArgEncoding::Float + | ArgEncoding::Float { .. } => Info { ty: ScalarType::Float, default: None, reg_ok: true, ty_color: None }, | ArgEncoding::String { .. } @@ -414,7 +506,7 @@ mod tests { #[test] fn test_parse() { - assert_eq!(parse("SSf").unwrap(), InstrAbi::from_encodings(Span::NULL, vec![Enc::dword(), Enc::dword(), Enc::Float]).unwrap()); + assert_eq!(parse("SSf").unwrap(), InstrAbi::from_encodings(Span::NULL, vec![Enc::dword(), Enc::dword(), Enc::Float { immediate: false }]).unwrap()); } #[test] diff --git a/src/llir/intrinsic.rs b/src/llir/intrinsic.rs index 021eb8c..5da3132 100644 --- a/src/llir/intrinsic.rs +++ b/src/llir/intrinsic.rs @@ -188,8 +188,6 @@ impl IntrinsicInstrKind { #[derive(Debug)] pub struct IntrinsicInstrAbiParts { pub num_instr_args: usize, - /// Number of padding args at the end - pub padding: abi_parts::PaddingInfo, /// Indices of args that should use the same logic as arguments in `ins_` instruction-call syntax. pub plain_args: Vec, /// Indices of args that are known registers. These show up in intrinsics. @@ -254,18 +252,8 @@ impl IntrinsicAbiHelper<'_> { } } - fn find_and_remove_padding(&self, arg_encodings: &mut Vec<(usize, &ArgEncoding)>) -> Result { - let mut count = 0; - let mut first_index = arg_encodings.len(); - while let Some(&(index, ArgEncoding::Padding)) = arg_encodings.last() { - // assumption that this func always runs first (nothing is deleted before us) - assert_eq!(index, arg_encodings.len() - 1); - - arg_encodings.pop(); - count += 1; - first_index = index; - } - Ok(abi_parts::PaddingInfo { count, index: first_index }) + fn find_and_remove_padding(&self, arg_encodings: &mut Vec<(usize, &ArgEncoding)>) { + arg_encodings.retain(|(_, enc)| !matches!(*enc, ArgEncoding::Padding { .. })); } fn find_and_remove_jump(&self, arg_encodings: &mut Vec<(usize, &ArgEncoding)>) -> Result<(usize, abi_parts::JumpArgOrder), Diagnostic> { @@ -295,7 +283,7 @@ impl IntrinsicAbiHelper<'_> { fn find_and_remove_sub_id(&self, arg_encodings: &mut Vec<(usize, &ArgEncoding)>) -> Result { let data = Self::remove_first_where(arg_encodings, |&(_, enc)| { match enc { - ArgEncoding::Integer { size: _, ty_color: Some(TypeColor::Enum(enum_name)), arg0: false } => { + ArgEncoding::Integer { ty_color: Some(TypeColor::Enum(enum_name)), arg0: false, .. } => { enum_name == &auto_enum_names::ecl_sub() }, _ => false, @@ -314,7 +302,7 @@ impl IntrinsicAbiHelper<'_> { }; match (ty_in_ast, encoding) { | (ScalarType::Int, ArgEncoding::Integer { .. }) - | (ScalarType::Float, ArgEncoding::Float) + | (ScalarType::Float, ArgEncoding::Float { .. }) => Ok((index, abi_parts::OutputArgMode::Natural)), | (ScalarType::Float, ArgEncoding::Integer { .. }) @@ -332,7 +320,7 @@ impl IntrinsicAbiHelper<'_> { }; match (ty_in_ast, encoding) { | (ScalarType::Int, ArgEncoding::Integer { .. }) - | (ScalarType::Float, ArgEncoding::Float) + | (ScalarType::Float, ArgEncoding::Float { .. }) => Ok(index), | (_, _) @@ -347,13 +335,12 @@ impl IntrinsicInstrAbiParts { use IntrinsicInstrKind as I; let mut encodings = abi.arg_encodings().enumerate().collect::>(); - let num_instr_args = encodings.len(); let helper = IntrinsicAbiHelper { intrinsic, abi_loc }; + helper.find_and_remove_padding(&mut encodings); - let padding = helper.find_and_remove_padding(&mut encodings)?; let mut out = IntrinsicInstrAbiParts { - num_instr_args, padding, + num_instr_args: encodings.len(), plain_args: vec![], outputs: vec![], jump: None, sub_id: None, }; diff --git a/src/llir/lower.rs b/src/llir/lower.rs index 82fade4..d131e91 100644 --- a/src/llir/lower.rs +++ b/src/llir/lower.rs @@ -551,28 +551,105 @@ fn encode_args( // The remaining args go into the argument byte blob. let mut args_blob = std::io::Cursor::new(vec![]); + let mut param_mask: raw::ParamMask = 0; + let mut current_param_mask_bit: raw::ParamMask = 1; + + let mut small_padding_value = 0i8; + // Important: we put the shortest iterator (args_iter) first in the zip list // to ensure that this loop reads an equal number of items from all iters. assert!(args_iter.len() <= arg_encodings_iter.len()); - for (arg, enc) in zip!(args_iter, arg_encodings_iter.by_ref()) { + for enc in arg_encodings_iter.by_ref() { + if let ArgEncoding::Padding { size } = enc { + match size { + 1 => { + if (args_blob.position() & 3) == 0 { small_padding_value = 0i8 } + args_blob.write_i8(small_padding_value).expect("Cursor failed?!") + }, + 4 => args_blob.write_u32(0).expect("Cursor failed?!"), + _ => unreachable!(), + } + continue; + } + small_padding_value = 0i8; + + let arg = args_iter.next().expect("function arity already checked"); + + let arg_bit = match &arg.value { + LowerArg::Raw(raw) if raw.is_reg => current_param_mask_bit, + LowerArg::Local { .. } => current_param_mask_bit, + LowerArg::DiffSwitch { .. } => panic!("should be handled earlier"), + _ => 0, + }; + // Verify this arg even applies to the param mask... + if enc.contributes_to_param_mask() { + if enc.is_always_immediate() && arg_bit != 0 { + // Warn if a register is used for an immediate arg + emitter.emit(warning!( + message("non-constant expression in immediate argument"), + primary(arg, "non-const expression"), + // FIXME: Find a way to display the resulting value! + // Could eventually be relevant for oversided values too + // note(format!()), + )).ignore(); + } else { + param_mask |= arg_bit; + } + current_param_mask_bit <<= 1; + } else if arg_bit != 0 { + // Conceptually invalid since adding this to the + // param mask would misalign all other mask bits + emitter.emit(warning!( + message("non-constant expression in non-parameter"), + primary(arg, "non-const expression"), + )).ignore(); + // Should be impossible to trigger once padding is + // converted to not be optional arguments? Panic? + } + match *enc { | ArgEncoding::Integer { arg0: true, .. } + | ArgEncoding::Padding { .. } => unreachable!(), - | ArgEncoding::Color | ArgEncoding::JumpOffset | ArgEncoding::JumpTime - | ArgEncoding::Padding - | ArgEncoding::Integer { size: 4, .. } => args_blob.write_i32(arg.expect_raw().expect_int()).expect("Cursor failed?!"), - | ArgEncoding::Integer { size: 2, .. } - => args_blob.write_i16(arg.expect_raw().expect_int() as _).expect("Cursor failed?!"), + | ArgEncoding::Integer { size: 4, extend, format: ast::IntFormat { unsigned: false, radix: _ }, .. } + => { + let value = arg.expect_raw().expect_int(); + if extend && value < 0 { small_padding_value = -1 } + args_blob.write_i32(value).expect("Cursor failed?!") + }, + + | ArgEncoding::Integer { size: 2, extend, format: ast::IntFormat { unsigned: false, radix: _ }, .. } + => { + let value = arg.expect_raw().expect_int(); + if extend && value < 0 { small_padding_value = -1 } + args_blob.write_i16(value as _).expect("Cursor failed?!") + }, + + | ArgEncoding::Integer { size: 1, extend, format: ast::IntFormat { unsigned: false, radix: _ }, .. } + => { + let value = arg.expect_raw().expect_int(); + if extend && value < 0 { small_padding_value = -1 } + args_blob.write_i8(value as _).expect("Cursor failed?!") + }, + + | ArgEncoding::Integer { size: 4, format: ast::IntFormat { unsigned: true, radix: _ }, .. } + => args_blob.write_u32(arg.expect_raw().expect_int() as _).expect("Cursor failed?!"), + + | ArgEncoding::Integer { size: 2, format: ast::IntFormat { unsigned: true, radix: _ }, .. } + => args_blob.write_u16(arg.expect_raw().expect_int() as _).expect("Cursor failed?!"), + + | ArgEncoding::Integer { size: 1, format: ast::IntFormat { unsigned: true, radix: _ }, .. } + => args_blob.write_u8(arg.expect_raw().expect_int() as _).expect("Cursor failed?!"), | ArgEncoding::Integer { size, .. } => panic!("unexpected integer size: {}", size), - | ArgEncoding::Float + | ArgEncoding::Float { .. } => args_blob.write_f32(arg.expect_raw().expect_float()).expect("Cursor failed?!"), | ArgEncoding::String { size: size_spec, mask, furibug } @@ -584,7 +661,8 @@ fn encode_args( // have to append null eagerly to correctly reproduce TH17 Extra files match size_spec { - | StringArgSize::Block { .. } + | StringArgSize::ToBlobEnd { .. } + | StringArgSize::Pascal { .. } | StringArgSize::Fixed { nulless: false, .. } => encoded.0.push(b'\0'), @@ -600,11 +678,12 @@ fn encode_args( } match size_spec { - StringArgSize::Block { block_size } => { + StringArgSize::ToBlobEnd { block_size } | StringArgSize::Pascal { block_size } => { if encoded.len() % block_size != 0 { encoded.null_pad(block_size); } }, + StringArgSize::Fixed { len, nulless: _ } => { if encoded.len() > len { return Err(emitter.emit(error!( @@ -622,14 +701,19 @@ fn encode_args( state.furibug_bytes = Some(encoded.clone()); } + if matches!(size_spec, StringArgSize::Pascal { .. }) { + args_blob.write_u32(encoded.len() as _).expect("Cursor failed?!"); + } args_blob.write_all(&encoded.0).expect("Cursor failed?!"); }, } } - for enc in arg_encodings_iter { - assert_eq!(enc, &ArgEncoding::Padding); - args_blob.write_u32(0).expect("Cursor failed?!"); + if current_param_mask_bit.trailing_zeros() > raw::ParamMask::BITS as _ { + return Err(emitter.emit(error!( + message("too many arguments in instruction!"), + primary(args[raw::ParamMask::BITS as usize], "too many arguments"), + ))); } Ok(RawInstr { @@ -637,7 +721,7 @@ fn encode_args( opcode: instr.opcode, param_mask: match instr.user_param_mask { Some(user_provided_mask) => user_provided_mask, - None => compute_param_mask(&args, emitter)?, + None => param_mask, }, args_blob: args_blob.into_inner(), extra_arg, @@ -648,26 +732,4 @@ fn encode_args( }) } -fn compute_param_mask(args: &[Sp], emitter: &impl Emitter) -> Result { - if args.len() > raw::ParamMask::BITS as _ { - return Err(emitter.emit(error!( - message("too many arguments in instruction!"), - primary(args[raw::ParamMask::BITS as usize], "too many arguments"), - ))); - } - let mut mask = 0; - for arg in args.iter().rev(){ - let bit = match &arg.value { - LowerArg::Raw(raw) => raw.is_reg as raw::ParamMask, - LowerArg::TimeOf { .. } => 0, - LowerArg::Label { .. } => 0, - LowerArg::Local { .. } => 1, - LowerArg::DiffSwitch { .. } => panic!("should be handled earlier"), - }; - mask *= 2; - mask += bit; - } - Ok(mask) -} - // ============================================================================= diff --git a/src/llir/lower/intrinsic.rs b/src/llir/lower/intrinsic.rs index b45b9a9..9b3d552 100644 --- a/src/llir/lower/intrinsic.rs +++ b/src/llir/lower/intrinsic.rs @@ -74,7 +74,7 @@ impl IntrinsicBuilder<'_> { ) -> Result>, ErrorReported> { // full pattern match to fail when new fields are added let &IntrinsicInstrAbiParts { - num_instr_args, padding: ref padding_info, plain_args: ref plain_args_info, + num_instr_args, plain_args: ref plain_args_info, outputs: ref outputs_info, jump: ref jump_info, sub_id: sub_id_info, } = abi_parts; // check that the caller's 'build' closure put all of the right things for this intrinsic @@ -88,7 +88,6 @@ impl IntrinsicBuilder<'_> { let mut out_args = vec![None; num_instr_args]; // padding gets added later during args -> bytes conversion so we don't need to fill it - out_args.truncate(padding_info.index); // fill in all of the options if let (Some(goto_ast), &Some(jump_info)) = (self.jump, jump_info) { diff --git a/src/llir/raise/early.rs b/src/llir/raise/early.rs index 3a23f07..122e9ee 100644 --- a/src/llir/raise/early.rs +++ b/src/llir/raise/early.rs @@ -181,7 +181,7 @@ fn early_raise_intrinsics( fn raise_mask(value: raw::ParamMask) -> ast::Expr { ast::Expr::LitInt { value: value.into(), - radix: ast::IntRadix::Bin, + format: ast::IntFormat { unsigned: true, radix: ast::IntRadix::Bin }, } } @@ -192,7 +192,7 @@ fn raise_nargs(value: raw::ArgCount) -> ast::Expr { fn raise_pop(value: raw::StackPop) -> ast::Expr { ast::Expr::LitInt { value: value.into(), - radix: ast::IntRadix::Hex, + format: ast::IntFormat { unsigned: true, radix: ast::IntRadix::Hex }, } } @@ -236,7 +236,7 @@ fn decode_args_with_abi( use crate::io::BinRead; let mut param_mask = instr.param_mask; - let mut args_blob = std::io::Cursor::new(&instr.args_blob); + let mut blob_reader = std::io::Cursor::new(&instr.args_blob); let mut args = vec![]; let mut pseudo_arg0 = instr.extra_arg; let mut remaining_len = instr.args_blob.len(); @@ -251,12 +251,34 @@ fn decode_args_with_abi( let reg_style = hooks.register_style(); for (arg_index, enc) in siggy.arg_encodings().enumerate() { + // Padding produces values for the sake of verifying the bytes are 0. TODO: Implement! + // They're filtered out later on after dealing with @arg0 in the argument-raising pass. + if let ArgEncoding::Padding { size } = enc { + decrease_len(emitter, &mut remaining_len, *size as usize)?; + let raw_value = match size { + 1 => blob_reader.read_u8().expect("already checked len") as i32, + 4 => blob_reader.read_u32().expect("already checked len") as i32, + _ => unreachable!(), + }; + args.push(SimpleArg { value: ScalarValue::Int(raw_value), is_reg: false } ); + continue; + } let ref emitter = add_argument_context(emitter, arg_index); - let param_mask_bit = param_mask % 2 == 1; - param_mask /= 2; + // TODO: Add a way to fallback to @mask for + // "bad" mask bits to allow roundtripping + let can_be_param = if enc.contributes_to_param_mask() { + let value = !enc.is_always_immediate() && param_mask & 1 == 1; + param_mask >>= 1; + value + } else { + false + }; let value = match *enc { + | ArgEncoding::Padding { .. } + => unreachable!(), + | ArgEncoding::Integer { arg0: true, .. } => { // a check that non-timeline languages don't have timeline args in their signature @@ -265,40 +287,66 @@ fn decode_args_with_abi( ScalarValue::Int(extra_arg as _) }, - | ArgEncoding::Integer { arg0: false, size: 4, ty_color: _ } - | ArgEncoding::Color | ArgEncoding::JumpOffset | ArgEncoding::JumpTime - | ArgEncoding::Padding + | ArgEncoding::Integer { arg0: false, size: 4, format: ast::IntFormat { unsigned: false, radix: _ }, .. } => { decrease_len(emitter, &mut remaining_len, 4)?; - ScalarValue::Int(args_blob.read_u32().expect("already checked len") as i32) + ScalarValue::Int(blob_reader.read_i32().expect("already checked len")) }, - | ArgEncoding::Integer { arg0: false, size: 2, ty_color: _ } + | ArgEncoding::Integer { arg0: false, size: 2, format: ast::IntFormat { unsigned: false, radix: _ }, .. } => { decrease_len(emitter, &mut remaining_len, 2)?; - ScalarValue::Int(args_blob.read_i16().expect("already checked len") as i32) + ScalarValue::Int(blob_reader.read_i16().expect("already checked len") as i32) + }, + + | ArgEncoding::Integer { arg0: false, size: 1, format: ast::IntFormat { unsigned: false, radix: _ }, .. } + => { + decrease_len(emitter, &mut remaining_len, 1)?; + ScalarValue::Int(blob_reader.read_i8().expect("already checked len") as i32) + }, + + | ArgEncoding::Integer { arg0: false, size: 4, format: ast::IntFormat { unsigned: true, radix: _ }, .. } + => { + decrease_len(emitter, &mut remaining_len, 4)?; + ScalarValue::Int(blob_reader.read_u32().expect("already checked len") as i32) + }, + + | ArgEncoding::Integer { arg0: false, size: 2, format: ast::IntFormat { unsigned: true, radix: _ }, .. } + => { + decrease_len(emitter, &mut remaining_len, 2)?; + ScalarValue::Int(blob_reader.read_u16().expect("already checked len") as i32) + }, + + | ArgEncoding::Integer { arg0: false, size: 1, format: ast::IntFormat { unsigned: true, radix: _ }, .. } + => { + decrease_len(emitter, &mut remaining_len, 1)?; + ScalarValue::Int(blob_reader.read_u8().expect("already checked len") as i32) }, | ArgEncoding::Integer { size, .. } => panic!("unexpected integer size: {size}"), - | ArgEncoding::Float + | ArgEncoding::Float { .. } => { decrease_len(emitter, &mut remaining_len, 4)?; - ScalarValue::Float(f32::from_bits(args_blob.read_u32().expect("already checked len"))) + ScalarValue::Float(f32::from_bits(blob_reader.read_u32().expect("already checked len"))) }, | ArgEncoding::String { size: size_spec, mask, furibug } => { let read_len = match size_spec { - StringArgSize::Block { .. } => remaining_len, // read to end + StringArgSize::ToBlobEnd { .. } => remaining_len, // read to end + StringArgSize::Pascal { .. } => { + decrease_len(emitter, &mut remaining_len, 4)?; + blob_reader.read_u32().expect("already checked len") as usize + }, StringArgSize::Fixed { len, nulless: _ } => len, }; decrease_len(emitter, &mut remaining_len, read_len)?; - let mut encoded = Encoded(args_blob.read_byte_vec(read_len).expect("already checked len")); + let mut encoded = Encoded(blob_reader.read_byte_vec(read_len).expect("already checked len")); encoded.apply_xor_mask(mask); if let StringArgSize::Fixed { nulless: true, .. } = size_spec { @@ -317,20 +365,20 @@ fn decode_args_with_abi( }; let is_reg = match reg_style { - RegisterEncodingStyle::ByParamMask => param_mask_bit, + RegisterEncodingStyle::ByParamMask => can_be_param, RegisterEncodingStyle::EosdEcl { does_value_look_like_a_register } => { - does_value_look_like_a_register(&value) + can_be_param && does_value_look_like_a_register(&value) }, }; args.push(SimpleArg { value, is_reg }); } - if args_blob.position() != args_blob.get_ref().len() as u64 { + if blob_reader.position() != blob_reader.get_ref().len() as u64 { emitter.emit(warning!( // this could mean the signature is incomplete "unexpected leftover bytes in ins_{}! (read {} bytes out of {}!)", - instr.opcode, args_blob.position(), args_blob.get_ref().len(), + instr.opcode, blob_reader.position(), blob_reader.get_ref().len(), )).ignore(); } @@ -545,21 +593,17 @@ impl AtomRaiser<'_, '_> { let pseudo_arg0 = match instr.pseudo_arg0 { None | Some(0) => None, Some(arg0) => { - let enc = ArgEncoding::Integer { size: 2, ty_color: None, arg0: true }; + let enc = ArgEncoding::Integer { size: 2, ty_color: None, arg0: true, immediate: true, extend: false, format: ast::IntFormat { unsigned: false, radix: ast::IntRadix::Dec } }; let expr = self.raise_arg(emitter, &SimpleArg::from(arg0 as i32), &enc, dest_label)?; Some(expr) } }; - // drop early STD padding args from the end as long as they're zero. + // drop padding args // // IMPORTANT: this is looking at the original arg list because the new lengths may differ due to arg0. - for (enc, arg) in abi.arg_encodings().zip(args).rev() { - match enc { - ArgEncoding::Padding if arg.is_immediate_zero() => raised_args.pop(), - _ => break, - }; - } + let mut arg_iter = abi.arg_encodings(); + raised_args.retain(|_| !matches!(arg_iter.next().unwrap(), ArgEncoding::Padding { .. })); Ok(RaisedIntrinsicParts { opcode: Some(instr.opcode), @@ -588,15 +632,10 @@ impl AtomRaiser<'_, '_> { let encodings = abi.arg_encodings().collect::>(); let IntrinsicInstrAbiParts { - num_instr_args: _, padding: ref padding_info, outputs: ref outputs_info, + num_instr_args: _, outputs: ref outputs_info, jump: ref jump_info, plain_args: ref plain_args_info, sub_id: ref sub_id_info, } = abi_parts; - let padding_range = padding_info.index..padding_info.index + padding_info.count; - if !args[padding_range].iter().all(|arg| arg.is_immediate_zero()) { - return Err(CannotRaiseIntrinsic); // data in padding - } - let mut jump = None; if let &Some((index, order)) = jump_info { let (offset_arg, time_arg) = match order { @@ -676,11 +715,13 @@ impl AtomRaiser<'_, '_> { ensure!(emitter, !raw.is_reg, "expected an immediate, got a register"); match enc { - | ArgEncoding::Padding - | ArgEncoding::Integer { ty_color: None, .. } + | ArgEncoding::Integer { ty_color: None, format, .. } + => Ok(ast::Expr::LitInt { value: raw.expect_int(), format: *format }), + + | ArgEncoding::Padding { .. } => Ok(ast::Expr::from(raw.expect_int())), - | ArgEncoding::Integer { ty_color: Some(ty_color), .. } + | ArgEncoding::Integer { ty_color: Some(ty_color), format, .. } => { let lookup_table = match ty_color { TypeColor::Enum(ident) => &self.const_names.enums[ident], @@ -689,13 +730,11 @@ impl AtomRaiser<'_, '_> { &lookup_table, raw.expect_int(), ty_color, + *format, )) } - | ArgEncoding::Color - => Ok(ast::Expr::LitInt { value: raw.expect_int(), radix: ast::IntRadix::Hex }), - - | ArgEncoding::Float + | ArgEncoding::Float { .. } => Ok(ast::Expr::from(raw.expect_float())), | ArgEncoding::String { .. } @@ -716,7 +755,7 @@ impl AtomRaiser<'_, '_> { | Err(IllegalOffset) => { emitter.emit(warning!("invalid offset in a jump instruction")).ignore(); - Ok(ast::Expr::LitInt { value: raw.expect_int(), radix: ast::IntRadix::SignedHex }) + Ok(ast::Expr::LitInt { value: raw.expect_int(), format: ast::IntFormat { unsigned: false, radix: ast::IntRadix::Hex } }) }, }, } @@ -764,7 +803,7 @@ impl AtomRaiser<'_, '_> { } } -fn raise_to_possibly_named_constant(names: &IdMap>, id: i32, ty_color: &TypeColor) -> ast::Expr { +fn raise_to_possibly_named_constant(names: &IdMap>, id: i32, ty_color: &TypeColor, format: ast::IntFormat) -> ast::Expr { match names.get(&id) { Some(ident) => { match ty_color { @@ -776,7 +815,7 @@ fn raise_to_possibly_named_constant(names: &IdMap>, id: i32, ty_c }, } }, - None => id.into(), + None => ast::Expr::LitInt { value: id, format: format }, } } diff --git a/src/llir/raise/late.rs b/src/llir/raise/late.rs index fb6f1a9..c445a08 100644 --- a/src/llir/raise/late.rs +++ b/src/llir/raise/late.rs @@ -87,6 +87,24 @@ impl SingleSubRaiser<'_, '_> { RIKind::Blob => { + /*let mut pseudos = vec![]; + + let pseudo_mask = pseudo_mask.unwrap(); + if pseudo_mask != 0 { + pseudos.push(sp!(ast::PseudoArg { + at_sign: sp!(()), eq_sign: sp!(()), + kind: sp!(token![mask]), + value: sp!(ast::Expr::LitInt { value: pseudo_mask as i32, format: ast::IntFormat { unsigned: true, radix: ast::IntRadix::Bin } }), + })); + } + + if let Some(extra_arg) = pseudo_arg0 { + pseudos.push(sp!(ast::PseudoArg { + at_sign: sp!(()), eq_sign: sp!(()), + kind: sp!(token![arg0]), + value: sp!(extra_arg), + })); + }*/ let mut pseudos = self.raise_common_intrinsics(instr_pseudos.unwrap()); pseudos.push(sp!(ast::PseudoArg { diff --git a/src/mapfile.rs b/src/mapfile.rs index 6165a5b..a686a3b 100644 --- a/src/mapfile.rs +++ b/src/mapfile.rs @@ -174,6 +174,7 @@ fn mapfile_from_seqmap(seqmap: SeqmapRaw<'_>, emitter: &impl Emitter) -> Result< "!anmmap" => LanguageKey::Anm, "!stdmap" => LanguageKey::Std, "!msgmap" => LanguageKey::Msg, + "!endmap" => LanguageKey::End, TIMELINE_MAP_MAGIC => LanguageKey::Timeline, _ => return Err(emitter.emit(error!( message("bad magic: {:?}", magic), @@ -285,6 +286,7 @@ fn borrowed_seqmap_from_mapfile(mapfile: &Mapfile) -> SeqmapRaw<'_> { LanguageKey::Anm => "!anmmap", LanguageKey::Std => "!stdmap", LanguageKey::Msg => "!msgmap", + LanguageKey::End => "!endmap", LanguageKey::Timeline => TIMELINE_MAP_MAGIC, _ => unimplemented!("unexpected language key: {language:?}"), }; diff --git a/src/parse/abi.rs b/src/parse/abi.rs index aeec8ac..32cbaf4 100644 --- a/src/parse/abi.rs +++ b/src/parse/abi.rs @@ -124,7 +124,7 @@ pub fn parse_abi( } match next_char { - format_char @ sp_pat!('a'..='z' | 'A'..='Z' | '_' | '0'..='9') => { + format_char @ sp_pat!('a'..='z' | 'A'..='Z' | '_' | '-' | '0'..='9') => { let next_non_ws = text.chars().filter(|&c| !is_ws(c)).next(); let attributes = match next_non_ws { // Type with attributes. diff --git a/src/parse/lalrparser.lalrpop b/src/parse/lalrparser.lalrpop index 0492f6a..73c6bec 100644 --- a/src/parse/lalrparser.lalrpop +++ b/src/parse/lalrparser.lalrpop @@ -604,7 +604,7 @@ ExprTerm: ast::Expr = { > > => ast::Expr::XcrementOp { var, op, order: ast::XcrementOpOrder::Post }, - => ast::Expr::LitInt { value, radix: ast::IntRadix::Dec }, + => ast::Expr::LitInt { value, format: ast::IntFormat { unsigned: false, radix: ast::IntRadix::Dec } }, => ast::Expr::LitFloat { value }, diff --git a/src/passes/const_simplify.rs b/src/passes/const_simplify.rs index 26ead1f..56144af 100644 --- a/src/passes/const_simplify.rs +++ b/src/passes/const_simplify.rs @@ -51,6 +51,9 @@ impl ast::UnOpKind { token![unop ~] => Some(ScalarValue::Int(!x)), token![unop sin] | token![unop cos] | + token![unop tan] | + token![unop acos] | + token![unop atan] | token![unop sqrt] => uncaught_type_error(), token![unop int] => Some(ScalarValue::Int(x)), token![unop float] => Some(ScalarValue::Float(x as f32)), @@ -64,6 +67,9 @@ impl ast::UnOpKind { token![unop ~] => uncaught_type_error(), token![unop sin] => Some(ScalarValue::Float(x.sin())), token![unop cos] => Some(ScalarValue::Float(x.cos())), + token![unop tan] => Some(ScalarValue::Float(x.tan())), + token![unop acos] => Some(ScalarValue::Float(x.acos())), + token![unop atan] => Some(ScalarValue::Float(x.atan())), token![unop sqrt] => Some(ScalarValue::Float(x.sqrt())), token![unop int] => Some(ScalarValue::Int(x as i32)), token![unop float] => Some(ScalarValue::Float(x)), diff --git a/src/passes/type_check.rs b/src/passes/type_check.rs index 96cd6d5..6552a93 100644 --- a/src/passes/type_check.rs +++ b/src/passes/type_check.rs @@ -652,6 +652,9 @@ impl ExprTypeChecker<'_, '_> { | token![unop sin] | token![unop cos] + | token![unop tan] + | token![unop acos] + | token![unop atan] | token![unop sqrt] => self.require_float(arg_ty, op.span, arg_span), } @@ -681,6 +684,9 @@ impl ast::Expr { token![unop sin] | token![unop cos] | + token![unop tan] | + token![unop acos] | + token![unop atan] | token![unop sqrt] => ScalarType::Float, token![unop $] => ScalarType::Int, diff --git a/src/quote.rs b/src/quote.rs index baa1546..6a9c540 100644 --- a/src/quote.rs +++ b/src/quote.rs @@ -33,6 +33,9 @@ macro_rules! token { ($(unop)? ~) => { $crate::ast::UnOpKind::BitNot }; ($(unop)? sin) => { $crate::ast::UnOpKind::Sin }; ($(unop)? cos) => { $crate::ast::UnOpKind::Cos }; + ($(unop)? tan) => { $crate::ast::UnOpKind::Tan }; + ($(unop)? acos) => { $crate::ast::UnOpKind::Acos }; + ($(unop)? atan) => { $crate::ast::UnOpKind::Atan }; ($(unop)? sqrt) => { $crate::ast::UnOpKind::Sqrt }; ( unop $) => { $crate::ast::UnOpKind::EncodeI }; ( unop %) => { $crate::ast::UnOpKind::EncodeF }; diff --git a/tests/compile-fail/integration__integration__mapfiles__abi_string_errors.snap b/tests/compile-fail/integration__integration__mapfiles__abi_string_errors.snap new file mode 100644 index 0000000..c877334 --- /dev/null +++ b/tests/compile-fail/integration__integration__mapfiles__abi_string_errors.snap @@ -0,0 +1,23 @@ +--- +source: tests/integration/mapfiles.rs +expression: stderr +--- +error: mutually exclusive attributes 'len' and 'bs' in 'z' format + ┌─ :3:5 + │ +3 │ 0 z(len=20;bs=4) + │ ^^^ ^^ + +error: 'len' attribute is not supported by 'p' + ┌─ :4:5 + │ +4 │ 1 p(len=20) + │ ^^^ + +error: missing length attribute ('len' or 'bs') for 'p' + ┌─ :5:3 + │ +5 │ 2 p + │ ^ + + diff --git a/tests/integration/general.rs b/tests/integration/general.rs index 412991d..ce02347 100644 --- a/tests/integration/general.rs +++ b/tests/integration/general.rs @@ -190,7 +190,7 @@ source_test!( ECL_06, const_cast_even_in_eosd, main_body: r#" const int x = int(2.0 + 3.0); - ins_1(x); + ins_61(x); "#, check_compiled: |output, format| { let ecl = output.read_olde_ecl(format); diff --git a/tests/integration/mapfiles.rs b/tests/integration/mapfiles.rs index 1334656..4a9e882 100644 --- a/tests/integration/mapfiles.rs +++ b/tests/integration/mapfiles.rs @@ -34,6 +34,17 @@ source_test!( main_body: r#""#, ); +source_test!( + ANM_10, abi_string_errors, + mapfile: r#"!anmmap +!ins_signatures +0 z(len=20;bs=4) //~ ERROR mutually exclusive +1 p(len=20) //~ ERROR not supported by +2 p //~ ERROR missing +"#, + main_body: r#""#, +); + source_test!( ANM_10, seqmap_duplicate_key, mapfile: r#"!anmmap