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 */ | ||