diff options
| -rw-r--r-- | Makefile.gc | 131 | ||||
| -rw-r--r-- | build.bat | 2 | ||||
| -rwxr-xr-x | build.sh | 2 | ||||
| -rw-r--r-- | source/link.c | 157 | ||||
| -rw-r--r-- | source/link.h | 14 | ||||
| -rw-r--r-- | source/main.c | 596 | ||||
| -rw-r--r-- | source/multiboot.c | 246 | ||||
| -rw-r--r-- | source/multiboot.h | 8 |
8 files changed, 550 insertions, 606 deletions
| diff --git a/Makefile.gc b/Makefile.gc deleted file mode 100644 index eaaf3b9..0000000 --- a/Makefile.gc +++ /dev/null | |||
| @@ -1,131 +0,0 @@ | |||
| 1 | #--------------------------------------------------------------------------------- | ||
| 2 | # Clear the implicit built in rules | ||
| 3 | #--------------------------------------------------------------------------------- | ||
| 4 | .SUFFIXES: | ||
| 5 | #--------------------------------------------------------------------------------- | ||
| 6 | ifeq ($(strip $(DEVKITPPC)),) | ||
| 7 | $(error "Please set DEVKITPPC in your environment. export DEVKITPPC=<path to>devkitPPC") | ||
| 8 | endif | ||
| 9 | |||
| 10 | include $(DEVKITPPC)/gamecube_rules | ||
| 11 | |||
| 12 | #--------------------------------------------------------------------------------- | ||
| 13 | # TARGET is the name of the output | ||
| 14 | # BUILD is the directory where object files & intermediate files will be placed | ||
| 15 | # SOURCES is a list of directories containing source code | ||
| 16 | # INCLUDES is a list of directories containing extra header files | ||
| 17 | #--------------------------------------------------------------------------------- | ||
| 18 | TARGET := gen3uploader_gc | ||
| 19 | BUILD := build | ||
| 20 | SOURCES := source | ||
| 21 | DATA := data | ||
| 22 | INCLUDES := source | ||
| 23 | |||
| 24 | #--------------------------------------------------------------------------------- | ||
| 25 | # options for code generation | ||
| 26 | #--------------------------------------------------------------------------------- | ||
| 27 | |||
| 28 | CFLAGS = -g -O3 -Wall $(MACHDEP) $(INCLUDE) | ||
| 29 | CXXFLAGS = $(CFLAGS) | ||
| 30 | |||
| 31 | LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map | ||
| 32 | |||
| 33 | #--------------------------------------------------------------------------------- | ||
| 34 | # any extra libraries we wish to link with the project | ||
| 35 | #--------------------------------------------------------------------------------- | ||
| 36 | LIBS := -logc | ||
| 37 | |||
| 38 | |||
| 39 | #--------------------------------------------------------------------------------- | ||
| 40 | # list of directories containing libraries, this must be the top level containing | ||
| 41 | # include and lib | ||
| 42 | #--------------------------------------------------------------------------------- | ||
| 43 | LIBDIRS := $(DEVKITPPC)/lib $(CURDIR) | ||
| 44 | |||
| 45 | #--------------------------------------------------------------------------------- | ||
| 46 | # no real need to edit anything past this point unless you need to add additional | ||
| 47 | # rules for different file extensions | ||
| 48 | #--------------------------------------------------------------------------------- | ||
| 49 | ifneq ($(BUILD),$(notdir $(CURDIR))) | ||
| 50 | #--------------------------------------------------------------------------------- | ||
| 51 | |||
| 52 | export OUTPUT := $(CURDIR)/$(TARGET) | ||
| 53 | |||
| 54 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ | ||
| 55 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) | ||
| 56 | |||
| 57 | export DEPSDIR := $(CURDIR)/$(BUILD) | ||
| 58 | |||
| 59 | #--------------------------------------------------------------------------------- | ||
| 60 | # automatically build a list of object files for our project | ||
| 61 | #--------------------------------------------------------------------------------- | ||
| 62 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) | ||
| 63 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) | ||
| 64 | sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) | ||
| 65 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) | ||
| 66 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) | ||
| 67 | |||
| 68 | #--------------------------------------------------------------------------------- | ||
| 69 | # use CXX for linking C++ projects, CC for standard C | ||
| 70 | #--------------------------------------------------------------------------------- | ||
| 71 | ifeq ($(strip $(CPPFILES)),) | ||
| 72 | export LD := $(CC) | ||
| 73 | else | ||
| 74 | export LD := $(CXX) | ||
| 75 | endif | ||
| 76 | |||
| 77 | export OFILES := $(addsuffix .o,$(BINFILES)) \ | ||
| 78 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ | ||
| 79 | $(sFILES:.s=.o) $(SFILES:.S=.o) | ||
| 80 | |||
| 81 | #--------------------------------------------------------------------------------- | ||
| 82 | # build a list of include paths | ||
| 83 | #--------------------------------------------------------------------------------- | ||
| 84 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ | ||
| 85 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ | ||
| 86 | -I$(CURDIR)/$(BUILD) \ | ||
| 87 | -I$(LIBOGC_INC) | ||
| 88 | |||
| 89 | #--------------------------------------------------------------------------------- | ||
| 90 | # build a list of library paths | ||
| 91 | #--------------------------------------------------------------------------------- | ||
| 92 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ | ||
| 93 | -L$(LIBOGC_LIB) | ||
| 94 | |||
| 95 | export OUTPUT := $(CURDIR)/$(TARGET) | ||
| 96 | .PHONY: $(BUILD) clean | ||
| 97 | |||
| 98 | #--------------------------------------------------------------------------------- | ||
| 99 | $(BUILD): | ||
| 100 | @[ -d $@ ] || mkdir -p $@ | ||
| 101 | @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile.gc | ||
| 102 | |||
| 103 | #--------------------------------------------------------------------------------- | ||
| 104 | clean: | ||
| 105 | @echo clean ... | ||
| 106 | @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol | ||
| 107 | |||
| 108 | #--------------------------------------------------------------------------------- | ||
| 109 | else | ||
| 110 | |||
| 111 | DEPENDS := $(OFILES:.o=.d) | ||
| 112 | |||
| 113 | #--------------------------------------------------------------------------------- | ||
| 114 | # main targets | ||
| 115 | #--------------------------------------------------------------------------------- | ||
| 116 | $(OUTPUT).dol: $(OUTPUT).elf | ||
| 117 | $(OUTPUT).elf: $(OFILES) | ||
| 118 | |||
| 119 | #--------------------------------------------------------------------------------- | ||
| 120 | # This rule links in binary data with the .jpg extension | ||
| 121 | #--------------------------------------------------------------------------------- | ||
| 122 | %.gba.o : %.gba | ||
| 123 | #--------------------------------------------------------------------------------- | ||
| 124 | @echo $(notdir $<) | ||
| 125 | $(bin2o) | ||
| 126 | |||
| 127 | -include $(DEPENDS) | ||
| 128 | |||
| 129 | #--------------------------------------------------------------------------------- | ||
| 130 | endif | ||
| 131 | #--------------------------------------------------------------------------------- | ||
| diff --git a/build.bat b/build.bat index 17c2889..e2ad539 100644 --- a/build.bat +++ b/build.bat | |||
| @@ -5,8 +5,6 @@ make | |||
| 5 | cd .. | 5 | cd .. |
| 6 | @md data | 6 | @md data |
| 7 | mv -f gba/gba_pkjb.gba data/gba_mb.gba | 7 | mv -f gba/gba_pkjb.gba data/gba_mb.gba |
| 8 | make -f Makefile.gc clean | ||
| 9 | make -f Makefile.gc | ||
| 10 | make -f Makefile.wii clean | 8 | make -f Makefile.wii clean |
| 11 | make -f Makefile.wii | 9 | make -f Makefile.wii |
| 12 | pause | 10 | pause |
| diff --git a/build.sh b/build.sh index b521d27..cae6808 100755 --- a/build.sh +++ b/build.sh | |||
| @@ -5,7 +5,5 @@ make | |||
| 5 | cd .. | 5 | cd .. |
| 6 | mkdir data | 6 | mkdir data |
| 7 | mv -f gba/gba_pkjb.gba data/gba_mb.gba | 7 | mv -f gba/gba_pkjb.gba data/gba_mb.gba |
| 8 | make -f Makefile.gc clean | ||
| 9 | make -f Makefile.gc | ||
| 10 | make -f Makefile.wii clean | 8 | make -f Makefile.wii clean |
| 11 | make -f Makefile.wii | 9 | make -f Makefile.wii |
| diff --git a/source/link.c b/source/link.c index 4178293..1091576 100644 --- a/source/link.c +++ b/source/link.c | |||
| @@ -5,18 +5,159 @@ | |||
| 5 | * of the MIT license. See the LICENSE file for details. | 5 | * of the MIT license. See the LICENSE file for details. |
| 6 | */ | 6 | */ |
| 7 | #include "link.h" | 7 | #include "link.h" |
| 8 | #include <unistd.h> | ||
| 9 | #include <stdio.h> | ||
| 10 | #include <string.h> | ||
| 11 | #include <malloc.h> | ||
| 8 | 12 | ||
| 9 | u32 waitForButtons(u32 mask) | 13 | //from my tests 50us seems to be the lowest |
| 14 | //safe si transfer delay in between calls | ||
| 15 | #define SI_TRANS_DELAY 50 | ||
| 16 | |||
| 17 | static u8* resbuf; | ||
| 18 | static u8* cmdbuf; | ||
| 19 | |||
| 20 | void initLink() | ||
| 21 | { | ||
| 22 | cmdbuf = memalign(32,32); | ||
| 23 | resbuf = memalign(32,32); | ||
| 24 | } | ||
| 25 | |||
| 26 | static volatile u32 transval = 0; | ||
| 27 | void transcb(s32 chan, u32 ret) | ||
| 28 | { | ||
| 29 | transval = 1; | ||
| 30 | } | ||
| 31 | |||
| 32 | static volatile u32 resval = 0; | ||
| 33 | void acb(s32 res, u32 val) | ||
| 34 | { | ||
| 35 | resval = val; | ||
| 36 | } | ||
| 37 | |||
| 38 | void doreset() | ||
| 39 | { | ||
| 40 | cmdbuf[0] = 0xFF; //reset | ||
| 41 | transval = 0; | ||
| 42 | SI_Transfer(1, cmdbuf, 1, resbuf, 3, transcb, SI_TRANS_DELAY); | ||
| 43 | |||
| 44 | while (transval == 0); | ||
| 45 | } | ||
| 46 | |||
| 47 | void getstatus() | ||
| 10 | { | 48 | { |
| 11 | for (;;) | 49 | cmdbuf[0] = 0; //status |
| 50 | transval = 0; | ||
| 51 | SI_Transfer(1, cmdbuf, 1, resbuf, 3, transcb, SI_TRANS_DELAY); | ||
| 52 | |||
| 53 | while (transval == 0); | ||
| 54 | } | ||
| 55 | |||
| 56 | u32 recv() | ||
| 57 | { | ||
| 58 | memset(resbuf,0,32); | ||
| 59 | cmdbuf[0]=0x14; //read | ||
| 60 | transval = 0; | ||
| 61 | SI_Transfer(1, cmdbuf, 1, resbuf, 5, transcb, SI_TRANS_DELAY); | ||
| 62 | |||
| 63 | while (transval == 0); | ||
| 64 | printf("%08lx\n", *(vu32*)resbuf); | ||
| 65 | return *(vu32*)resbuf; | ||
| 66 | } | ||
| 67 | |||
| 68 | void send(u32 msg) | ||
| 69 | { | ||
| 70 | cmdbuf[0] = 0x15; | ||
| 71 | cmdbuf[1] = (msg >> 0) & 0xFF; | ||
| 72 | cmdbuf[2] = (msg >> 8) & 0xFF; | ||
| 73 | cmdbuf[3] = (msg >> 16) & 0xFF; | ||
| 74 | cmdbuf[4] = (msg >> 24) & 0xFF; | ||
| 75 | |||
| 76 | transval = 0; | ||
| 77 | resbuf[0] = 0; | ||
| 78 | SI_Transfer(1, cmdbuf, 5, resbuf, 1, transcb, SI_TRANS_DELAY); | ||
| 79 | |||
| 80 | while (transval == 0); | ||
| 81 | } | ||
| 82 | |||
| 83 | u32 getMsg() | ||
| 84 | { | ||
| 85 | u32 val = 0; | ||
| 86 | while (val == 0) | ||
| 12 | { | 87 | { |
| 13 | PAD_ScanPads(); | 88 | val = __builtin_bswap32(recv()); |
| 14 | VIDEO_WaitVSync(); | 89 | sleep(1); |
| 90 | } | ||
| 15 | 91 | ||
| 16 | u32 btns = PAD_ButtonsDown(0); | 92 | send(0); |
| 17 | if (btns & mask) | 93 | while (recv()!=0) {sleep(1);} |
| 94 | send(0); | ||
| 95 | |||
| 96 | return val; | ||
| 97 | } | ||
| 98 | |||
| 99 | void getMsgArr(u32* arr, int len) | ||
| 100 | { | ||
| 101 | for (int i=0; i<len; i++) | ||
| 102 | { | ||
| 103 | *(vu32*)(arr+i) = __builtin_bswap32(recv()); | ||
| 104 | usleep(500000); | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | void sendMsg(u32 msg) | ||
| 109 | { | ||
| 110 | while (recv()==0) {sleep(1);} | ||
| 111 | send(msg); | ||
| 112 | while (recv()!=0) {sleep(1);} | ||
| 113 | send(0); | ||
| 114 | } | ||
| 115 | |||
| 116 | void waitForGBA() | ||
| 117 | { | ||
| 118 | resval = 0; | ||
| 119 | |||
| 120 | do | ||
| 121 | { | ||
| 122 | SI_GetTypeAsync(1, acb); | ||
| 123 | |||
| 124 | for (;;) | ||
| 18 | { | 125 | { |
| 19 | return btns; | 126 | if (resval) |
| 127 | { | ||
| 128 | if (resval == 0x80 || resval & 8) | ||
| 129 | { | ||
| 130 | resval = 0; | ||
| 131 | SI_GetTypeAsync(1, acb); | ||
| 132 | } else if (resval) | ||
| 133 | { | ||
| 134 | break; | ||
| 135 | } | ||
| 136 | } | ||
| 20 | } | 137 | } |
| 21 | } | 138 | } while (!(resval & SI_GBA)); |
| 139 | } | ||
| 140 | |||
| 141 | void waitForBIOS() | ||
| 142 | { | ||
| 143 | resbuf[2]=0; | ||
| 144 | |||
| 145 | do | ||
| 146 | { | ||
| 147 | doreset(); | ||
| 148 | } while (!(resbuf[1] > 4)); | ||
| 149 | } | ||
| 150 | |||
| 151 | void waitForGame() | ||
| 152 | { | ||
| 153 | do | ||
| 154 | { | ||
| 155 | doreset(); | ||
| 156 | } while ((resbuf[0] != 0) || !(resbuf[2] & 0x10)); | ||
| 157 | } | ||
| 158 | |||
| 159 | void waitForAck() | ||
| 160 | { | ||
| 161 | while (recv() != 0) {sleep(1);}; | ||
| 162 | send(0); | ||
| 22 | } | 163 | } |
| diff --git a/source/link.h b/source/link.h index 24a60b5..15eff62 100644 --- a/source/link.h +++ b/source/link.h | |||
| @@ -9,6 +9,18 @@ | |||
| 9 | 9 | ||
| 10 | #include <gccore.h> | 10 | #include <gccore.h> |
| 11 | 11 | ||
| 12 | u32 waitForButtons(u32 mask); | 12 | void initLink(); |
| 13 | |||
| 14 | u32 recv(); | ||
| 15 | void send(u32 msg); | ||
| 16 | |||
| 17 | u32 getMsg(); | ||
| 18 | void getMsgArr(u32* arr, int len); | ||
| 19 | void sendMsg(u32 msg); | ||
| 20 | |||
| 21 | void waitForGBA(); | ||
| 22 | void waitForBIOS(); | ||
| 23 | void waitForGame(); | ||
| 24 | void waitForAck(); | ||
| 13 | 25 | ||
| 14 | #endif | 26 | #endif |
| diff --git a/source/main.c b/source/main.c index dd252b5..a3b7525 100644 --- a/source/main.c +++ b/source/main.c | |||
| @@ -10,17 +10,9 @@ | |||
| 10 | #include <stdio.h> | 10 | #include <stdio.h> |
| 11 | #include <stdlib.h> | 11 | #include <stdlib.h> |
| 12 | #include <unistd.h> | 12 | #include <unistd.h> |
| 13 | #include <string.h> | ||
| 14 | #include <malloc.h> | ||
| 15 | #include "link.h" | 13 | #include "link.h" |
| 16 | #include "encoding.h" | 14 | #include "encoding.h" |
| 17 | 15 | #include "multiboot.h" | |
| 18 | //from my tests 50us seems to be the lowest | ||
| 19 | //safe si transfer delay in between calls | ||
| 20 | #define SI_TRANS_DELAY 50 | ||
| 21 | |||
| 22 | extern u8 gba_mb_gba[]; | ||
| 23 | extern u32 gba_mb_gba_size; | ||
| 24 | 16 | ||
| 25 | void printmain() | 17 | void printmain() |
| 26 | { | 18 | { |
| @@ -31,91 +23,27 @@ void printmain() | |||
| 31 | printf("Based on GBA Link Cable Dumper v1.6 by FIX94\n"); | 23 | printf("Based on GBA Link Cable Dumper v1.6 by FIX94\n"); |
| 32 | } | 24 | } |
| 33 | 25 | ||
| 34 | u8 *resbuf,*cmdbuf; | ||
| 35 | |||
| 36 | volatile u32 transval = 0; | ||
| 37 | void transcb(s32 chan, u32 ret) | ||
| 38 | { | ||
| 39 | transval = 1; | ||
| 40 | } | ||
| 41 | |||
| 42 | volatile u32 resval = 0; | ||
| 43 | void acb(s32 res, u32 val) | ||
| 44 | { | ||
| 45 | resval = val; | ||
| 46 | } | ||
| 47 | |||
| 48 | unsigned int docrc(u32 crc,u32 val) | ||
| 49 | { | ||
| 50 | u32 result; | ||
| 51 | |||
| 52 | result = val ^ crc; | ||
| 53 | for (int i = 0; i < 0x20; i++) | ||
| 54 | { | ||
| 55 | if (result & 1) | ||
| 56 | { | ||
| 57 | result >>= 1; | ||
| 58 | result ^= 0xA1C1; | ||
| 59 | } else { | ||
| 60 | result >>= 1; | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | return result; | ||
| 65 | } | ||
| 66 | |||
| 67 | void doreset() | ||
| 68 | { | ||
| 69 | cmdbuf[0] = 0xFF; //reset | ||
| 70 | transval = 0; | ||
| 71 | SI_Transfer(1, cmdbuf, 1, resbuf, 3, transcb, SI_TRANS_DELAY); | ||
| 72 | |||
| 73 | while (transval == 0); | ||
| 74 | } | ||
| 75 | |||
| 76 | void getstatus() | ||
| 77 | { | ||
| 78 | cmdbuf[0] = 0; //status | ||
| 79 | transval = 0; | ||
| 80 | SI_Transfer(1, cmdbuf, 1, resbuf, 3, transcb, SI_TRANS_DELAY); | ||
| 81 | |||
| 82 | while (transval == 0); | ||
| 83 | } | ||
| 84 | |||
| 85 | void endproc() | 26 | void endproc() |
| 86 | { | 27 | { |
| 87 | doreset(); | ||
| 88 | printf("Start pressed, exit\n"); | 28 | printf("Start pressed, exit\n"); |
| 89 | VIDEO_WaitVSync(); | 29 | VIDEO_WaitVSync(); |
| 90 | VIDEO_WaitVSync(); | 30 | VIDEO_WaitVSync(); |
| 91 | exit(0); | 31 | exit(0); |
| 92 | } | 32 | } |
| 93 | 33 | ||
| 94 | u32 recv() | 34 | u32 waitForButtons(u32 mask) |
| 95 | { | ||
| 96 | memset(resbuf,0,32); | ||
| 97 | cmdbuf[0]=0x14; //read | ||
| 98 | transval = 0; | ||
| 99 | SI_Transfer(1, cmdbuf, 1, resbuf, 5, transcb, SI_TRANS_DELAY); | ||
| 100 | |||
| 101 | while (transval == 0); | ||
| 102 | printf("%08lx\n", *(vu32*)resbuf); | ||
| 103 | return *(vu32*)resbuf; | ||
| 104 | } | ||
| 105 | |||
| 106 | void send(u32 msg) | ||
| 107 | { | 35 | { |
| 108 | cmdbuf[0] = 0x15; | 36 | for (;;) |
| 109 | cmdbuf[1] = (msg >> 0) & 0xFF; | 37 | { |
| 110 | cmdbuf[2] = (msg >> 8) & 0xFF; | 38 | PAD_ScanPads(); |
| 111 | cmdbuf[3] = (msg >> 16) & 0xFF; | 39 | VIDEO_WaitVSync(); |
| 112 | cmdbuf[4] = (msg >> 24) & 0xFF; | ||
| 113 | |||
| 114 | transval = 0; | ||
| 115 | resbuf[0] = 0; | ||
| 116 | SI_Transfer(1, cmdbuf, 5, resbuf, 1, transcb, SI_TRANS_DELAY); | ||
| 117 | 40 | ||
| 118 | while (transval == 0); | 41 | u32 btns = PAD_ButtonsDown(0); |
| 42 | if (btns & mask) | ||
| 43 | { | ||
| 44 | return btns; | ||
| 45 | } | ||
| 46 | } | ||
| 119 | } | 47 | } |
| 120 | 48 | ||
| 121 | void warnError(char *msg) | 49 | void warnError(char *msg) |
| @@ -135,132 +63,148 @@ void fatalError(char *msg) | |||
| 135 | exit(0); | 63 | exit(0); |
| 136 | } | 64 | } |
| 137 | 65 | ||
| 138 | u32 genKeyA() | 66 | void* extractor(void* userdata) |
| 139 | { | 67 | { |
| 140 | u32 retries = 0; | ||
| 141 | |||
| 142 | for (;;) | 68 | for (;;) |
| 143 | { | 69 | { |
| 144 | u32 key = 0; | 70 | printmain(); |
| 145 | 71 | ||
| 146 | if (retries > 32) | 72 | printf("Press A when GBA is plugged in and turned off.\n"); |
| 147 | { | 73 | waitForButtons(PAD_BUTTON_A); |
| 148 | key = 0xDD654321; | 74 | |
| 149 | } else { | 75 | printf("Turn on your GBA.\n"); |
| 150 | key = (rand() & 0x00ffffff) | 0xDD000000; | 76 | printf("Waiting for a GBA in port 2...\n"); |
| 151 | } | 77 | waitForGBA(); |
| 152 | 78 | ||
| 153 | u32 unk = (key % 2 != 0); | 79 | printf("GBA Found! Sending multiboot image...\n"); |
| 154 | u32 v12 = key; | 80 | if (!sendMultibootImage()) |
| 155 | for (u32 v13 = 1; v13 < 32; v13++) | ||
| 156 | { | 81 | { |
| 157 | v12 >>= 1; | 82 | warnError("Failed sending multiboot image.\n"); |
| 158 | unk += (v12 % 2 != 0); | 83 | |
| 84 | continue; | ||
| 159 | } | 85 | } |
| 160 | 86 | ||
| 161 | if ((unk >= 10 && unk <= 24)) | 87 | printf("Waiting for GBA...\n"); |
| 88 | waitForAck(); | ||
| 89 | |||
| 90 | VIDEO_WaitVSync(); | ||
| 91 | |||
| 92 | // Get game | ||
| 93 | // -1 - unsupported game | ||
| 94 | // 1 - Ruby | ||
| 95 | // 2 - Sapphire | ||
| 96 | // 3 - FireRed | ||
| 97 | // 4 - LeafGreen | ||
| 98 | // 5 - Emerald | ||
| 99 | u32 gameId = getMsg(); | ||
| 100 | if (gameId == -1) | ||
| 162 | { | 101 | { |
| 163 | if (retries > 4) | 102 | warnError("ERROR: Unsupported GBA game inserted!\n"); |
| 164 | { | ||
| 165 | printf("KeyA retries = %ld", retries); | ||
| 166 | } | ||
| 167 | 103 | ||
| 168 | printf("KeyA = 0x%08lx\n", key); | 104 | continue; |
| 105 | } | ||
| 169 | 106 | ||
| 170 | return key; | 107 | printf("\nPokemon "); |
| 108 | switch (gameId) | ||
| 109 | { | ||
| 110 | case 1: printf("Ruby"); break; | ||
| 111 | case 2: printf("Sapphire"); break; | ||
| 112 | case 3: printf("FireRed"); break; | ||
| 113 | case 4: printf("LeafGreen"); break; | ||
| 114 | case 5: printf("Emerald"); break; | ||
| 171 | } | 115 | } |
| 172 | 116 | ||
| 173 | retries++; | 117 | printf("\n"); |
| 174 | } | 118 | VIDEO_WaitVSync(); |
| 175 | } | ||
| 176 | 119 | ||
| 177 | u32 checkKeyB(u32 KeyBRaw) | 120 | u32 isValid = getMsg(); |
| 178 | { | 121 | if (isValid == -1) |
| 179 | if ((KeyBRaw & 0xFF) != 0xEE) | 122 | { |
| 180 | { | 123 | warnError("ERROR: Unsupported game version inserted!\n"); |
| 181 | printf("Invalid KeyB - lowest 8 bits should be 0xEE, actually 0x%02x\n", | ||
| 182 | ((u8)(KeyBRaw))); | ||
| 183 | 124 | ||
| 184 | return 0; | 125 | continue; |
| 185 | } | 126 | } |
| 186 | 127 | ||
| 187 | u32 KeyB = KeyBRaw & 0xffffff00; | 128 | // Get trainer name |
| 188 | u32 val = KeyB; | 129 | u8 trainerName[8]; |
| 189 | u32 unk = (val < 0); | ||
| 190 | for (u32 i = 1; i < 24; i++) | ||
| 191 | { | ||
| 192 | val <<= 1; | ||
| 193 | unk += (val < 0); | ||
| 194 | } | ||
| 195 | 130 | ||
| 196 | if (unk > 14) | 131 | u32 tnd = getMsg(); |
| 197 | { | 132 | trainerName[0] = (tnd & 0xFF000000) >> 24; |
| 198 | printf("Invalid KeyB - high 24 bits bad: 0x%08lx\n", KeyB); | 133 | trainerName[1] = (tnd & 0x00FF0000) >> 16; |
| 134 | trainerName[2] = (tnd & 0x0000FF00) >> 8; | ||
| 135 | trainerName[3] = (tnd & 0x000000FF); | ||
| 199 | 136 | ||
| 200 | return 0; | 137 | tnd = getMsg(); |
| 201 | } | 138 | trainerName[4] = (tnd & 0xFF000000) >> 24; |
| 139 | trainerName[5] = (tnd & 0x00FF0000) >> 16; | ||
| 140 | trainerName[6] = (tnd & 0x0000FF00) >> 8; | ||
| 141 | trainerName[7] = (tnd & 0x000000FF); | ||
| 202 | 142 | ||
| 203 | printf("Valid KeyB: 0x%08lx\n", KeyB); | 143 | // Get trainer ID |
| 144 | u32 trainerId = getMsg(); | ||
| 204 | 145 | ||
| 205 | return KeyB; | 146 | printf("Trainer: "); |
| 206 | } | ||
| 207 | 147 | ||
| 208 | u32 deriveKeyC(u32 keyCderive, u32 kcrc) | 148 | for (int i = 0; i < 8; i++) |
| 209 | { | 149 | { |
| 210 | u32 keyc = 0; | 150 | if (trainerName[i] == 0xFF) |
| 211 | u32 keyCi = 0; | 151 | { |
| 152 | break; | ||
| 153 | } else { | ||
| 154 | printf("%c", debugGen3Decode(trainerName[i])); | ||
| 155 | } | ||
| 156 | } | ||
| 212 | 157 | ||
| 213 | do | 158 | printf(" (%ld)\n", trainerId); |
| 214 | { | 159 | |
| 215 | u32 v5 = 0x1000000 * keyCi - 1; | 160 | // Wait for confirmation. |
| 216 | u32 keyCattempt = docrc(kcrc,v5); | 161 | printf("Press A to import the data from this game.\n"); |
| 162 | printf("Press B to cancel.\n"); | ||
| 163 | VIDEO_WaitVSync(); | ||
| 217 | 164 | ||
| 218 | if (keyCderive == keyCattempt) | 165 | if (waitForButtons(PAD_BUTTON_A | PAD_BUTTON_B) & PAD_BUTTON_B) |
| 219 | { | 166 | { |
| 220 | keyc = v5; | 167 | printf("Cancelling...\n"); |
| 168 | VIDEO_WaitVSync(); | ||
| 221 | 169 | ||
| 222 | printf("Found keyC: %08lx\n",keyc); | 170 | sendMsg(0); |
| 223 | 171 | ||
| 224 | return keyc; | 172 | continue; |
| 225 | } | 173 | } |
| 226 | 174 | ||
| 227 | keyCi++; | 175 | printf("Importing...\n"); |
| 228 | } while (keyCi < 256); | 176 | VIDEO_WaitVSync(); |
| 229 | 177 | ||
| 230 | return keyc; | 178 | sendMsg(1); |
| 231 | } | ||
| 232 | 179 | ||
| 233 | u32 getMsg() | 180 | // Get Pokédex data |
| 234 | { | 181 | u32 pokedexSeen[13]; |
| 235 | u32 val = 0; | 182 | u32 pokedexCaught[13]; |
| 236 | while (val == 0) | ||
| 237 | { | ||
| 238 | val = __builtin_bswap32(recv()); | ||
| 239 | sleep(1); | ||
| 240 | } | ||
| 241 | 183 | ||
| 242 | send(0); | 184 | getMsgArr(pokedexSeen, 13); |
| 243 | while (recv()!=0) {sleep(1);} | 185 | getMsgArr(pokedexCaught, 13); |
| 244 | send(0); | 186 | int numCaught = 0; |
| 187 | int numSeen = 0; | ||
| 188 | for (int i=0; i<(13*32); i++) | ||
| 189 | { | ||
| 190 | if (pokedexCaught[i >> 5] >> (i & 31) & 1) | ||
| 191 | { | ||
| 192 | //printf("Caught #%d\n", i); | ||
| 193 | numCaught++; | ||
| 194 | numSeen++; | ||
| 195 | } else if (pokedexSeen[i >> 5] >> (i & 31) & 1) | ||
| 196 | { | ||
| 197 | //printf("Saw #%d\n", i); | ||
| 198 | numSeen++; | ||
| 199 | } | ||
| 200 | } | ||
| 245 | 201 | ||
| 246 | return val; | 202 | printf("Caught: %d\nSeen: %d\n", numCaught, numSeen); |
| 247 | } | ||
| 248 | 203 | ||
| 249 | void getMsgArr(u32* arr, int len) | 204 | waitForButtons(PAD_BUTTON_START); |
| 250 | { | ||
| 251 | for (int i=0; i<len; i++) | ||
| 252 | { | ||
| 253 | *(vu32*)(arr+i) = __builtin_bswap32(recv()); | ||
| 254 | usleep(500000); | ||
| 255 | } | 205 | } |
| 256 | } | ||
| 257 | 206 | ||
| 258 | void sendMsg(u32 msg) | 207 | return NULL; |
| 259 | { | ||
| 260 | while (recv()==0) {sleep(1);} | ||
| 261 | send(msg); | ||
| 262 | while (recv()!=0) {sleep(1);} | ||
| 263 | send(0); | ||
| 264 | } | 208 | } |
| 265 | 209 | ||
| 266 | int main(int argc, char *argv[]) | 210 | int main(int argc, char *argv[]) |
| @@ -282,299 +226,27 @@ int main(int argc, char *argv[]) | |||
| 282 | CON_InitEx(rmode, x, y, w, h); | 226 | CON_InitEx(rmode, x, y, w, h); |
| 283 | VIDEO_ClearFrameBuffer(rmode, xfb, COLOR_BLACK); | 227 | VIDEO_ClearFrameBuffer(rmode, xfb, COLOR_BLACK); |
| 284 | PAD_Init(); | 228 | PAD_Init(); |
| 285 | cmdbuf = memalign(32,32); | ||
| 286 | resbuf = memalign(32,32); | ||
| 287 | 229 | ||
| 288 | for (;;) | 230 | initLink(); |
| 289 | { | ||
| 290 | printmain(); | ||
| 291 | 231 | ||
| 292 | printf("Press A to begin, press Start to quit.\n"); | 232 | lwp_t extractorHandle = (lwp_t)NULL; |
| 293 | if (waitForButtons(PAD_BUTTON_A | PAD_BUTTON_START) & PAD_BUTTON_START) | ||
| 294 | { | ||
| 295 | endproc(); | ||
| 296 | } | ||
| 297 | 233 | ||
| 298 | printf("Waiting for a GBA in port 2...\n"); | 234 | LWP_CreateThread( |
| 299 | resval = 0; | 235 | &extractorHandle, // thread handle |
| 236 | extractor, // code | ||
| 237 | NULL, // userdata | ||
| 238 | NULL, // stack base | ||
| 239 | 16*1024, // stack size | ||
| 240 | LWP_PRIO_HIGHEST); // thread priority | ||
| 300 | 241 | ||
| 301 | SI_GetTypeAsync(1,acb); | 242 | for (;;) |
| 302 | while(1) | 243 | { |
| 303 | { | 244 | VIDEO_WaitVSync(); |
| 304 | if (resval) | 245 | PAD_ScanPads(); |
| 305 | { | ||
| 306 | if (resval == 0x80 || resval & 8) | ||
| 307 | { | ||
| 308 | resval = 0; | ||
| 309 | SI_GetTypeAsync(1,acb); | ||
| 310 | } else if (resval) | ||
| 311 | { | ||
| 312 | break; | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | PAD_ScanPads(); | ||
| 317 | VIDEO_WaitVSync(); | ||
| 318 | if (PAD_ButtonsHeld(0) & PAD_BUTTON_START) | ||
| 319 | { | ||
| 320 | getstatus(); | ||
| 321 | endproc(); | ||
| 322 | } | ||
| 323 | } | ||
| 324 | 246 | ||
| 325 | if (resval & SI_GBA) | 247 | if (PAD_ButtonsDown(0) & PAD_BUTTON_START) |
| 326 | { | 248 | { |
| 327 | printf("GBA Found! Waiting on BIOS\n"); | 249 | endproc(); |
| 328 | |||
| 329 | resbuf[2]=0; | ||
| 330 | |||
| 331 | // wait for the BIOS to hand over to the game | ||
| 332 | do | ||
| 333 | { | ||
| 334 | doreset(); | ||
| 335 | } while (!(resbuf[1] > 4)); | ||
| 336 | |||
| 337 | printf("BIOS handed over to game, waiting on game\n"); | ||
| 338 | |||
| 339 | do | ||
| 340 | { | ||
| 341 | doreset(); | ||
| 342 | } while ((resbuf[0] != 0) || !(resbuf[2] & 0x10)); | ||
| 343 | |||
| 344 | // receive the game-code from GBA side. | ||
| 345 | u32 gamecode = recv(); | ||
| 346 | |||
| 347 | printf("Ready, sending multiboot ROM\n"); | ||
| 348 | |||
| 349 | unsigned int sendsize = ((gba_mb_gba_size+7)&~7); | ||
| 350 | |||
| 351 | // generate KeyA | ||
| 352 | unsigned int ourkey = genKeyA(); | ||
| 353 | |||
| 354 | //printf("Our Key: %08x\n", ourkey); | ||
| 355 | printf("Sending game code that we got: 0x%08lx\n", | ||
| 356 | __builtin_bswap32(gamecode)); | ||
| 357 | |||
| 358 | // send the game code back, then KeyA. | ||
| 359 | send(__builtin_bswap32(gamecode)); | ||
| 360 | send(ourkey); | ||
| 361 | |||
| 362 | // get KeyB from GBA, check it to make sure its valid, then xor with KeyA | ||
| 363 | // to derive the initial CRC value and the sessionkey. | ||
| 364 | u32 sessionkeyraw = 0; | ||
| 365 | do | ||
| 366 | { | ||
| 367 | sessionkeyraw = recv(); | ||
| 368 | } while (sessionkeyraw == gamecode); | ||
| 369 | |||
| 370 | sessionkeyraw = checkKeyB(__builtin_bswap32(sessionkeyraw)); | ||
| 371 | if (sessionkeyraw == 0) | ||
| 372 | { | ||
| 373 | warnError("Cannot continue.\n"); | ||
| 374 | |||
| 375 | continue; | ||
| 376 | } | ||
| 377 | |||
| 378 | u32 sessionkey = sessionkeyraw ^ ourkey; | ||
| 379 | u32 kcrc = sessionkey; | ||
| 380 | printf("start kCRC=%08lx\n",kcrc); | ||
| 381 | |||
| 382 | sessionkey = (sessionkey*0x6177614b)+1; | ||
| 383 | |||
| 384 | // send hacked up send-size in uint32s | ||
| 385 | u32 hackedupsize = (sendsize >> 3) - 1; | ||
| 386 | |||
| 387 | printf("Sending hacked up size 0x%08lx\n",hackedupsize); | ||
| 388 | send(hackedupsize); | ||
| 389 | |||
| 390 | //unsigned int fcrc = 0x00bb; | ||
| 391 | // send over multiboot binary header, in the clear until the end of the | ||
| 392 | // nintendo logo. GBA checks this, if nintendo logo does not match the | ||
| 393 | // one in currently inserted cart's ROM, it will not accept any more data. | ||
| 394 | for (int i = 0; i < 0xA0; i+=4) | ||
| 395 | { | ||
| 396 | vu32 rom_dword = *(vu32*)(gba_mb_gba+i); | ||
| 397 | send(__builtin_bswap32(rom_dword)); | ||
| 398 | } | ||
| 399 | |||
| 400 | printf("\n"); | ||
| 401 | printf("Header done! Sending ROM...\n"); | ||
| 402 | |||
| 403 | // Add each uint32 of the multiboot image to the checksum, encrypt the | ||
| 404 | // uint32 with the session key, increment the session key, send the | ||
| 405 | // encrypted uint32. | ||
| 406 | for (int i = 0xA0; i < sendsize; i+=4) | ||
| 407 | { | ||
| 408 | u32 dec = ( | ||
| 409 | (((gba_mb_gba[i+3]) << 24) & 0xff000000) | | ||
| 410 | (((gba_mb_gba[i+2]) << 16) & 0x00ff0000) | | ||
| 411 | (((gba_mb_gba[i+1]) << 8) & 0x0000ff00) | | ||
| 412 | (((gba_mb_gba[i]) << 0) & 0x000000ff) | ||
| 413 | ); | ||
| 414 | |||
| 415 | u32 enc = (dec - kcrc) ^ sessionkey; | ||
| 416 | kcrc = docrc(kcrc,dec); | ||
| 417 | sessionkey = (sessionkey * 0x6177614B) + 1; | ||
| 418 | //enc^=((~(i+(0x20<<20)))+1); | ||
| 419 | //enc^=0x6f646573;//0x20796220; | ||
| 420 | |||
| 421 | send(enc); | ||
| 422 | } | ||
| 423 | |||
| 424 | //fcrc |= (sendsize<<16); | ||
| 425 | printf("ROM done! CRC: %08lx\n", kcrc); | ||
| 426 | //get crc back (unused) | ||
| 427 | |||
| 428 | // Get KeyC derivation material from GBA (eventually) | ||
| 429 | u32 keyCderive = 0; | ||
| 430 | do | ||
| 431 | { | ||
| 432 | keyCderive = recv(); | ||
| 433 | } while (keyCderive <= 0xfeffffff); | ||
| 434 | |||
| 435 | keyCderive = __builtin_bswap32(keyCderive); | ||
| 436 | keyCderive >>= 8; | ||
| 437 | |||
| 438 | printf("KeyC derivation material: %08lx\n",keyCderive); | ||
| 439 | |||
| 440 | // (try to) find the KeyC, using the checksum of the multiboot image, and | ||
| 441 | // the derivation material that GBA sent to us | ||
| 442 | u32 keyc = deriveKeyC(keyCderive,kcrc); | ||
| 443 | if (keyc == 0) | ||
| 444 | { | ||
| 445 | printf("Could not find keyC - kcrc=0x%08lx\n",kcrc); | ||
| 446 | warnError("Cannot continue.\n"); | ||
| 447 | |||
| 448 | continue; | ||
| 449 | } | ||
| 450 | |||
| 451 | // derive the boot key from the found KeyC, and send to GBA. if this is | ||
| 452 | // not correct, GBA will not jump to the multiboot image it was sent. | ||
| 453 | u32 bootkey = docrc(0xBB,keyc) | 0xbb000000; | ||
| 454 | printf("BootKey = 0x%08lx\n",bootkey); | ||
| 455 | |||
| 456 | send(bootkey); | ||
| 457 | sleep(2); | ||
| 458 | |||
| 459 | printf("Waiting for GBA...\n"); | ||
| 460 | while (recv() != 0) {sleep(1);}; | ||
| 461 | send(0); | ||
| 462 | |||
| 463 | VIDEO_WaitVSync(); | ||
| 464 | |||
| 465 | // Get game | ||
| 466 | // -1 - unsupported game | ||
| 467 | // 1 - Ruby | ||
| 468 | // 2 - Sapphire | ||
| 469 | // 3 - FireRed | ||
| 470 | // 4 - LeafGreen | ||
| 471 | // 5 - Emerald | ||
| 472 | u32 gameId = getMsg(); | ||
| 473 | if (gameId == -1) | ||
| 474 | { | ||
| 475 | warnError("ERROR: Unsupported GBA game inserted!\n"); | ||
| 476 | |||
| 477 | continue; | ||
| 478 | } | ||
| 479 | |||
| 480 | printf("\nPokemon "); | ||
| 481 | switch (gameId) | ||
| 482 | { | ||
| 483 | case 1: printf("Ruby"); break; | ||
| 484 | case 2: printf("Sapphire"); break; | ||
| 485 | case 3: printf("FireRed"); break; | ||
| 486 | case 4: printf("LeafGreen"); break; | ||
| 487 | case 5: printf("Emerald"); break; | ||
| 488 | } | ||
| 489 | |||
| 490 | printf("\n"); | ||
| 491 | VIDEO_WaitVSync(); | ||
| 492 | |||
| 493 | u32 isValid = getMsg(); | ||
| 494 | if (isValid == -1) | ||
| 495 | { | ||
| 496 | warnError("ERROR: Unsupported game version inserted!\n"); | ||
| 497 | |||
| 498 | continue; | ||
| 499 | } | ||
| 500 | |||
| 501 | // Get trainer name | ||
| 502 | u8 trainerName[8]; | ||
| 503 | |||
| 504 | u32 tnd = getMsg(); | ||
| 505 | trainerName[0] = (tnd & 0xFF000000) >> 24; | ||
| 506 | trainerName[1] = (tnd & 0x00FF0000) >> 16; | ||
| 507 | trainerName[2] = (tnd & 0x0000FF00) >> 8; | ||
| 508 | trainerName[3] = (tnd & 0x000000FF); | ||
| 509 | |||
| 510 | tnd = getMsg(); | ||
| 511 | trainerName[4] = (tnd & 0xFF000000) >> 24; | ||
| 512 | trainerName[5] = (tnd & 0x00FF0000) >> 16; | ||
| 513 | trainerName[6] = (tnd & 0x0000FF00) >> 8; | ||
| 514 | trainerName[7] = (tnd & 0x000000FF); | ||
| 515 | |||
| 516 | // Get trainer ID | ||
| 517 | u32 trainerId = getMsg(); | ||
| 518 | |||
| 519 | printf("Trainer: "); | ||
| 520 | |||
| 521 | for (int i = 0; i < 8; i++) | ||
| 522 | { | ||
| 523 | if (trainerName[i] == 0xFF) | ||
| 524 | { | ||
| 525 | break; | ||
| 526 | } else { | ||
| 527 | printf("%c", debugGen3Decode(trainerName[i])); | ||
| 528 | } | ||
| 529 | } | ||
| 530 | |||
| 531 | printf(" (%ld)\n", trainerId); | ||
| 532 | |||
| 533 | // Wait for confirmation. | ||
| 534 | printf("Press A to import the data from this game.\n"); | ||
| 535 | printf("Press B to cancel.\n"); | ||
| 536 | VIDEO_WaitVSync(); | ||
| 537 | |||
| 538 | if (waitForButtons(PAD_BUTTON_A | PAD_BUTTON_B) & PAD_BUTTON_B) | ||
| 539 | { | ||
| 540 | printf("Cancelling...\n"); | ||
| 541 | VIDEO_WaitVSync(); | ||
| 542 | |||
| 543 | sendMsg(0); | ||
| 544 | |||
| 545 | continue; | ||
| 546 | } | ||
| 547 | |||
| 548 | printf("Importing...\n"); | ||
| 549 | VIDEO_WaitVSync(); | ||
| 550 | |||
| 551 | sendMsg(1); | ||
| 552 | |||
| 553 | // Get Pokédex data | ||
| 554 | u32 pokedexSeen[13]; | ||
| 555 | u32 pokedexCaught[13]; | ||
| 556 | |||
| 557 | getMsgArr(pokedexSeen, 13); | ||
| 558 | getMsgArr(pokedexCaught, 13); | ||
| 559 | int numCaught = 0; | ||
| 560 | int numSeen = 0; | ||
| 561 | for (int i=0; i<(13*32); i++) | ||
| 562 | { | ||
| 563 | if (pokedexCaught[i >> 5] >> (i & 31) & 1) | ||
| 564 | { | ||
| 565 | //printf("Caught #%d\n", i); | ||
| 566 | numCaught++; | ||
| 567 | numSeen++; | ||
| 568 | } else if (pokedexSeen[i >> 5] >> (i & 31) & 1) | ||
| 569 | { | ||
| 570 | //printf("Saw #%d\n", i); | ||
| 571 | numSeen++; | ||
| 572 | } | ||
| 573 | } | ||
| 574 | |||
| 575 | printf("Caught: %d\nSeen: %d\n", numCaught, numSeen); | ||
| 576 | |||
| 577 | waitForButtons(PAD_BUTTON_START); | ||
| 578 | } | 250 | } |
| 579 | } | 251 | } |
| 580 | 252 | ||
| diff --git a/source/multiboot.c b/source/multiboot.c new file mode 100644 index 0000000..06cbd6f --- /dev/null +++ b/source/multiboot.c | |||
| @@ -0,0 +1,246 @@ | |||
| 1 | #include "multiboot.h" | ||
| 2 | #include <gccore.h> | ||
| 3 | #include <unistd.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | #include <stdlib.h> | ||
| 6 | #include "link.h" | ||
| 7 | |||
| 8 | extern u8 gba_mb_gba[]; | ||
| 9 | extern u32 gba_mb_gba_size; | ||
| 10 | |||
| 11 | unsigned int docrc(u32 crc,u32 val) | ||
| 12 | { | ||
| 13 | u32 result; | ||
| 14 | |||
| 15 | result = val ^ crc; | ||
| 16 | for (int i = 0; i < 0x20; i++) | ||
| 17 | { | ||
| 18 | if (result & 1) | ||
| 19 | { | ||
| 20 | result >>= 1; | ||
| 21 | result ^= 0xA1C1; | ||
| 22 | } else { | ||
| 23 | result >>= 1; | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | return result; | ||
| 28 | } | ||
| 29 | |||
| 30 | u32 genKeyA() | ||
| 31 | { | ||
| 32 | u32 retries = 0; | ||
| 33 | |||
| 34 | for (;;) | ||
| 35 | { | ||
| 36 | u32 key = 0; | ||
| 37 | |||
| 38 | if (retries > 32) | ||
| 39 | { | ||
| 40 | key = 0xDD654321; | ||
| 41 | } else { | ||
| 42 | key = (rand() & 0x00ffffff) | 0xDD000000; | ||
| 43 | } | ||
| 44 | |||
| 45 | u32 unk = (key % 2 != 0); | ||
| 46 | u32 v12 = key; | ||
| 47 | for (u32 v13 = 1; v13 < 32; v13++) | ||
| 48 | { | ||
| 49 | v12 >>= 1; | ||
| 50 | unk += (v12 % 2 != 0); | ||
| 51 | } | ||
| 52 | |||
| 53 | if ((unk >= 10 && unk <= 24)) | ||
| 54 | { | ||
| 55 | if (retries > 4) | ||
| 56 | { | ||
| 57 | printf("KeyA retries = %ld", retries); | ||
| 58 | } | ||
| 59 | |||
| 60 | printf("KeyA = 0x%08lx\n", key); | ||
| 61 | |||
| 62 | return key; | ||
| 63 | } | ||
| 64 | |||
| 65 | retries++; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | u32 checkKeyB(u32 KeyBRaw) | ||
| 70 | { | ||
| 71 | if ((KeyBRaw & 0xFF) != 0xEE) | ||
| 72 | { | ||
| 73 | printf("Invalid KeyB - lowest 8 bits should be 0xEE, actually 0x%02x\n", | ||
| 74 | ((u8)(KeyBRaw))); | ||
| 75 | |||
| 76 | return 0; | ||
| 77 | } | ||
| 78 | |||
| 79 | u32 KeyB = KeyBRaw & 0xffffff00; | ||
| 80 | u32 val = KeyB; | ||
| 81 | u32 unk = (val < 0); | ||
| 82 | for (u32 i = 1; i < 24; i++) | ||
| 83 | { | ||
| 84 | val <<= 1; | ||
| 85 | unk += (val < 0); | ||
| 86 | } | ||
| 87 | |||
| 88 | if (unk > 14) | ||
| 89 | { | ||
| 90 | printf("Invalid KeyB - high 24 bits bad: 0x%08lx\n", KeyB); | ||
| 91 | |||
| 92 | return 0; | ||
| 93 | } | ||
| 94 | |||
| 95 | printf("Valid KeyB: 0x%08lx\n", KeyB); | ||
| 96 | |||
| 97 | return KeyB; | ||
| 98 | } | ||
| 99 | |||
| 100 | u32 deriveKeyC(u32 keyCderive, u32 kcrc) | ||
| 101 | { | ||
| 102 | u32 keyc = 0; | ||
| 103 | u32 keyCi = 0; | ||
| 104 | |||
| 105 | do | ||
| 106 | { | ||
| 107 | u32 v5 = 0x1000000 * keyCi - 1; | ||
| 108 | u32 keyCattempt = docrc(kcrc,v5); | ||
| 109 | |||
| 110 | if (keyCderive == keyCattempt) | ||
| 111 | { | ||
| 112 | keyc = v5; | ||
| 113 | |||
| 114 | printf("Found keyC: %08lx\n",keyc); | ||
| 115 | |||
| 116 | return keyc; | ||
| 117 | } | ||
| 118 | |||
| 119 | keyCi++; | ||
| 120 | } while (keyCi < 256); | ||
| 121 | |||
| 122 | return keyc; | ||
| 123 | } | ||
| 124 | |||
| 125 | bool sendMultibootImage() | ||
| 126 | { | ||
| 127 | printf("Waiting on BIOS\n"); | ||
| 128 | waitForBIOS(); | ||
| 129 | |||
| 130 | printf("BIOS handed over to game, waiting on game\n"); | ||
| 131 | waitForGame(); | ||
| 132 | |||
| 133 | // receive the game-code from GBA side. | ||
| 134 | u32 gamecode = recv(); | ||
| 135 | |||
| 136 | printf("Ready, sending multiboot ROM\n"); | ||
| 137 | |||
| 138 | unsigned int sendsize = ((gba_mb_gba_size+7)&~7); | ||
| 139 | |||
| 140 | // generate KeyA | ||
| 141 | unsigned int ourkey = genKeyA(); | ||
| 142 | |||
| 143 | //printf("Our Key: %08x\n", ourkey); | ||
| 144 | printf("Sending game code that we got: 0x%08lx\n", | ||
| 145 | __builtin_bswap32(gamecode)); | ||
| 146 | |||
| 147 | // send the game code back, then KeyA. | ||
| 148 | send(__builtin_bswap32(gamecode)); | ||
| 149 | send(ourkey); | ||
| 150 | |||
| 151 | // get KeyB from GBA, check it to make sure its valid, then xor with KeyA | ||
| 152 | // to derive the initial CRC value and the sessionkey. | ||
| 153 | u32 sessionkeyraw = 0; | ||
| 154 | do | ||
| 155 | { | ||
| 156 | sessionkeyraw = recv(); | ||
| 157 | } while (sessionkeyraw == gamecode); | ||
| 158 | |||
| 159 | sessionkeyraw = checkKeyB(__builtin_bswap32(sessionkeyraw)); | ||
| 160 | if (sessionkeyraw == 0) | ||
| 161 | { | ||
| 162 | return false; | ||
| 163 | } | ||
| 164 | |||
| 165 | u32 sessionkey = sessionkeyraw ^ ourkey; | ||
| 166 | u32 kcrc = sessionkey; | ||
| 167 | printf("start kCRC=%08lx\n",kcrc); | ||
| 168 | |||
| 169 | sessionkey = (sessionkey*0x6177614b)+1; | ||
| 170 | |||
| 171 | // send hacked up send-size in uint32s | ||
| 172 | u32 hackedupsize = (sendsize >> 3) - 1; | ||
| 173 | |||
| 174 | printf("Sending hacked up size 0x%08lx\n",hackedupsize); | ||
| 175 | send(hackedupsize); | ||
| 176 | |||
| 177 | //unsigned int fcrc = 0x00bb; | ||
| 178 | // send over multiboot binary header, in the clear until the end of the | ||
| 179 | // nintendo logo. GBA checks this, if nintendo logo does not match the | ||
| 180 | // one in currently inserted cart's ROM, it will not accept any more data. | ||
| 181 | for (int i = 0; i < 0xA0; i+=4) | ||
| 182 | { | ||
| 183 | vu32 rom_dword = *(vu32*)(gba_mb_gba+i); | ||
| 184 | send(__builtin_bswap32(rom_dword)); | ||
| 185 | } | ||
| 186 | |||
| 187 | printf("\n"); | ||
| 188 | printf("Header done! Sending ROM...\n"); | ||
| 189 | |||
| 190 | // Add each uint32 of the multiboot image to the checksum, encrypt the | ||
| 191 | // uint32 with the session key, increment the session key, send the | ||
| 192 | // encrypted uint32. | ||
| 193 | for (int i = 0xA0; i < sendsize; i+=4) | ||
| 194 | { | ||
| 195 | u32 dec = ( | ||
| 196 | (((gba_mb_gba[i+3]) << 24) & 0xff000000) | | ||
| 197 | (((gba_mb_gba[i+2]) << 16) & 0x00ff0000) | | ||
| 198 | (((gba_mb_gba[i+1]) << 8) & 0x0000ff00) | | ||
| 199 | (((gba_mb_gba[i]) << 0) & 0x000000ff) | ||
| 200 | ); | ||
| 201 | |||
| 202 | u32 enc = (dec - kcrc) ^ sessionkey; | ||
| 203 | kcrc = docrc(kcrc,dec); | ||
| 204 | sessionkey = (sessionkey * 0x6177614B) + 1; | ||
| 205 | //enc^=((~(i+(0x20<<20)))+1); | ||
| 206 | //enc^=0x6f646573;//0x20796220; | ||
| 207 | |||
| 208 | send(enc); | ||
| 209 | } | ||
| 210 | |||
| 211 | //fcrc |= (sendsize<<16); | ||
| 212 | printf("ROM done! CRC: %08lx\n", kcrc); | ||
| 213 | //get crc back (unused) | ||
| 214 | |||
| 215 | // Get KeyC derivation material from GBA (eventually) | ||
| 216 | u32 keyCderive = 0; | ||
| 217 | do | ||
| 218 | { | ||
| 219 | keyCderive = recv(); | ||
| 220 | } while (keyCderive <= 0xfeffffff); | ||
| 221 | |||
| 222 | keyCderive = __builtin_bswap32(keyCderive); | ||
| 223 | keyCderive >>= 8; | ||
| 224 | |||
| 225 | printf("KeyC derivation material: %08lx\n",keyCderive); | ||
| 226 | |||
| 227 | // (try to) find the KeyC, using the checksum of the multiboot image, and | ||
| 228 | // the derivation material that GBA sent to us | ||
| 229 | u32 keyc = deriveKeyC(keyCderive,kcrc); | ||
| 230 | if (keyc == 0) | ||
| 231 | { | ||
| 232 | printf("Could not find keyC - kcrc=0x%08lx\n",kcrc); | ||
| 233 | |||
| 234 | return false; | ||
| 235 | } | ||
| 236 | |||
| 237 | // derive the boot key from the found KeyC, and send to GBA. if this is | ||
| 238 | // not correct, GBA will not jump to the multiboot image it was sent. | ||
| 239 | u32 bootkey = docrc(0xBB,keyc) | 0xbb000000; | ||
| 240 | printf("BootKey = 0x%08lx\n",bootkey); | ||
| 241 | |||
| 242 | send(bootkey); | ||
| 243 | sleep(2); | ||
| 244 | |||
| 245 | return true; | ||
| 246 | } | ||
| diff --git a/source/multiboot.h b/source/multiboot.h new file mode 100644 index 0000000..a8f5d0d --- /dev/null +++ b/source/multiboot.h | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #ifndef MULTIBOOT_H_5C3CB904 | ||
| 2 | #define MULTIBOOT_H_5C3CB904 | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | |||
| 6 | bool sendMultibootImage(); | ||
| 7 | |||
| 8 | #endif /* end of include guard: MULTIBOOT_H_5C3CB904 */ | ||
