| name | decompile-a-function |
| description | Given a function name, decompile it from assembly to C code. Use this skill if you're asked to decompile a function. |
Decompile a Function
Decompiling a function is a difficult task. Use thinking. Avoid delegation to less capable models (i.e. Sonnet).
Instructions
Step 1: Match Function in Decomp Env
First we need to spin up a decomp environment for the function, run:
./tools/claude --bootstrap <function name>
Move to the directory created by the script. This will be nonmatchings/<function name>-<number (optional)>.
Use the tools in this directory to match the function. You may need to make several attempts. Each attempt should be in a new file (base1.c, base_2.c, ... base_n.c, etc). It's okay to give up if you're unable to match after _30 attempts.
Step 2: Integrate Match into Project
If you are able to match the function, update the C code to use it. The C code will be importing an assembly file, something along the lines of INCLUDE_ASM/asm/nonmatchings/<function name>. Replace this with the actual C code.
- Update the rest of the project to fix any build issues.
- If the function is defined in a header file (located in include/), this will also need to be updated. These other usages may teach you about the correct type of your function arguments or return types. DO NOT JUST MAKE EVERYTHING void*!.
- Make sure to search for any existing function / struct declarations in the project (under src/ and include/). We do not want duplicate or redundent declarations.
Verify that the project still builds successfully by running ./tools/build-and-verify.sh. If this check fails, the decompilation is NOT complete, even if individual functions appear to match.
- If the checksum fails after your changes, use
python3 tools/asm-differ/diff.py --no-pager <function>to check ALL functions in the modified file(s). Look for functions that access the same structs you modified. Fix any mismatches before declaring success. - If you are unable to fix the build issues the match should be marked with all changes reverted and the function name added to tools/difficult_functions
Step 3: Commit Your Changes
If you are able to get a perfect matching decompilation, commit the change with the message matched <function name> <attempts>.
Respect any pre-commit hooks that prevent you from committing your change. A failed hook indicates that you have not correctly updates the C code.
What if I can't match?
Try up to 30 times to get a match. If you still can't get a match:
- Revert any changes you've made adding the function to the C file or other project files (we do not want to save incomplete matches).
- Add the function name to
tools/difficult_functionsalong with the number of attempts and best match percentage (function names should be separated by newlines). This should be in the form\n<function name> <number of attempts to match> <best match percentage>\n. By adding the function name to difficult_functions. - Commit your changes
Tools
These tools will help you accomplish the decompilation task:
./tools/build-and-verify.shbuild the project and verify that it matches the target.diff.pyyou can view the difference between the compiled and target assembly code of a given function by runningpython3 tools/asm-differ/diff.py --no-pager <function name>./tools/claude --bootstrap <function name>spin up a decompilation environment for a given function.python3 tools/score_functions.py <directory>find the easiest function to decompile in a given directory (and its subdirectories).python3 tools/check_pointer_arithmetic.py <file or directory>detect pointer arithmetic with casts that should be replaced with struct field access. Use--strictto fail on violations.
Validation Checklist
Before declaring any changes to C code complete (including decompiling functions), verify:
- No pointer arithmetic with manual offset calculations
- All struct field accesses use
->or.operators - No
void*parameters that should be typed structs - Struct sizes match the assembly access patterns
-
./tools/build-and-verify.shsucceeds
Code Quality Standards
Avoid Pointer Arithmetic
When you see pointer arithmetic patterns like *(type*)((u8*)ptr + offset):
Identify the access pattern:
- What offset is being accessed? (e.g.,
0xCmeans field at offset 12) - Is it accessing an array element? (e.g.,
arg1 * 36means 36-byte elements) - What field within the element? (e.g.,
+ 0xAmeans field at offset 10)
- What offset is being accessed? (e.g.,
Create appropriate structs:
- Define the element struct with correct size and field offsets
- Define the container struct with pointer at correct offset
- Use meaningful names or
unk[Offset]naming convention
Verify struct sizes:
- Calculate total size to ensure it matches the multiplier in pointer arithmetic
- Example:
arg1 * 36means struct must be exactly 36 (0x24) bytes
Struct Modification and Extension
When modifying struct definitions:
- Search the entire codebase for other references to the same struct
- Check if other functions access fields at nearby offsets
- Verify ALL affected functions still match after struct changes
- Example: If you add a field at offset 0x14, search for all functions accessing that struct and verify they still compile to the correct offsets
Avoid Redundent Declarations
After adding your decompiled function, check for any redundant extern declarations:
Search for existing declarations: For each extern function you used, search the codebase to see if it's already declared in a header file:
- Use
grep -r "void functionName" include/to search headers - Use
grep -r "void functionName" src/*.hto search source headers
- Use
Remove redundant externs: If a function is already declared in an included header file, remove your extern declaration to avoid duplication
Verify the build still works after removing redundant externs
Example: If you added extern void setCallback(void *); but task_scheduler.h (which is already included) declares it, remove your extern declaration.
Decompilation tips
Assets
Typically small 5-6 length symbols (e.g. D_4237C0) are asset addresses. Another strong hint that they are assets if if they are passed to dmaRequestAndUpdateStateWithSize.
Use USE_ASSET(symbol) to load symbols, for example D_4237C0 and dmaRequestAndUpdateStateWithSize would become:
USE_ASSET(_4237C0);
dmaRequestAndUpdateStateWithSize(_4237C0_ROM_START, _4237C0_ROM_END);
Failure to handle assets properly will almost certainly guarantee a mismatch.