diff --git a/ReadFile.c b/ReadFile.c index 92c756a..cae9a25 100644 --- a/ReadFile.c +++ b/ReadFile.c @@ -42,15 +42,15 @@ int onReadOptionBlock(TCardManager *this, CARDFileInfo *fileInfo) { if ((rc = CARDRead(fileInfo, dst, size, 0))) { // should not fail if (dst, size) is properly set } else { - // everything is good - // TODO apply gecko code - *(char*)0x817effff = 1; // ready (temporary solution) + // everything is good => apply gecko code + // TODO entry + ((void(*)())0x817f0000)(); } // close file CARDClose(fileInfo); orig: - // original function call + // original function call return open_(this, fileInfo); } diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..69976f7 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,5 @@ +*.o +*.out +*.bin +*.gci +*.map diff --git a/example/Makefile b/example/Makefile new file mode 100644 index 0000000..3ba7aec --- /dev/null +++ b/example/Makefile @@ -0,0 +1,31 @@ +CC = powerpc-eabi-gcc +AS = powerpc-eabi-as +LD = powerpc-eabi-ld +OBJCOPY = powerpc-eabi-objcopy +CFLAGS = -Os -I. -Wa,-mregnames,-mgekko -Wall -fno-asynchronous-unwind-tables -fno-unwind-tables +ASFLAGS = -mregnames -mgekko +PYTHON = python3 + +OBJS = _start.o gameLoop.o qfsync.o +LDFILE = sms.ld +MAPOUT = gci.map + +GCI = 01-GMSJ-gct.gci +OBJOUT = a.out +OBJBIN = a.bin +blockCount = 7 + +all: $(GCI) + +$(GCI): $(OBJBIN) + $(PYTHON) make-gci.py $(blockCount) $< $@ + +$(OBJOUT): $(OBJS) | $(LDFILE) + $(LD) -o $@ -T $(LDFILE) -Map $(MAPOUT) $^ +$(OBJBIN): $(OBJOUT) + $(OBJCOPY) -O binary $^ $@ + + +.PHONY: clean +clean: + rm -f *.gci*.out *.bin *.o *.map diff --git a/example/QFSync.s b/example/QFSync.s new file mode 100644 index 0000000..99eacfd --- /dev/null +++ b/example/QFSync.s @@ -0,0 +1,8 @@ +.globl _getQFSync +_getQFSync: + lis r12, 0x8180 + lwz r12, -0x4000(r12) + rlwinm. r12, r12, 2, 0x3 + li r3, 600 + slw r3, r3, r12 + blr diff --git a/example/_start.s b/example/_start.s new file mode 100644 index 0000000..64a6c5e --- /dev/null +++ b/example/_start.s @@ -0,0 +1,38 @@ +.include "./macros.s" + +.macro .rwbl src dst + li32 r3, \src + li32 r4, \dst + bl replace_with_bl +.endm + +.globl _start +_start: # right after gci loaded into RAM + mflr r11 # need to make sure r11 is not destroyed + +## replace original instruction @src with `bl dst` +## TODO version + .rwbl 0x800f9a10, _gameLoop + .rwbl 0x800ecde0, _getQFSync + +## return + mtlr r11 + blr + +/** + * r3 = address of original code + * r4 = address of practice code function + */ +replace_with_bl: +## bl from r3 to r4 + sub r4, r4, r3 + rlwinm r4, r4, 0, 6, 29 + oris r4, r4, 0x4800 + ori r4, r4, 0x1 + stw r4, 0(r3) +## invalidate cache + dcbst 0, r3 + sync + icbi 0, r3 + isync + blr diff --git a/example/gameLoop.c b/example/gameLoop.c new file mode 100644 index 0000000..a7b44dd --- /dev/null +++ b/example/gameLoop.c @@ -0,0 +1,18 @@ +#include + +typedef struct { + uint8_t qfsync : 2; +} CodeConfig; +extern CodeConfig config; + +void _gameLoop(void *gamePad) { + // original function + TMarioGamePad_read(gamePad); + + // check the controller input + uint16_t btn = JUTGamePad_mPadStatus.button; + config.qfsync = + btn == (PRESS_B | PRESS_DL) ? 2 : // 4x + btn == (PRESS_B | PRESS_DR) ? 3 : // 8x + 0; // 1x +} diff --git a/example/macros.s b/example/macros.s new file mode 100644 index 0000000..50d0f05 --- /dev/null +++ b/example/macros.s @@ -0,0 +1,4 @@ +.macro li32 reg val + lis \reg, \val@h + ori \reg, \reg, \val@l +.endm diff --git a/example/main.c b/example/main.c new file mode 100644 index 0000000..17d985d --- /dev/null +++ b/example/main.c @@ -0,0 +1,3 @@ +int f(int x) { + return x*x; +} diff --git a/example/make-gci.py b/example/make-gci.py new file mode 100644 index 0000000..383a40a --- /dev/null +++ b/example/make-gci.py @@ -0,0 +1,28 @@ +import sys + +if len(sys.argv) != 4: + print('Usage: %s {block-count} {content.bin} {out.gci}'%sys.argv[0], file=sys.stderr) + sys.exit(1) + +blockCount = int(sys.argv[1]) +contentPath = sys.argv[2] +outPath = sys.argv[3] + +gameId = b'GMSJ01\xff\x00' # TODO other version +fileName = b'gct' + +pad = lambda buf, size: buf+b'\x00'*(size-len(buf)) +with open(contentPath, 'rb') as fr, open(outPath, 'wb') as fw: + content = fr.read() + bodySize = 0x2000*blockCount + assert len(content)<=bodySize, f'{contentPath} is too big: expect at most {bodySize} bytes, got {len(content)}' + fw.write(b''.join(( + # header + gameId, + pad(fileName, 0x20), + b'\x00'*16, + blockCount.to_bytes(2, 'big'), + b'\xff'*6, + # body + pad(content, bodySize), + ))) diff --git a/example/sms.h b/example/sms.h new file mode 100644 index 0000000..24db64e --- /dev/null +++ b/example/sms.h @@ -0,0 +1,34 @@ +#ifndef SMS_H +#define SMS_H + +#include + +void TMarioGamePad_read(void*); + +typedef struct { + uint16_t button; + int8_t stickX; + int8_t stickY; + int8_t substickX; + int8_t substickY; + uint8_t triggerLeft; + uint8_t triggerRight; + uint8_t analogA; + uint8_t analogB; + int8_t err; +} PADStatus; +extern PADStatus JUTGamePad_mPadStatus; +#define PRESS_START 0x1000 +#define PRESS_Y 0x0800 +#define PRESS_X 0x0400 +#define PRESS_B 0x0200 +#define PRESS_A 0x0100 +#define PRESS_L 0x0040 +#define PRESS_R 0x0020 +#define PRESS_Z 0x0010 +#define PRESS_DU 0x0008 +#define PRESS_DD 0x0004 +#define PRESS_DR 0x0002 +#define PRESS_DL 0x0001 + +#endif diff --git a/example/sms.ld b/example/sms.ld new file mode 100644 index 0000000..73e590d --- /dev/null +++ b/example/sms.ld @@ -0,0 +1,14 @@ +SECTIONS { + . = 0x817F0000; + .init : { _start.o } + .text : { *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + .bss : { *(.bss) } +} + +/** TODO version */ +TMarioGamePad_read = 0x800fba58; +JUTGamePad_mPadStatus = 0x80400d50; + +config = 0x817fc000;