diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..ab144e57 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,36 @@ +name: Build + +on: + push: + pull_request: + +jobs: + build: + container: ghcr.io/zeldaret/ph-build:main + + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + version: [eur, usa] + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Prepare + run: | + mv /extract/* ./extract + + - name: Build + run: | + python tools/configure.py ${{ matrix.version }} -w wibo --compiler /mwccarm --no-extract + ninja arm9 report check + + - name: Upload report + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.version }}_report + path: build/${{ matrix.version }}/report.json diff --git a/.gitignore b/.gitignore index b510ca5d..6ee4c378 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ ph_*/ *.sav *.xMAP objdiff.json +/objdiff-cli +/objdiff-cli.exe /dsd /dsd.exe /dsd.pdb diff --git a/INSTALL.md b/INSTALL.md index 91c4a96c..525f22f6 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -24,23 +24,19 @@ python -m pip install -r tools/requirements.txt ```shell pre-commit install ``` -5. Run the setup script: -```shell -python tools/setup.py -``` -6. Run the Ninja configure script: +5. Run the Ninja configure script: ```shell python tools/configure.py ``` +6. Put one or more base ROMs in the [`/extract/`](/extract/README.md) directory of this repository. + +Now you can run `ninja` to build a ROM for the chosen version. > [!IMPORTANT] > Rerun `configure.py` often to ensure that all C/C++ code gets compiled. > [!NOTE] > For Linux users: Wibo is used by default. If you want to use Wine instead, run `configure.py` with `-w `. -7. Put one or more base ROMs in the [`/extract/`](/extract/README.md) directory of this repository. - -Now you can run `ninja` to build a ROM for the chosen version. ### Matching the base ROM diff --git a/config/eur/arm9/overlays/ov000/relocs.txt b/config/eur/arm9/overlays/ov000/relocs.txt index 6ec33322..5513c2e0 100644 --- a/config/eur/arm9/overlays/ov000/relocs.txt +++ b/config/eur/arm9/overlays/ov000/relocs.txt @@ -6526,7 +6526,7 @@ from:0x020b8628 kind:load to:0x020dc7f8 module:overlay(0) from:0x020b8644 kind:arm_call to:0x0202e9dc module:main from:0x020b8650 kind:arm_call_thumb to:0x02107810 module:overlay(4) from:0x020b8664 kind:load to:0x027e0fe0 module:dtcm -from:0x020b8668 kind:load to:0x020e667c module:overlay(0) +from:0x020b8668 kind:load to:0x020e6674 add:8 module:overlay(0) from:0x020b867c kind:arm_call_thumb to:0x02107994 module:overlay(4) from:0x020b8684 kind:arm_call to:0x0202ea0c module:main from:0x020b8698 kind:arm_call_thumb to:0x02107994 module:overlay(4) @@ -7436,7 +7436,7 @@ from:0x020c0b5c kind:load to:0x02050f54 module:main from:0x020c0b7c kind:arm_call to:0x0202e9dc module:main from:0x020c0b88 kind:arm_call_thumb to:0x02107810 module:overlay(4) from:0x020c0b9c kind:load to:0x027e0fe0 module:dtcm -from:0x020c0ba0 kind:load to:0x020e70a0 module:overlay(0) +from:0x020c0ba0 kind:load to:0x020e7098 add:8 module:overlay(0) from:0x020c0bb4 kind:arm_call_thumb to:0x02107994 module:overlay(4) from:0x020c0bbc kind:arm_call to:0x0202ea0c module:main from:0x020c0bd0 kind:arm_call_thumb to:0x02107994 module:overlay(4) diff --git a/config/eur/arm9/overlays/ov000/symbols.txt b/config/eur/arm9/overlays/ov000/symbols.txt index d2968311..07ca6e05 100644 --- a/config/eur/arm9/overlays/ov000/symbols.txt +++ b/config/eur/arm9/overlays/ov000/symbols.txt @@ -3955,11 +3955,11 @@ __sinit_ov000_020e11d8 kind:function(arm,size=0x34) addr:0x20e11d8 __sinit_ov000_020e120c kind:function(arm,size=0x44) addr:0x20e120c __sinit_ov000_020e1250 kind:function(arm,size=0x40) addr:0x20e1250 __sinit_ov000_020e1290 kind:function(arm,size=0x48) addr:0x20e1290 -__sinit_ActorNavi.cpp kind:function(arm,size=0x64) addr:0x20e12d8 -__sinit_ActorForceNavi.cpp kind:function(arm,size=0x40) addr:0x20e133c +__sinit_ActorNavi.cpp kind:function(arm,size=0x64) addr:0x20e12d8 local +__sinit_ActorForceNavi.cpp kind:function(arm,size=0x40) addr:0x20e133c local __sinit_ov000_020e137c kind:function(arm,size=0x798) addr:0x20e137c -__sinit_ActorActionObject.cpp kind:function(arm,size=0x40) addr:0x20e1b14 -__sinit_ActorWisdomNavi.cpp kind:function(arm,size=0x40) addr:0x20e1b54 +__sinit_ActorActionObject.cpp kind:function(arm,size=0x40) addr:0x20e1b14 local +__sinit_ActorWisdomNavi.cpp kind:function(arm,size=0x40) addr:0x20e1b54 local __sinit_ov000_020e1b94 kind:function(arm,size=0x40) addr:0x20e1b94 __sinit_ov000_020e1bd4 kind:function(arm,size=0x40) addr:0x20e1bd4 __sinit_ov000_020e1c14 kind:function(arm,size=0x40) addr:0x20e1c14 @@ -4768,7 +4768,7 @@ data_ov000_020e59fc kind:data(any) addr:0x20e59fc data_ov000_020e5a0c kind:data(any) addr:0x20e5a0c data_ov000_020e5a20 kind:data(any) addr:0x20e5a20 data_ov000_020e5a34 kind:data(any) addr:0x20e5a34 -_ZTV13LinkStateItem kind:data(any) addr:0x20e5a48 +_ZTV13LinkStateItem kind:data(any) addr:0x20e5a40 data_ov000_020e5a8c kind:data(any) addr:0x20e5a8c data_ov000_020e5a90 kind:data(any) addr:0x20e5a90 data_ov000_020e5a94 kind:data(any) addr:0x20e5a94 @@ -4958,7 +4958,7 @@ data_ov000_020e6530 kind:data(any) addr:0x20e6530 data_ov000_020e6534 kind:data(any) addr:0x20e6534 _ZTV9ActorNavi kind:data(any) addr:0x20e6560 _ZTV24UnkStruct_ov000_020b8110 kind:data(any) addr:0x20e6658 -_ZTV14ActorForceNavi kind:data(any) addr:0x20e667c +_ZTV14ActorForceNavi kind:data(any) addr:0x20e6674 data_ov000_020e676c kind:data(any) addr:0x20e676c data_ov000_020e6770 kind:data(any) addr:0x20e6770 data_ov000_020e6774 kind:data(any) addr:0x20e6774 @@ -5057,7 +5057,7 @@ data_ov000_020e7014 kind:data(any) addr:0x20e7014 data_ov000_020e7018 kind:data(any) addr:0x20e7018 data_ov000_020e701c kind:data(any) addr:0x20e701c data_ov000_020e7048 kind:data(any) addr:0x20e7048 -_ZTV15ActorWisdomNavi kind:data(any) addr:0x20e70a0 +_ZTV15ActorWisdomNavi kind:data(any) addr:0x20e7098 data_ov000_020e7198 kind:data(any) addr:0x20e7198 data_ov000_020e71a0 kind:data(any) addr:0x20e71a0 data_ov000_020e71a4 kind:data(any) addr:0x20e71a4 @@ -5070,14 +5070,14 @@ data_ov000_020e71bc kind:data(any) addr:0x20e71bc sShipTypes kind:data(any) addr:0x20e71c0 _ZTV17KillPickupsFilter kind:data(any) addr:0x20e71e8 _ZTV5Actor kind:data(any) addr:0x20e71f4 -data_ov000_020e72a8 kind:data(any) addr:0x20e72a8 -data_ov000_020e72ac kind:data(any) addr:0x20e72ac -data_ov000_020e72b0 kind:data(any) addr:0x20e72b0 -data_ov000_020e72b4 kind:data(any) addr:0x20e72b4 -data_ov000_020e72b8 kind:data(any) addr:0x20e72b8 -data_ov000_020e72bc kind:data(any) addr:0x20e72bc -data_ov000_020e72c0 kind:data(any) addr:0x20e72c0 -data_ov000_020e72c4 kind:data(any) addr:0x20e72c4 +@965 kind:data(any) addr:0x20e72a8 local +@964 kind:data(any) addr:0x20e72ac local +@963 kind:data(any) addr:0x20e72b0 local +@962 kind:data(any) addr:0x20e72b4 local +@961 kind:data(any) addr:0x20e72b8 local +@960 kind:data(any) addr:0x20e72bc local +@959 kind:data(any) addr:0x20e72c0 local +@958 kind:data(any) addr:0x20e72c4 local _ZTV11FilterActor kind:data(any) addr:0x20e72e8 data_ov000_020e72fc kind:data(any) addr:0x20e72fc data_ov000_020e7300 kind:data(any) addr:0x20e7300 @@ -5879,7 +5879,7 @@ data_ov000_020ee198 kind:bss addr:0x20ee198 _ZN9ActorNavi5gTypeE kind:bss addr:0x20ee1ac @419 kind:bss addr:0x20ee1c0 local data_ov000_020ee1cc kind:bss addr:0x20ee1cc -@405 kind:bss addr:0x20ee1d8 local +@403 kind:bss addr:0x20ee1d8 local _ZN14ActorForceNavi5gTypeE kind:bss addr:0x20ee1e4 data_ov000_020ee1f8 kind:bss addr:0x20ee1f8 data_ov000_020ee1fc kind:bss addr:0x20ee1fc @@ -5943,7 +5943,7 @@ data_ov000_020ee548 kind:bss addr:0x20ee548 _ZN17ActorActionObject5gTypeE kind:bss addr:0x20ee568 gPlayerAnimHandler kind:bss addr:0x20ee57c data_ov000_020ee588 kind:bss addr:0x20ee588 -@405 kind:bss addr:0x20ee590 local +@403 kind:bss addr:0x20ee590 local _ZN15ActorWisdomNavi5gTypeE kind:bss addr:0x20ee59c data_ov000_020ee5b0 kind:bss addr:0x20ee5b0 data_ov000_020ee5bc kind:bss addr:0x20ee5bc diff --git a/config/eur/arm9/overlays/ov004/relocs.txt b/config/eur/arm9/overlays/ov004/relocs.txt index ce85e811..d277363f 100644 --- a/config/eur/arm9/overlays/ov004/relocs.txt +++ b/config/eur/arm9/overlays/ov004/relocs.txt @@ -1960,7 +1960,7 @@ from:0x02109a4c kind:thumb_call_arm to:0x020d0530 module:overlay(0) from:0x02109a52 kind:thumb_call to:0x021099b0 module:overlay(4) from:0x02109a5e kind:thumb_call to:0x0210add4 module:overlay(4) from:0x02109aba kind:thumb_call_arm to:0x0204f614 module:main -from:0x02109ad8 kind:load to:0x020e5a48 add:8 module:overlay(0) +from:0x02109ad8 kind:load to:0x020e5a40 add:8 module:overlay(0) from:0x02109adc kind:load to:0x021079d5 module:overlay(4) from:0x02109ae0 kind:load to:0x02057200 module:main from:0x02109ae4 kind:load to:0x020b7d74 module:overlay(0) diff --git a/config/usa/arm9/overlays/ov000/relocs.txt b/config/usa/arm9/overlays/ov000/relocs.txt index eb606487..5ae92103 100644 --- a/config/usa/arm9/overlays/ov000/relocs.txt +++ b/config/usa/arm9/overlays/ov000/relocs.txt @@ -6526,7 +6526,7 @@ from:0x020b85c8 kind:load to:0x020dc798 module:overlay(0) from:0x020b85e4 kind:arm_call to:0x0202e9d8 module:main from:0x020b85f0 kind:arm_call_thumb to:0x02107790 module:overlay(4) from:0x020b8604 kind:load to:0x027e0fe0 module:dtcm -from:0x020b8608 kind:load to:0x020e661c module:overlay(0) +from:0x020b8608 kind:load to:0x020e6614 add:8 module:overlay(0) from:0x020b861c kind:arm_call_thumb to:0x02107914 module:overlay(4) from:0x020b8624 kind:arm_call to:0x0202ea08 module:main from:0x020b8638 kind:arm_call_thumb to:0x02107914 module:overlay(4) @@ -7436,7 +7436,7 @@ from:0x020c0afc kind:load to:0x02050f10 module:main from:0x020c0b1c kind:arm_call to:0x0202e9d8 module:main from:0x020c0b28 kind:arm_call_thumb to:0x02107790 module:overlay(4) from:0x020c0b3c kind:load to:0x027e0fe0 module:dtcm -from:0x020c0b40 kind:load to:0x020e7040 module:overlay(0) +from:0x020c0b40 kind:load to:0x020e7038 add:8 module:overlay(0) from:0x020c0b54 kind:arm_call_thumb to:0x02107914 module:overlay(4) from:0x020c0b5c kind:arm_call to:0x0202ea08 module:main from:0x020c0b70 kind:arm_call_thumb to:0x02107914 module:overlay(4) diff --git a/config/usa/arm9/overlays/ov000/symbols.txt b/config/usa/arm9/overlays/ov000/symbols.txt index 82bfe5cf..84b9f721 100644 --- a/config/usa/arm9/overlays/ov000/symbols.txt +++ b/config/usa/arm9/overlays/ov000/symbols.txt @@ -3955,11 +3955,11 @@ __sinit_ov000_020e11d8 kind:function(arm,size=0x34) addr:0x20e1178 __sinit_ov000_020e120c kind:function(arm,size=0x44) addr:0x20e11ac __sinit_ov000_020e1250 kind:function(arm,size=0x40) addr:0x20e11f0 __sinit_ov000_020e1290 kind:function(arm,size=0x48) addr:0x20e1230 -__sinit_ActorNavi.cpp kind:function(arm,size=0x64) addr:0x20e1278 -__sinit_ActorForceNavi.cpp kind:function(arm,size=0x40) addr:0x20e12dc +__sinit_ActorNavi.cpp kind:function(arm,size=0x64) addr:0x20e1278 local +__sinit_ActorForceNavi.cpp kind:function(arm,size=0x40) addr:0x20e12dc local __sinit_ov000_020e137c kind:function(arm,size=0x798) addr:0x20e131c -__sinit_ActorActionObject.cpp kind:function(arm,size=0x40) addr:0x20e1ab4 -__sinit_ActorWisdomNavi.cpp kind:function(arm,size=0x40) addr:0x20e1af4 +__sinit_ActorActionObject.cpp kind:function(arm,size=0x40) addr:0x20e1ab4 local +__sinit_ActorWisdomNavi.cpp kind:function(arm,size=0x40) addr:0x20e1af4 local __sinit_ov000_020e1b94 kind:function(arm,size=0x40) addr:0x20e1b34 __sinit_ov000_020e1bd4 kind:function(arm,size=0x40) addr:0x20e1b74 __sinit_ov000_020e1c14 kind:function(arm,size=0x40) addr:0x20e1bb4 @@ -4768,7 +4768,7 @@ data_ov000_020e59fc kind:data(any) addr:0x20e599c data_ov000_020e5a0c kind:data(any) addr:0x20e59ac data_ov000_020e5a20 kind:data(any) addr:0x20e59c0 data_ov000_020e5a34 kind:data(any) addr:0x20e59d4 -_ZTV13LinkStateItem kind:data(any) addr:0x20e59e8 +_ZTV13LinkStateItem kind:data(any) addr:0x20e59e0 data_ov000_020e5a8c kind:data(any) addr:0x20e5a2c data_ov000_020e5a90 kind:data(any) addr:0x20e5a30 data_ov000_020e5a94 kind:data(any) addr:0x20e5a34 @@ -4958,7 +4958,7 @@ data_ov000_020e6530 kind:data(any) addr:0x20e64d0 data_ov000_020e6534 kind:data(any) addr:0x20e64d4 _ZTV9ActorNavi kind:data(any) addr:0x20e6500 _ZTV24UnkStruct_ov000_020b8110 kind:data(any) addr:0x20e65f8 -_ZTV14ActorForceNavi kind:data(any) addr:0x20e661c +_ZTV14ActorForceNavi kind:data(any) addr:0x20e6614 data_ov000_020e676c kind:data(any) addr:0x20e670c data_ov000_020e6770 kind:data(any) addr:0x20e6710 data_ov000_020e6774 kind:data(any) addr:0x20e6714 @@ -5057,7 +5057,7 @@ data_ov000_020e7014 kind:data(any) addr:0x20e6fb4 data_ov000_020e7018 kind:data(any) addr:0x20e6fb8 data_ov000_020e701c kind:data(any) addr:0x20e6fbc data_ov000_020e7048 kind:data(any) addr:0x20e6fe8 -_ZTV15ActorWisdomNavi kind:data(any) addr:0x20e7040 +_ZTV15ActorWisdomNavi kind:data(any) addr:0x20e7038 data_ov000_020e7198 kind:data(any) addr:0x20e7138 data_ov000_020e71a0 kind:data(any) addr:0x20e7140 data_ov000_020e71a4 kind:data(any) addr:0x20e7144 @@ -5070,14 +5070,14 @@ data_ov000_020e71bc kind:data(any) addr:0x20e715c sShipTypes kind:data(any) addr:0x20e7160 _ZTV17KillPickupsFilter kind:data(any) addr:0x20e7188 _ZTV5Actor kind:data(any) addr:0x20e7194 -data_ov000_020e72a8 kind:data(any) addr:0x20e7248 -data_ov000_020e72ac kind:data(any) addr:0x20e724c -data_ov000_020e72b0 kind:data(any) addr:0x20e7250 -data_ov000_020e72b4 kind:data(any) addr:0x20e7254 -data_ov000_020e72b8 kind:data(any) addr:0x20e7258 -data_ov000_020e72bc kind:data(any) addr:0x20e725c -data_ov000_020e72c0 kind:data(any) addr:0x20e7260 -data_ov000_020e72c4 kind:data(any) addr:0x20e7264 +@965 kind:data(any) addr:0x20e7248 local +@964 kind:data(any) addr:0x20e724c local +@963 kind:data(any) addr:0x20e7250 local +@962 kind:data(any) addr:0x20e7254 local +@961 kind:data(any) addr:0x20e7258 local +@960 kind:data(any) addr:0x20e725c local +@959 kind:data(any) addr:0x20e7260 local +@958 kind:data(any) addr:0x20e7264 local _ZTV11FilterActor kind:data(any) addr:0x20e7288 data_ov000_020e72fc kind:data(any) addr:0x20e729c data_ov000_020e7300 kind:data(any) addr:0x20e72a0 @@ -5879,7 +5879,7 @@ data_ov000_020ee198 kind:bss addr:0x20ee138 _ZN9ActorNavi5gTypeE kind:bss addr:0x20ee14c @419 kind:bss addr:0x20ee160 local data_ov000_020ee1cc kind:bss addr:0x20ee16c -@405 kind:bss addr:0x20ee178 local +@403 kind:bss addr:0x20ee178 local _ZN14ActorForceNavi5gTypeE kind:bss addr:0x20ee184 data_ov000_020ee1f8 kind:bss addr:0x20ee198 data_ov000_020ee1fc kind:bss addr:0x20ee19c @@ -5943,7 +5943,7 @@ data_ov000_020ee548 kind:bss addr:0x20ee4e8 _ZN17ActorActionObject5gTypeE kind:bss addr:0x20ee508 gPlayerAnimHandler kind:bss addr:0x20ee51c data_ov000_020ee588 kind:bss addr:0x20ee528 -@405 kind:bss addr:0x20ee530 local +@403 kind:bss addr:0x20ee530 local _ZN15ActorWisdomNavi5gTypeE kind:bss addr:0x20ee53c data_ov000_020ee5b0 kind:bss addr:0x20ee550 data_ov000_020ee5bc kind:bss addr:0x20ee55c diff --git a/config/usa/arm9/overlays/ov004/relocs.txt b/config/usa/arm9/overlays/ov004/relocs.txt index 76d78e03..13a0063b 100644 --- a/config/usa/arm9/overlays/ov004/relocs.txt +++ b/config/usa/arm9/overlays/ov004/relocs.txt @@ -1960,7 +1960,7 @@ from:0x021099cc kind:thumb_call_arm to:0x020d04d0 module:overlay(0) from:0x021099d2 kind:thumb_call to:0x02109930 module:overlay(4) from:0x021099de kind:thumb_call to:0x0210ad54 module:overlay(4) from:0x02109a3a kind:thumb_call_arm to:0x0204f5d0 module:main -from:0x02109a58 kind:load to:0x020e59e8 add:8 module:overlay(0) +from:0x02109a58 kind:load to:0x020e59e0 add:8 module:overlay(0) from:0x02109a5c kind:load to:0x02107955 module:overlay(4) from:0x02109a60 kind:load to:0x020571b8 module:main from:0x02109a64 kind:load to:0x020b7d14 module:overlay(0) diff --git a/tools/configure.py b/tools/configure.py index 45d9824a..3ae6286c 100755 --- a/tools/configure.py +++ b/tools/configure.py @@ -2,21 +2,30 @@ import os from pathlib import Path -import platform import argparse import sys import ninja_syntax +from get_platform import get_platform + + +DEFAULT_WIBO_PATH = "./wibo" parser = argparse.ArgumentParser(description="Generates build.ninja") -parser.add_argument('-w', type=str, default="./wibo", dest="wine", required=False, help="Path to Wine/Wibo (linux only)") +parser.add_argument('-w', type=str, default=DEFAULT_WIBO_PATH, dest="wine", required=False, help="Path to Wine/Wibo (linux only)") +parser.add_argument("--compiler", type=Path, required=False, help="Path to pre-installed compiler root directory") +parser.add_argument("--no-extract", action="store_true", help="Skip extract step") +parser.add_argument("--dsd", type=Path, required=False, help="Path to pre-installed dsd CLI") parser.add_argument('version', help='Game version') args = parser.parse_args() # Config GAME = "ph" +DSD_VERSION = 'v0.6.0' +WIBO_VERSION = '0.6.16' +OBJDIFF_VERSION = 'v2.7.1' MWCC_VERSION = "2.0/sp1p5" DECOMP_ME_COMPILER = "mwcc_30_131" CC_FLAGS = " ".join([ @@ -65,7 +74,8 @@ src_path = root_path / "src" libs_path = root_path / "libs" extract_path = root_path / "extract" tools_path = root_path / "tools" -mwcc_path = tools_path / "mwccarm" / MWCC_VERSION +mwcc_root = args.compiler or tools_path / "mwccarm" +mwcc_path = mwcc_root / MWCC_VERSION # Includes @@ -80,38 +90,96 @@ CC_INCLUDES = " ".join(f"-i {include}" for include in includes) # Platform info -EXE = "" -WINE = "" -system = platform.system() -if system == "Windows": - system = "windows" - EXE = ".exe" -elif system == "Linux": - system = "linux" - WINE = args.wine -else: - print(f"Unknown system '{system}'") +platform = get_platform() +if platform is None: exit(1) -match platform.machine().lower(): - case "amd64" | "x86_64": machine = "x86_64" - case machine: - print(f"Unknown machine: {machine}") - exit(1) - - +EXE = platform.exe +WINE = args.wine if platform.system != "windows" else "" +DSD = str(args.dsd or os.path.join('.', str(root_path / f"dsd{EXE}"))) +OBJDIFF = os.path.join('.', str(root_path / f"objdiff-cli{EXE}")) +CC = os.path.join('.', str(mwcc_path / "mwccarm.exe")) +LD = os.path.join('.', str(mwcc_path / "mwldarm.exe")) PYTHON = sys.executable +class Project: + def __init__(self, game_version: str): + self.game_version = game_version + '''Version of the game''' + self.game_config = config_path / game_version + '''Root directory for dsd configs''' + + if not self.game_config.is_dir(): + print(f"Version '{game_version}' not recognized") + exit(1) + + self.game_build = build_path / game_version + '''Path to build directory''' + self.game_extract = extract_path / game_version + '''Path to extract directory''' + + self.delinks_files = get_config_files(self.game_config, "delinks.txt") + '''Paths to every delinks.txt file''' + self.relocs_files = get_config_files(self.game_config, "relocs.txt") + '''Paths to every relocs.txt file''' + self.symbols_files = get_config_files(self.game_config, "symbols.txt") + '''Paths to every symbols.txt file''' + + def dsd_configs(self) -> list[str]: + return self.delinks_files + self.relocs_files + self.symbols_files + + def arm9_config_yaml(self) -> Path: + return self.game_config / "arm9" / "config.yaml" + + def baserom(self) -> Path: + return extract_path / f'baserom_{GAME}_{self.game_version}.nds' + + def build_rom(self) -> str: + return f"{GAME}_{self.game_version}.nds" + + def baserom_config(self) -> Path: + return self.game_extract / 'config.yaml' + + def build_rom_config(self) -> Path: + return self.game_build / "build" / "rom_config.yaml" + + def source_object_files(self) -> list[str]: + return [ + str(self.game_build / source_file.with_suffix(".o")) + for source_file in get_c_cpp_files([src_path, libs_path]) + ] + + def arm9_lcf(self) -> Path: + return self.game_build / "linker_script.lcf" + + def arm9_objects_txt(self) -> Path: + return self.game_build / "objects.txt" + + def arm9_delink_yaml(self) -> Path: + return self.game_build / "delinks" / "delink.yaml" + + def arm9_o(self) -> Path: + return self.game_build / "arm9.o" + + def arm9_delinks(self) -> Path: + return self.game_build / "delinks" + + def objdiff_report(self) -> Path: + return self.game_build / "report.json" + + def main(): - game_version: str = args.version - game_config = config_path / game_version - if not game_config.is_dir(): - print(f"Version '{game_version}' not recognized") - return + project = Project(args.version) with build_ninja_path.open("w") as file: n = ninja_syntax.Writer(file) + n.rule( + name="download_tool", + command=f'{PYTHON} tools/download_tool.py $tool $tag --path $path' + ) + n.newline() + if arm7_bios_path.is_file(): n.variable("arm7_bios_flag", f"--arm7-bios {arm7_bios_path.relative_to(root_path)}") else: @@ -120,20 +188,20 @@ def main(): n.rule( name="extract", - command="./dsd rom extract --rom $in --output-path $output_path $arm7_bios_flag" + command=f"{DSD} rom extract --rom $in --output-path $output_path $arm7_bios_flag" ) n.newline() n.rule( name="delink", - command="./dsd delink --config-path $config_path" + command=f"{DSD} delink --config-path $config_path" ) n.newline() # -MMD excludes all includes instead of just system includes for some reason, so use -MD instead. - mwcc_cmd = f'{WINE} "{mwcc_path}/mwccarm.exe" {CC_FLAGS} {CC_INCLUDES} $cc_flags -d $game_version -MD -c $in -o $basedir' - mwcc_implicit = [] - if system != "windows": + mwcc_cmd = f'{WINE} "{CC}" {CC_FLAGS} {CC_INCLUDES} $cc_flags -d $game_version -MD -c $in -o $basedir' + mwcc_implicit = [CC] + if platform.system != "windows": transform_dep = "tools/transform_dep.py" mwcc_cmd += f" && $python {transform_dep} $basefile.d $basefile.d" mwcc_implicit.append(transform_dep) @@ -146,31 +214,37 @@ def main(): n.rule( name="lcf", - command="./dsd lcf -c $config_path --lcf-file $lcf_file --objects-file $objects_file" + command=f"{DSD} lcf -c $config_path --lcf-file $lcf_file --objects-file $objects_file" ) n.newline() n.rule( name="mwld", - command=f'{WINE} "{mwcc_path}/mwldarm.exe" {LD_FLAGS} @$objects_file $lcf_file -o $out' + command=f'{WINE} "{LD}" {LD_FLAGS} @$objects_file $lcf_file -o $out' ) n.newline() n.rule( name="rom_config", - command="./dsd rom config --elf $in --config $config_path" + command=f"{DSD} rom config --elf $in --config $config_path" ) n.newline() n.rule( name="rom_build", - command="./dsd rom build --config $in --rom $out $arm7_bios_flag" + command=f"{DSD} rom build --config $in --rom $out $arm7_bios_flag" ) n.newline() n.rule( name="objdiff", - command=f"./dsd objdiff --config-path $config_path {DSD_OBJDIFF_ARGS}" + command=f"{DSD} objdiff --config-path $config_path {DSD_OBJDIFF_ARGS}" + ) + n.newline() + + n.rule( + name="objdiff_report", + command=f"{OBJDIFF} report generate -o $out" ) n.newline() @@ -180,70 +254,137 @@ def main(): ) n.newline() + n.rule( + name="check_modules", + command=f"{DSD} check modules --config-path $config_path --fail" + ) + n.newline() + + n.rule( + name="check_symbols", + command=f"{DSD} check symbols --config-path $config_path --elf-path $elf_path --fail" + ) + n.newline() + n.rule( name="sha1", command=f"{PYTHON} tools/sha1.py $in -c $sha1_file" ) n.newline() - game_build = build_path / game_version - game_extract = extract_path / game_version - - add_extract_build(n, game_extract, game_version) - add_delink_and_lcf_builds(n, game_config, game_build, game_extract) - add_mwcc_builds(n, game_version, game_build, mwcc_implicit) - add_mwld_and_rom_builds(n, game_build, game_config, game_version) + add_download_tool_builds(n) + add_extract_build(n, project) + add_delink_and_lcf_builds(n, project) + add_mwcc_builds(n, project, mwcc_implicit) + add_mwld_and_rom_builds(n, project) + add_check_builds(n, project) + add_objdiff_builds(n, project) -def add_extract_build(n: ninja_syntax.Writer, game_extract: Path, game_version: str): - rom_path = extract_path / f'baserom_{GAME}_{game_version}.nds' - rom_config = game_extract / 'config.yaml' +def add_download_tool_builds(n: ninja_syntax.Writer): + if args.dsd is None: + n.build( + rule="download_tool", + outputs=DSD, + variables={ + "tool": "dsd", + "tag": DSD_VERSION, + "path": DSD, + }, + ) + n.newline() + n.build( - inputs=str(rom_path), - rule="extract", - outputs=str(rom_config), + rule="download_tool", + outputs=OBJDIFF, variables={ - "output_path": str(game_extract) + "tool": "objdiff", + "tag": OBJDIFF_VERSION, + "path": OBJDIFF, } ) n.newline() + if args.compiler is None: + n.build( + rule="download_tool", + outputs=[CC, LD], + variables={ + "tool": "mwccarm", + "tag": "latest", + "path": tools_path, + }, + ) + n.newline() -def add_mwld_and_rom_builds(n: ninja_syntax.Writer, game_build: Path, game_config: Path, game_version: str): - source_object_files = [ - str(game_build / source_file.with_suffix(".o")) - for source_file in get_c_cpp_files([src_path, libs_path]) - ] - lcf_file = str(game_build / "linker_script.lcf") - objects_file = str(game_build / "objects.txt") - delink_file = str(game_build / "delinks" / "delink.yaml") - elf_file = str(game_build / "arm9.o") + if platform.system != "windows" and WINE == DEFAULT_WIBO_PATH: + n.build( + rule="download_tool", + outputs=WINE, + variables={ + "tool": "wibo", + "tag": WIBO_VERSION, + "path": WINE, + }, + ) + n.newline() + + +def add_extract_build(n: ninja_syntax.Writer, project: Project): + if not args.no_extract: + n.build( + inputs=str(project.baserom()), + implicit=DSD, + rule="extract", + outputs=str(project.baserom_config()), + variables={ + "output_path": str(project.game_extract) + } + ) + n.newline() + + +def add_mwld_and_rom_builds(n: ninja_syntax.Writer, project: Project): + lcf_file = str(project.arm9_lcf()) + objects_file = str(project.arm9_objects_txt()) + delink_file = str(project.arm9_delink_yaml()) + elf_file = str(project.arm9_o()) n.build( - inputs=source_object_files + [lcf_file, objects_file, delink_file], + inputs=project.source_object_files() + [lcf_file, objects_file, delink_file], + implicit=LD, rule="mwld", outputs=elf_file, variables={ - "target_dir": game_build, + "target_dir": project.game_build, "objects_file": objects_file, "lcf_file": lcf_file, } ) n.newline() - rom_config_file = str(game_build / "build" / "rom_config.yaml") n.build( inputs=elf_file, + rule="phony", + outputs="arm9", + ) + n.newline() + + rom_config_file = str(project.build_rom_config()) + n.build( + inputs=elf_file, + implicit=DSD, rule="rom_config", outputs=rom_config_file, variables={ - "config_path": game_config / "arm9" / "config.yaml", + "config_path": project.arm9_config_yaml(), } ) n.newline() - rom_file = f"{GAME}_{game_version}.nds" + rom_file = project.build_rom() n.build( - inputs=[rom_config_file], + inputs=rom_config_file, + implicit=DSD, rule="rom_build", outputs=rom_file, ) @@ -267,28 +408,28 @@ def add_mwld_and_rom_builds(n: ninja_syntax.Writer, game_build: Path, game_confi n.newline() -def add_mwcc_builds(n: ninja_syntax.Writer, game_version: str, game_build: Path, mwcc_implicit: list[Path]): +def add_mwcc_builds(n: ninja_syntax.Writer, project: Project, mwcc_implicit: list[Path]): for source_file in get_c_cpp_files([src_path, libs_path]): - src_obj_path = game_build / source_file + src_obj_path = project.game_build / source_file cc_flags = [] if is_cpp(source_file): cc_flags.append("-lang=c++") elif is_c(source_file): cc_flags.append("-lang=c") n.build( inputs=str(source_file), + implicit=mwcc_implicit, rule="mwcc", outputs=str(src_obj_path.with_suffix(".o")), variables={ - "game_version": game_version, + "game_version": project.game_version, "cc_flags": " ".join(cc_flags), "basedir": os.path.dirname(src_obj_path), "basefile": str(src_obj_path.with_suffix("")), }, - implicit=mwcc_implicit, ) n.newline() extension = source_file.suffix - ctx_file = str(game_build / source_file.with_suffix(f".ctx{extension}")) + ctx_file = str(project.game_build / source_file.with_suffix(f".ctx{extension}")) n.build( inputs=str(source_file), rule="m2ctx", @@ -314,19 +455,17 @@ def is_c(name: str): return Path(name).suffix in [".c"] -def add_delink_and_lcf_builds(n: ninja_syntax.Writer, game_config: Path, game_build: Path, game_extract: Path): +def add_delink_and_lcf_builds(n: ninja_syntax.Writer, project: Project): n.comment("Delink ELF binaries when any delinks.txt file is modified") - delinks_files = get_config_files(game_config, "delinks.txt") - relocs_files = get_config_files(game_config, "relocs.txt") - symbols_files = get_config_files(game_config, "symbols.txt") - rom_config = str(game_extract / 'config.yaml') - delinks_path = game_build / "delinks" + rom_config = str(project.baserom_config()) + delinks_path = project.arm9_delinks() n.build( - inputs=delinks_files + relocs_files + symbols_files + [rom_config], + inputs=project.dsd_configs() + [rom_config], + implicit=DSD, rule="delink", outputs=str(delinks_path / "delink.yaml"), variables={ - "config_path": game_config / "arm9" / "config.yaml", + "config_path": project.arm9_config_yaml(), } ) n.newline() @@ -338,26 +477,60 @@ def add_delink_and_lcf_builds(n: ninja_syntax.Writer, game_config: Path, game_bu ) n.newline() - lcf_file = game_build / "linker_script.lcf" - objects_file = game_build / "objects.txt" + lcf_file = project.arm9_lcf() + objects_file = project.arm9_objects_txt() n.build( - inputs=delinks_files + [str(rom_config)], + inputs=project.delinks_files + [str(rom_config)], + implicit=DSD, rule="lcf", outputs=[str(lcf_file), str(objects_file)], variables={ - "config_path": game_config / "arm9" / "config.yaml", + "config_path": project.arm9_config_yaml(), "lcf_file": lcf_file, "objects_file": objects_file, } ) n.newline() + +def add_check_builds(n: ninja_syntax.Writer, project: Project): n.build( - inputs=delinks_files + relocs_files + symbols_files, + inputs=str(project.arm9_o()), + rule="check_modules", + outputs="check_modules", + variables={ + "config_path": project.arm9_config_yaml(), + }, + ) + n.newline() + + n.build( + inputs=str(project.arm9_o()), + rule="check_symbols", + outputs="check_symbols", + variables={ + "config_path": project.arm9_config_yaml(), + "elf_path": project.arm9_o(), + }, + ) + n.newline() + + n.build( + inputs=["check_modules", "check_symbols"], + rule="phony", + outputs="check", + ) + n.newline() + + +def add_objdiff_builds(n: ninja_syntax.Writer, project: Project): + n.build( + inputs=project.dsd_configs(), + implicit=DSD, rule="objdiff", outputs="objdiff.json", variables={ - "config_path": game_config / "arm9" / "config.yaml", + "config_path": project.arm9_config_yaml(), } ) n.newline() @@ -369,8 +542,23 @@ def add_delink_and_lcf_builds(n: ninja_syntax.Writer, game_config: Path, game_bu ) n.newline() + n.build( + inputs=["objdiff.json"], + implicit=[OBJDIFF] + project.source_object_files(), + rule="objdiff_report", + outputs=str(project.objdiff_report()), + ) + n.newline() -def get_config_files(game_config: Path, name: str): + n.build( + inputs=str(project.objdiff_report()), + rule="phony", + outputs="report", + ) + n.newline() + + +def get_config_files(game_config: Path, name: str) -> list[str]: return [ f"{root}/{file}" for root, _, files in os.walk(game_config) diff --git a/tools/download_tool.py b/tools/download_tool.py new file mode 100644 index 00000000..755acd8f --- /dev/null +++ b/tools/download_tool.py @@ -0,0 +1,53 @@ +import argparse +from pathlib import Path +from get_platform import get_platform +import zipfile +import io +import requests +import stat + +root_path = Path(__file__).parent.parent + + +platform = get_platform() +if platform is None: + exit(1) + +parser = argparse.ArgumentParser() +parser.add_argument("tool") +parser.add_argument("tag") +parser.add_argument("--path", type=Path, required=True) +args = parser.parse_args() + + +def dsd_url(tag: str) -> str: + return f'https://github.com/AetiasHax/ds-decomp/releases/download/{tag}/dsd-{platform.system}-{platform.machine}{platform.exe}' + +def mwccarm_url(tag: str) -> str: + return 'http://decomp.aetias.com/files/mwccarm.zip' + +def wibo_url(tag: str) -> str: + return f'https://github.com/decompals/wibo/releases/download/{tag}/wibo' + +def objdiff_url(tag: str) -> str: + return f'https://github.com/encounter/objdiff/releases/download/{tag}/objdiff-cli-{platform.system}-{platform.machine}{platform.exe}' + +TOOLS = { + "dsd": dsd_url, + "mwccarm": mwccarm_url, + "wibo": wibo_url, + "objdiff": objdiff_url, +} + + +download_url = TOOLS[args.tool](args.tag) +print(f'\nDownloading {args.tool} {args.tag}...') +response = requests.get(download_url) +if download_url.endswith('.zip'): + zip_file = zipfile.ZipFile(io.BytesIO(response.content)) + zip_file.extractall(args.path) +else: + out_path: Path = args.path + with out_path.open('wb') as f: + f.write(response.content) + out_path.chmod(out_path.stat().st_mode | stat.S_IEXEC) diff --git a/tools/get_platform.py b/tools/get_platform.py new file mode 100644 index 00000000..a4fe40d8 --- /dev/null +++ b/tools/get_platform.py @@ -0,0 +1,31 @@ +import platform + + +class Platform: + def __init__(self, *, system: str, machine: str, exe: str): + self.system = system + '''Name of operating system: "windows" or "linux"''' + self.machine = machine + '''Name of machine architecture: "x86_64"''' + self.exe = exe + '''Executable file extension: ".exe" for Windows, "" otherwise''' + + +def get_platform() -> Platform | None: + exe = "" + system = platform.system() + if system == "Windows": + system = "windows" + exe = ".exe" + elif system == "Linux": + system = "linux" + else: + print(f"Unknown system '{system}'") + return None + match platform.machine().lower(): + case "amd64" | "x86_64": machine = "x86_64" + case machine: + print(f"Unknown machine: {machine}") + return None + + return Platform(system=system, machine=machine, exe=exe) diff --git a/tools/setup.py b/tools/setup.py deleted file mode 100755 index 8e5f9c0d..00000000 --- a/tools/setup.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python3 - -import requests -import zipfile -import io -from pathlib import Path -import platform -import stat - -DSD_VERSION = 'v0.5.0' -WIBO_VERSION = '0.6.16' - - -tools_path = Path(__file__).parent -root_path = tools_path.parent - - -EXE = "" -system = platform.system() -if system == "Windows": - system = "windows" - EXE = ".exe" -elif system == "Linux": - system = "linux" -else: - print(f"Unknown system '{system}'") - exit(1) -match platform.machine().lower(): - case 'amd64' | 'x86_64': machine = 'x86_64' - case machine: - print(f'Unknown machine: {machine}') - exit(1) - - -print('\nInstalling dsd...') -response = requests.get(f'https://github.com/AetiasHax/ds-decomp/releases/download/{DSD_VERSION}/dsd-{system}-{machine}{EXE}') -dsd_path = root_path / f'dsd{EXE}' -with open(dsd_path, 'wb') as f: - f.write(response.content) -dsd_path.chmod(dsd_path.stat().st_mode | stat.S_IEXEC) - - -print('\nInstalling toolchain...') -response = requests.get('http://decomp.aetias.com/files/mwccarm.zip') -zip_file = zipfile.ZipFile(io.BytesIO(response.content)) -zip_file.extractall(tools_path) - - -if system == "linux": - print('\nInstalling wibo...') - response = requests.get(f'https://github.com/decompals/wibo/releases/download/{WIBO_VERSION}/wibo') - wibo_path = root_path / 'wibo' - with open(wibo_path, 'wb') as f: - f.write(response.content) - wibo_path.chmod(wibo_path.stat().st_mode | stat.S_IEXEC) -