diff options
author | Kelly Rauchenberger <fefferburbia@gmail.com> | 2017-07-13 20:38:04 -0400 |
---|---|---|
committer | Kelly Rauchenberger <fefferburbia@gmail.com> | 2017-07-13 20:38:04 -0400 |
commit | 2190722ac1f0732cf35e7b63572afa698a47789d (patch) | |
tree | 004d2302ece1d9f8feb4d3d435d0393739ebd1bd | |
parent | 252e2911383a5267673f00c08419e2afeac35a31 (diff) | |
download | gen3uploader-2190722ac1f0732cf35e7b63572afa698a47789d.tar.gz gen3uploader-2190722ac1f0732cf35e7b63572afa698a47789d.tar.bz2 gen3uploader-2190722ac1f0732cf35e7b63572afa698a47789d.zip |
Organized code more
Now the link-specific stuff is abstracted into its own file, and the code for negotiating the "different" multiboot protocol is in its own file. Also, removed support for compiling for GC because eventually we will be using Wii-only features. Also put the main extractor code into a thread so that we can monitor for the user pressing the start button to exit.
-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 */ | ||