├── .gitignore
├── LICENSE
├── README.md
├── arduino
└── pksploit_link_bridge
│ ├── pksploit_link_bridge.ino
│ └── pokemon.h
├── build
├── build.py
├── libgcc_s_sjlj-1.dll
├── libpng16-16.dll
├── libwinpthread-1.dll
├── rgbasm.exe
├── rgblink.exe
├── sample_config.ini
└── zlib1.dll
├── gb_asm
└── main.asm
├── pksploitlogo.png
└── python
├── constants.py
└── pksploit.py
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | build/config.txt
3 | build/dist/*
4 | build/config.ini
5 | .vscode/tasks.json
6 | *.pyc
7 | *.gb
8 | *.sav
9 | *.bin
10 | *.bak
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 BinaryCounter
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ---
6 | WARNING: UNDER HEAVY DEVELOPMENT!
7 |
8 | BOOTLEG FLASH FUNCTION SOMEWHAT UNTESTED BECAUSE I WRECKED MINE WHILE TRYING TO SOLDER A BATTERY TO IT. WORKED BEFORE I KILLED IT THO :P
9 |
10 | ---
11 |
12 |
13 | ## TL;DR
14 |
15 | This is a suite of tools allowing you to dump rom/save data and reflash save data on any GB and GBC cartridge using nothing but a Pokemon Gen 1 cart, a link cable and an Arduino compatible microcontroller (e.g. Arduino Nano or any ATmega368p board).
16 |
17 | Exploit and some arduino code based on: https://github.com/vaguilar/pokemon-red-cable-club-hack
18 |
19 |
20 | ## Features
21 | * **All Gen 1 carts supported** *(I think)*: Tested on US Pokemon Red, German Pokemon Blue, French Pokemon Yellow.
22 | * **ROM dumping:** Play those classic games on an emulator without relying on virus-riddled (and illegal) ROM download sites.
23 | * **SRAM dumping:** Save your childhood save games from imminent battery doom.
24 | * **SRAM writing:** Ever wanted to try out someone else's save game?
25 | * **Write and execute code:** Try out your small gameboy programs on actual hardware!
26 | * **Quick start without trading:** The included build script generates save files that directly jump into PkSploit's main routine after loading. Have your link up and running in only a few seconds! Those can (of course) be uploaded via PkSploit itself.
27 |
28 | *... And here's where it gets interesting ...*
29 | * **Erase/Rewrite ROM on flash based bootlegs/flashcarts!** Replace the game completely with whatever ROM you want. (Special patches to support saving on carts without battery soon(TM))
30 | #
31 | **Yes that means you can replace that crappy bootleg romhack with Pokemon Prism, LSDj, Nanoloop or whatever you want!**
32 |
33 | *Did i mention those bootlegs cost like 4 USD and basically replace a flashcart!?*
34 | *Did i also mention that the required hardware to do all of this costs less than 2 USD and is easy to build!?*
35 |
36 | ## How?
37 | * The Arduino communicates with the Gameboy using the Link Port. It pretends to be another Gameboy running a Pokemon Gen 1 game, willing to trade.
38 | * When entering the trade, it sends corrupted party data which causes the gameboy to execute a chunk of the party data as code.
39 | * The code can be made to do pretty much anything that fits in ~192 byte, but in this instance it's a routine that reopens the serial link to provide a interface that can be used to read/write to any part of memory within the Gameboy's limitations.
40 | * Since the code is running in WRAM, you can cartswap to read/write to any gameboy cartridge. This works best on a Gameboy Color or Pocket, but should work on all gameboy platforms (better results when using a cheat device as passthrough. See FAQ)
41 | * Overwriting the bootleg works because it uses flash memory instead of ROM. These flash chips have a special command interface allowing to erase sectors and reprogram them. I discovered that this interface is accessable within the gameboy when i was [debugging/reverse engineering](https://gist.github.com/binarycounter/9bd93ef4271a11aee3e395d04b93ed3a) how my Pokemon bootleg could save without a battery. (Hint: The rom is hacked to save to flash instead of SRAM ;))
42 |
43 | ## Hardware Prerequisites
44 | * Arduino compatible microcontroller, preferably one with 5V logic and >16Mhz clock speed. (E.g. an Atmega326p)
45 | * Gameboy DMG, Color, Pocket, Light, GBA, GBA SP... *Basically any Gameboy with a link port.*
46 | * Link Cable or any other way to connect the Arduino to the Gameboy's Link Interface
47 |
48 | ## Software Prerequisites
49 | * Python 2.7
50 | * PySerial
51 | * Arduino IDE
52 | * RGBDS (Included)
53 |
54 | ## Hardware Build
55 | *TODO. Basically cut a link cable in half and wire up 4 pins from it to the arduino.*
56 | ## Software Build
57 | **Currently only builds on Windows, but there's no reason it shouldn't work on other OS (I'm just lazy)**
58 | 1. Install Prerequisites
59 | 2. Clone: `git clone http://github.com/binarycounter/pksploit` (Or just download the repository as a zip)
60 | 3. Enter build directory: `cd PkSploit/build/`
61 | 4. Copy `sample_config.ini` to `config.ini` and edit your path, board name and port.
62 | 5. Build: `py build.py`
63 | 6. Enter python directory: `cd ../python/`
64 | 7. Run: `py pksploit.py`
65 |
66 | ## FAQ
67 | *Q: My bootleg doesn't save when i write to SRAM!*
68 |
69 | **A: Your bootleg likely doesn't include a battery and instead relies on patching the ROM to backup SRAM into flash. In some Pokemon bootlegs you can call `$3FA6` to trigger the routine that does this. For other bootleg games... I don't know. If you send me a tracelog or a romdump/patch i'll let you know if i can support it!**
70 |
71 | *Q: My gameboy keeps crashing or restarting when I attempt cartswapping!*
72 |
73 | **A: How many times did you try it? It can take me up to 10 times (on a bad day) before i successfully cartswap. Use the hacked save files to make attempts faster!**
74 |
75 | *Q: My gameboy still keeps crashing!*
76 |
77 | **A: Try a cheat device (e.g. Action Replay) as passthrough adapter. Those don't connect the RESET line between gameboy and cartridge. This prevents the gameboy from attempting to restart. You can also try putting tape over the 3rd Pin from the right, if you have more patience than me.**
78 |
79 | *Q: I looked into your code and.... what the....*
80 |
81 | **A: Yes, i know. Bare with me, this is my first serious python project. Feel free to refactor this mess...;)**
82 |
83 |
84 | ## Planned Features
85 |
86 | * Find and Port Code to other Link Cable related ACE exploits
87 | * Make GBA version (exploiting multiboot) to allow dump/write/flash of GBA bootlegs.
88 |
89 | ---
90 |
91 | **DISCLAIMER: I AM NOT RESPONSIBLE FOR ANYTHING, INCLUDING LOSS OF DATA, BROKEN GAMEBOYS, OR TEARS BECAUSE YOU ACCIDENTALLY OVERWRITE YOUR CHILDHOOD SAVE FILE WITH YOUR SICK 3 STARTER TEAM**
92 |
93 | **I AM NOT AFFILIATED OR ENDORSED BY NINTENDO. THIS REPOSITORY DOES NOT CONTAIN NINTENDO OR GAMEFREAK CODE OR DATA**
94 |
--------------------------------------------------------------------------------
/arduino/pksploit_link_bridge/pksploit_link_bridge.ino:
--------------------------------------------------------------------------------
1 | // Original file by
2 | // Esteban Fuentealba
3 | // 2014/05/12
4 |
5 | //Heavily modified by BinaryCounter
6 | //to support the PkSploit Interface
7 |
8 | //Hope this satisfies the Apache License :/
9 |
10 |
11 | // Link Cable Arduino Desc
12 | // 6 GND GND
13 | // 5 2 SC
14 | // 2 3 SI
15 | // 3 6 SO
16 | #include
17 |
18 | #include "pokemon.h"
19 | #include "data.h"
20 |
21 | //Faster digital read write code from
22 | //http://masteringarduino.blogspot.de/2013/10/fastest-and-smallest-digitalread-and.html
23 | #define portOfPin(P)\
24 | (((P)>=0&&(P)<8)?&PORTD:(((P)>7&&(P)<14)?&PORTB:&PORTC))
25 | #define ddrOfPin(P)\
26 | (((P)>=0&&(P)<8)?&DDRD:(((P)>7&&(P)<14)?&DDRB:&DDRC))
27 | #define pinOfPin(P)\
28 | (((P)>=0&&(P)<8)?&PIND:(((P)>7&&(P)<14)?&PINB:&PINC))
29 | #define pinIndex(P)((uint8_t)(P>13?P-14:P&7))
30 | #define pinMask(P)((uint8_t)(1<0)
38 | #define isLow(P)((*(pinOfPin(P))& pinMask(P))==0)
39 | #define digitalState(P)((uint8_t)isHigh(P))
40 |
41 | int volatile CLOCK_PIN = 2;
42 | int volatile SO_PIN = 6;
43 | int volatile SI_PIN = 3;
44 | int volatile data = 0;
45 | int volatile val = 0;
46 | byte volatile lsend = 0x00;
47 | int ledStatus = 13;
48 |
49 | unsigned volatile long lastReceive = 0;
50 | volatile byte outputBuffer = 0x00;
51 | volatile int counterRead = 0;
52 | volatile boolean sending = false;
53 | volatile trade_centre_state_t nextstate = PKSPLOIT_MENU;
54 | volatile int counter = 0;
55 | volatile byte command = 0x00;
56 | volatile int counter2 = 00;
57 | volatile int cmddata = -1;
58 | volatile connection_state_t connection_state = NOT_CONNECTED;
59 | volatile trade_centre_state_t trade_centre_state = INIT;
60 | char senddata[1024];
61 | volatile int counter3=0;
62 | volatile boolean fillbuffer = false;
63 |
64 |
65 | void setup() {
66 | pinMode(SI_PIN, INPUT);
67 | digitalWrite( SI_PIN, HIGH);
68 | pinMode(SO_PIN, OUTPUT);
69 | pinMode(ledStatus, OUTPUT);
70 | digitalWrite(ledStatus, LOW);
71 | digitalWrite(SO_PIN, LOW);
72 | pinMode(CLOCK_PIN, INPUT);
73 | digitalWrite(CLOCK_PIN, HIGH);
74 | attachInterrupt( 0, clockInterrupt, RISING );
75 | Serial.begin(28800);
76 | Serial.println("Boot complete");
77 | }
78 |
79 |
80 | void clockInterrupt(void) {
81 | //timer=millis();
82 | byte in;
83 | unsigned long t = 0;
84 | if(lastReceive > 0) {
85 | if( micros() - lastReceive > 120 ) {
86 | counterRead = 0;
87 | val = 0;
88 | in = 0x00;
89 | }
90 | }
91 |
92 | if(digitalState(SI_PIN) == HIGH){
93 | val |= ( 1 << (7-counterRead) );
94 | in |= ( 1 << (7-counterRead) );
95 | }
96 | if(counterRead == 7) {
97 | //Serial.write((byte)val);
98 | outputBuffer = handleIncomingByte((byte)val);
99 |
100 |
101 | val = 0;
102 | in = 0x00;
103 | counterRead = -1;
104 | }
105 |
106 | counterRead++;
107 | lastReceive = micros();
108 | while( ((digitalState(CLOCK_PIN) | CLOCK_PIN) & CLOCK_PIN) == 0);
109 | if(outputBuffer & 0x80 ? SO_PIN : 0!=0)
110 | {
111 | digitalHigh(SO_PIN);
112 | digitalHigh(ledStatus);
113 | }else{digitalLow(SO_PIN);
114 | digitalLow(ledStatus);}
115 |
116 | outputBuffer = outputBuffer << 1;
117 |
118 | }
119 | byte handleIncomingByte(byte in) {
120 |
121 | byte send = 0x00;
122 |
123 | switch(connection_state) {
124 | case NOT_CONNECTED:
125 | Serial.write(".");
126 | if (in==0xCD)
127 | {connection_state = TRADE_CENTRE;
128 | trade_centre_state = PKSPLOIT_MENU;
129 | Serial.print("StatusMQ");}
130 |
131 | if(in == PKMN_MASTER)
132 | send = PKMN_SLAVE;
133 | else if(in == PKMN_BLANK)
134 | send = PKMN_BLANK;
135 | else if(in == PKMN_CONNECTED) {
136 | send = PKMN_CONNECTED;
137 | connection_state = CONNECTED;
138 | Serial.print("StatusConnected");
139 | }
140 | break;
141 |
142 | case CONNECTED:
143 | if(in == PKMN_CONNECTED)
144 | send = PKMN_CONNECTED;
145 | else if(in == PKMN_TRADE_CENTRE){
146 | connection_state = TRADE_CENTRE;
147 | Serial.print("StatusTradeCenter");}
148 | else if(in == PKMN_COLOSSEUM)
149 | connection_state = COLOSSEUM;
150 | else if(in == PKMN_BREAK_LINK || in == PKMN_MASTER) {
151 | connection_state = NOT_CONNECTED;
152 | send = PKMN_BREAK_LINK;
153 | Serial.print("StatusDisconnected");
154 |
155 | } else {
156 | send = in;
157 | }
158 | break;
159 |
160 | case TRADE_CENTRE:
161 | if(trade_centre_state == INIT && in == 0x00) {
162 | if(counter++ == 5) {
163 | trade_centre_state = READY_TO_GO;
164 | Serial.print("StatusSending");
165 | }
166 | send = in;
167 | } else if(trade_centre_state == READY_TO_GO && (in & 0xF0) == 0xF0) {
168 | trade_centre_state = SEEN_FIRST_WAIT;
169 | send = in;
170 | } else if(trade_centre_state == SEEN_FIRST_WAIT && (in & 0xF0) != 0xF0) {
171 | send = in;
172 | counter = 0;
173 | trade_centre_state = SENDING_RANDOM_DATA;
174 | } else if(trade_centre_state == SENDING_RANDOM_DATA && (in & 0xF0) == 0xF0) {
175 | if(counter++ == 5) {
176 | trade_centre_state = WAITING_TO_SEND_DATA;
177 | }
178 | send = in;
179 | } else if(trade_centre_state == WAITING_TO_SEND_DATA && (in & 0xF0) != 0xF0) {
180 | counter = 0;
181 | send = pgm_read_byte_near(DATA_BLOCK+counter++);
182 | trade_centre_state = SENDING_DATA;
183 | } else if(trade_centre_state == SENDING_DATA) {
184 | send = pgm_read_byte_near(DATA_BLOCK+counter++);
185 | if(counter == 619) {
186 | trade_centre_state = PKSPLOIT_MENU;
187 | Serial.print("StatusMenu");
188 | }
189 | }
190 | else if(trade_centre_state == PKSPLOIT_MENU) {
191 |
192 | if(Serial.available()<3 && !sending){trade_centre_state=nextstate;return send;}
193 |
194 | send=Serial.read();
195 | if(sending==false){ //Not already sending, must be first byte (aka command)
196 | switch (send)
197 | {
198 | case 34: //Internal CMD 0x22, set nextstate to PKSPLOIT_DUMP_BLOCK
199 | nextstate=PKSPLOIT_DUMP_BLOCK;
200 | send=00;
201 |
202 | break;
203 | case 68: //Internal CMD 0x44, set nextstate to PKSPLOIT_SEND_BLOCK
204 | nextstate=PKSPLOIT_SEND_BLOCK;
205 | //Serial.print("e");
206 | send=00;
207 | counter3=0;
208 | cmddata=5;
209 | break;
210 | case 119:
211 | //Failsafe to ensure we're still in sync, we don't want to write off by one data or garbage data to the gameboy
212 | if(Serial.read()!=0xDE){Serial.print("no");return 0x00;}
213 | if(Serial.read()!=0xAD){Serial.print("no");return 0x00;}
214 | if(Serial.read()!=0xBE){Serial.print("no");return 0x00;}
215 | if(Serial.read()!=0xEF){Serial.print("no");return 0x00;}
216 | cli();
217 | fillbuffer=true;
218 | return 0x00;
219 | break;
220 | case 17: //Internal CMD 0x11
221 | counter2=Serial.read()*256+Serial.read();
222 | send=00;
223 |
224 | break;
225 | }
226 |
227 | sending=true;}
228 | if(cmddata>0){cmddata--;}
229 | if(cmddata==0){sending=false;}
230 | if(!Serial.available()){sending=false;}
231 |
232 | }
233 | else if(trade_centre_state == PKSPLOIT_DUMP_BLOCK) {
234 | Serial.write(in);
235 | send=Serial.read();
236 | if(counter2==0)
237 | {
238 | trade_centre_state = PKSPLOIT_MENU;
239 | nextstate = PKSPLOIT_MENU;
240 | Serial.print("StatusMenu");
241 | }
242 | counter2--;
243 |
244 |
245 |
246 | }else if(trade_centre_state == PKSPLOIT_SEND_BLOCK) {
247 | send=senddata[counter3];
248 |
249 | if(counter2==counter3)
250 | {
251 | //Serial.print(counter2);
252 | //Serial.print("-");
253 | //Serial.print(counter3);
254 | counter2=0;
255 | counter3=0;
256 | trade_centre_state = PKSPLOIT_MENU;
257 | nextstate = PKSPLOIT_MENU;
258 | cmddata=-1;
259 | Serial.print("StatusMenu");
260 | }
261 | counter3++;
262 | return send;
263 |
264 |
265 | } else {
266 | send = in;
267 | }
268 | break;
269 |
270 | case COLOSSEUM:
271 | send = in;
272 | break;
273 |
274 | default:
275 | send = in;
276 | break;
277 | }
278 | return send;
279 | }
280 |
281 | void loop() {
282 | if(fillbuffer==true)
283 | {
284 | Serial.print("buffer");
285 | Serial.print(counter2);
286 | Serial.flush();
287 | connection_state = NOT_CONNECTED;
288 |
289 | trade_centre_state = INIT;
290 |
291 | //Disconnect because of possible desync. reconnect should be seemless (fingers crossed)
292 | Serial.readBytes(senddata,counter2);
293 |
294 | fillbuffer=false;
295 |
296 | sei();
297 |
298 |
299 |
300 |
301 | }
302 | }
303 |
--------------------------------------------------------------------------------
/arduino/pksploit_link_bridge/pokemon.h:
--------------------------------------------------------------------------------
1 | // Original file by
2 | // Esteban Fuentealba
3 | // 2014/05/12
4 |
5 | //Added definitions for PkSploit related States.
6 | //to support the PkSploit Interface
7 |
8 | //Hope this satisfies the Apache License :/
9 |
10 | typedef enum {
11 | NOT_CONNECTED,
12 | CONNECTED,
13 | TRADE_CENTRE,
14 | COLOSSEUM
15 | } connection_state_t;
16 |
17 | typedef enum {
18 | INIT,
19 | READY_TO_GO,
20 | SEEN_FIRST_WAIT,
21 | SENDING_RANDOM_DATA,
22 | WAITING_TO_SEND_DATA,
23 | START_SENDING_DATA,
24 | SENDING_DATA,
25 | PKSPLOIT_MENU,
26 | PKSPLOIT_SEND_BLOCK,
27 | PKSPLOIT_DUMP_BLOCK
28 |
29 | } trade_centre_state_t;
30 |
31 | typedef unsigned char byte;
32 |
33 | #define PKMN_BLANK 0x00
34 |
35 | #define ITEM_1_HIGHLIGHTED 0xD0
36 | #define ITEM_2_HIGHLIGHTED 0xD1
37 | #define ITEM_3_HIGHLIGHTED 0xD2
38 | #define ITEM_1_SELECTED 0xD4
39 | #define ITEM_2_SELECTED 0xD5
40 | #define ITEM_3_SELECTED 0xD6
41 |
42 | #define PKMN_MASTER 0x01
43 | #define PKMN_SLAVE 0x02
44 | #define PKMN_CONNECTED 0x60
45 | #define PKMN_WAIT 0x7F
46 |
47 | #define PKMN_ACTION 0x60
48 |
49 | #define PKMN_TRADE_CENTRE ITEM_1_SELECTED
50 | #define PKMN_COLOSSEUM ITEM_2_SELECTED
51 | #define PKMN_BREAK_LINK ITEM_3_SELECTED
52 |
53 | #define TRADE_CENTRE_WAIT 0xFD
54 |
55 |
--------------------------------------------------------------------------------
/build/build.py:
--------------------------------------------------------------------------------
1 | #!python2
2 | import os
3 | import sys
4 | import shutil
5 | import ConfigParser
6 |
7 |
8 | print "PkSploit Build Script - Revision 24-11-17"
9 |
10 | if not os.path.exists("config.ini"):
11 | print "No config.ini file! Rename and edit sample_config.ini in this folder!"
12 | sys.exit(1)
13 |
14 | Config = ConfigParser.ConfigParser()
15 | Config.read("config.ini")
16 |
17 | #ConfigParser Helper function from the Python wiki
18 | #https://wiki.python.org/moin/ConfigParserExamples
19 |
20 | def ConfigSectionMap(section):
21 | dict1 = {}
22 | options = Config.options(section)
23 | for option in options:
24 | try:
25 | dict1[option] = Config.get(section, option)
26 | if dict1[option] == -1:
27 | DebugPrint("skip: %s" % option)
28 | except:
29 | print("exception on %s!" % option)
30 | dict1[option] = None
31 | return dict1
32 |
33 |
34 |
35 | choice=""
36 | everything=5
37 | if len(sys.argv) > 1:
38 | if sys.argv[1] == "full":
39 | choice=everything
40 |
41 | odir=ConfigSectionMap("General")["outputdir"]
42 | while choice not in range(1,6):
43 | print "---------------------------"
44 | print "What do you want to build?"
45 | print ""
46 | print "1. Assemble Gameboy ASM"
47 | print "2. Assemble Gameboy ASM (Patch Savefile for quick start)"
48 | print "3. Prepare Arduino Project"
49 | print "4. Build and Upload Arduino Project"
50 | print "5. Everything (run \"py build.py full\" to skip this menu in the future)"
51 | print ""
52 | print "9. Exit"
53 | try:
54 | choice = int(raw_input('--> '))
55 | if choice == 9:
56 | sys.exit(0)
57 | except SystemExit:
58 | sys.exit(0)
59 | except:
60 | print "Invalid Number"
61 | choice = 0
62 |
63 | def assemble():
64 | print "Assembling Gameboy Code with trade offsets"
65 | if os.path.exists(odir+"/gb_asm_trade/"):
66 | shutil.rmtree(odir+"/gb_asm_trade/")
67 | shutil.copytree("../gb_asm/",odir+"/gb_asm_trade/")
68 | fa=open(odir+"/gb_asm_trade/main.asm","rb")
69 | code=fa.read()
70 | fa.close()
71 | fa=open(odir+"/gb_asm_trade/main.asm","wb")
72 | fa.write("rOFFSET EQUS \"$c486\"\n\rrEXTRA EQUS \"\""+code)
73 | fa.close()
74 | os.system("rgbasm.exe -o "+odir+"temp.o "+odir+"gb_asm_trade/main.asm")
75 | os.system("rgblink.exe -o "+odir+"temp.gb "+odir+"temp.o")
76 | fi = open(odir+"temp.gb","rb")
77 | gb_rom = fi.read()
78 | fi.close()
79 | fo = open(odir+"main.bin", "wb")
80 | fo.write(gb_rom[0x150:0x214])
81 | fo.close()
82 | print "Done Assembling Gameboy Code"
83 | return
84 |
85 | def makesave():
86 | print "Assembling Gameboy Code with save file offsets"
87 | if os.path.exists(odir+"/gb_asm_save/"):
88 | shutil.rmtree(odir+"/gb_asm_save/")
89 | shutil.copytree("../gb_asm/",odir+"/gb_asm_save/")
90 | fa=open(odir+"/gb_asm_save/main.asm","rb")
91 | code=fa.read()
92 | fa.close()
93 | fa=open(odir+"/gb_asm_save/main.asm","wb")
94 | fa.write("rOFFSET EQUS \"$d280\"\n\rrEXTRA EQUS \"jr .turnoff\""+code)
95 | fa.close()
96 | os.system("rgbasm.exe -o "+odir+"temp_save.o "+odir+"gb_asm_save/main.asm")
97 | os.system("rgblink.exe -o "+odir+"temp_save.gb "+odir+"temp_save.o")
98 | fi = open(odir+"temp_save.gb","rb")
99 | gb_rom = fi.read()
100 | fi.close()
101 | fo = open(odir+"main_save.bin", "wb")
102 | fo.write(gb_rom[0x150:0x214])
103 | fo.close()
104 | print "Done Assembling Gameboy Code"
105 |
106 | fsg=open("../savefile_templates/pokemon_blue_german.sav","rb")
107 | save=fsg.read()
108 |
109 |
110 |
111 | fsg.close()
112 | fp = open(odir+"main_save.bin","rb")
113 | code=fp.read()
114 | fp.close()
115 |
116 | save=save[:9847]+code+save[10043:]
117 |
118 | #generate valid checksum
119 | checksum=0
120 | save_data = map(ord, save[9624:13602])
121 | for num,bb in enumerate(save_data):
122 | checksum=checksum+bb
123 | flip=0xFF
124 | checksum=chr((checksum%256)^flip)
125 |
126 | save=save[:13603]+checksum+save[13604:]
127 |
128 | fsgs = open(odir+"pokemon_blue_german_pksploit.sav", "wb")
129 | fsgs.write(save)
130 | fsgs.close()
131 | return
132 |
133 |
134 | def prepare():
135 | print "Preparing Arduino Project"
136 |
137 | if not os.path.exists(odir+"main.bin"):
138 | print "Gameboy Code not assembled yet, doing that now..."
139 | assemble()
140 | if os.path.exists(odir+ConfigSectionMap("Arduino")['projectname']+"/"):
141 | shutil.rmtree(odir+ConfigSectionMap("Arduino")['projectname']+"/")
142 | shutil.copytree("../arduino/"+ConfigSectionMap("Arduino")["projectname"]+"/", odir+ConfigSectionMap("Arduino")["projectname"]+"/")
143 |
144 | #Data Preperation Code By
145 | #Esteban Fuentealba
146 |
147 | # load program to run
148 | fp = open(odir+"main.bin","rb")
149 | program_str = fp.read()
150 | fp.close()
151 | program = map(ord, program_str)
152 | data = []
153 |
154 | # seed
155 | data += [182, 147, 113, 81, 51, 23, 228, 205, 184, 165]
156 |
157 | # preamble
158 | data += [253, 253, 253, 253, 253, 253, 253, 253]
159 |
160 | # party (bootstrap)
161 | party = [248, 0, 54, 253, 1, 62, 88, 197, 195, 0xd6, 0xc5, 6, 21, 21, 21, 21, 21, 21, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 206, 227, 227, 255, 33, 160, 195, 1, 136, 1, 62, 0, 205, 224, 54, 17, 24, 218, 33, 89, 196, 205, 85, 25, 195, 21, 218, 139, 142, 128, 131, 136, 141, 134, 232, 232, 232, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 64, 0, 0]
162 | data += party
163 |
164 | # preamble
165 | data += [253, 253, 253, 253, 253]
166 |
167 | # patchlist (196 bytes total)
168 | patchlist = [255, 255] + program + ([0] * 200)
169 | patchlist = patchlist[:196]
170 | data += patchlist
171 |
172 | party_and_patchlist = ", ".join(map(str, party + [253, 253, 253, 253, 253] + patchlist))
173 | fileo = open(odir+ConfigSectionMap("Arduino")["projectname"]+"/data.h","wb")
174 | fileo.write("unsigned const char DATA_BLOCK[] PROGMEM = {" + party_and_patchlist + "};")
175 | fileo.close()
176 | print "Done Preparing Arduino Project"
177 | return
178 |
179 |
180 | def buildarduino():
181 | print "Building and uploading Arduino Project"
182 | if not os.path.exists(odir+ConfigSectionMap("Arduino")["projectname"]+"/"):
183 | print "Arduino Project not prepared yet, doing that now..."
184 | prepare()
185 | os.system("\""+ConfigSectionMap("Arduino")['path']+"/arduino_debug.exe\" -v --upload "+odir+ConfigSectionMap("Arduino")["projectname"]+"/"+ConfigSectionMap("Arduino")["projectname"]+".ino --board "+ConfigSectionMap("Arduino")["board"]+" --port "+ConfigSectionMap("Arduino")["port"])
186 | print "Done Building and uploading Arduino Project"
187 | return
188 |
189 | if not os.path.exists(odir):
190 | os.makedirs(odir)
191 |
192 |
193 | if choice == 1 or choice == everything:
194 | assemble()
195 |
196 | if choice == 2 or choice == everything:
197 | makesave()
198 |
199 | if choice == 3 or choice == everything:
200 | prepare()
201 |
202 | if choice == 4 or choice == everything:
203 | buildarduino()
204 |
205 |
206 |
--------------------------------------------------------------------------------
/build/libgcc_s_sjlj-1.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binarycounter/PkSploit/8c9ba99abb7018dfde181bdd1257941da2c41cd8/build/libgcc_s_sjlj-1.dll
--------------------------------------------------------------------------------
/build/libpng16-16.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binarycounter/PkSploit/8c9ba99abb7018dfde181bdd1257941da2c41cd8/build/libpng16-16.dll
--------------------------------------------------------------------------------
/build/libwinpthread-1.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binarycounter/PkSploit/8c9ba99abb7018dfde181bdd1257941da2c41cd8/build/libwinpthread-1.dll
--------------------------------------------------------------------------------
/build/rgbasm.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binarycounter/PkSploit/8c9ba99abb7018dfde181bdd1257941da2c41cd8/build/rgbasm.exe
--------------------------------------------------------------------------------
/build/rgblink.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binarycounter/PkSploit/8c9ba99abb7018dfde181bdd1257941da2c41cd8/build/rgblink.exe
--------------------------------------------------------------------------------
/build/sample_config.ini:
--------------------------------------------------------------------------------
1 | #EDIT THIS CONFIG AND SAVE IT AS config.ini
2 |
3 | [General]
4 | outputdir=dist/
5 | [Arduino]
6 | path=C:\Program Files (x86)\Arduino
7 | projectname=pksploit_link_bridge
8 | board=arduino:avr:nano:cpu=atmega328
9 | port=COM3
10 |
--------------------------------------------------------------------------------
/build/zlib1.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binarycounter/PkSploit/8c9ba99abb7018dfde181bdd1257941da2c41cd8/build/zlib1.dll
--------------------------------------------------------------------------------
/gb_asm/main.asm:
--------------------------------------------------------------------------------
1 | ;Author: BinaryCounter (23-09-17)
2 |
3 |
4 |
5 | ;Uncomment one of the following 2 lines when not assembling using the buildscript.
6 | ;rOFFSET EQUS "$c486" ;OFFSET for Trade
7 | ;rOFFSET EQUS "$d280" ;OFFSET for SaveFile
8 |
9 | ;When manually assembling for SaveFile uncomment one of these linse too, to turnoff LCD (to prevent burn-in of pokemon center still frame)
10 | ;rEXTRA EQUS ".turnoff" ;Extra for Save
11 | ;rEXTRA EQUS "" ;Extra for trade
12 |
13 | valAA EQUS "$AA"
14 | val55 EQUS "$55"
15 |
16 | SECTION "Program Start",ROM0[$150]
17 | Boot::
18 |
19 | .setup
20 | ld a, [$ffff] ;Disable those pesky serial interrupts, ugh.
21 | and $f7
22 | ld [$ffff], a
23 |
24 | ;set default vars
25 |
26 | ld a, $00
27 | ld [$FFF1], a
28 |
29 | ;Maybe draw something to the screen here?
30 | ;After that, disable interrupts so we have full control
31 |
32 |
33 | di
34 |
35 |
36 | .premenu
37 |
38 | ld a, $FF
39 | ld [$FFF0], a
40 |
41 | .menu
42 |
43 | ;Basic Command interface.
44 | ;Usage: GB sends $CD, Client responds with command
45 | ;Hex commands:
46 | ; $AA - Set Byte
47 | ; $55 - Run Block Transfer Routine (Read/Write blocks of memory)
48 | ; $33 - Jump to Address
49 |
50 | ld a, $CD
51 | call serial + rOFFSET
52 | cp $AA
53 | jr z, .setbyte
54 | cp $55
55 | jr z, .transfer
56 | cp $33
57 | jr z, .jump
58 | cp $66
59 | jr z, .turnoff
60 | jr .menu
61 |
62 | ;Command 66, jump to address
63 | ;Usage: GB waits for Vblank and turns off LCD.
64 | .turnoff:
65 | ld a, [$FF40]
66 | bit 7, a
67 | jr z, .premenu ;Return right away if screen already off
68 | ld a,[$FF44] ; Loop until in first part of vblank
69 | cp 145
70 | jr nz,.turnoff
71 | ld hl, $FF40
72 | res 7,[hl]
73 | jr .premenu
74 |
75 | ;Command 33, jump to address
76 | ;Usage: GB sends $10, Client responds with High byte of address,
77 | ; GB sends $20, Client responds with low byte of address.
78 | ; GB jumps to address, return to menu with a ret instruction
79 |
80 | .jump
81 | call getaddress + rOFFSET
82 | call callwrapper + rOFFSET
83 | jr .premenu
84 |
85 |
86 |
87 | ;Command AA, set byte
88 | ;Usage: GB sends $10, Client responds with High byte of address,
89 | ; GB sends $20, Client responds with low byte of address.
90 | ; GB sends $30, Client responds with byte to be written,
91 | ; command writes byte and returns to menu
92 | .setbyte
93 | call getaddress + rOFFSET
94 | ld a, $30
95 | call serial + rOFFSET
96 | ld [hl], a
97 | jr .menu
98 |
99 |
100 | ;Command 55, Block transfer
101 | ;Usage: GB sends $10, Client responds with High byte of byte count,
102 | ; GB sends $20, Client responds with low byte of byte count.
103 | ; GB sends $10, Client responds with High byte of start address,
104 | ; GB sends $20, Client responds with low byte of start address.
105 | ; Command performs block transfer, returns to menu
106 |
107 | .transfer
108 | ld a, [$FFF1]
109 | ld d, a ; load command Settings (See set byte for options)
110 | call getaddress + rOFFSET
111 | ld b, h
112 | ld c, l
113 | call getaddress + rOFFSET
114 |
115 | .loop1
116 | ld a, [hl]
117 | call serial + rOFFSET
118 | bit 0, d ;Is write bit set?
119 | jr z, .skipwrite
120 | bit 1, d ;Bootleg write set?
121 | jr z, .skipbootleg
122 | call bootlegwrite + rOFFSET
123 | .skipbootleg
124 | ld [hl], a
125 | .skipwrite
126 | inc hl
127 | ;------
128 | dec bc
129 | ld a, b
130 | or c
131 | jr nz, .loop1
132 | ;loopend
133 |
134 |
135 | jr .premenu
136 |
137 | bootlegwrite:
138 |
139 | ld e, a
140 | ld a, [$FFF2]
141 | ld [$2100], a
142 | call delay + rOFFSET
143 | ld a, valAA
144 | ld [$0AAA], a
145 | nop
146 | ld a, val55
147 | ld [$0555], a
148 | nop
149 | ld a, $A0
150 | ld [$0AAA], a
151 | nop
152 | ld a,e
153 | ld [hl], a
154 | ; .waitforwrite
155 | ; ld a, [hl]
156 | ; cp b
157 | ; jr nz, .waitforwrite
158 | call delay + rOFFSET
159 | ret
160 |
161 |
162 |
163 | ;General-Purpose functions
164 |
165 | getaddress: ; GB sends $10, Client responds with High byte of address,
166 | ; GB sends $20, Client responds with low byte of address.
167 | ; destroys a, returns address in hl
168 | ld a, $10
169 | call serial + rOFFSET
170 | ld h, a
171 | ld a, $20
172 | call serial + rOFFSET
173 | ld l, a
174 | ret
175 |
176 | serial: ;writes value in A to serial and puts response in A
177 |
178 |
179 | ld [$ff01],a ;Serial Data Register
180 | ld a, $81 ;
181 | ld [$ff02],a ;Serial Mode
182 | .waitloop1
183 | ld a, [$ff02]
184 | and $80
185 | jr nz, .waitloop1 ;Waits for data
186 | call delay + rOFFSET
187 | ld a, [$ff01]
188 | ret
189 |
190 | callwrapper:
191 | jp hl
192 |
193 | delay: ;delays by value in FFF0
194 | ld a, [$FFF0]
195 | jr z, .skipdelay
196 | .wastetime
197 | dec a
198 | nop
199 | nop
200 | nop
201 | nop
202 | nop
203 | nop
204 | nop
205 | nop
206 | nop
207 | nop
208 | jr nz, .wastetime
209 | .skipdelay
210 | ret
211 | ;loopend
212 |
--------------------------------------------------------------------------------
/pksploitlogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binarycounter/PkSploit/8c9ba99abb7018dfde181bdd1257941da2c41cd8/pksploitlogo.png
--------------------------------------------------------------------------------
/python/constants.py:
--------------------------------------------------------------------------------
1 | #Com port to use
2 | port="COM8"
3 |
4 | #Nintendo logo hex dump extracted from a original Pokemon Blue cart
5 | nintendologo = "".join(map(chr,[0xCE,0xED,0x66,0x66,0xCC,0x0D,0x00,0x0B,0x03,0x73,0x00,0x83,0x00,0x0C,0x00,0x0D,0x00,0x08,0x11,0x1F,0x88,0x89,0x00,0x0E,0xDC,0xCC,0x6E,0xE6,0xDD,0xDD,0xD9,0x99,0xBB,0xBB,0x67,0x63,0x6E,0x0E,0xEC,0xCC,0xDD,0xDC,0x99,0x9F,0xBB,0xB9,0x33,0x3E]))
6 |
7 | #PkSploit logo using Pokemon Blue tiles (does not work in rom/sram mode, only for trade ACE)
8 | pksploitlogo = "".join(map(chr,[0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x78,0x79,0x79,0x79,0x79,0x79,0x79,0x79,0x79,0x79,0x79,0x79,0x79,0x7A,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7B,0x7F,0x19,0x19,0x19,0x7F,0x7F,0x19,0x7F,0x7F,0x7F,0x19,0x7F,0x77,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7B,0x7F,0x19,0x7F,0x7F,0x19,0x7F,0x19,0x7F,0x7F,0x19,0x7F,0x7F,0x77,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7B,0x7F,0x19,0x7F,0x7F,0x19,0x7F,0x19,0x7F,0x19,0x7F,0x7F,0x7F,0x77,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7B,0x7F,0x19,0x19,0x19,0x7F,0x7F,0x19,0x19,0x7F,0x7F,0x7F,0x7F,0x77,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7B,0x7F,0x19,0x7F,0x7F,0x7F,0x7F,0x19,0x7F,0x19,0x7F,0x7F,0x7F,0x77,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7B,0x7F,0x19,0x7F,0x7F,0x7F,0x7F,0x19,0x7F,0x7F,0x19,0x7F,0x7F,0x77,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7B,0x7F,0x19,0x7F,0x7F,0x7F,0x7F,0x19,0x7F,0x7F,0x7F,0x19,0x7F,0x77,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7B,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x77,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7B,0x7F,0x7F,0x7F,0x92,0xAF,0xAB,0xAE,0xA8,0xB3,0x7F,0x7F,0x7F,0x77,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7C,0x76,0x76,0x76,0x76,0x76,0x76,0x76,0x76,0x76,0x76,0x76,0x76,0x7D,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F]))
9 |
10 |
11 | #Header information from http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header
12 | rombanks=range(0,0x55)
13 | rombanks[0]=2
14 | rombanks[1]=4
15 | rombanks[2]=8
16 | rombanks[3]=16
17 | rombanks[4]=32
18 | rombanks[5]=64
19 | rombanks[6]=128
20 | rombanks[7]=256
21 | rombanks[8]=512
22 | rombanks[0x52]=72
23 | rombanks[0x53]=80
24 | rombanks[0x54]=96
25 | srambanks=range(0,6)
26 | srambanks[0]=0
27 | srambanks[1]=-1 # Special condition: Only 1/4th of a bank is used
28 | srambanks[2]=1
29 | srambanks[3]=4
30 | srambanks[4]=16
31 | srambanks[5]=8
32 |
33 | modeRead=0x00
34 | modeWrite=0x01
35 | modeFlash=0x03
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/python/pksploit.py:
--------------------------------------------------------------------------------
1 | #!python2
2 | import os
3 | import sys
4 | if (sys.version_info > (3, 0)):
5 | print("This script is for Python 2.x only. Sorry about that :/")
6 | exit(1)
7 | import serial
8 | import threading
9 | import time
10 | import Tkinter, tkFileDialog
11 | import constants
12 |
13 | #Let's define some stuff
14 |
15 | ser = serial.Serial()
16 |
17 | ser.baudrate = 28800
18 | ser.port = constants.port
19 | ser.timeout = 0.05
20 |
21 | state = "none"
22 | cmdstr=""
23 | cmdpar1=0
24 | cmdpar2=0
25 | cmdpar3=0
26 | cmdpar4=0
27 | cmddone=False
28 | cmdoutput=""
29 | lcdon=True
30 | mq=False
31 |
32 | #Change to 0xA9 and 0x56 if bootleg flash functions don't work
33 | valAA=0xAA
34 | val55=0x55
35 |
36 |
37 | root = Tkinter.Tk()
38 | root.withdraw()
39 |
40 | cartmbc=0
41 | cartbanks=0
42 | cartsrambanks=0
43 | cartname=""
44 |
45 |
46 |
47 | def main():
48 | global state, mq
49 | state="noserial"
50 |
51 | print "Waiting for Serial Port to Open... (If the program hangs here, close your Arduino Software)."
52 |
53 |
54 | ser.open() #Open Serial Connection (This blocks until port is open)
55 |
56 | print "Serial port opened."
57 |
58 | state="serialopen" #Signal state of the port to serial handler
59 |
60 | #Start serial handler thread
61 | serialprocess = threading.Thread(target = serialf)
62 | serialprocess.daemon = True
63 | serialprocess.start()
64 |
65 | print "Waiting for Arduino Reset. (Press the reset button on your arduino if this hangs)"
66 |
67 | while state!="arduinoconnected": #Block until serial thread signals that the arduino is ready
68 | time.sleep(0.1)
69 |
70 | print "Reset complete. Please initiate the trade (or load the prepared save file) on the gameboy."
71 |
72 | while state!="pksploit_menu": #Block until serial thread signals that Gameboy loaded PkSploit
73 | time.sleep(0.1)
74 | if state=="pksploit_mq": #Special case for when the gameboy is already running PkSploit without trading (e.g from a rom, or after a restart)
75 | print "Quick Menu triggered. Gameboy is already running PkSploit!"
76 | state="pksploit_menu"
77 | mq=True
78 | print "Initializing..."
79 |
80 |
81 | #Start Cli interface thread
82 | interfaceprocess = threading.Thread(target = interfacef)
83 | interfaceprocess.daemon = True
84 | interfaceprocess.start()
85 | while True:
86 | time.sleep(1)
87 |
88 |
89 |
90 |
91 |
92 | #Main cli interface
93 | def interfacef():
94 | global cmdoutput, state, mq
95 | if mq == False:
96 | time.sleep(2)
97 | initpksploit()
98 | while True:
99 | choice=""
100 | while choice not in range(1,20):
101 | print "---------------------------"
102 | print "What do you want to do?"
103 | print ""
104 | print "1. Display And Verify Header 5. Write SRAM (Deletes Save) 9. Bootleg/FC: Erase Flash"
105 | print "2. Dump ROM 6. Write Byte 10. Bootleg/FC: Flash ROM"
106 | print "3. Dump SRAM 7. Call address 11. Bootleg/FC: Flash Byte"
107 | print "4. Dump Custom Block 8. Restart Gameboy (Call $100) 12. Toggle LCD"
108 | print " "
109 | print "0. Exit"
110 |
111 |
112 |
113 | try:
114 | choice = int(raw_input('--> '))
115 | if choice == 0:
116 | sys.exit(0)
117 | except SystemExit:
118 | os._exit(1)
119 | except:
120 | print "Invalid Number"
121 | choice = 0
122 |
123 | options = {1: verifyheader,
124 | 2: dumprom,
125 | 3: dumpsram,
126 | 4: dumpcustom,
127 | 5: writesram,
128 | 6: writebyte,
129 | 7: jumpto,
130 | 8: restartgb,
131 | 9: eraseflash,
132 | 10:flashrom,
133 | 11:flashbyte,
134 | 12:togglelcd,
135 | 13:flashexperiment,
136 | 14:flashromexpe,
137 | 15:teststuff}
138 |
139 | options[choice]()
140 |
141 |
142 |
143 | def initpksploit():
144 |
145 | command("setbyte",0xFF,0x26,0x00,0x00) #Mute Sound
146 | command("turnofflcd",0,0,0,0)
147 | #command("send",0x9C,0x00,constants.pksploitlogo,constants.modeWrite)
148 | command("turnonlcd",0,0,0,0)
149 | command("setbyte",0x00,0x00,0x00,0x00) #Disable SRAM access for safety reasons
150 | command("setbyte",0xFF,0xF1,constants.modeRead,0x00) #Disable writing for safety reasons
151 | command("setbyte",0xFF,0xF0,0x00,0x00) #Let's speed this bi--- up
152 | return
153 | def verifyheader():
154 | #Header information from http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header
155 | #This doesn't actually verify the checksum, only the logo. But i figured, if 48 byte in a row are dumped correctly, the cart is probably working.
156 | global cmdoutput, nintendologo, cartmbc, cartbanks, srambanks, cartname, cartsrambanks
157 | command("dump",0x00,0x50,0x01,0x00) #Dump $50 bytes at address $0100
158 | if cmdoutput[4:-28]!=constants.nintendologo: #Check if logo on the cart matches nintendo logo
159 | print "Nintendo logo doesn't match."
160 | print "Cartridge possibly drity or broken."
161 | print "Header dump ($100-$150):"
162 | print " ".join("{:02x}".format(ord(c)) for c in cmdoutput)
163 | return False #Return false if it doesn't. Stops rom dump functions from dumping invalid roms
164 | else:
165 | print "Cart has a valid Nintendo Logo in the header"
166 | print "Cart name: "+cmdoutput[52:-12]
167 | cartname=cmdoutput[52:-12]
168 |
169 | if ord(cmdoutput[67:-12]) == 0x80:
170 | print "Cart has Gameboy Color enhancements"
171 | if ord(cmdoutput[67:-12]) == 0xC0:
172 | print "Cart Is a Gameboy Color Exclusive"
173 |
174 | if ord(cmdoutput[70:-9]) == 0x03:
175 | print "Cart has Super Gameboy enhancements"
176 | if ord(cmdoutput[74:-5]) == 0x00:
177 | print "Cart is a Japanese exclusive"
178 | if ord(cmdoutput[74:-5]) == 0x01:
179 | print "Cart is a Non-Japanese exclusive"
180 | print "Cart Type: "+str(ord(cmdoutput[71:-8]))
181 | cartmbc=ord(cmdoutput[71:-8])
182 | print "ROM Size: "+str(constants.rombanks[ord(cmdoutput[72:-7])]*16)+ "KB / " + str(constants.rombanks[ord(cmdoutput[72:-7])])+ " Banks"
183 | cartbanks=constants.rombanks[ord(cmdoutput[72:-7])]
184 |
185 | if ord(cmdoutput[73:-6]) == 0:
186 | print "RAM Size: 0KB (Cart doesn't have SRAM)"
187 | else:
188 | if ord(cmdoutput[73:-6]) == -1:
189 | print "RAM Size: 2KB / 1 Bank (Only 1/4th of the bank is mapped)"
190 | else:
191 | print "RAM Size: " + str(constants.srambanks[ord(cmdoutput[73:-6])]*8) + "KB / " + str(constants.srambanks[ord(cmdoutput[73:-6])]) + " Bank(s)"
192 |
193 | cartsrambanks=constants.srambanks[ord(cmdoutput[73:-6])]
194 |
195 | print "Header dump ($100-$150):"
196 | print " ".join("{:02x}".format(ord(c)) for c in cmdoutput)
197 |
198 |
199 | return True
200 |
201 | def dumprom():
202 | global root, cartname, cartbanks
203 |
204 | if verifyheader()==False:
205 | print "Header is broken, aborted dump."
206 | return False
207 | print "Validated header."
208 | file_path = tkFileDialog.asksaveasfilename(defaultextension=".gb", initialfile=''.join(e for e in cartname if e.isalnum())+".gb") #Get filename to write
209 | if file_path == "None" or file_path == "":
210 | return False
211 | fo = open(file_path, "wb")
212 | rom=""
213 | banks=cartbanks #Get number of banks
214 | i=0
215 | command("setbyte",0xFF,0xF0,0x00,0x00)
216 | command("dump",0x40,0x00,0x00,0x00)
217 | rom=cmdoutput
218 | fo.write(cmdoutput)
219 | i+=1
220 | print "Dumped "+str(i) +"/"+str(banks)+ " Banks."
221 | while i < banks:
222 | command("setbyte",0xFF,0xF0,0x00,0x00)
223 | command("setbyte",0x20,0x00,i,0x00)
224 | command("dump",0x40,0x00,0x40,0x00)
225 | rom+=cmdoutput
226 | fo.write(cmdoutput)
227 | fo.flush()
228 | os.fsync(fo)
229 | i+=1
230 | print "Dumped "+str(i) +"/"+str(banks)+ " Banks."
231 |
232 | print "Rom dump complete"
233 |
234 |
235 | fo.close()
236 | return rom
237 |
238 |
239 | def dumpsram():
240 | global root, cartname, cartsrambanks
241 |
242 | if verifyheader()==False:
243 | print "Header is broken, aborted dump."
244 | return False
245 | print "Validated header."
246 |
247 | banks=cartsrambanks
248 | if banks == 0:
249 | print "No SRAM detected. aborted dump."
250 | return False
251 |
252 | file_path = tkFileDialog.asksaveasfilename(defaultextension=".sav", initialfile=''.join(e for e in cartname if e.isalnum())+".sav")
253 | if file_path == "None" or file_path == "":
254 | return False
255 | fo = open(file_path, "wb")
256 | sram=""
257 | command("setbyte",0x00,0x00,0x0a,0x00) #Enable SRAM
258 | if banks == -1:
259 | print "2KB SRAM dumping not supported yet."
260 | else:
261 | i=0
262 | while i < banks:
263 | i+=1
264 |
265 | command("setbyte",0x40,0x00,i-1,0x00) #Switch SRAM Bank
266 | command("dump",0x20,0x00,0xa0,0x00)
267 | sram=cmdoutput
268 | fo.write(cmdoutput)
269 | fo.flush()
270 | os.fsync(fo)
271 | print "Dumped "+str(i) +"/"+str(banks)+ " Banks."
272 |
273 | command("setbyte",0x00,0x00,0x00,0x00) #Disable SRAM
274 | print "SRAM dump complete"
275 |
276 |
277 | fo.close()
278 | return sram
279 |
280 | def dumpcustom():
281 | a1 = int(raw_input('Start Address (High Byte)> '), 16)
282 | a2 = int(raw_input('Start Address (Low Byte)> '), 16)
283 | c1 = int(raw_input('Amount of bytes (High Byte)> '), 16)
284 | c2 = int(raw_input('Amount of bytes (Low Byte)> '), 16)
285 | command("dump",c1,c2,a1,a2)
286 | print " ".join("{:02x}".format(ord(c)) for c in cmdoutput)
287 |
288 |
289 | def writesram():
290 | global root, cartname, cartsrambanks
291 |
292 | if verifyheader()==False:
293 | print "Header is broken, aborted write."
294 | return False
295 | print "Validated header."
296 |
297 | banks=cartsrambanks
298 | if banks == 0:
299 | print "No SRAM detected. aborting write."
300 | return False
301 |
302 | file_path = tkFileDialog.askopenfilename(defaultextension=".sav", initialfile=''.join(e for e in cartname if e.isalnum())+".sav")
303 | if file_path == "None" or file_path == "":
304 | return False
305 | fo = open(file_path, "rb")
306 | save = fo.read()
307 | sram=""
308 |
309 | command("setbyte",0x00,0x00,0x0a,0x00) #Enable SRAM
310 |
311 | if banks == -1:
312 | print "2KB SRAM writing not supported yet."
313 | else:
314 | i=0
315 | while i < banks:
316 | i+=1
317 |
318 | data=save[0+(i-1)*8192:8192+(i-1)*8192]
319 | command("setbyte",0x00,0x00,0x0a,0x00) #Enable SRAM
320 | command("setbyte",0x40,0x00,i-1,0x00) #Switch SRAM Bank
321 |
322 | #TODO make this mess into a loop
323 | command("send",0xA0,0x00,data[0:1024],constants.modeWrite)
324 | command("send",0xA4,0x00,data[1024:2048],constants.modeWrite)
325 | command("send",0xA8,0x00,data[2048:3072],constants.modeWrite)
326 | command("send",0xAC,0x00,data[3072:4096],constants.modeWrite)
327 | command("send",0xB0,0x00,data[4096:5120],constants.modeWrite)
328 | command("send",0xB4,0x00,data[5120:6144],constants.modeWrite)
329 | command("send",0xB8,0x00,data[6144:7168],constants.modeWrite)
330 | command("send",0xBC,0x00,data[7168:8192],constants.modeWrite)
331 |
332 |
333 |
334 | print "Wrote "+str(i) +"/"+str(banks)+ " Banks."
335 | command("setbyte",0xFF,0xF1,constants.modeRead,0x00) #Disable Writing
336 | command("setbyte",0x00,0x00,0x00,0x00) #Disable SRAM
337 | print "SRAM write complete"
338 |
339 |
340 | fo.close()
341 | return
342 |
343 |
344 | def writebyte():
345 | a1 = int(raw_input(' Address (High Byte)> '), 16)
346 | a2 = int(raw_input(' Address (Low Byte)> '), 16)
347 | b = int(raw_input('Byte to be written> '), 16)
348 | command("setbyte",a1,a2,b,0x00)
349 | return
350 |
351 | def jumpto():
352 | a1 = int(raw_input(' Address (High Byte)> '), 16)
353 | a2 = int(raw_input(' Address (Low Byte)> '), 16)
354 | command("jump",a1,a2,0,0)
355 | print "Jumped. PkSploit will be unresponsive until the Gameboy returns. (If it ever does)"
356 | return
357 |
358 | def restartgb():
359 | command("jump",0x01,0x00,0,0)
360 | print "Restarted gameboy, exiting PkSploit."
361 | os._exit(1)
362 | return
363 |
364 | def eraseflash():
365 | command("setbyte",0x21,0x00,0x00,0x00)
366 | command("setbyte",0x0A,0xAA,valAA,0x00)
367 | command("setbyte",0x05,0x55,val55,0x00)
368 | command("setbyte",0x0A,0xAA,0x80,0x00)
369 | command("setbyte",0x0A,0xAA,valAA,0x00)
370 | command("setbyte",0x05,0x55,val55,0x00)
371 | command("setbyte",0x0a,0xaa,0x10,0x00)
372 | print "Erasing flash..."
373 | print "This can take up to 30 seconds, so let's wait that long."
374 | time.sleep(30)
375 | return
376 |
377 | def flashrom():
378 | print "Not yet implemented, sorry :/"
379 | return
380 |
381 | def togglelcd():
382 | global lcdon
383 | if lcdon == True:
384 | command("turnofflcd",0,0,0,0)
385 | print "LCD off."
386 | else:
387 | command("turnonlcd",0,0,0,0)
388 | print "LCD on."
389 | return
390 |
391 | def teststuff():
392 | data=constants.data[0:1024]
393 | c=chr(len(data) / 256)
394 | print "{:02x}".format(ord(c))
395 | c=chr(len(data) % 256)
396 | print "{:02x}".format(ord(c))
397 | return
398 |
399 | def flashromexpe():
400 | global cmdoutput
401 | #data="".join(map(chr,[0xAF,0xEA,0x05,0xC0,0xFA,0x05,0xC0,0xA7,0xC0,0x76,0x00,0x18,0xF7,0xFF,0xFF,0xFF,0xC9,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC3,0x40,0xC2,0xFF,0xFF,0xFF,0xFF,0xFF,0xC3,0x43,0xC2,0xFF,0xFF,0xFF,0xFF,0xFF,0xD9,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xD9,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xD9,0x21,0x01,0xC0,0x01,0xFF,0x1F,0x18,0x0E,0x21,0x00,0x98,0x01,0x00,0x08,0x18,0x06,0x21,0x00,0x80,0x01,0x00,0x20,0xAF,0xC3,0x1C,0x0C,0xF5,0xF0,0x41,0xE6,0x02,0x28,0xFA,0xF0,0x41,0xE6,0x02,0x20,0xFA,0xF1,0xC9,0x3E,0x20,0xE0,0x00,0xF0,0x00,0xF0,0x00,0x2F,0xE6,0x0F,0xCB,0x37,0x47,0x3E,0x10,0xE0,0x00,0xF0,0x00,0xF0,0x00,0xF0,0x00,0xF0,0x00,0xF0,0x00,0xF0,0x00,0x2F,0xE6,0x0F,0xB0,0x47,0xFA,0x03,0xC0,0xA8,0xA0,0xEA,0x02,0xC0,0x78,0xEA,0x03,0xC0,0x3E,0x30,0xE0,0x00,0xC9,0xF0,0x40,0xCB,0x7F,0xC2,0x53,0x0F,0x11,0x00,0x98,0x06,0x12,0x0E,0x14,0x2A,0x12,0x13,0x0D,0x20,0xFA,0x0E,0x14,0x7B,0xC6,0x0C,0x30,0x01,0x14,0x5F,0x05,0x20,0xEE,0xC9,0x11,0x00,0x98,0x06,0x12,0x0E,0x14,0x2A,0xD6,0x20,0x12,0x13,0x0D,0x20,0xF8,0x0E,0x14,0x7B,0xC6,0x0C,0x30,0x01,0x14,0x5F,0x05,0x20,0xEC,0xC9,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0xC3,0x50,0x01,0xCE,0xED,0x66,0x66,0xCC,0x0D,0x00,0x0B,0x03,0x73,0x00,0x83,0x00,0x0C,0x00,0x0D,0x00,0x08,0x11,0x1F,0x88,0x89,0x00,0x0E,0xDC,0xCC,0x6E,0xE6,0xDD,0xDD,0xD9,0x99,0xBB,0xBB,0x67,0x63,0x6E,0x0E,0xEC,0xCC,0xDD,0xDC,0x99,0x9F,0xBB,0xB9,0x33,0x3E,0x42,0x4F,0x54,0x42,0x20,0x49,0x4E,0x56,0x49,0x54,0x45,0x00,0x00,0x00,0x00,0x00,0x44,0x53,0x00,0x19,0x00,0x00,0x01,0x33,0x00,0xED,0x90,0xC0,0xF5,0xF3,0xCD,0x61,0x00,0x3E,0x5D,0xEA,0x00,0xC0,0x3E,0xD9,0xEA,0x40,0xC2,0xEA,0x43,0xC2,0x3E,0x01,0xE0,0xFF,0xFB,0x21,0xA0,0xC2,0x11,0x04,0x01,0x06,0x30,0x1A,0x13,0xD5,0x5F,0xCD,0xDC,0x01,0xCB,0x33,0xCD,0xDC,0x01,0xD1,0x05,0x20,0xF0,0x21,0xA0,0xC2,0x11,0x10,0x80,0x0E,0x10,0x2A,0x47,0xF0,0x41,0xE6,0x02,0x20,0xFA,0x1A,0x13,0xB8,0x20,0x05,0x0D,0x20,0xF0,0x18,0x05,0x3E,0x01,0xEA,0x04,0xC0,0xF1,0xFE,0x11,0xC2,0x5C,0x03,0x3E,0x01,0xEA,0x04,0xC0,0xF0,0x44,0xFE,0x90,0x20,0xFA,0xAF,0xE0,0x40,0x01,0x00,0x04,0x21,0xDA,0x10,0x11,0x00,0x80,0xCD,0x72,0x0E,0x21,0xF4,0x01,0xCD,0xDF,0x00,0x3E,0xE4,0xE0,0x47,0xEE,0x75,0xE0,0x40,0xFB,0xCD,0x8A,0x00,0xFA,0x02,0xC0,0xCB,0x47,0xC2,0x5C,0x03,0x76,0x00,0x18,0xF1,0xAF,0x16,0x04,0x4B,0x07,0x07,0xCB,0x21,0x30,0x02,0xC6,0x03,0x15,0x20,0xF5,0x22,0x36,0x00,0x23,0x22,0x36,0x00,0x23,0xC9,0x20,0x20,0x20,0x20,0x21,0x20,0x57,0x41,0x52,0x4E,0x49,0x4E]))
402 | #data=[0xAF,0xEA,0x05,0xC0,0xFA,0x05,0xC0,0xA7,0xC0,0x76,0x00,0x18,0xF7,0xFF,0xFF,0xFF,0xC9,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC3,0x40,0xC2,0xFF,0xFF,0xFF,0xFF,0xFF,0xC3,0x43,0xC2,0xFF,0xFF,0xFF,0xFF,0xFF,0xD9,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xD9,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xD9,0x21,0x01,0xC0,0x01,0xFF,0x1F,0x18,0x0E,0x21,0x00,0x98,0x01,0x00,0x08,0x18,0x06,0x21,0x00,0x80,0x01,0x00,0x20,0xAF,0xC3,0x1C,0x0C,0xF5,0xF0,0x41,0xE6,0x02,0x28,0xFA,0xF0,0x41,0xE6,0x02,0x20,0xFA,0xF1,0xC9,0x3E,0x20,0xE0,0x00,0xF0,0x00,0xF0,0x00,0x2F,0xE6,0x0F,0xCB,0x37,0x47,0x3E,0x10,0xE0,0x00,0xF0,0x00,0xF0,0x00,0xF0,0x00,0xF0,0x00,0xF0,0x00,0xF0,0x00,0x2F,0xE6,0x0F,0xB0,0x47,0xFA,0x03,0xC0,0xA8,0xA0,0xEA,0x02,0xC0,0x78,0xEA,0x03,0xC0,0x3E,0x30,0xE0,0x00,0xC9,0xF0,0x40,0xCB,0x7F,0xC2,0x53,0x0F,0x11,0x00,0x98,0x06,0x12,0x0E,0x14,0x2A,0x12,0x13,0x0D,0x20,0xFA,0x0E,0x14,0x7B,0xC6,0x0C,0x30,0x01,0x14,0x5F,0x05,0x20,0xEE,0xC9,0x11,0x00,0x98,0x06,0x12,0x0E,0x14,0x2A,0xD6,0x20,0x12,0x13,0x0D,0x20,0xF8,0x0E,0x14,0x7B,0xC6,0x0C,0x30,0x01,0x14,0x5F,0x05,0x20,0xEC,0xC9,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0xC3,0x50,0x01,0xCE,0xED,0x66,0x66,0xCC,0x0D,0x00,0x0B,0x03,0x73,0x00,0x83,0x00,0x0C,0x00,0x0D,0x00,0x08,0x11,0x1F,0x88,0x89,0x00,0x0E,0xDC,0xCC,0x6E,0xE6,0xDD,0xDD,0xD9,0x99,0xBB,0xBB,0x67,0x63,0x6E,0x0E,0xEC,0xCC,0xDD,0xDC,0x99,0x9F,0xBB,0xB9,0x33,0x3E,0x42,0x4F,0x54,0x42,0x20,0x49,0x4E,0x56,0x49,0x54,0x45,0x00,0x00,0x00,0x00,0x00,0x44,0x53,0x00,0x19,0x00,0x00,0x01,0x33,0x00,0xED,0x90,0xC0,0xF5,0xF3,0xCD,0x61,0x00,0x3E,0x5D,0xEA,0x00,0xC0,0x3E,0xD9,0xEA,0x40,0xC2,0xEA,0x43,0xC2,0x3E,0x01,0xE0,0xFF,0xFB,0x21,0xA0,0xC2,0x11,0x04,0x01,0x06,0x30,0x1A,0x13,0xD5,0x5F,0xCD,0xDC,0x01,0xCB,0x33,0xCD,0xDC,0x01,0xD1,0x05,0x20,0xF0,0x21,0xA0,0xC2,0x11,0x10,0x80,0x0E,0x10,0x2A,0x47,0xF0,0x41,0xE6,0x02,0x20,0xFA,0x1A,0x13,0xB8,0x20,0x05,0x0D,0x20,0xF0,0x18,0x05,0x3E,0x01,0xEA,0x04,0xC0,0xF1,0xFE,0x11,0xC2,0x5C,0x03,0x3E,0x01,0xEA,0x04,0xC0,0xF0,0x44,0xFE,0x90,0x20,0xFA,0xAF,0xE0,0x40,0x01,0x00,0x04,0x21,0xDA,0x10,0x11,0x00,0x80,0xCD,0x72,0x0E,0x21,0xF4,0x01,0xCD,0xDF,0x00,0x3E,0xE4,0xE0,0x47,0xEE,0x75,0xE0,0x40,0xFB,0xCD,0x8A,0x00,0xFA,0x02,0xC0,0xCB,0x47,0xC2,0x5C,0x03,0x76,0x00,0x18,0xF1,0xAF,0x16,0x04,0x4B,0x07,0x07,0xCB,0x21,0x30,0x02,0xC6,0x03,0x15,0x20,0xF5,0x22,0x36,0x00,0x23,0x22,0x36,0x00,0x23,0xC9,0x20,0x20,0x20,0x20,0x21,0x20,0x57,0x41,0x52,0x4E,0x49,0x4E]
403 |
404 | #tetris as testrom
405 | file_path = tkFileDialog.askopenfilename(defaultextension=".gb", initialfile=''.join(e for e in cartname if e.isalnum())+".gb")
406 | if file_path == "None" or file_path == "":
407 | return False
408 | fo = open(file_path, "rb")
409 | data = fo.read()
410 |
411 | # for num,bb in enumerate(data):
412 | # #command("setbyte",0x21,0x00,0x00,0x00)
413 | # command("setbyte",0x0A,0xAA,valAA,0x00)
414 | # command("setbyte",0x05,0x55,val55,0x00)
415 | # command("setbyte",0x0A,0xAA,0xA0,0x00)
416 | # command("setbyte",0x00+num / 256,num % 256,bb,0x00)
417 | # print ".",
418 | # time.sleep(0.05)
419 | # time.sleep(2)
420 | # command("setbyte",0xFF,0xF2,0x00,0x00)
421 | # command("setbyte",0xFF,0xF1,0xFF,0x00)
422 |
423 | time.sleep(1)
424 | amount=len(data)
425 | steps=1024
426 | i=0
427 | j=0
428 | while i < amount:
429 | print i,
430 | print ":",
431 | print i / 256,
432 | print "-",
433 | print i % 256,
434 | command("setbyte",0xFF,0xF2, i / 16384 ,0x00)
435 | command("send",j , i % 256,data[i:i+steps],constants.modeFlash)
436 | i=i+steps
437 | print i
438 | j=j+4
439 | if j==128:
440 | j=64
441 | time.sleep(0.2)
442 | # command("setbyte",0x21,0x00,0x00,0x00)
443 | # time.sleep(0.2)
444 | # command("send",0x04,0x00,data[1024:2048],constants.modeFlash)
445 | # time.sleep(1)
446 | # command("setbyte",0x21,0x00,0x00,0x00)
447 | # time.sleep(0.2)
448 | # command("send",0x08,0x00,data[2048:3072],constants.modeFlash)
449 | # time.sleep(1)
450 | # command("setbyte",0x21,0x00,0x00,0x00)
451 | # time.sleep(0.2)
452 | # command("send",0x0C,0x00,data[3072:4096],constants.modeFlash)
453 | # time.sleep(1)
454 | # command("setbyte",0x21,0x00,0x00,0x00)
455 | # time.sleep(0.2)
456 | # command("send",0x10,0x00,data[4096:5120],constants.modeFlash)
457 | # time.sleep(1)
458 | # command("setbyte",0x21,0x00,0x00,0x00)
459 | # time.sleep(0.2)
460 | # command("send",0x14,0x00,data[5120:6144],constants.modeFlash)
461 | # time.sleep(1)
462 | # command("setbyte",0x21,0x00,0x00,0x00)
463 | # time.sleep(0.2)
464 | # command("send",0x18,0x00,data[6144:7168],constants.modeFlash)
465 | # time.sleep(1)
466 | # command("setbyte",0x21,0x00,0x00,0x00)
467 | # time.sleep(0.2)
468 | # command("send",0x1C,0x00,data[7168:8192],constants.modeFlash)
469 |
470 |
471 | time.sleep(5)
472 | command("dump",0x02,0x00,0x00,0x00)
473 |
474 | print " ".join("{:02x}".format(ord(c)) for c in cmdoutput)
475 | return
476 | def flashexperiment(): #Don't use
477 | global cmdoutput
478 | # command("setbyte",0x21,0x00,0x00,0x00)
479 | # command("setbyte",0x0A,0xAA,0xA9,0x00)
480 | # command("setbyte",0x05,0x55,0x56,0x00)
481 | # command("setbyte",0x0A,0xAA,0x80,0x00)
482 | # command("setbyte",0x0A,0xAA,0xA9,0x00)
483 | # command("setbyte",0x05,0x55,0x56,0x00)
484 | # #command("setbyte",0x0a,0xaa,0x10,0x00)
485 | # command("setbyte",0x00,0x00,0x30,0x00)
486 | # time.sleep(1)
487 |
488 | #command("setbyte",0xFF,0xF2,0x00,0x00)
489 | #command("setbyte",0xFF,0xF1,0x03,0x00)
490 |
491 | #data="".join(map(chr,range(0,20)))
492 | #command("send",0x00,0x00,data,0x00)
493 |
494 | command("setbyte",0x0A,0xAA,0xA9,0x00)
495 | command("setbyte",0x05,0x55,0x56,0x00)
496 | command("setbyte",0x0A,0xAA,0xA0,0x00)
497 | command("setbyte",0x00,0xF3,0xEF,0x00)
498 | time.sleep(1)
499 | command("setbyte",0x0A,0xAA,0xA9,0x00)
500 | command("setbyte",0x05,0x55,0x56,0x00)
501 | command("setbyte",0x0A,0xAA,0xA0,0x00)
502 | command("setbyte",0x00,0xF2,0xBE,0x00)
503 | time.sleep(1)
504 | command("setbyte",0x0A,0xAA,0xA9,0x00)
505 | command("setbyte",0x05,0x55,0x56,0x00)
506 | command("setbyte",0x0A,0xAA,0xA0,0x00)
507 | command("setbyte",0x00,0xF0,0xDE,0x00)
508 | time.sleep(1)
509 | command("setbyte",0x0A,0xAA,0xA9,0x00)
510 | command("setbyte",0x05,0x55,0x56,0x00)
511 | command("setbyte",0x0A,0xAA,0xA0,0x00)
512 | command("setbyte",0x00,0xF1,0xAD,0x00)
513 | time.sleep(1)
514 |
515 |
516 |
517 |
518 |
519 | command("dump",0x00,0xFF,0x00,0x00)
520 | print " ".join("{:02x}".format(ord(c)) for c in cmdoutput)
521 | return
522 |
523 | def flashbyte():
524 | a1 = int(raw_input('A1> '), 16)
525 | a2 = int(raw_input('A2> '), 16)
526 | b = int(raw_input('B> '), 16)
527 | command("setbyte",0x0A,0xAA,valAA,0x00)
528 | command("setbyte",0x05,0x55,val55,0x00)
529 | command("setbyte",0x0A,0xAA,0xA0,0x00)
530 | command("setbyte",a1,a2,b,0x00)
531 | return
532 |
533 |
534 | def serialf():
535 | global ser, state, cmdstr, cmdpar1, cmdpar2, cmdpar3, cmdpar4, cmddone, cmdoutput, lcdon
536 | buf=""
537 |
538 | while True:
539 | buf+=ser.read(1)
540 | # print buf
541 | if state == "serialopen":
542 | if "Boot complete" in buf:
543 | state="arduinoconnected"
544 | buf=""
545 | time.sleep(0.2) #Giving the arduino some time to init
546 | if state == "arduinoconnected":
547 | if "StatusConnected" in buf:
548 | print "Link established"
549 | buf=""
550 | if "StatusMQ" in buf:
551 | state="pksploit_mq"
552 | buf=""
553 | if "StatusTradeCenter" in buf:
554 | print "Gameboy selected Trade Center"
555 | buf=""
556 | if "StatusSending" in buf:
557 | print "Sending data..."
558 | buf=""
559 | if "StatusMenu" in buf:
560 | state="pksploit_menu"
561 | buf=""
562 | if state == "pksploit_menu":
563 | #wait for commands
564 | time.sleep(0.05)
565 | if cmdstr == "setcounter":
566 | ser.write(chr(0x11)) #Arduino command HEX 11: Set internal counter
567 | ser.write(chr(cmdpar1))
568 | ser.write(chr(cmdpar2))
569 | buf=""
570 | cmdstr=""
571 | cmdoutput=""
572 | cmddone=True
573 | if cmdstr == "setbyte":
574 | ser.write(chr(0xAA))
575 | ser.write(chr(cmdpar1))
576 | ser.write(chr(cmdpar2))
577 | ser.write(chr(cmdpar3))
578 | buf=""
579 | cmdstr=""
580 | cmdoutput=""
581 | cmddone=True
582 | if cmdstr == "turnofflcd":
583 | ser.write(chr(0x66))
584 | ser.write(chr(0x00))
585 | ser.write(chr(0x00))
586 | buf=""
587 | cmdstr=""
588 | cmdoutput=""
589 | lcdon=False
590 | cmddone=True
591 | if cmdstr == "turnonlcd":
592 | ser.write(chr(0xFF))
593 | ser.write(chr(0x40))
594 | ser.write(chr(0xE3))
595 |
596 | buf=""
597 | cmdstr=""
598 | cmdoutput=""
599 | lcdon=True
600 | cmddone=True
601 | if cmdstr == "jump":
602 | ser.write(chr(0x33))
603 | ser.write(chr(cmdpar1))
604 | ser.write(chr(cmdpar2))
605 | buf=""
606 | cmdstr=""
607 | cmdoutput=""
608 | cmddone=True
609 | if cmdstr == "dump":
610 | ser.write(chr(0x11)) #Arduino command HEX 11: Set internal counter
611 | ser.write(chr(cmdpar1))
612 | ser.write(chr(cmdpar2))
613 |
614 | time.sleep(0.10)
615 | ser.write(chr(0x22)) #Arduino command HEX 22: Set block mode to "dump"
616 |
617 | ser.write(chr(0x55)) #PkSploit Command HEX 55: Block transfer
618 | ser.write(chr(cmdpar1)) #High byte of byte number
619 | ser.write(chr(cmdpar2)) #Low byte of byte number
620 | ser.write(chr(cmdpar3)) #High byte of start address
621 | ser.write(chr(cmdpar4)) #Low Byte of start address
622 | buf=""
623 |
624 | while "StatusMenu" not in buf:
625 | buf+=ser.read(1)
626 |
627 | if len(buf) % 10==0:
628 | print ".",
629 | print "."
630 | cmdoutput=buf[:-11]
631 | cmdstr=""
632 | buf=""
633 | cmddone=True
634 | if cmdstr == "send":
635 | ser.write(chr(0xAA)) #Setbyte command
636 | ser.write(chr(0xFF))
637 | ser.write(chr(0xF1)) #Address FFF1
638 | ser.write(chr(cmdpar4)) #Set Write mode (See constants.py)
639 |
640 | ser.write(chr(0xAA)) #Setbyte command
641 | ser.write(chr(0xFF))
642 | ser.write(chr(0xF0)) #Address FFF0
643 | ser.write(chr(0xFF)) #Set FF delay time to ensure data makes it to the gameboy uncorrupted
644 | time.sleep(0.10)
645 | #cmdpar3 is data to be sent
646 | ser.write(chr(0x11)) #Arduino command HEX 11: Set internal counter
647 | ser.write(chr(len(cmdpar3) / 256)) #High Byte of Byte counter
648 | ser.write(chr(len(cmdpar3) % 256)) #Low Byte of Byte counter
649 | ser.write(chr(0x00))
650 | time.sleep(0.10)
651 |
652 | ser.write(chr(0x77))
653 | ser.write(chr(0xDE))
654 | ser.write(chr(0xAD))
655 | ser.write(chr(0xBE))
656 | ser.write(chr(0xEF)) # sync failsafe
657 | i=0
658 | while i