m2c
m2c copied to clipboard
Array indexing
Currently outputs *(pointer arithmetic)
This would definitely speed up my workflow, any directions how I could help with this?
We do have this to some extent, though the pattern matching for it is far from all-encompassing. Do you have some example cases where it doesn't work? (e.g. links to decomp.me scratches) Generally it's a case of adding more logic to evaluate.py and translate.py, both of which can be a bit gnarly to get into.
I took a look and you are right, I realized it usually only doesn't work if the type of the variable does not get recognized, for example when the array is a field of a variable that m2c outputs as void *. This can be solved for stack variables by declaring a struct for them, but what about the case where the struct pointer in question is in a saved register or is an argument to the function?
For arguments to the function you can use contexts, and saved registers are usually taken care of automatically by type inference but for sure we are lacking a mechanism there. Do you have an example case to aid the discussion?
This is an obvious case, the type of var_r31 cannot be infered because it's heap allocated on the spot. What I would find helpful is for example getting something like var_r31->unk_02[var_r30 /* size 0x02 */] instead of *(var_r31 + ((var_r30 * 2) + 2)). But I don't know how much sense that would make in the big scheme of things.
This is often a problem in our project, since functions often get an argument which contains a void* which then gets casted into one of many structs. Perhaps m2c could create a struct in such cases too, not just for globalish labels?
In that case, if fn_1_7E0 was declared as taking a pointer of the correct type, the arg0->unk5C = var_r31 assignment should type-infer it into the correct thing. I feel like generating var_r31->unk_02[var_r30 /* size 0x02 */] would be a fair bit of complexity for little gain.
It wouldn't be able to because the argument is omObjData* and its member at 0x5C (called data) is indeed a void* which later in other functions gets casted to the correct struct. It's basically a universal wrapper and I had to get used to figuring out which one is used because usually there are more than 3 per file. I think it would be fairly hard to recognize the type automatically (finding the function that allocates the struct and track the functions the wrapper object gets passed to, including wrapper objects saved in bss). But perhaps it could be done by internally considering those wrappers completely different structs, creating a new one every time the data member gets assigned.
The only workaround to my knowledge currently is to change the definition of omObjData to point to the correct struct for the function I'm working on and giving m2c a single function to decomp, but this is cumbersome since most functions are not that long for this to be worth the effort.
The first thing functions in Mario Party 4 that use this concept do is storing that void* in a saved register so I wish I could say "var_r31 of this function is a Foo*", similarly to how it's done with the stack. Additionally there could be a switch to enable the emission of the struct that m2c internally generates (does it actually?), similar to "stack struct templates" that'd simplify the process of creating the struct that should be used for var_r31 for example.
This would be a huge thing because cleaning up such code takes up a rather big amount of my time decomping.
I was actually successful at implementing the feature that lets you declare the type of saved variables using a struct and it works beautifully for my needs. I'd gladly do a PR if you are interested, I'd appreciate comments about potential better ways to do it.