-
Notifications
You must be signed in to change notification settings - Fork 2
Format improvements (WIP) #63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
2ed6200
a8b2ad4
e66c92d
f3de737
96c1d4c
7fe31f4
4d089ac
9605ae3
5ff6724
b3e8508
b70b2ed
17ce209
92292d5
3d32e3b
88e5a47
26f14dc
6e5e625
8d9e7a8
7a8dd56
4e2e6a2
ac21a91
fba8534
e04f291
20a4da0
b2dcd1c
7778c45
21c30dd
3bdc5fa
e9c3d28
471b51d
1a5964e
8eb4c4b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -261,7 +261,7 @@ 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); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ZUN did something extremely cursed in a few old engine files and uses -1 as the sprite ID to intentionally overwrite sprites from a different ANM. This didn't break previously because truth didn't dump the ID field to roundtrip it but blew up once the field was added. |
||
|
|
||
| write_sprite(w, sprite_id, sprite)?; | ||
| Ok(sprite_offset) | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -42,7 +42,7 @@ pub enum ArgEncoding { | |||||
| /// 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) | ||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Document
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What sort of formatting should be used for that? The only currently documented attributes are
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh bugger, I forgot to document It should be in prose in the documentation of
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. well,
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, we probably want to make a markdown list ///
/// Supported attributes:
///
/// * **`imm`**: lorem ipsum dolor sit amet
/// i have a barnicle in my backseat
/// * **`arg0`**: blah blah blah blah
etc |
||||||
| Integer { size: u8, ty_color: Option<TypeColor>, arg0: bool, immediate: bool, format: ast::IntFormat }, | ||||||
| Integer { size: u8, ty_color: Option<TypeColor>, 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. | ||||||
|
|
@@ -91,7 +91,7 @@ pub enum StringArgSize { | |||||
| } | ||||||
|
|
||||||
| impl ArgEncoding { | ||||||
| pub fn dword() -> Self { ArgEncoding::Integer { size: 4, ty_color: None, arg0: false, immediate: false, format: ast::IntFormat { unsigned: false, radix: ast::IntRadix::Dec } } } | ||||||
| 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 { | ||||||
|
|
@@ -115,10 +115,10 @@ 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, immediate, format } => write!( | ||||||
| Enc::Integer { arg0: true, ty_color, size, immediate, format, extend } => write!( | ||||||
ExpHP marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| f, | ||||||
| "{} (in timeline arg0)", | ||||||
| Enc::Integer { format: *format, immediate: *immediate, arg0: false, ty_color: ty_color.clone(), size: *size }.descr(), | ||||||
| Enc::Integer { extend: *extend, format: *format, immediate: *immediate, arg0: false, ty_color: ty_color.clone(), size: *size }.descr(), | ||||||
ExpHP marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| ), | ||||||
| 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()), | ||||||
|
|
@@ -275,6 +275,7 @@ fn int_from_attrs(param: &abi_ast::Param, emitter: &dyn Emitter) -> Result<Optio | |||||
| 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.wrapping_sub(1) >= 2 { | ||||||
|
|
@@ -299,6 +300,7 @@ fn int_from_attrs(param: &abi_ast::Param, emitter: &dyn Emitter) -> Result<Optio | |||||
| 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 }, | ||||||
| })) | ||||||
| }) | ||||||
|
|
@@ -424,7 +426,7 @@ 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)| { | ||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line is too noisy. Funnily, this is a flat_map and yet it doesn't even look like anything is currently taking advantage of that; but this is exactly the sort of use case for that, so let's make use of it:
Suggested change
And change the Padding match arm to: | ArgEncoding::Padding { .. }
=> return None, // hide from signature
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was concerned something like that would end up inserting a None into the resulting collection. Didn't know it would skip over it. Seems like there could be issues in the future though if
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops I missed the fact that this would change indices, lemme read this over again.
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
So the index is only used to generate arg names. I would pull that out. // in a line of code outside the loop
let arg_names = (1..).map(|i| ident!("arg_{i}"));
// inside the loop, change this line:
let name = sp!(abi_span => ctx.resolutions.attach_fresh_res(arg_names.next().unwrap()));
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and of course get rid of the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make more sense to just add another function to the
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmmmmm. Crap, that's a pain. Writing this function is probably a good idea. It will have to return the slightly weaker |
||||||
| let Info { ty, default, reg_ok, ty_color } = match *enc { | ||||||
| | ArgEncoding::Integer { arg0: false, ref ty_color, .. } | ||||||
| => Info { ty: ScalarType::Int, default: None, reg_ok: true, ty_color: ty_color.clone() }, | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -554,18 +554,25 @@ fn encode_args( | |||
| let mut param_mask: raw::ParamMask = 0; | ||||
| let mut current_param_mask_bit: raw::ParamMask = 1; | ||||
|
|
||||
| let mut small_padding_value = 0i8; | ||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I want to see tests for:
That last one would involve
truth/tests/integration_impl/mod.rs Line 227 in c41358e
|
||||
|
|
||||
| // 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 enc in arg_encodings_iter.by_ref() { | ||||
| if let ArgEncoding::Padding { size } = enc { | ||||
| match size { | ||||
| 1 => args_blob.write_u8(0).expect("Cursor<Vec> failed?!"), | ||||
| 1 => { | ||||
| if (args_blob.position() & 3) == 0 { small_padding_value = 0i8 } | ||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please avoid one-line ifs. I reserve these for extreme cases (usually only when there are multiple similar ifs in a row and I want to emphasize the similarities between them)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll freely admit the entire logic for handling that was a 5 minute quick fix that bolts a random functionality where it doesn't quite belong. I'd love to replace the logic with something better that doesn't feel as dirty.
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As the complexity of this function increases (which it is no doubt going to, without end), we're going to want to start factoring out pieces of it into separable units. This thing right here is easily separated out into a Finite State Machine. You could have something like: /// Finite-state machine helper for the value of `-` padding.
struct SmallPaddingFillHelper {
fill_value: i8,
}
impl SmallPaddingFillHelper {
/// Update the helper in response to the next arg encoding.
///
/// Returns the current fill value for `-` padding.
fn step(&mut self, enc: &ArgEncoding, blob_position: u64) -> i8 {
todo!("update self.fill_value");
self.fill_value
}
}and then you could put
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I meant more that I'd rather leave the padding bytes as exclusively 0, eliminate the padding fill variable entirely, and let the values be handled by a |
||||
| args_blob.write_i8(small_padding_value).expect("Cursor<Vec> failed?!") | ||||
| }, | ||||
| 4 => args_blob.write_u32(0).expect("Cursor<Vec> failed?!"), | ||||
| _ => unreachable!(), | ||||
| } | ||||
| continue; | ||||
| } | ||||
| small_padding_value = 0i8; | ||||
|
|
||||
| let arg = args_iter.next().expect("function arity already checked"); | ||||
|
|
||||
| let arg_bit = match &arg.value { | ||||
|
|
@@ -607,14 +614,28 @@ fn encode_args( | |||
|
|
||||
| | ArgEncoding::JumpOffset | ||||
| | ArgEncoding::JumpTime | ||||
| | ArgEncoding::Integer { size: 4, format: ast::IntFormat { unsigned: false, radix: _ }, .. } | ||||
| => args_blob.write_i32(arg.expect_raw().expect_int()).expect("Cursor<Vec> failed?!"), | ||||
|
|
||||
| | ArgEncoding::Integer { size: 2, format: ast::IntFormat { unsigned: false, radix: _ }, .. } | ||||
| => args_blob.write_i16(arg.expect_raw().expect_int() as _).expect("Cursor<Vec> 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<Vec> failed?!") | ||||
| }, | ||||
|
|
||||
| | ArgEncoding::Integer { size: 1, format: ast::IntFormat { unsigned: false, radix: _ }, .. } | ||||
| => args_blob.write_i8(arg.expect_raw().expect_int() as _).expect("Cursor<Vec> 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<Vec> 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<Vec> failed?!") | ||||
| }, | ||||
|
|
||||
| | ArgEncoding::Integer { size: 4, format: ast::IntFormat { unsigned: true, radix: _ }, .. } | ||||
| => args_blob.write_u32(arg.expect_raw().expect_int() as _).expect("Cursor<Vec> failed?!"), | ||||
|
|
||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"extend" is too short of a name for something that is probably only ever going to be used once. Probably want something with multiple words, meaning we might need to pick now whether we want
snake_caseorkebab-caseThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There're quite a few places where the game engines will read a full dword from the signature in order to check for variables but then only read the lower bits of the resulting value. Ideally
extendcould be replaced with a more general purpose attribute applicable to those situations as well since that would eliminate the need for extended padding anyway. Maybe this could be written asE(imm;range="s")or something?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was also thinking this should probably be a dword-sized value with an attribute that limits its value size. However, I really do not want to see abi letters get used inside string literals.