Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -258,11 +258,12 @@ jobs:
strategy:
fail-fast: false
matrix:
game: ["sa1", "sa2"]
platform: ["sdl", "sdl_win32", "sdl_psp", "win32", "ps2"]
os: ["ubuntu-24.04", "macos-15"]
if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork || github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.fork}}
runs-on: ${{ matrix.os }}
name: Build ${{ matrix.platform }} (${{ matrix.os }})
name: Build ${{ matrix.game }} ${{ matrix.platform }} (${{ matrix.os }})
steps:
- name: Checkout PR
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target'}}
Expand All @@ -273,6 +274,8 @@ jobs:
- name: Checkout
if: ${{ github.event_name == 'push' }}
uses: actions/checkout@master
- name: Fetch assets
run: curl -o baserom_${{ matrix.game }}.gba -u ${{ secrets.STORAGE_USERNAME }}:${{ secrets.STORAGE_PASSWORD }} ${{ secrets.STORAGE_BASE_URL }}/${{ matrix.game }}/baserom.gba

- name: Install tools (Linux)
if: ${{ matrix.os == 'ubuntu-24.04' }}
Expand Down Expand Up @@ -315,7 +318,7 @@ jobs:

- name: ${{ matrix.platform }}
run: |
make -j${nproc} ${{ matrix.platform }}
make -j${nproc} ${{ matrix.platform }} GAME_NAME=${{ matrix.game }}

test:
strategy:
Expand Down
13 changes: 7 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -541,13 +541,11 @@ else
endif


ifeq ($(PLATFORM),gba)
$(ROM): $(ELF) libagbsyscall
ifeq ($(PLATFORM),gba)
$(OBJCOPY) -O binary --pad-to 0x8400000 $< $@
$(FIX) $@ -p -t"$(TITLE)" -c$(GAME_CODE) -m$(MAKER_CODE) -r$(GAME_REVISION) --silent
else
$(ROM): $(ELF) libagbsyscall
ifeq ($(PLATFORM),win32)
else ifeq ($(PLATFORM),win32)
$(OBJCOPY) -O pei-x86-64 $< $@
else ifeq ($(PLATFORM),sdl)
cp $< $@
Expand All @@ -560,13 +558,13 @@ else ifeq ($(PLATFORM),sdl_psp)
@-rm -f $(BUILD_NAME).psp_strip.elf
else ifeq ($(PLATFORM),ps2)
@echo Creating $(ROM) from $(ELF)
@cp -r ps2/$(BUILD_NAME)/ntsc $(OBJ_DIR)/iso
@mkdir -p $(OBJ_DIR)/iso
@printf "BOOT2 = cdrom0:\\$(PS2_GAME_CODE);1\nVER = 1.00\nVMODE = NTSC" > $(OBJ_DIR)/iso/SYSTEM.CNF
@cp $< $(OBJ_DIR)/iso/$(PS2_GAME_CODE)
@mkisofs -o $(ROM) $(OBJ_DIR)/iso/
else
$(OBJCOPY) -O pei-x86-64 $< $@
endif
endif

# Build c sources, and ensure alignment
$(C_BUILDDIR)/%.o: $(C_SUBDIR)/%.c
Expand Down Expand Up @@ -678,6 +676,9 @@ bribasa:
sa1:
@$(MAKE) GAME_NAME=sa1

sa2:
@$(MAKE) GAME_NAME=sa2

$(TOOLDIRS): tool_libs
@$(MAKE) -C $@

Expand Down
78 changes: 56 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<h1 align="center">Sonic Advance 2</h1>
<h1 align="center">Sonic Advance Trilogy</h1>
<p align="center">
<img height="240" src=".github/media/titlescreen.png" alt="Sonic Advance 2 title screen">
</p>
Expand All @@ -12,9 +12,10 @@

> :warning: **This project is not completed and still under active development**

