diff --git a/Decompiling-a-function.md b/Decompiling-a-function.md index ecc0d81..3e5f834 100644 --- a/Decompiling-a-function.md +++ b/Decompiling-a-function.md @@ -238,7 +238,27 @@ That did the trick! The compiled and target assembly are now matching. Note that not all functions will be this simple to match with automated decompiler output. Longer functions and more complicated logic give automated decompilers more trouble, and will take more tweaks and possibly large refactors to match. Some people prefer to avoid automated decompilers and stick to writing the function from scratch, and it is up to you to decide which approach you prefer. ## Inserting a decompiled function -Now that the function has been decompiled, you'll need to add it into the decomp project and remove the corresponding raw assembly code. +Now that the function has been decompiled, you'll need to add it into the decomp project and remove the corresponding raw assembly code. The repo has an `extract_function` script to move the function from assembly into C, or you can manually move it as a fallback. + +You can check out this [sample PR](https://github.com/pret/pmd-sky/pull/6) for an example of the changes needed to add a decompiled function to the decomp. + +### Using extract_function +To use [extract_function](https://github.com/pret/pmd-sky/blob/main/tools/extract_function/extract_function.py), you will need Python 3 installed. The script will move a function from its assembly file to C files (.h and .c), splitting the assembly file in two if needed. + +1. Make sure to have no uncommitted changes, in case something goes wrong with the script that needs a revert. +2. Run this command from the project root. + ``` + python tools/extract_function/extract_function.py + ``` + * `asm_file` is the the assembly file containing the function you decompiled. + * `function_header` is the C function header of the function you decompiled. Since it has spaces, remember to surround it in quotes. +3. The script will either create new C files for the function or add the function to an existing C file, depending on its location within the assembly file. Go to the .c file to find an empty function with the header you specified, and add the decompiled function code to this function. +4. Search for any externs in other files that reference the newly decompiled function. These externs can be removed and replaced with an `#include` to the new `.h` file. +5. Run `make tidy` and `make` to ensure that the project compiles and produces a matching ROM. If the ROM doesn't match, you can compare the mismatched files with the [asmdiff tool](https://github.com/pret/pmd-sky/tree/main/tools/asmdiff) or a hex editor to troubleshoot the issue. +6. If you want to decompile more functions, repeat the decomp process by finding a new function and creating a new scratch. If you are done, make a PR to the main `pmd-sky` repo. + +### Manually inserting a function +If `extract_function` doesn't work for you, or if you want to manually move the function from assembly to C for any other reason, here are the steps to do so. The end result is the same as using `extract_function`. 1. Create a `.c` in the `src` folder and a corresponding `.h` file in `include`. * Alternatively, if the function is at the beginning or end of the `.s` file, you may be able to add it to an existing C file. Check `main.lsf` to see which C (`.o`) file is right before/after the `.s` file. @@ -251,12 +271,10 @@ Now that the function has been decompiled, you'll need to add it into the decomp 8. Run `make tidy` and `make` to ensure that the project compiles and produces a matching ROM. If the ROM doesn't match, you can compare the mismatched files with the [asmdiff tool](https://github.com/pret/pmd-sky/tree/main/tools/asmdiff) or a hex editor to troubleshoot the issue. 9. If you want to decompile more functions, repeat the decomp process by finding a new function and creating a new scratch. If you are done, make a PR to the main `pmd-sky` repo. -You can check out this [sample PR](https://github.com/pret/pmd-sky/pull/6) for an example of the changes needed to add a decompiled function to the decomp. - ## Function decomp tips The example function in this guide shows the overall process of decompiling a function, though it doesn't cover every situation you may encounter. While it is impractical to go over every possible assembly construct and its C equivalent, here are some assorted tips. * When working in decomp.me, any calls to other functions can be represented as extern functions, even if they are already decompiled in the decomp project. When adding the function to the decomp, you can replace these extern functions with #includes as needed, or leave the externs there for functions that have not been decompiled yet. -* Keep in mind some of the more eclectic C constructs like (static) inline functions, ternary statements, gotos, and C library functions like `memcpy()`. All of these may produce different assembly compared to not using them. +* Keep in mind some of the more eclectic C constructs like (static) inline functions, ternary statements, gotos, and C library functions like `memcpy()`. All of these may produce different assembly compared to more conventional C constructs. * If there are multiple places in a function with the same C code, the compiler may merge them into a single block of assembly and use unconditional branches to connect the different places to this block. This is known as a **tail merge**. * There are times where you'll match everything in the function aside from which registers are used. For example, two variables are assigned to registers `r4` and `r5` respectively, but the target assembly assigns the first variable to `r5` and the second to `r4` instead. This is known in the decomp community as a register swap or **regswap**, and is one of the more frustrating issues to run into. There are a number of possible code changes to try and fix a regswap. Note that this list is not exhaustive. * Ensure that the register use is actually identical in functionality. It is easy to dismiss a difference as a regswap when it is actually a value being assigned to the wrong variable. @@ -266,6 +284,6 @@ The example function in this guide shows the overall process of decompiling a fu * Play with the structure of conditionals and loops. ## Asking for help -If you are having trouble matching a function, the pret Discord has an #asm2c channel where you can ask for help with matching. If you post the link to your decomp.me scratch on the channel, other people can fork the scratch to experiment on their own, and you'll see a notification on decomp.me if someone else successfully matches your function. You can also browse through previously matched functions for inspiration on tricks used by others to produce matching assembly. As you continue to decompile more, you can try helping others in the channel, which in turn will help you practice and gain exposure to the nuances of the decompilation process. +If you have trouble matching a function, you can ask for help on the pret Discord's #asm2c channel. Post the link to your decomp.me scratch on the channel, and other people can fork the scratch to experiment on their own. You'll see a notification on decomp.me if anyone successfully matches your function. You can also browse through previously matched functions for inspiration on tricks used by others to produce matching assembly. As you continue to decompile more, you can try helping others in the channel, which in turn will help you practice and gain exposure to the nuances of the decompilation process. -If all else fails, you can leave the function in assembly within the decomp project and add a comment linking to your decomp.me scratch. This will provide others with a starting point if they want to try matching the function later on. \ No newline at end of file +If all else fails, leave the function in assembly within the decomp project and add a comment linking to your decomp.me scratch. This provides others with a starting point to try matching the function later on. \ No newline at end of file