diff --git a/Makefile b/Makefile index d017fd5..2cf0be6 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ LD_FILES := $(LD_DIR)/sms.ld $(LD_DIR)/$(GAME_VERSION).ld GECKO_FILE_LOADER := $(GECKO_DIR)/$(GECKO_OUT_DIR)/gecko-gosub.bin GECKO_FILES := $(GECKO_FILE_LOADER) $(GECKO_DIR)/$(GECKO_OUT_DIR)/gecko-return.bin -OBJS := main.o card.o +OBJS := main.o card.o drawText.o all: $(OUT_DIR)/$(OUT_BIN) $(OUT_DIR)/$(OUT_LST) $(OUT_DIR)/$(OUT_ASM) $(OUT_DIR)/$(OUT_LOADER_TXT) @@ -70,8 +70,8 @@ $(OBJ_FILES): $(SRC_FILES) check-set-GAME_VERSION #check-set-TARGET_VERSION $(CC) $(CFLAGS) -D VERSION=$(TARGET_VERSION) -I $(INC_DIR) -o $@ -c $(@:.o=.c) $(OUT_DIR)/$(OUT_LOADER_TXT): $(OUT_DIR)/$(OUT_BIN) $(OUT_DIR)/$(OUT_MAP) $(GECKO_FILE_LOADER) | $(OUT_DIR) - $(BIN2GCT) hook:$(OUT_DIR)/$(OUT_MAP):$(OUT_DIR)/$(OUT_BIN):onReadOptionBlock C0:$(GECKO_FILE_LOADER) > $@ -$(GECKO_FILES): + $(BIN2GCT) hook:$(OUT_DIR)/$(OUT_MAP):$(OUT_DIR)/$(OUT_BIN):onReadOptionBlock:willDrawFader C0:$(GECKO_FILE_LOADER) > $@ +$(GECKO_FILES): $(LD_FILES) $(MAKE) -C$(GECKO_DIR) OUT_DIR=$(GECKO_OUT_DIR) $(patsubst $(GECKO_DIR)/%,%,$@) $(OUT_DIR): diff --git a/gecko/Makefile b/gecko/Makefile index 595eca2..e584aae 100644 --- a/gecko/Makefile +++ b/gecko/Makefile @@ -25,8 +25,8 @@ all: $(OUT_TXT) %.o: %.s $(AS) $(ASFLAGS) -o $@ -c $^ -%.ld.o: %.o - $(LD) $(LDFLAGS) -o $@ -T $(LD_FILES) $^ +%.ld.o: %.o $(LD_FILES) + $(LD) $(LDFLAGS) -o $@ -T $(LD_FILES) $< $(OUT_DIR)/%.bin: $(SRC_DIR)/%.ld.o | $(OUT_DIR) $(OBJCOPY) $(OBJCOPYFLAGS) $< $@ $(OUT_DIR)/%.txt: $(OUT_DIR)/%.bin | $(OUT_DIR) diff --git a/gecko/hook.csv b/gecko/hook.csv index 81e5309..3b7adfa 100644 --- a/gecko/hook.csv +++ b/gecko/hook.csv @@ -1,2 +1,3 @@ symbol,GMSE01,GMSJ01,GMSP01,GMSJ0A onReadOptionBlock,802B17D0,801069F4,802A96E0,80291538 +willDrawFader,802A62B0,800F9CB4,8029E1C0,8028610C diff --git a/include/card.h b/include/card.h index 4eccc44..eb248db 100644 --- a/include/card.h +++ b/include/card.h @@ -9,6 +9,9 @@ #define LOAD_ERR_SIZE -3 #define LOAD_ERR_FILE_NOT_EXIST -4 #define LOAD_ERR_READ -5 +#define LOAD_STAT_INIT 0 +#define LOAD_STAT_LOADING 1 +#define LOAD_STAT_DONE 2 int loadCard(TCardManager *cm, CARDFileInfo *fileInfo, char *fileName, u32 *dst); #endif diff --git a/include/drawText.h b/include/drawText.h new file mode 100644 index 0000000..5b82ad4 --- /dev/null +++ b/include/drawText.h @@ -0,0 +1,15 @@ +#include + +#ifndef __DRAW_H__ +#define __DRAW_H__ + +typedef struct { + int16_t x; + int16_t y; + uint32_t fontSize; + uint32_t colorTop; + uint32_t colorBot; +} DrawTextOpt; +void drawText(DrawTextOpt *opt, const char *fmt, ...); + +#endif diff --git a/include/sms.h b/include/sms.h index e8f88a9..ef336d3 100644 --- a/include/sms.h +++ b/include/sms.h @@ -1,3 +1,7 @@ +#include +#include "typedefs.h" +#include "gcn.h" + #ifndef __SMS_H__ #define __SMS_H__ @@ -9,4 +13,39 @@ typedef struct int mount_(TCardManager *this, bool); int open_(TCardManager *this, CARDFileInfo *fileInfo); +typedef struct TApplication { + struct TApplication *self; + void *director; + u8 directorType; +} TApplication; +extern TApplication gpApplication; + +// Render related +extern struct {} *gpSystemFont; +typedef float Mtx[3][4]; +typedef struct { + void* __vt__; + char unk[0x1c-4]; + int xInt; + int yInt; + float xFloat; + float yFloat; + float zFloat; + char unk1[12]; + uint32_t bgMask; + uint32_t fgMask; + uint32_t colorTop; + uint32_t colorBot; + int x4c; + int lineHeight; + int x54; + int fontWidth; + int fontHeight; + char unk3[8]; +} J2DPrint; + +void new_J2DPrint(void *this, void *font, int x4c, int lineHeight, uint32_t *colorTop, uint32_t *colorBot); +// color.alpha = printer->color.alpha * alphaMask/0xff +void J2DPrint_print_alpha_va(void *printer, uint8_t alphaMask, const char *fmt, va_list args); + #endif diff --git a/include/typedefs.h b/include/typedefs.h index 13babcb..c8492fe 100644 --- a/include/typedefs.h +++ b/include/typedefs.h @@ -1,5 +1,6 @@ #ifndef __TYPEDEF_H__ #define __TYPEDEF_H__ +#include typedef int bool; #define true 1 diff --git a/ldscript/GMSE01.ld b/ldscript/GMSE01.ld index c801869..d4e354f 100644 --- a/ldscript/GMSE01.ld +++ b/ldscript/GMSE01.ld @@ -3,3 +3,8 @@ open_ = 0x802b20d0; CARDOpen = 0x8035938c; CARDRead = 0x80359d20; CARDClose = 0x80359504; +JUTRect_set = 0x802ca1e0; +gpSystemFont = 0x8040e188; +new_J2DPrint = 0x802cdcf8; +J2DPrint_print_alpha_va = 0x802cdbbc; +gpApplication = 0x803e9700; diff --git a/ldscript/GMSJ01.ld b/ldscript/GMSJ01.ld index b4953a6..3339388 100644 --- a/ldscript/GMSJ01.ld +++ b/ldscript/GMSJ01.ld @@ -3,3 +3,8 @@ open_ = 0x801072f4; CARDOpen = 0x800a3cac; CARDRead = 0x800a4640; CARDClose = 0x800a3e24; +JUTRect_set = 0x8001321c; +gpSystemFont = 0x8040a2b8; +new_J2DPrint = 0x80016db4; +J2DPrint_print_alpha_va = 0x80016c78; +gpApplication = 0x803e6000; diff --git a/ldscript/GMSJ0A.ld b/ldscript/GMSJ0A.ld index 8202a66..027873a 100644 --- a/ldscript/GMSJ0A.ld +++ b/ldscript/GMSJ0A.ld @@ -3,3 +3,8 @@ open_ = 0x80291e38; CARDOpen = 0x80338c8c; CARDRead = 0x80339620; CARDClose = 0x80338e04; +JUTRect_set = 0x802a99f0; +gpSystemFont = 0x803ff028; +new_J2DPrint = 0x802ad508; +J2DPrint_print_alpha_va = 0x802ad3cc; +gpApplication = 0x803da8e0; diff --git a/ldscript/GMSP01.ld b/ldscript/GMSP01.ld index dfc9cd9..a504879 100644 --- a/ldscript/GMSP01.ld +++ b/ldscript/GMSP01.ld @@ -3,3 +3,8 @@ open_ = 0x802a9fe0; CARDOpen = 0x803515ac; CARDRead = 0x80351f40; CARDClose = 0x80351724; +JUTRect_set = 0x802c2274; +gpSystemFont = 0x80405850; +new_J2DPrint = 0x802c5d8c; +J2DPrint_print_alpha_va = 0x802c5c50; +gpApplication = 0x803e10c0; diff --git a/ldscript/sms.ld b/ldscript/sms.ld index e16880d..7255fa5 100644 --- a/ldscript/sms.ld +++ b/ldscript/sms.ld @@ -1,11 +1,12 @@ /** * There seems to be an alignment issue * addr of GCTDST.code may need to be a multiple of 0x200 or something? + * The address should be sync with the value set in GCI Generator */ GCTDST = 0x817F1800 - 8; SECTIONS { - . = 0x817FE800; /* TODO: Determine Best Address */ + . = 0x817FD800; /* TODO: Determine Best Address */ .text : { *(.text) } .rodata : { *(.rodata) } .data : { *(.data) } diff --git a/source/drawText.c b/source/drawText.c new file mode 100644 index 0000000..193e233 --- /dev/null +++ b/source/drawText.c @@ -0,0 +1,27 @@ +#include "sms.h" +#include "drawText.h" + +void drawText(DrawTextOpt *opt, const char *fmt, ...) { + J2DPrint printer; + + va_list args; + va_start(args, fmt); + + // new J2DPrinter + new_J2DPrint(&printer, gpSystemFont, 0, opt->fontSize, &opt->colorTop, &opt->colorBot); + printer.fontWidth = printer.fontHeight = opt->fontSize; + + // set coordinate + printer.xInt = opt->x; + // printer.yInt = opt->y; // (actually unused) + printer.xFloat = opt->x; + printer.yFloat = opt->y; + printer.zFloat = 0; + + // print text + J2DPrint_print_alpha_va(&printer, 0xff, fmt, args); + va_end(args); + + // no need to delete if not allocating memory from heap + // delete_J2DPrint(&printer, -1); +} diff --git a/source/main.c b/source/main.c index 899b1dc..e1fc78b 100644 --- a/source/main.c +++ b/source/main.c @@ -1,5 +1,6 @@ #include "gcn.h" #include "card.h" +#include "drawText.h" #if VERSION == 1 #define FILENAME "GCT_GMSE01" @@ -48,3 +49,48 @@ int onReadOptionBlock(TCardManager *this, CARDFileInfo *fileInfo) loadCodes(this, fileInfo); return open_(this, fileInfo); } + +// TODO better hook address? +// e.g. hook after fader.draw() (but also avoid conflict with controller display) +void JUTRect_set(void *r3, u32 r4, u32 r5, u32 r6, u32 r7); +void willDrawFader(void *r3, u32 r4, u32 r5, u32 r6, u32 r7) { + JUTRect_set(r3, r4, r5, r6, r7); + + // do not show GCI status until boot finished + if (gpApplication.directorType < 4) return; + DrawTextOpt opt = { + .x = 16, + .y = 440, + .fontSize = 32, + .colorTop = 0xff3333bb, + .colorBot = 0xff3333bb + }; + switch (GCTDST.status) { + case LOAD_ERR_MOUNT: + drawText(&opt, "Fail to mount memory card"); + break; + case LOAD_ERR_OPEN: + drawText(&opt, "Fail to open GCI file: "FILENAME); + break; + case LOAD_ERR_SIZE: + drawText(&opt, "GCI file too large: "FILENAME); + break; + case LOAD_ERR_FILE_NOT_EXIST: + drawText(&opt, "GCI file not exists: "FILENAME); + break; + case LOAD_ERR_READ: + drawText(&opt, "Fail to read GCI file"); + break; + case LOAD_STAT_INIT: + drawText(&opt, "Preparing to load GCI file..."); + break; + case LOAD_STAT_LOADING: + drawText(&opt, "Loading GCI file..."); + break; + case LOAD_STAT_DONE: + break; + default: + drawText(&opt, "Unknown GCI Error: %d", GCTDST.status); + break; + } +}