This is a work in progress matching decompilation of Sonic Advance 2
This is a work in progress matching decompilation of Sonic Advance 1, 2, [& eventually 3](https://github.com/SAT-R/sa3/)

It builds the following ROMs:
* [**sa1.gba**](https://datomatic.no-intro.org/index.php?page=show_record&s=23&n=0330) `sha1: eb00f101af23d728075ac2117e27ecd8a4b4c3e9` (Europe) (En,Ja,Fr,De,Es)
* [**sa2.gba**](https://datomatic.no-intro.org/index.php?page=show_record&s=23&n=0890) `sha1: 7bcd6a07af7c894746fa28073fe0c0e34408022d` (USA) (En,Ja,Fr,De,Es,It)
* [**sa2_europe.gba**](https://datomatic.no-intro.org/index.php?page=show_record&s=23&n=0900) `sha1: b0f64bdca097f2de8f05ac4c8caea2b80c5faeb1` (Europe) (En,Ja,Fr,De,Es,It)
* [**sa2_japan.gba**](https://datomatic.no-intro.org/index.php?page=show_record&s=23&n=0799) `sha1: dffd0188fc78154b42b401398a224ae0713edf23` (Japan) (En,Ja,Fr,De,Es,It)
Expand All @@ -28,7 +29,7 @@ It can also build:
* **sa2.ps2.iso** `make ps2` (PlayStation 2 port, requires [PS2DEV](https://github.com/ps2dev/ps2dev))
* **EBOOT.PBP** `make sdl_psp` (PlayStation Portable SDL port, requires [PSPDEV](https://github.com/pspdev/pspdev))

## Current state
## Current state (SA2)

- :tada: The build is 100% from C files with [~60 functions which currently don't match](./asm/non_matching)
- All assembly code extracted, disassembled, and decompiled by hand to their C equivilent
Expand All @@ -40,38 +41,71 @@ It can also build:

## Sub games

Within the Sonic Advance 2 rom there are some "Sub games" which we have begun to decompile as part of this project. These sub games are automatically compiled and bundled during the main build process, as they are necessary to create a matching binary.
Within the Sonic Advance Trilogy roms there are some "Sub games" which we have begun to decompile as part of this project. These sub games are automatically compiled and bundled during the main build process, as they are necessary to create a matching binary.

| Multiboot rom | Decompilation |
| --- | --- |
| [**mb_chao_garden.gba**](./chao_garden) | ![Decompilation Progress](https://img.shields.io/endpoint?url=https://sat-r.github.io/sa2/reports/progress-mb_chao_garden-shield.json) |
| [**collect_rings.gba**](./multi_boot/roms/collect_rings/) | ![Decompilation Progress](https://img.shields.io/endpoint?url=https://sat-r.github.io/sa2/reports/progress-collect_rings-shield.json) |
| Game | Multiboot rom | Decompilation |
| --- | --- | --- |
| SA2 | [**mb_chao_garden.gba**](./chao_garden) | ![Decompilation Progress](https://img.shields.io/endpoint?url=https://sat-r.github.io/sa2/reports/progress-mb_chao_garden-shield.json) |
| SA2 | [**collect_rings.gba**](./multi_boot/roms/collect_rings/) | ![Decompilation Progress](https://img.shields.io/endpoint?url=https://sat-r.github.io/sa2/reports/progress-collect_rings-shield.json) |


## Setting up the repo

Please see follow [these instructions](./INSTALL.md)

## Community

Join us on [discord](https://discord.gg/vZTvVH3gA9) to get started in helping out

## Screenshots


## Notes

- The [Kirby & The Amazing Mirror](https://github.com/jiangzhengwenjz/katam/) decompilation uses a very similar codebase, as it was written by the same dev team (Dimps)
- https://decomp.me is a great resource for helping to create matching functions
- `ldscript.txt` tells the linker the order which files should be linked
## Contributing

Join us on [discord](https://discord.gg/vZTvVH3gA9) to get started in helping out. Otherwise, we are always looking for people to document the code base, and there are [many non matching functions](./asm/non_matching) which need human input to resolve.

### Strict No LLM / No AI Policy

- No LLMs for issues.
- No LLMs for pull requests.

This project was created as a learning excerise, I (freshollie) like learning new things. Of course it also has real world outcomes (having the ability to play, mod, port this game), but we as maintainers are not interested in submissions from LLM origin.

We want humans to use this project to practice their skills. Reverse engineering, documenting, understanding, being part of the process. If you submit a pull request we want you to have accountability as well as knowledge of what you've done. We want you to develop on those skills and continue to contribute. If "the computer" did all the work for you, we are not interested.

Our policy is based off [Zig's](https://ziglang.org/code-of-conduct/)

### Layout
```
├── asm
│ ├── macros # Common macros used in asm files
│ └── non_matching # Non matching assembly pulled in the C compilation process
├── chao_garden # The tiny chao garden project (based on the SA2 version)
├── data # Raw data pulled into the rom via ASM files
├── graphics # Graphics related assets
├── include # Headers
├── libagbsyscall # The agbsyscall library included as part of the nintendo SDK
├── multi_boot # Any multiboot roms (used for singlepak multiplayer)
├── scripts # Scripts used in the decompilation process
├── sound # Sound related assets
├── src
│ ├── data # C files which pull in assets as part of the compilation process
│ ├── game
│ │ ├── sa1
│ │ ├── sa2
│ │ └── shared # C files used across all 3 games
│ ├── lib # libraries which were originally statically linked
│ │ ├── agb_flash
│ │ └── m4a
│ ├── platform # port related code
│ └── . # src root contains Dimps "engine" C files
└── tools # tools used for compilation or extraction
```

### Notable info

- The [Kirby & The Amazing Mirror](https://github.com/jiangzhengwenjz/katam/) decompilation uses the same "engine", as it was written by the same dev team (Dimps)
- https://decomp.me is a great resource for helping to create matching function
- For more info, see the [FAQs section](https://zelda64.dev/games/tmc) of TMC

## Credits

- [JaceCear](https://github.com/JaceCear) for his dedication to understanding the internals of the graphics engine, writing [tools to extract this data](https://github.com/JaceCear/SA-Trilogy-Animation-Exporter), as well as massive effort in contributing towards the decompilation process, *and* setting up the PC ports
- [JaceCear](https://github.com/JaceCear) for his dedication to understanding the internals of the graphics engine, writing [tools to extract this data](https://github.com/JaceCear/SA-Trilogy-Animation-Exporter), as well as massive effort in contributing towards the decompilation process (including decompiling the whole of sa1!), *and* setting up the PC ports
- Shout out to [@froggestspirit](https://github.com/froggestspirit) for the drive to set this project up
- Special thanks to [@normmatt](https://github.com/normmatt) for the initial repo setup and sounds disassembly

- [Pokemon Reverse Engineering Tools](https://github.com/pret) community for their help with the project, and tooling for GBA decompilations
- [Kermalis](https://github.com/Kermalis) for [their tool](https://github.com/Kermalis/VGMusicStudio) which was used to dump the game midis
- [琪姬](https://github.com/laqieer) for their exellent work [documenting](https://github.com/FireEmblemUniverse/fireemblem8u/pull/137) all the quirks of matching midis
5 changes: 0 additions & 5 deletions include/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,13 +296,8 @@ extern u16 gDispCnt;
extern winreg_t gWinRegs[6];
extern struct BlendRegs gBldRegs;
extern BgAffineReg gBgAffineRegs[NUM_AFFINE_BACKGROUNDS];
#if (GAME == GAME_SA1)
extern ColorRaw gObjPalette[OBJ_PLTT_SIZE / sizeof(u16)];
extern ColorRaw gBgPalette[BG_PLTT_SIZE / sizeof(u16)];
#else
extern ColorRaw gObjPalette[16 * PALETTE_LEN_4BPP];
extern ColorRaw gBgPalette[16 * PALETTE_LEN_4BPP];
#endif
extern u16 gBgCntRegs[4];

// TODO: Turn this into a struct-array?
Expand Down
3 changes: 1 addition & 2 deletions include/game/sa1/stage/stage_ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ typedef struct {
} StageUI_20;

typedef struct {
/* 0x00 */ u8 digitsRings[3];
/* 0x03 */ u8 filler3[0x5];
/* 0x00 */ u8 digitsRings[8];
/* 0x08 */ s16 unk8;
/* 0x0A */ s16 unkA;
/* 0x0C */ s16 unkC;
Expand Down
2 changes: 1 addition & 1 deletion include/game/shared/stage/collision.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ EHit Coll_Player_Boss_1(Sprite *s, CamCoord worldX, CamCoord worldY, Player *p);
EHit Coll_Player_Bosses_2_6(Sprite *s, CamCoord worldX, CamCoord worldY, Player *p);
EHit sub_800BF10(Sprite *s, CamCoord worldX, CamCoord worldY, Player *p);
EHit sub_800BFEC(Sprite *s, CamCoord worldX, CamCoord worldY, Player *p);
u32 sub_800C0E0(Sprite *s, CamCoord screenX, CamCoord screenY, Player *p);
EHit sub_800C0E0(Sprite *s, CamCoord screenX, CamCoord screenY, Player *p);
#endif

u32 Coll_Player_Entity_Intersection(Sprite *s, CamCoord x, CamCoord y, Player *p);
Expand Down
3 changes: 0 additions & 3 deletions ps2/sa1/ntsc/SYSTEM.CNF

This file was deleted.

3 changes: 0 additions & 3 deletions ps2/sa2/ntsc/SYSTEM.CNF

This file was deleted.

2 changes: 1 addition & 1 deletion src/game/sa1/stage/enemies/boss_5_mecha_knuckles.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ bool32 sub_804FC78(MechaKnuckles *boss, Player *p);
bool32 sub_804FCA4(MechaKnuckles *boss, Player *p);
bool32 sub_804FCEC(MechaKnuckles *boss, Player *p);
bool32 sub_804FD54(MechaKnuckles *boss, Player *p);
bool32 sub_804ED80(MechaKnuckles *boss, Player *p);
MechaKnucklesStates sub_804ED80(MechaKnuckles *boss, Player *p);

typedef struct ProcDataBoss5 {
MechaKnuxPlayerFn knuxPlayerFn;
Expand Down
8 changes: 8 additions & 0 deletions src/game/sa1/stage/interactables/forced_slide.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#include "core.h"
#include "malloc_vram.h"
#include "lib/m4a/m4a.h"

#include "game/globals.h"
#include "game/shared/stage/entity.h"
#include "game/sa1/stage/player_controls.h"
#include "game/shared/stage/player.h"
Expand Down Expand Up @@ -195,6 +197,12 @@ void Task_ForcedSlide(void)
if (IS_OUT_OF_CAM_RANGE(x, y)) {
s32 i = 0;
do {
#ifdef BUG_FIX
// This isn't really needed but it makes the compiler happy on some platforms
if (i > NUM_SINGLEPLAYER_CHARS_MAX - 1) {
break;
}
#endif
if (slide->unk3D[i] != 0) {
m4aSongNumStop(SE_201);

Expand Down
8 changes: 7 additions & 1 deletion src/game/sa1/stage/stage_ui.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ NONMATCH("asm/non_matching/game/sa1/stage/stage_ui__Task_StageUIMain.inc", void
remainder = gLevelScore;

for (i = 8; i >= 0; i--) {
#ifdef BUG_FIX
// How did this even happen?
if (i >= ARRAY_COUNT(ui->digitsRings)) {
continue;
}
#endif
remainder0 = Div(remainder, 10);

ui->digitsRings[i] = UI_DIGIT(remainder - ((remainder0 << 3) + (remainder0 << 1)));
Expand Down Expand Up @@ -441,7 +447,7 @@ NONMATCH("asm/non_matching/game/sa1/stage/stage_ui__Task_SpecialStageUIMain.inc"
} else {
// _08053DF0
remainder = gSpecialStageCollectedRings;
for (i = 0; i < (s32)ARRAY_COUNT(ui->digitsRings); i++) {
for (i = 0; i < 3; i++) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This used to be 3, but it's obvious the array is larger

remainder0 = Div(remainder, 10);

ui->digitsRings[2 - i] = UI_DIGIT(remainder - ((remainder0 << 3) + (remainder0 << 1)));
Expand Down
2 changes: 1 addition & 1 deletion src/game/sa1/stage/stage_ui_z.c
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,7 @@ void Task_8055B18()
{
// TODO: Inline the mem calls!
// const u8 arr0[16] = { /* data here */ };
const u8 arr0[22 + 5];
u8 arr0[22 + 5];
memcpy((void *)arr0, &gUnknown_086883CC[0], sizeof(arr0) - 5);
memset((void *)(arr0 + 22), 0, 5);

Expand Down
4 changes: 2 additions & 2 deletions src/game/shared/stage/demo_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ void CreateDemoManager(void)
DemoManager *dm;
Sprite *s;
#if (GAME == GAME_SA1)
const AnimId arr[2];
memcpy((void *)&arr, gPressStartTiles, sizeof(arr));
AnimId arr[2];
memcpy(&arr, gPressStartTiles, sizeof(arr));
#endif
blendCtrl = gBldRegs.bldCnt & 0xC0;
#if (GAME == GAME_SA2)
Expand Down
Loading