From 1f5630246e6d14ad257dcd1c0e6e7931bde25acf Mon Sep 17 00:00:00 2001 From: Pheenoh Date: Tue, 22 Mar 2022 23:41:57 -0400 Subject: [PATCH] Update README and Contributing (#185) --- README.md | 156 ++++++++++++++++++++++++++++++++++--------- docs/Contributing.md | 57 ++++++++++++++++ docs/ghidra.png | Bin 0 -> 14827 bytes 3 files changed, 182 insertions(+), 31 deletions(-) create mode 100644 docs/Contributing.md create mode 100644 docs/ghidra.png diff --git a/README.md b/README.md index faacd2159c9..5d7377926f4 100644 --- a/README.md +++ b/README.md @@ -2,46 +2,140 @@ This repo contains a WIP decompilation of The Legend of Zelda: Twilight Princess (GCN USA). -It builds the following DOL: + +* [Project Setup](#project-setup) +* [Building The Game](#build-game) +* [Extract Game Assets](#extract-game-assets) +* [Create Expected Directory](#create-expected-directory) +* [Clean Directories](#clean-directories) +* [Project Overview](#project-overview) +* [Contributing](./docs/Contributing.md) +* [FAQ](https://zelda64.dev/games/tp) + + -main.dol - `sha1: 4997D93B9692620C40E90374A0F1DBF0E4889395` +Project Setup +================= -And will eventually build all the [RELs](./docs/rels_sha1.md). - -## Windows Prerequisites - -1. Download and run the latest release of the [Windows devkitpro installer](https://github.com/devkitPro/installer/releases) -2. Run the executable located at `devkitPro\msys2\msys2.exe` -3. Update pacman by running the following command: `pacman -Syu` -4. Install the necessary dependencies by running the following command: `pacman -S python3-pip base-devel gcc vim cmake` -5. Change to the directory of where you cloned this repository -6. Install the required python dependencies with `python3 -mpip install -r tools/requirements.txt` - -## Build Instructions - -1. Obtain a clean DOL of TP (GCN USA) and place it at the root of the repo and name it `baserom.dol`. -2. Obtain a copy of the MWCC PowerPC compiler (version 2.7 to be exact). See below for a link to our Discord server which has the CodeWarrior compilers pinned in the #tp-decomp channel. -3. Replace `tools/mwcc_compiler/2.7/mwcceppc.exe` with the custom one also pinned in the #tp-decomp channel. -4. Run `make` at the root of the repo. - -## Dump Assets - -1. Place a vanilla copy of the NTSC-U version at the root of the folder and call it `gz2e01.iso`. -2. Make the game directory. +1. Clone down project ```bash -mkdir game +$ git clone https://github.com/zeldaret/tp ``` -3. Run make assets. +2. Setup compiler directory ```bash -make assets -```` +$ mkdir -p tools/mwcc_compiler/ +``` + +3. Download `GC_COMPILERS.zip` from the [Discord](https://discord.gg/Nshw5pHS4h) server. See the pins in the `#tp-decomp` channel under the Twilight Princess group. + +4. Extract `GC_COMPILERS.zip` into the previously created `mwcc_compiler` directory + +```bash +$ unzip GC_COMPILERS.zip -d tools/mwcc_compiler/ +``` + +5. Place a copy of NTSC-U GCN Twilight Princess in the root directory and call it `gz2e01.iso` (find this on your own) + +6. Setup the project + +```bash +$ ./tp setup +``` + +Building The Game +----- + +1. To build a playable game, complete the [Project Setup](#project-setup) steps, then run + +```bash +$ make game +``` + +The completed build is under `build/dolzel2/game/sys/main.dol` + +Build DOL + +```bash +$ make +``` + +Build RELs + +```bash +$ make rels +``` + +The completed RELs will be under `build/dolzel2/rel` + +Extract Game Assets + +```bash +$ make assets +``` + +Create Expected Directory + +1. [Build the DOL](#build-dol) +2. Run: + +```bash +$ ./tp expected +``` -## Contributions +Clean Directories +----- -All contributions are welcome. This is a group effort, and even small contributions can make a difference. Some tasks also don't require much knowledge to get started. +Clean RELs -Most discussions happen on our [Discord Server](https://discord.zelda64.dev/), where you are welcome to ask if you need help getting started, or if you have any questions regarding this project and other decompilation projects. \ No newline at end of file +```bash +$ make clean_rels +``` + +Clean Game Directory + +```bash +$ make clean_game +``` + +Clean Build Directory + +```bash +$ make clean_all +``` + +Clean everything + +```bash +$ make clean +``` + + +Project Overview +================= +``` +tp/ +├── .github # Github actions for this project. +├── asm # The assembly for unmatched functions. +├── defs # Python modules used by dol2asm. +├── docs # Notes and documentation about this project. +├── include # Header files used by this project. +├── libs # Source code for the libraries based on the symbol map. +├── rel # Source code for the game RELs. +├── src # Source code for the main game. +├── tools # Various tools to support the project. +├── .clang-format # Clang format file. +├── .gitignore # Files/folders to ignore changes to when making commits. +├── Doxyfile # Doxygen configuration file. +├── Makefile # Makefile for the project containing various targets. +├── README.md # The file you're currently reading. +├── diff.py # Python script to diff two functions. +├── diff_settings.py # Settings for the diff.py script. +├── dolzel2.sha1 # SHA1 of the dol. +├── include_link.mk # Makefiles to include in the main Makefile. +├── obj_files.mk # Object files to include in the main Makefile. +└── tp # Bash script used to call the main tp python script in tools directory. +``` \ No newline at end of file diff --git a/docs/Contributing.md b/docs/Contributing.md new file mode 100644 index 00000000000..941ca655cc1 --- /dev/null +++ b/docs/Contributing.md @@ -0,0 +1,57 @@ +Contributing +================= +Ghidra Setup +----- +1. Install [Java 11 64-bit Runtime and Development Kit](https://www.oracle.com/java/technologies/javase/jdk11-archive-downloads.html). +2. Download Ghidra from [here](https://github.com/NationalSecurityAgency/ghidra/releases). +3. Request an account from Pheenoh#0001 on Discord (either ping him in one of the [Discord](https://discord.gg/Nshw5pHS4h) channeld or DM directly). +4. Login using the following server info: + +``` +Server: ghidra.tpgz.io +Port: 13100 +Username: +Password: +``` + +After logging in you will see a list of all the Twilight Princess DOLs for each version. + +![](./ghidra.png) + +Getting Started +----- +To begin, select a file to begin work on. You can check what files are available on [Trello](https://trello.com/b/Y04SAxbw/twilight-princess-decompilation). + +This project has been automatically generated using `dol2asm`. This process generates blank headers for every source file, default includes, necessary types to be used within the file, forward references, and external references of data / functions used within the file's functions. Additionally, all data and functions have been split and generated with default types. + +When decompiling a file, you will need to manually fix structs/classes, function return types, data types, and data definitions. Parameters are already setup for any function with a mangled name. Some functions / data may need to be changed to be auto-generated by the compiler, such as by inlining functions or making data in-function static. + +The TP Nvidia Shield Debug Rom contains extra useful info, as it was compiled with less aggressive function inlining, and also contains debug tools / debug strings. While the primary focus currently is on the NTSC-U GameCube version, it is highly recommended to reference the Debug rom for help / documentation. + +### diff.py +`diff.py` is the tool we use to fix matching errors. You can invoke `diff.py` from command line like: + `python diff.py -mwo mangled_symbol_name` +- `-m` will run make before opening diff. +- `-w` will automatically update the diff when you save your changes. +- `-o` will diff only the necessary `.o` file, which is recommended for this project. +- You can find the mangled symbol name in the function info comment. (Ex. `onEventBit__11dSv_event_cFUs`) + +NOTE: You must run `./tp expected` with an OK build before diffing to have a correct binary to compare to. + +You can install the requirements for `diff.py` by running `pip install -r tools/requirements.txt`. + +### tp.py +`tp.py` is a multi-purpose tool that can be used for things such as checking SHA1 checksums, current decompilation progress, and removing unused assembly files. +You can invoke `tp.py` from command line either directly in the `tools/` folder, or by running the `tp` bash script. +Useful commands: +- `check` - Compares the SHA1 checksums of your current build to the ones of the expected build. Add `--rels` to check rel checksums. +- `expected` - Copies your current build folder to the expected build folder. +- `progress` - Calculates and displays the decompilation progress of the main dol. Add `--rels` to calculate rel progress. +- `remove-unused-asm` - Finds and removes all assembly files that are no longer being used. +- `pull-request` - Verifies that everything is OK before you set up a pull request. + +### decomp.me +[decomp.me](https://decomp.me/) is a very useful website for sharing individual functions with other contributers. +To use decomp.me with TP, select the `GameCube / Wii` platform and `Twilight Princess` compiler preset. Enter your function's mangled name under `Function name`, and copy / paste the function assembly from the project into the `Target assembly` box. Provide any necessary context within the provided box, then create your scratch. + +From here you can use the built-in editor to write your function code. Make sure to save the scratch when you are done, then you can copy and share the URL to receive help matching the function. diff --git a/docs/ghidra.png b/docs/ghidra.png new file mode 100644 index 0000000000000000000000000000000000000000..460c6d4ea90697f386a9b092e884c8b39226a321 GIT binary patch literal 14827 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%0DbOVa!|YtCx}lV8G&iFpFe+o{P^+LFO3AH zpZ-|Lcj<5O^m7q<6_vZqNEfa5)$(M{n}9RJQy9q^Xg#% zxQK_3lbf6SufHB~#Js2Fm(9}vMnlp_BtTlkSUeuDo5e2fn7+R;J?d6Wq{&kL4$qhw z3u4P&wF1A=33(t07obBaWRO+E@TfyWFJR)83;!Ud$u(${Bhv`SIqe7VnK4a4{5M0|RygPCzM8bDTb42UWs=yxoGb zV!0ZLYl#mS+YJ2FYr)(ZS$n!S5=y~diADpqTn8phKu`wRk}~4;NPz0of;!sm=_ILi zTO9_-%b?EhbUw}B&A|ci7han;8+QWs03rYcz*9Lzl!}aOa&{I7bA>=ykYs0XE+THo zfQIq^_W16BL8;S`iL(I*$%~(Y>iMu%Q!qt0i+9s8_AbBx(*wB^m9EyNrkTCF(YK!^ zegdBKhYuhbcK1f?EzHb1(eAO1E5q_0`ti4pLXlW5ejoU>EGhXi!*K`_Bk^ zJovh>A{g!l{XSy+B^@zOFLC6QJNR`l2Q&}VgM0Dlr6GFUX`pw5v5fGbPq!Wte)a`3 zXTVjxOWAfi5V6^+k9hn=O#lor5!*hpgE=_dFIBXI4HoFPYT3oWk*7z?g@CYda&UxL z{tTxD>g76s&*_ zc7nSFMgC-;De|6f*~#Q}{|Am!Z}UDXV0Vaz3%f&$