# Contributing guide for Pokémon Diamond - [AI Policy](#ai-policy) - [Code Formatting](#code-formatting) - [Contributing Guide](#contributing-guide) **The repository is in a volatile state.** This is a living document which lays out the procedure and loose guidelines for decompiling the game code of Pokémon Diamond Version (5.0-US) for the Nintendo DS. ALL PERSONS OPENING PULL REQUESTS TO THIS REPOSITORY AGREE TO ABIDE BY THE POLICIES OUTLINED IN THIS DOCUMENT. ## AI Policy We unequivocally prohibit the use of artifical intelligence (AI) large language models (LLMs) to generate contributions to this project, meaningful or otherwise. Any pull request found to have used AI for these tasks will be closed, and the contributor will be banned from interacting with the repository. This is a zero-tolerance policy, and we do not provide any avenue for appeal. The following use cases are deemed acceptable and stand as exceptions to the above statement, provided that they are disclosed in full. While this document is not legally binding, we do expect you to represent yourself truthfully. Undisclosed use of AI may result in a ban. - Automating the boring stuff. So long as the task is clearly defined by a human, is boiled down to pure execution with no further creative decision-making, and is sufficiently tedious to perform by hand, you may delegate it to an AI. However, we do strongly encourage you to do as much as you can by hand or write a script to automate the task, rather than invoking an AI to do it for you. - Asking general knowledge questions about C code, the ARM processor, Pokémon, etc. ## Code Formatting This repository includes an opinionated `clang-format` specification to ensure that we maintain a common code style. For convenience, a pre-commit hook is also provided in `.githooks` which will run `clang-format` against any staged changes prior to executing a commit. ### Requirements - `clang-format@18` or newer ### Usage To set up the pre-commit hook: ```sh git config --local core.hooksPath .githooks/ ``` To run the formatter on the full source tree: ```bash ./format.sh ``` ### Nonmatching functions clang-format does not recognize the syntax for inline asm that is required by mwccarm, so it should be disabled for non-matching functions specifically. clang-format accepts directives via comments of the form `// clang-format [on|off]`. Example: ```c #ifdef NONMATCHING void func() { // ... } #else // clang-format off asm void func() { push {lr} // ... pop {pc} } // clang-format on #endif // NONMATCHING ``` ### Ubuntu (WSL) Installation On older versions of Ubuntu, clang-format will default to earlier versions. To install clang-format-18 on Ubuntu (WSL), run the following: ```sh wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh sudo ./llvm.sh 18 sudo apt install clang-format-18 ``` And then create a symbolic link: ```sh ln -s /usr/bin/clang-format-18 /usr/bin/clang-format ``` If you're using the pre-commit hook, you also want to set up a symlink for git: ```sh git config alias.clang-format clang-format-18 ``` ## Contributing Guide ## Structure of the repository Nintendo DS games contain separate static binaries and overlays for the ARM7 and ARM9 processors as well as a filesystem. Therefore the repository is laid out as such: ``` root `- arm9 `- asm `- data `- graphics `- lib `- src `- include `- overlays `- 00 `- asm `- src `- ... `- src `- global.inc `- arm9.lsf `- Makefile `- arm7 `- asm `- global.inc `- arm7.lsf `- Makefile `- files `- data `- graphics `- include `- include-mw `- tools `- Makefile ``` In the above structure, ASM files (.s) in the asm/ directories contains the machine code extracted from the ROM (baserom.nds). They are to be decompiled into C or C++ (.c, .cpp) files in the respective src/ directories. ## Decompilation of ASM to C **Decompilation** entails writing C code which the project compiler (mwccarm) will translate to the exact same assembly code. For example, consider this ASM function: ```armasm thumb_func_start sub_0201B578 sub_0201B578: ; 0x0201B578 lsl r0, r0, #0x5 add r0, #0x34 bx lr .balign 4 ``` Without knowing anything else about the function prototypes, we can make an educated guess as to what C code would produce this function. Function arguments are passed in registers r0-r2 or r0-r3, and the return value (if any) is held in r0. The `LSL` instruction means "logical shift left", which is equivalent to multiplying the input operand by a power of 2. In this case, the input operand is being shifted left by 5 bits (multiplied by 20h). The following instruction (`ADD r0, #0x34`) adds 34h to that value, and the final instruction `BX lr` returns to the parent routine with the result of the ADD instruction held in r0. Thus we can surmise that the C function ```c s32 sub_0201B578(s32 arg0) { return 32 * arg0 + 52; } ``` will produce that assembly code. Most functions in the codebase will be longer and more complicated than this. ## Creating a new C file Section link order is specified in the Linker Spec File, arm9/arm9.lsf. Only the basenames of each object (.o) file are specified in the lsf and recognized by the project linker, mwldarm. Therefore, no two compiled objects can have the same name. When decompiling asm/foo.s, please create the C file with a different name (basename minus extension i.e. src/foo_c.c). Except in rare cases, you need only insert "Object .o" into the "Static" container. For instance: ```diff Object file1.o Object file2.o + Object file3_c.o Object file3.o Object file4.o ``` ## Testing the build After placing your C file into the LSF as described above, test your build by running `make`. Here are some common errors you may encounter and how to resolve them: Unknown identifier, sub_0201B578 Append the line `.extern sub_0201B578` to arm9/global.inc and recompile. build/arm9.sbin: FAILED build/OVERLAY_00.sbin: FAILED ... Your attempt was incorrect. Don't be discouraged, this is all part of the process. The following bash script will allow you to compare your code to the original ROM; save it as arm9/asmdiff.sh ```bash #!/bin/bash OBJDUMP_ARCH="${OBJDUMP_ARCH:-armv5te}" OBJDUMP_MODE="${OBJDUMP_MODE:-force-thumb}" OBJDUMP_VMA=0x02000000 OBJDUMP="arm-none-eabi-objdump -Drz -bbinary -m${OBJDUMP_ARCH} -M${OBJDUMP_MODE} --adjust-vma=$((OBJDUMP_VMA))" OPTIONS="--start-address=$(($1 + OBJDUMP_VMA)) --stop-address=$(($1 + $2 + OBJDUMP_VMA))" $OBJDUMP $OPTIONS $(dirname $0)/baserom.sbin > $(dirname $0)/baserom.dump || exit 1 $OBJDUMP $OPTIONS $(dirname $0)/build/diamond.us/arm9.sbin > $(dirname $0)/arm9.dump diff -u $(dirname $0)/baserom.dump $(dirname $0)/arm9.dump ``` Place a clean version of the ARM9 binary as arm9/baserom.sbin (arm9/build/arm9.bin from a successful build should suffice). In your terminal, navigate to the arm9 directory and run `./asmdiff.sh 0 $(wc -c baserom.sbin) | less`, then scroll through to where the grievances begin. Fix any obvious problems in your code/tree, and rerun. If the differences are extensive, you may have induced a shift in the binary either by writing incorrect code or placing it incorrectly into the LSF. *Tip: you can specify a start address and size to only compare the portion of the ROM you are working on.* ## Decompiling data **This section describes a target repository specification and does not reflect the current state of the project.** ASM files may own one or more data/RAM sections. The types of these sections is not guaranteed to be accurate. When decompiling data, you are expected to translate the raw bytes into the actual structures used by the source code. These may be simple values (char, short, word, or pointer), or they could be C structs or unions. Some overlays are suspected to contain C++ classes, the handling of which is not yet described. Because the Nintendo DS architecture is ARM, all data is aligned. This means 16-bit integers are aligned to 2 bytes within a structure, and anything 4 bytes or wider is aligned to 4 bytes (long, long long, float, double, struct, union, void *). All data requiring alignment are padded with 0. For example: ```armasm u8_var_foo: .byte 0x05, 0x00, 0x00, 0x00 ptr_var_bar: .word u8_var_foo ``` could have been compiled from ```c u8 u8_var_foo = 5; u8 * ptr_var_bar = &u8_var_foo; ``` Notice that the three extra 0 bytes are treated as implicit padding.