FlashGBX 3.36__py3-none-any.whl → 4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- FlashGBX/FlashGBX.py +14 -7
- FlashGBX/FlashGBX_CLI.py +208 -28
- FlashGBX/FlashGBX_GUI.py +644 -209
- FlashGBX/Flashcart.py +195 -84
- FlashGBX/GBMemory.py +4 -5
- FlashGBX/LK_Device.py +4526 -0
- FlashGBX/Mapper.py +534 -356
- FlashGBX/RomFileAGB.py +92 -2
- FlashGBX/RomFileDMG.py +1 -1
- FlashGBX/UserInputDialog.py +20 -0
- FlashGBX/Util.py +116 -71
- FlashGBX/fw_GBFlash.py +426 -0
- FlashGBX/fw_GBxCartRW_v1_3.py +1 -1
- FlashGBX/fw_JoeyJr.py +472 -0
- FlashGBX/hw_GBFlash.py +244 -0
- FlashGBX/hw_GBxCartRW.py +200 -3715
- FlashGBX/hw_JoeyJr.py +309 -0
- FlashGBX/res/Third Party Notices.md +342 -0
- FlashGBX/res/config.zip +0 -0
- FlashGBX/res/fw_GBFlash.zip +0 -0
- FlashGBX/res/fw_GBxCart_RW_v1_3.zip +0 -0
- FlashGBX/res/fw_GBxCart_RW_v1_4.zip +0 -0
- FlashGBX/res/fw_GBxCart_RW_v1_4a.zip +0 -0
- FlashGBX/res/fw_JoeyJr.zip +0 -0
- {FlashGBX-3.36.dist-info → FlashGBX-4.0.dist-info}/METADATA +35 -14
- FlashGBX-4.0.dist-info/RECORD +43 -0
- {FlashGBX-3.36.dist-info → FlashGBX-4.0.dist-info}/WHEEL +1 -1
- FlashGBX/hw_GBxCartRW_ofw.py +0 -2599
- FlashGBX-3.36.dist-info/RECORD +0 -36
- {FlashGBX-3.36.dist-info → FlashGBX-4.0.dist-info}/LICENSE +0 -0
- {FlashGBX-3.36.dist-info → FlashGBX-4.0.dist-info}/entry_points.txt +0 -0
- {FlashGBX-3.36.dist-info → FlashGBX-4.0.dist-info}/top_level.txt +0 -0
FlashGBX/hw_GBxCartRW.py
CHANGED
|
@@ -2,128 +2,20 @@
|
|
|
2
2
|
# FlashGBX
|
|
3
3
|
# Author: Lesserkuma (github.com/lesserkuma)
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
from serial import SerialException
|
|
8
|
-
from .RomFileDMG import RomFileDMG
|
|
9
|
-
from .RomFileAGB import RomFileAGB
|
|
10
|
-
from .Mapper import DMG_MBC, AGB_GPIO
|
|
11
|
-
from .Flashcart import Flashcart, Flashcart_DMG_MMSA
|
|
12
|
-
from .Util import ANSI, dprint, bitswap, ParseCFI
|
|
13
|
-
from .GBMemory import GBMemoryMap
|
|
14
|
-
from . import Util
|
|
5
|
+
# pylint: disable=wildcard-import, unused-wildcard-import
|
|
6
|
+
from .LK_Device import *
|
|
15
7
|
|
|
16
|
-
class GbxDevice:
|
|
8
|
+
class GbxDevice(LK_Device):
|
|
17
9
|
DEVICE_NAME = "GBxCart RW"
|
|
18
10
|
DEVICE_MIN_FW = 1
|
|
19
|
-
DEVICE_MAX_FW =
|
|
20
|
-
DEVICE_LATEST_FW_TS = { 4:
|
|
21
|
-
|
|
22
|
-
DEVICE_CMD = {
|
|
23
|
-
"NULL":0x30,
|
|
24
|
-
"OFW_RESET_AVR":0x2A,
|
|
25
|
-
"OFW_CART_MODE":0x43,
|
|
26
|
-
"OFW_FW_VER":0x56,
|
|
27
|
-
"OFW_PCB_VER":0x68,
|
|
28
|
-
"OFW_USART_1_0M_SPEED":0x3C,
|
|
29
|
-
"OFW_USART_1_7M_SPEED":0x3E,
|
|
30
|
-
"OFW_CART_PWR_ON":0x2F,
|
|
31
|
-
"OFW_CART_PWR_OFF":0x2E,
|
|
32
|
-
"OFW_QUERY_CART_PWR":0x5D,
|
|
33
|
-
"OFW_DONE_LED_ON":0x3D,
|
|
34
|
-
"OFW_ERROR_LED_ON":0x3F,
|
|
35
|
-
"OFW_GB_CART_MODE":0x47,
|
|
36
|
-
"OFW_GB_FLASH_BANK_1_COMMAND_WRITES":0x4E,
|
|
37
|
-
"OFW_LNL_QUERY":0x25,
|
|
38
|
-
"QUERY_FW_INFO":0xA1,
|
|
39
|
-
"SET_MODE_AGB":0xA2,
|
|
40
|
-
"SET_MODE_DMG":0xA3,
|
|
41
|
-
"SET_VOLTAGE_3_3V":0xA4,
|
|
42
|
-
"SET_VOLTAGE_5V":0xA5,
|
|
43
|
-
"SET_VARIABLE":0xA6,
|
|
44
|
-
"SET_FLASH_CMD":0xA7,
|
|
45
|
-
"SET_ADDR_AS_INPUTS":0xA8,
|
|
46
|
-
"CLK_HIGH":0xA9,
|
|
47
|
-
"CLK_LOW":0xAA,
|
|
48
|
-
"ENABLE_PULLUPS":0xAB,
|
|
49
|
-
"DISABLE_PULLUPS":0xAC,
|
|
50
|
-
"DMG_CART_READ":0xB1,
|
|
51
|
-
"DMG_CART_WRITE":0xB2,
|
|
52
|
-
"DMG_CART_WRITE_SRAM":0xB3,
|
|
53
|
-
"DMG_MBC_RESET":0xB4,
|
|
54
|
-
"DMG_MBC7_READ_EEPROM":0xB5,
|
|
55
|
-
"DMG_MBC7_WRITE_EEPROM":0xB6,
|
|
56
|
-
"DMG_MBC6_MMSA_WRITE_FLASH":0xB7,
|
|
57
|
-
"DMG_SET_BANK_CHANGE_CMD":0xB8,
|
|
58
|
-
"DMG_EEPROM_WRITE":0xB9,
|
|
59
|
-
"AGB_CART_READ":0xC1,
|
|
60
|
-
"AGB_CART_WRITE":0xC2,
|
|
61
|
-
"AGB_CART_READ_SRAM":0xC3,
|
|
62
|
-
"AGB_CART_WRITE_SRAM":0xC4,
|
|
63
|
-
"AGB_CART_READ_EEPROM":0xC5,
|
|
64
|
-
"AGB_CART_WRITE_EEPROM":0xC6,
|
|
65
|
-
"AGB_CART_WRITE_FLASH_DATA":0xC7,
|
|
66
|
-
"AGB_CART_READ_3D_MEMORY":0xC8,
|
|
67
|
-
"AGB_BOOTUP_SEQUENCE":0xC9,
|
|
68
|
-
"DMG_FLASH_WRITE_BYTE":0xD1,
|
|
69
|
-
"AGB_FLASH_WRITE_SHORT":0xD2,
|
|
70
|
-
"FLASH_PROGRAM":0xD3,
|
|
71
|
-
"CART_WRITE_FLASH_CMD":0xD4,
|
|
72
|
-
"CALC_CRC32":0xD5,
|
|
73
|
-
}
|
|
74
|
-
# \#define VAR(\d+)_([^\t]+)\t+(.+)
|
|
75
|
-
DEVICE_VAR = {
|
|
76
|
-
"ADDRESS":[32, 0x00],
|
|
77
|
-
"TRANSFER_SIZE":[16, 0x00],
|
|
78
|
-
"BUFFER_SIZE":[16, 0x01],
|
|
79
|
-
"DMG_ROM_BANK":[16, 0x02],
|
|
80
|
-
"CART_MODE":[8, 0x00],
|
|
81
|
-
"DMG_ACCESS_MODE":[8, 0x01],
|
|
82
|
-
"FLASH_COMMAND_SET":[8, 0x02],
|
|
83
|
-
"FLASH_METHOD":[8, 0x03],
|
|
84
|
-
"FLASH_WE_PIN":[8, 0x04],
|
|
85
|
-
"FLASH_PULSE_RESET":[8, 0x05],
|
|
86
|
-
"FLASH_COMMANDS_BANK_1":[8, 0x06],
|
|
87
|
-
"FLASH_SHARP_VERIFY_SR":[8, 0x07],
|
|
88
|
-
"DMG_READ_CS_PULSE":[8, 0x08],
|
|
89
|
-
"DMG_WRITE_CS_PULSE":[8, 0x09],
|
|
90
|
-
"FLASH_DOUBLE_DIE":[8, 0x0A],
|
|
91
|
-
"DMG_READ_METHOD":[8, 0x0B],
|
|
92
|
-
"AGB_READ_METHOD":[8, 0x0C],
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
PCB_VERSIONS = {4:'v1.3', 5:'v1.4', 6:'v1.4a/b/c', 101:'Mini v1.0d'}
|
|
96
|
-
ACTIONS = {"ROM_READ":1, "SAVE_READ":2, "SAVE_WRITE":3, "ROM_WRITE":4, "ROM_WRITE_VERIFY":4, "SAVE_WRITE_VERIFY":3}
|
|
97
|
-
SUPPORTED_CARTS = {}
|
|
98
|
-
|
|
99
|
-
FW = []
|
|
100
|
-
FW_UPDATE_REQ = False
|
|
101
|
-
FW_VAR = {}
|
|
102
|
-
MODE = None
|
|
103
|
-
PORT = ''
|
|
104
|
-
DEVICE = None
|
|
105
|
-
WORKER = None
|
|
106
|
-
INFO = { "action":None, "last_action":None, "dump_info":{} }
|
|
107
|
-
ERROR = False
|
|
108
|
-
ERROR_ARGS = {}
|
|
109
|
-
CANCEL = False
|
|
110
|
-
CANCEL_ARGS = {}
|
|
111
|
-
SIGNAL = None
|
|
112
|
-
POS = 0
|
|
113
|
-
NO_PROG_UPDATE = False
|
|
114
|
-
FAST_READ = False
|
|
115
|
-
SKIPPING = False
|
|
11
|
+
DEVICE_MAX_FW = 1
|
|
12
|
+
DEVICE_LATEST_FW_TS = { 4:1709317610, 5:1719609931, 6:1719609931, 2:0, 90:0, 100:0 }
|
|
13
|
+
PCB_VERSIONS = { 5:'v1.4', 6:'v1.4a/b/c', 2:'v1.1/v1.2', 4:'v1.3', 90:'XMAS v1.0', 100:'Mini v1.0' }
|
|
116
14
|
BAUDRATE = 1000000
|
|
117
|
-
MAX_BUFFER_READ =
|
|
15
|
+
MAX_BUFFER_READ = 0x1000
|
|
118
16
|
MAX_BUFFER_WRITE = 0x400
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
READ_ERRORS = 0
|
|
122
|
-
|
|
123
|
-
def __init__(self):
|
|
124
|
-
pass
|
|
125
|
-
|
|
126
|
-
def Initialize(self, flashcarts, port=None, max_baud=1700000):
|
|
17
|
+
|
|
18
|
+
def Initialize(self, flashcarts, port=None, max_baud=2000000):
|
|
127
19
|
if self.IsConnected(): self.DEVICE.close()
|
|
128
20
|
if platform.system() == "Darwin": max_baud = 1000000
|
|
129
21
|
|
|
@@ -139,101 +31,132 @@ class GbxDevice:
|
|
|
139
31
|
if len(ports) == 0: return False
|
|
140
32
|
|
|
141
33
|
for i in range(0, len(ports)):
|
|
142
|
-
|
|
34
|
+
for baudrate in (1000000, 1700000, 2000000):
|
|
35
|
+
if max_baud < baudrate: continue
|
|
36
|
+
try:
|
|
37
|
+
if self.TryConnect(ports[i], baudrate):
|
|
38
|
+
self.BAUDRATE = baudrate
|
|
39
|
+
dev = serial.Serial(ports[i], self.BAUDRATE, timeout=0.1)
|
|
40
|
+
self.DEVICE = dev
|
|
41
|
+
break
|
|
42
|
+
except SerialException as e:
|
|
43
|
+
if "Permission" in str(e):
|
|
44
|
+
conn_msg.append([3, "The device on port " + ports[i] + " couldn’t be accessed. Make sure your user account has permission to use it and it’s not already in use by another application."])
|
|
45
|
+
elif "FileNotFoundError" in str(e):
|
|
46
|
+
continue
|
|
47
|
+
else:
|
|
48
|
+
conn_msg.append([3, "A critical error occured while trying to access the device on port " + ports[i] + ".\n\n" + str(e)])
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
if self.FW is None or self.FW == {}: continue
|
|
52
|
+
if max_baud >= 1700000 and self.FW is not None and "pcb_ver" in self.FW and self.FW["pcb_ver"] in (5, 6, 101) and self.BAUDRATE < 1700000:
|
|
53
|
+
self.ChangeBaudRate(baudrate=1700000)
|
|
54
|
+
self.DEVICE.close()
|
|
143
55
|
dev = serial.Serial(ports[i], self.BAUDRATE, timeout=0.1)
|
|
144
56
|
self.DEVICE = dev
|
|
145
|
-
if not self.LoadFirmwareVersion() and max_baud >= 1700000:
|
|
146
|
-
dev.close()
|
|
147
|
-
self.BAUDRATE = 1700000
|
|
148
|
-
dev = serial.Serial(ports[i], self.BAUDRATE, timeout=0.1)
|
|
149
|
-
self.DEVICE = dev
|
|
150
|
-
if not self.LoadFirmwareVersion():
|
|
151
|
-
dev.close()
|
|
152
|
-
self.DEVICE = None
|
|
153
|
-
self.BAUDRATE = 1000000
|
|
154
|
-
continue
|
|
155
|
-
elif max_baud >= 1700000 and self.FW["pcb_ver"] in (5, 6, 101) and self.BAUDRATE < 1700000:
|
|
156
|
-
self.ChangeBaudRate(baudrate=1700000)
|
|
157
|
-
self.DEVICE.close()
|
|
158
|
-
dev = serial.Serial(ports[i], self.BAUDRATE, timeout=0.1)
|
|
159
|
-
self.DEVICE = dev
|
|
160
|
-
|
|
161
|
-
dprint("Firmware information:", self.FW)
|
|
162
|
-
dprint("Baud rate:", self.BAUDRATE)
|
|
163
57
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if self.FW is not None:
|
|
168
|
-
conn_msg.append([0, "Couldn’t communicate with the GBxCart RW device on port " + ports[i] + ". Please disconnect and reconnect the device, then try again."])
|
|
169
|
-
continue
|
|
170
|
-
elif self.FW is None or "cfw_id" not in self.FW or self.FW["cfw_id"] != 'L' or self.FW["fw_ver"] < self.DEVICE_MIN_FW or (self.FW["pcb_ver"] < 5 and self.FW["fw_ver"] != 1): # Not a CFW by Lesserkuma
|
|
171
|
-
dev.close()
|
|
172
|
-
self.DEVICE = None
|
|
173
|
-
continue
|
|
174
|
-
elif self.FW["fw_ts"] > self.DEVICE_LATEST_FW_TS[self.FW["pcb_ver"]]:
|
|
175
|
-
conn_msg.append([0, "Note: The GBxCart RW device on port " + ports[i] + " is running a firmware version that is newer than what this version of FlashGBX was developed to work with, so errors may occur."])
|
|
176
|
-
|
|
177
|
-
if self.FW["pcb_ver"] not in (4, 5, 6, 101): # only the v1.3, v1.4, v1.4a/b/c, Mini v1.1 PCB revisions are supported
|
|
178
|
-
dev.close()
|
|
179
|
-
self.DEVICE = None
|
|
180
|
-
continue
|
|
181
|
-
elif self.FW["pcb_ver"] in (5, 6, 101) and self.BAUDRATE > 1000000:
|
|
182
|
-
self.MAX_BUFFER_READ = 0x2000
|
|
183
|
-
self.MAX_BUFFER_WRITE = 0x400
|
|
184
|
-
else:
|
|
185
|
-
self.MAX_BUFFER_READ = 0x1000
|
|
186
|
-
self.MAX_BUFFER_WRITE = 0x100
|
|
187
|
-
|
|
188
|
-
conn_msg.append([0, "For help please visit the insideGadgets Discord: https://gbxcart.com/discord"])
|
|
58
|
+
dprint(f"Found a {self.DEVICE_NAME}")
|
|
59
|
+
dprint("Firmware information:", self.FW)
|
|
60
|
+
dprint("Baud rate:", self.BAUDRATE)
|
|
189
61
|
|
|
190
|
-
|
|
191
|
-
self.DEVICE
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
62
|
+
if self.DEVICE is None or not self.IsConnected() or self.FW is None or self.FW["pcb_ver"] not in self.DEVICE_LATEST_FW_TS:
|
|
63
|
+
self.DEVICE = None
|
|
64
|
+
if self.FW is not None:
|
|
65
|
+
conn_msg.append([0, "Couldn’t communicate with the " + self.DEVICE_NAME + " device on port " + ports[i] + ". Please disconnect and reconnect the device, then try again."])
|
|
66
|
+
continue
|
|
67
|
+
elif self.FW["fw_ts"] > self.DEVICE_LATEST_FW_TS[self.FW["pcb_ver"]]:
|
|
68
|
+
conn_msg.append([0, "Note: The " + self.DEVICE_NAME + " device on port " + ports[i] + " is running a firmware version that is newer than what this version of FlashGBX was developed to work with, so errors may occur."])
|
|
69
|
+
elif self.FW["pcb_ver"] in (5, 6, 101) and self.BAUDRATE > 1000000:
|
|
70
|
+
self.MAX_BUFFER_READ = 0x1000
|
|
71
|
+
self.MAX_BUFFER_WRITE = 0x400
|
|
72
|
+
else:
|
|
73
|
+
self.MAX_BUFFER_READ = 0x1000
|
|
74
|
+
self.MAX_BUFFER_WRITE = 0x100
|
|
75
|
+
|
|
76
|
+
conn_msg.append([0, "For help with your GBxCart RW device, please visit the insideGadgets Discord: https://gbxcart.com/discord"])
|
|
195
77
|
|
|
196
|
-
|
|
197
|
-
|
|
78
|
+
self.PORT = ports[i]
|
|
79
|
+
self.DEVICE.timeout = self.DEVICE_TIMEOUT
|
|
198
80
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
81
|
+
# Load Flash Cartridge Handlers
|
|
82
|
+
self.UpdateFlashCarts(flashcarts)
|
|
83
|
+
|
|
84
|
+
# Stop after first found device
|
|
85
|
+
break
|
|
86
|
+
|
|
206
87
|
return conn_msg
|
|
207
|
-
|
|
88
|
+
|
|
208
89
|
def LoadFirmwareVersion(self):
|
|
209
|
-
dprint("
|
|
90
|
+
dprint("Querying firmware version")
|
|
210
91
|
try:
|
|
92
|
+
self.DEVICE.timeout = 0.075
|
|
211
93
|
self.DEVICE.reset_input_buffer()
|
|
212
94
|
self.DEVICE.reset_output_buffer()
|
|
213
95
|
self._write(self.DEVICE_CMD["OFW_PCB_VER"])
|
|
214
96
|
temp = self.DEVICE.read(1)
|
|
97
|
+
self.DEVICE.timeout = self.DEVICE_TIMEOUT
|
|
98
|
+
if len(temp) == 0:
|
|
99
|
+
dprint("No response")
|
|
100
|
+
self.FW = None
|
|
101
|
+
return False
|
|
215
102
|
pcb = temp[0]
|
|
216
103
|
if pcb == b'': return False
|
|
217
104
|
self._write(self.DEVICE_CMD["OFW_FW_VER"])
|
|
218
105
|
ofw = self._read(1)
|
|
219
|
-
if (pcb
|
|
106
|
+
if (pcb == 2 and ofw == 2):
|
|
107
|
+
dprint(f"Not a {self.DEVICE_NAME}")
|
|
220
108
|
self.FW = None
|
|
221
109
|
return False
|
|
222
|
-
|
|
110
|
+
if (pcb >= 5 and ofw == 0):
|
|
111
|
+
dprint(f"Not a {self.DEVICE_NAME}")
|
|
112
|
+
self.FW = None
|
|
113
|
+
return False
|
|
114
|
+
if (pcb < 5 and ofw > 0):
|
|
115
|
+
self.FW = {
|
|
116
|
+
"ofw_ver":ofw,
|
|
117
|
+
"pcb_ver":pcb,
|
|
118
|
+
"pcb_name":"GBxCart RW",
|
|
119
|
+
"cfw_id":"",
|
|
120
|
+
"fw_ver":0,
|
|
121
|
+
"fw_ts":0,
|
|
122
|
+
"fw_dt":"",
|
|
123
|
+
}
|
|
124
|
+
return True
|
|
125
|
+
|
|
223
126
|
self._write(self.DEVICE_CMD["QUERY_FW_INFO"])
|
|
224
127
|
size = self._read(1)
|
|
225
|
-
|
|
128
|
+
if size != 8: return False
|
|
129
|
+
data = self._read(size)
|
|
130
|
+
info = data[:8]
|
|
226
131
|
keys = ["cfw_id", "fw_ver", "pcb_ver", "fw_ts"]
|
|
227
132
|
values = struct.unpack(">cHBI", bytearray(info))
|
|
228
133
|
self.FW = dict(zip(keys, values))
|
|
229
134
|
self.FW["cfw_id"] = self.FW["cfw_id"].decode('ascii')
|
|
230
135
|
self.FW["fw_dt"] = datetime.datetime.fromtimestamp(self.FW["fw_ts"]).astimezone().replace(microsecond=0).isoformat()
|
|
231
136
|
self.FW["ofw_ver"] = ofw
|
|
232
|
-
|
|
137
|
+
self.FW["pcb_name"] = ""
|
|
138
|
+
self.FW["cart_power_ctrl"] = False
|
|
139
|
+
self.FW["bootloader_reset"] = False
|
|
140
|
+
if self.FW["cfw_id"] == "L" and self.FW["fw_ver"] >= 12:
|
|
141
|
+
size = self._read(1)
|
|
142
|
+
name = self._read(size)
|
|
143
|
+
if len(name) > 0:
|
|
144
|
+
try:
|
|
145
|
+
self.FW["pcb_name"] = name.decode("UTF-8").replace("\x00", "").strip()
|
|
146
|
+
except:
|
|
147
|
+
self.FW["pcb_name"] = "Unnamed Device"
|
|
148
|
+
self.DEVICE_NAME = self.FW["pcb_name"]
|
|
149
|
+
|
|
150
|
+
# Cartridge Power Control support
|
|
151
|
+
self.FW["cart_power_ctrl"] = True if self._read(1) == 1 else False
|
|
152
|
+
|
|
153
|
+
# Reset to bootloader support
|
|
154
|
+
self.FW["bootloader_reset"] = True if self._read(1) == 1 else False
|
|
155
|
+
|
|
233
156
|
return True
|
|
234
157
|
|
|
235
|
-
except:
|
|
236
|
-
dprint("Disconnecting
|
|
158
|
+
except Exception as e:
|
|
159
|
+
dprint("Disconnecting due to an error", e, sep="\n")
|
|
237
160
|
try:
|
|
238
161
|
if self.DEVICE.isOpen():
|
|
239
162
|
self.DEVICE.reset_input_buffer()
|
|
@@ -243,95 +166,53 @@ class GbxDevice:
|
|
|
243
166
|
except:
|
|
244
167
|
pass
|
|
245
168
|
return False
|
|
246
|
-
|
|
247
|
-
def GetBaudRate(self):
|
|
248
|
-
return self.BAUDRATE
|
|
249
|
-
|
|
169
|
+
|
|
250
170
|
def ChangeBaudRate(self, baudrate):
|
|
251
171
|
if not self.IsConnected(): return
|
|
172
|
+
dprint("Changing baud rate to", baudrate)
|
|
252
173
|
if baudrate == 1700000:
|
|
253
174
|
self._write(self.DEVICE_CMD["OFW_USART_1_7M_SPEED"])
|
|
254
175
|
elif baudrate == 1000000:
|
|
255
176
|
self._write(self.DEVICE_CMD["OFW_USART_1_0M_SPEED"])
|
|
256
177
|
self.BAUDRATE = baudrate
|
|
257
|
-
|
|
258
|
-
def CanSetVoltageManually(self):
|
|
259
|
-
return False
|
|
260
|
-
|
|
261
|
-
def CanSetVoltageAutomatically(self):
|
|
262
|
-
return True
|
|
263
|
-
|
|
264
|
-
def CanPowerCycleCart(self):
|
|
265
|
-
if self.FW is None or self.DEVICE is None: return False
|
|
266
|
-
if not self.DEVICE.is_open: return False
|
|
267
|
-
return self.FW["pcb_ver"] in (5, 6)
|
|
268
|
-
|
|
269
|
-
def GetSupprtedModes(self):
|
|
270
|
-
if self.FW["pcb_ver"] == 101:
|
|
271
|
-
return ["DMG"]
|
|
272
|
-
else:
|
|
273
|
-
return ["DMG", "AGB"]
|
|
274
|
-
|
|
275
|
-
def IsSupportedMbc(self, mbc):
|
|
276
|
-
if self.CanPowerCycleCart():
|
|
277
|
-
return mbc in ( 0x00, 0x01, 0x02, 0x03, 0x05, 0x06, 0x08, 0x09, 0x0B, 0x0D, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x20, 0x22, 0xFC, 0xFD, 0xFE, 0xFF, 0x101, 0x103, 0x104, 0x105, 0x110, 0x201, 0x202, 0x203, 0x204, 0x205 )
|
|
278
|
-
else:
|
|
279
|
-
return mbc in ( 0x00, 0x01, 0x02, 0x03, 0x05, 0x06, 0x08, 0x09, 0x0B, 0x0D, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x20, 0x22, 0xFC, 0xFD, 0xFE, 0xFF, 0x101, 0x103, 0x104, 0x105, 0x110, 0x202, 0x205 )
|
|
280
|
-
|
|
281
|
-
def IsSupported3dMemory(self):
|
|
282
|
-
return True
|
|
283
|
-
|
|
284
|
-
def IsClkConnected(self):
|
|
285
|
-
return self.FW["pcb_ver"] in (5, 6, 101)
|
|
286
178
|
|
|
287
|
-
def
|
|
288
|
-
self.
|
|
289
|
-
|
|
290
|
-
"AGB":{ "Generic ROM Cartridge":"RETAIL" }
|
|
291
|
-
}
|
|
292
|
-
for mode in flashcarts.keys():
|
|
293
|
-
for key in sorted(flashcarts[mode].keys(), key=str.casefold):
|
|
294
|
-
self.SUPPORTED_CARTS[mode][key] = flashcarts[mode][key]
|
|
295
|
-
|
|
296
|
-
def IsConnected(self):
|
|
179
|
+
def CheckActive(self):
|
|
180
|
+
if time.time() < self.LAST_CHECK_ACTIVE + 1: return True
|
|
181
|
+
dprint("Checking if device is active")
|
|
297
182
|
if self.DEVICE is None: return False
|
|
298
|
-
if
|
|
183
|
+
if self.FW["pcb_name"] is None:
|
|
184
|
+
if self.LoadFirmwareVersion():
|
|
185
|
+
self.LAST_CHECK_ACTIVE = time.time()
|
|
186
|
+
return True
|
|
187
|
+
return False
|
|
299
188
|
try:
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
189
|
+
if self.FW["fw_ver"] == 0: # legacy GBxCart RW firmware
|
|
190
|
+
return True
|
|
191
|
+
if self.FW["fw_ver"] >= 12:
|
|
192
|
+
temp = bytearray([self.DEVICE_CMD["QUERY_CART_PWR"]])
|
|
193
|
+
self._get_fw_variable("CART_MODE")
|
|
194
|
+
else:
|
|
195
|
+
temp = bytearray([self.DEVICE_CMD["OFW_QUERY_CART_PWR"]])
|
|
196
|
+
self._write(temp)
|
|
197
|
+
self._read(1)
|
|
198
|
+
self.LAST_CHECK_ACTIVE = time.time()
|
|
199
|
+
return True
|
|
200
|
+
except Exception as e:
|
|
201
|
+
dprint("Disconnecting...", e)
|
|
308
202
|
try:
|
|
309
|
-
if
|
|
203
|
+
if self.DEVICE.isOpen():
|
|
204
|
+
self.DEVICE.reset_input_buffer()
|
|
205
|
+
self.DEVICE.reset_output_buffer()
|
|
310
206
|
self.DEVICE.close()
|
|
311
|
-
|
|
207
|
+
self.DEVICE = None
|
|
312
208
|
except:
|
|
313
209
|
pass
|
|
314
|
-
print(str(e))
|
|
315
210
|
return False
|
|
316
|
-
|
|
317
|
-
def Close(self, cartPowerOff=False):
|
|
318
|
-
if self.IsConnected():
|
|
319
|
-
try:
|
|
320
|
-
if cartPowerOff:
|
|
321
|
-
if self.FW["pcb_ver"] in (5, 6, 101):
|
|
322
|
-
self._write(self.DEVICE_CMD["OFW_CART_MODE"])
|
|
323
|
-
self._read(1)
|
|
324
|
-
self._write(self.DEVICE_CMD["OFW_CART_PWR_OFF"])
|
|
325
|
-
self._write(self.DEVICE_CMD["SET_ADDR_AS_INPUTS"])
|
|
326
|
-
self.DEVICE.close()
|
|
327
|
-
except:
|
|
328
|
-
self.DEVICE = None
|
|
329
|
-
self.MODE = None
|
|
330
|
-
|
|
331
|
-
def GetName(self):
|
|
332
|
-
return "GBxCart RW"
|
|
333
|
-
|
|
211
|
+
|
|
334
212
|
def GetFirmwareVersion(self, more=False):
|
|
213
|
+
if self.FW["fw_ver"] == 0: # old GBxCart RW
|
|
214
|
+
return "R{:d}".format(self.FW["ofw_ver"])
|
|
215
|
+
|
|
335
216
|
if self.FW["pcb_ver"] in (5, 6, 101):
|
|
336
217
|
s = "R{:d}+{:s}{:d}".format(self.FW["ofw_ver"], self.FW["cfw_id"], self.FW["fw_ver"])
|
|
337
218
|
else:
|
|
@@ -340,25 +221,41 @@ class GbxDevice:
|
|
|
340
221
|
s += " ({:s})".format(self.FW["fw_dt"])
|
|
341
222
|
return s
|
|
342
223
|
|
|
343
|
-
def GetPCBVersion(self):
|
|
344
|
-
if self.FW["pcb_ver"] in self.PCB_VERSIONS:
|
|
345
|
-
return self.PCB_VERSIONS[self.FW["pcb_ver"]]
|
|
346
|
-
else:
|
|
347
|
-
return "(unknown revision)"
|
|
348
|
-
|
|
349
|
-
def GetFullName(self):
|
|
350
|
-
self.DEVICE_NAME = "{:s} {:s}".format(self.GetName(), self.GetPCBVersion())
|
|
351
|
-
return self.DEVICE_NAME
|
|
352
|
-
|
|
353
224
|
def GetFullNameExtended(self, more=False):
|
|
225
|
+
if self.FW["fw_ver"] == 0: # old GBxCart RW
|
|
226
|
+
return "{:s} – Firmware {:s} ({:s})".format(self.GetFullName(), self.GetFirmwareVersion(), self.GetPort())
|
|
227
|
+
|
|
354
228
|
if more:
|
|
355
|
-
return "{:s} – Firmware {:s} ({:s}) on {:s} at {:.1f}M baud".format(self.GetFullName(), self.GetFirmwareVersion(), self.FW["fw_dt"], self.
|
|
229
|
+
return "{:s} – Firmware {:s} ({:s}) on {:s} at {:.1f}M baud".format(self.GetFullName(), self.GetFirmwareVersion(), self.FW["fw_dt"], self.GetPort(), self.BAUDRATE/1000/1000)
|
|
356
230
|
else:
|
|
357
|
-
return "{:s} – Firmware {:s}".format(self.GetFullName(), self.GetFirmwareVersion())
|
|
231
|
+
return "{:s} – Firmware {:s} ({:s})".format(self.GetFullName(), self.GetFirmwareVersion(), self.GetPort())
|
|
358
232
|
|
|
359
|
-
def
|
|
360
|
-
return
|
|
233
|
+
def CanSetVoltageManually(self):
|
|
234
|
+
return False
|
|
235
|
+
|
|
236
|
+
def CanSetVoltageAutomatically(self):
|
|
237
|
+
return True
|
|
238
|
+
|
|
239
|
+
def CanPowerCycleCart(self):
|
|
240
|
+
if self.FW is None or self.DEVICE is None: return False
|
|
241
|
+
if not self.DEVICE.is_open: return False
|
|
242
|
+
if self.FW["fw_ver"] >= 12:
|
|
243
|
+
return self.FW["cart_power_ctrl"]
|
|
244
|
+
else:
|
|
245
|
+
return self.FW["pcb_ver"] in (5, 6)
|
|
246
|
+
|
|
247
|
+
def GetSupprtedModes(self):
|
|
248
|
+
if self.FW["pcb_ver"] == 101:
|
|
249
|
+
return ["DMG"]
|
|
250
|
+
else:
|
|
251
|
+
return ["DMG", "AGB"]
|
|
252
|
+
|
|
253
|
+
def IsSupported3dMemory(self):
|
|
254
|
+
return True
|
|
361
255
|
|
|
256
|
+
def IsClkConnected(self):
|
|
257
|
+
return self.FW["pcb_ver"] in (5, 6, 101)
|
|
258
|
+
|
|
362
259
|
def SupportsFirmwareUpdates(self):
|
|
363
260
|
if self.FW["ofw_ver"] == 30:
|
|
364
261
|
self._write(self.DEVICE_CMD["OFW_LNL_QUERY"])
|
|
@@ -368,3470 +265,58 @@ class GbxDevice:
|
|
|
368
265
|
self.DEVICE.timeout = old_timeout
|
|
369
266
|
dprint("LinkNLoad detected:", is_lnl)
|
|
370
267
|
if is_lnl: return False
|
|
371
|
-
return self.FW["pcb_ver"] in (4, 5, 6)
|
|
268
|
+
return self.FW["pcb_ver"] in (2, 4, 5, 6, 90, 100, 101)
|
|
372
269
|
|
|
373
270
|
def FirmwareUpdateAvailable(self):
|
|
271
|
+
if self.FW["fw_ver"] == 0 and self.FW["pcb_ver"] in (2, 4, 90, 100, 101):
|
|
272
|
+
if self.FW["pcb_ver"] == 4:
|
|
273
|
+
self.FW_UPDATE_REQ = True
|
|
274
|
+
else:
|
|
275
|
+
self.FW_UPDATE_REQ = 2
|
|
276
|
+
return True
|
|
374
277
|
if self.FW["pcb_ver"] not in (4, 5, 6): return False
|
|
375
278
|
if self.FW["pcb_ver"] in (5, 6) and self.FW["fw_ts"] < self.DEVICE_LATEST_FW_TS[self.FW["pcb_ver"]]:
|
|
376
279
|
return True
|
|
377
|
-
if self.FW["pcb_ver"] == 4 and self.FW["fw_ts"]
|
|
280
|
+
if self.FW["pcb_ver"] == 4 and self.FW["fw_ts"] < self.DEVICE_LATEST_FW_TS[self.FW["pcb_ver"]]:
|
|
378
281
|
self.FW_UPDATE_REQ = True
|
|
379
282
|
return True
|
|
380
283
|
|
|
381
284
|
def GetFirmwareUpdaterClass(self):
|
|
382
|
-
if self.FW["pcb_ver"]
|
|
383
|
-
try:
|
|
384
|
-
from . import fw_GBxCartRW_v1_3
|
|
385
|
-
return (None, fw_GBxCartRW_v1_3.FirmwareUpdaterWindow)
|
|
386
|
-
except:
|
|
387
|
-
return False
|
|
388
|
-
elif self.FW["pcb_ver"] in (5, 6): # v1.4 / v1.4a/b/c
|
|
285
|
+
if self is None or self.FW["pcb_ver"] in (5, 6): # v1.4 / v1.4a/b/c
|
|
389
286
|
try:
|
|
390
287
|
from . import fw_GBxCartRW_v1_4
|
|
391
288
|
return (fw_GBxCartRW_v1_4.FirmwareUpdater, fw_GBxCartRW_v1_4.FirmwareUpdaterWindow)
|
|
392
289
|
except:
|
|
393
|
-
return
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
def GetFWBuildDate(self):
|
|
401
|
-
return self.FW["fw_dt"]
|
|
402
|
-
|
|
403
|
-
def SetWriteDelay(self, enable=True):
|
|
404
|
-
if self.WRITE_DELAY != enable:
|
|
405
|
-
dprint("Setting Write Delay to", enable)
|
|
406
|
-
self.WRITE_DELAY = enable
|
|
407
|
-
|
|
408
|
-
def SetTimeout(self, seconds=0.5):
|
|
409
|
-
self.DEVICE_TIMEOUT = seconds
|
|
410
|
-
self.DEVICE.timeout = self.DEVICE_TIMEOUT
|
|
411
|
-
|
|
412
|
-
def wait_for_ack(self, values=None):
|
|
413
|
-
if values is None: values = [0x01, 0x03]
|
|
414
|
-
buffer = self._read(1)
|
|
415
|
-
if buffer not in values:
|
|
416
|
-
tb_stack = traceback.extract_stack()
|
|
417
|
-
stack = tb_stack[len(tb_stack)-2] # caller only
|
|
418
|
-
if stack.name == "_write": stack = tb_stack[len(tb_stack)-3]
|
|
419
|
-
dprint("CANCEL_ARGS:", self.CANCEL_ARGS)
|
|
420
|
-
if "from_user" in self.CANCEL_ARGS and self.CANCEL_ARGS["from_user"]:
|
|
421
|
-
return False
|
|
422
|
-
elif buffer is False:
|
|
423
|
-
print("{:s}Error: The USB connection timed out.{:s}".format(ANSI.RED, ANSI.RESET))
|
|
424
|
-
dprint("Timeout error ({:s}(), line {:d})".format(stack.name, stack.lineno))
|
|
425
|
-
self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"A timeout error has occured at {:s}() in line {:d}. Please make sure that the cartridge contacts are clean, re-connect the device and try again from the beginning.".format(stack.name, stack.lineno)})
|
|
426
|
-
else:
|
|
427
|
-
dprint("Communication error ({:s}(), line {:d})".format(stack.name, stack.lineno))
|
|
428
|
-
self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"A communication error has occured at {:s}() in line {:d}. Please make sure that the cartridge contacts are clean, re-connect the device and try again from the beginning.".format(stack.name, stack.lineno)})
|
|
429
|
-
self.ERROR = True
|
|
430
|
-
self.CANCEL = True
|
|
431
|
-
self.SetWriteDelay(enable=True)
|
|
432
|
-
return False
|
|
433
|
-
return buffer
|
|
434
|
-
|
|
435
|
-
def _write(self, data, wait=False):
|
|
436
|
-
if not isinstance(data, bytearray):
|
|
437
|
-
data = bytearray([data])
|
|
438
|
-
|
|
439
|
-
#dstr = ' '.join(format(x, '02X') for x in data)
|
|
440
|
-
#dprint("[{:02X}] {:s}".format(int(len(dstr)/3) + 1, dstr[:96]))
|
|
441
|
-
|
|
442
|
-
self.DEVICE.write(data)
|
|
443
|
-
self.DEVICE.flush()
|
|
444
|
-
|
|
445
|
-
# On MacOS it’s possible not all bytes are transmitted successfully,
|
|
446
|
-
# even though we’re using flush() which is the tcdrain function.
|
|
447
|
-
# Still looking for a better solution than delaying here.
|
|
448
|
-
if platform.system() == "Darwin" or self.WRITE_DELAY is True:
|
|
449
|
-
time.sleep(0.00125)
|
|
450
|
-
|
|
451
|
-
if wait: return self.wait_for_ack()
|
|
452
|
-
|
|
453
|
-
def _read(self, count):
|
|
454
|
-
if self.DEVICE.in_waiting > 1000: dprint("in_waiting = {:d} bytes".format(self.DEVICE.in_waiting))
|
|
455
|
-
buffer = self.DEVICE.read(count)
|
|
456
|
-
if len(buffer) != count:
|
|
457
|
-
tb_stack = traceback.extract_stack()
|
|
458
|
-
stack = tb_stack[len(tb_stack)-2] # caller only
|
|
459
|
-
if stack.name == "_read": stack = tb_stack[len(tb_stack)-3]
|
|
460
|
-
#traceback.print_stack()
|
|
461
|
-
dprint("Warning: Received {:d} byte(s) instead of the expected {:d} byte(s) ({:s}(), line {:d})".format(len(buffer), count, stack.name, stack.lineno))
|
|
462
|
-
self.READ_ERRORS += 1
|
|
463
|
-
while self.DEVICE.in_waiting > 0:
|
|
464
|
-
self.DEVICE.reset_input_buffer()
|
|
465
|
-
time.sleep(0.5)
|
|
466
|
-
self.DEVICE.reset_output_buffer()
|
|
467
|
-
return False
|
|
468
|
-
|
|
469
|
-
if count == 1:
|
|
470
|
-
return buffer[0]
|
|
290
|
+
return None
|
|
291
|
+
elif self.FW["pcb_ver"] in (2, 4, 90, 100, 101): # v1.3
|
|
292
|
+
try:
|
|
293
|
+
from . import fw_GBxCartRW_v1_3
|
|
294
|
+
return (None, fw_GBxCartRW_v1_3.FirmwareUpdaterWindow)
|
|
295
|
+
except:
|
|
296
|
+
return None
|
|
471
297
|
else:
|
|
472
|
-
return
|
|
473
|
-
|
|
474
|
-
def _set_fw_variable(self, key, value):
|
|
475
|
-
dprint("Setting firmware variable {:s} to 0x{:X}".format(key, value))
|
|
476
|
-
self.FW_VAR[key] = value
|
|
477
|
-
|
|
478
|
-
size = 0
|
|
479
|
-
for (k, v) in self.DEVICE_VAR.items():
|
|
480
|
-
if key in k:
|
|
481
|
-
if v[0] == 8: size = 1
|
|
482
|
-
elif v[0] == 16: size = 2
|
|
483
|
-
elif v[0] == 32: size = 4
|
|
484
|
-
key = v[1]
|
|
485
|
-
break
|
|
486
|
-
if size == 0:
|
|
487
|
-
raise KeyError("Unknown variable name specified.")
|
|
488
|
-
|
|
489
|
-
buffer = bytearray([self.DEVICE_CMD["SET_VARIABLE"], size])
|
|
490
|
-
buffer.extend(struct.pack(">I", key))
|
|
491
|
-
buffer.extend(struct.pack(">I", value))
|
|
492
|
-
self._write(buffer)
|
|
493
|
-
if self.WRITE_DELAY is True:
|
|
494
|
-
time.sleep(0.001)
|
|
495
|
-
|
|
496
|
-
def _cart_read(self, address, length=0, agb_save_flash=False):
|
|
497
|
-
if self.MODE == "DMG":
|
|
498
|
-
if length == 0:
|
|
499
|
-
length = 1
|
|
500
|
-
if address < 0xA000:
|
|
501
|
-
return struct.unpack("B", self.ReadROM(address, 1))[0]
|
|
502
|
-
else:
|
|
503
|
-
return struct.unpack("B", self.ReadRAM(address - 0xA000, 1))[0]
|
|
504
|
-
else:
|
|
505
|
-
if address < 0xA000:
|
|
506
|
-
return self.ReadROM(address, length)
|
|
507
|
-
else:
|
|
508
|
-
return self.ReadRAM(address - 0xA000, length)
|
|
509
|
-
elif self.MODE == "AGB":
|
|
510
|
-
if length == 0:
|
|
511
|
-
length = 2
|
|
512
|
-
if agb_save_flash:
|
|
513
|
-
return struct.unpack(">H", self.ReadRAM(address, length, command=self.DEVICE_CMD["AGB_CART_READ_SRAM"]))[0]
|
|
514
|
-
else:
|
|
515
|
-
return struct.unpack(">H", self.ReadROM(address >> 1, length))[0]
|
|
516
|
-
else:
|
|
517
|
-
if agb_save_flash:
|
|
518
|
-
return self.ReadRAM(address, length, command=self.DEVICE_CMD["AGB_CART_READ_SRAM"])
|
|
519
|
-
else:
|
|
520
|
-
return self.ReadROM(address, length)
|
|
521
|
-
|
|
522
|
-
def _cart_write(self, address, value, flashcart=False, sram=False):
|
|
523
|
-
dprint("Writing to cartridge: 0x{:X} = 0x{:X} (args: {:s}, {:s})".format(address, value & 0xFF, str(flashcart), str(sram)))
|
|
524
|
-
if self.MODE == "DMG":
|
|
525
|
-
if flashcart:
|
|
526
|
-
buffer = bytearray([self.DEVICE_CMD["DMG_FLASH_WRITE_BYTE"]])
|
|
527
|
-
else:
|
|
528
|
-
if sram: self._set_fw_variable("DMG_WRITE_CS_PULSE", 1)
|
|
529
|
-
buffer = bytearray([self.DEVICE_CMD["DMG_CART_WRITE"]])
|
|
530
|
-
buffer.extend(struct.pack(">I", address))
|
|
531
|
-
buffer.extend(struct.pack("B", value & 0xFF))
|
|
532
|
-
elif self.MODE == "AGB":
|
|
533
|
-
if sram:
|
|
534
|
-
self._set_fw_variable("TRANSFER_SIZE", 1)
|
|
535
|
-
self._set_fw_variable("ADDRESS", address)
|
|
536
|
-
self._write(self.DEVICE_CMD["AGB_CART_WRITE_SRAM"])
|
|
537
|
-
self._write(value)
|
|
538
|
-
self._read(1)
|
|
539
|
-
return
|
|
540
|
-
elif flashcart:
|
|
541
|
-
buffer = bytearray([self.DEVICE_CMD["AGB_FLASH_WRITE_SHORT"]])
|
|
542
|
-
else:
|
|
543
|
-
buffer = bytearray([self.DEVICE_CMD["AGB_CART_WRITE"]])
|
|
544
|
-
|
|
545
|
-
buffer.extend(struct.pack(">I", address >> 1))
|
|
546
|
-
buffer.extend(struct.pack(">H", value & 0xFFFF))
|
|
547
|
-
|
|
548
|
-
self._write(buffer)
|
|
549
|
-
|
|
550
|
-
if self.MODE == "DMG" and sram: self._set_fw_variable("DMG_WRITE_CS_PULSE", 0)
|
|
551
|
-
|
|
552
|
-
def _cart_write_flash(self, commands, flashcart=False):
|
|
553
|
-
if self.FW["fw_ver"] < 6 and not (self.MODE == "AGB" and not flashcart):
|
|
554
|
-
for command in commands:
|
|
555
|
-
self._cart_write(command[0], command[1], flashcart=flashcart)
|
|
556
|
-
return
|
|
557
|
-
|
|
558
|
-
num = len(commands)
|
|
559
|
-
buffer = bytearray([self.DEVICE_CMD["CART_WRITE_FLASH_CMD"]])
|
|
560
|
-
if self.FW["fw_ver"] >= 6:
|
|
561
|
-
buffer.extend(struct.pack("B", 1 if flashcart else 0))
|
|
562
|
-
buffer.extend(struct.pack("B", num))
|
|
563
|
-
for i in range(0, num):
|
|
564
|
-
dprint("Writing to cartridge: 0x{:X} = 0x{:X} ({:d} of {:d})".format(commands[i][0], commands[i][1], i+1, num))
|
|
565
|
-
if self.MODE == "AGB" and flashcart:
|
|
566
|
-
buffer.extend(struct.pack(">I", commands[i][0] >> 1))
|
|
567
|
-
else:
|
|
568
|
-
buffer.extend(struct.pack(">I", commands[i][0]))
|
|
569
|
-
|
|
570
|
-
if self.FW["fw_ver"] < 6:
|
|
571
|
-
buffer.extend(struct.pack("B", commands[i][1]))
|
|
572
|
-
else:
|
|
573
|
-
buffer.extend(struct.pack(">H", commands[i][1]))
|
|
574
|
-
|
|
575
|
-
self._write(buffer)
|
|
576
|
-
ret = self._read(1)
|
|
577
|
-
if ret != 0x01:
|
|
578
|
-
dprint("Communication error in _cart_write_flash():", ret)
|
|
579
|
-
self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"A critical communication error occured during a write. Please avoid passive USB hubs, try different USB ports/cables and re-connect the device."})
|
|
580
|
-
self.CANCEL = True
|
|
581
|
-
self.ERROR = True
|
|
582
|
-
return False
|
|
583
|
-
|
|
584
|
-
def _clk_toggle(self, num):
|
|
585
|
-
if self.FW["pcb_ver"] not in (5, 6, 101): return False
|
|
586
|
-
for _ in range(0, num):
|
|
587
|
-
self._write(self.DEVICE_CMD["CLK_HIGH"])
|
|
588
|
-
self._write(self.DEVICE_CMD["CLK_LOW"])
|
|
589
|
-
return True
|
|
590
|
-
|
|
591
|
-
def _set_we_pin_wr(self):
|
|
592
|
-
if self.MODE == "DMG":
|
|
593
|
-
self._set_fw_variable("FLASH_WE_PIN", 0x01) # FLASH_WE_PIN_WR
|
|
594
|
-
def _set_we_pin_audio(self):
|
|
595
|
-
if self.MODE == "DMG":
|
|
596
|
-
self._set_fw_variable("FLASH_WE_PIN", 0x02) # FLASH_WE_PIN_AUDIO
|
|
597
|
-
|
|
598
|
-
def CartPowerCycle(self, delay=0.1):
|
|
599
|
-
if self.CanPowerCycleCart():
|
|
600
|
-
dprint("Power cycling cartridge with a delay of {:.1f} seconds".format(delay))
|
|
601
|
-
self.CartPowerOff(delay=delay)
|
|
602
|
-
self.CartPowerOn(delay=delay)
|
|
603
|
-
if self.MODE == "DMG":
|
|
604
|
-
self._write(self.DEVICE_CMD["SET_MODE_DMG"])
|
|
605
|
-
elif self.MODE == "AGB":
|
|
606
|
-
self._write(self.DEVICE_CMD["SET_MODE_AGB"])
|
|
298
|
+
return None
|
|
607
299
|
|
|
608
|
-
def
|
|
609
|
-
if self.
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
else:
|
|
613
|
-
self._write(self.DEVICE_CMD["SET_ADDR_AS_INPUTS"])
|
|
614
|
-
|
|
615
|
-
def CartPowerOn(self, delay=0.1):
|
|
616
|
-
if self.FW["pcb_ver"] in (5, 6):
|
|
617
|
-
self._write(self.DEVICE_CMD["OFW_QUERY_CART_PWR"])
|
|
618
|
-
if self._read(1) == 0:
|
|
619
|
-
self._write(self.DEVICE_CMD["OFW_CART_PWR_ON"])
|
|
620
|
-
time.sleep(delay)
|
|
621
|
-
self.DEVICE.reset_input_buffer() # bug workaround
|
|
622
|
-
|
|
623
|
-
def GetMode(self):
|
|
624
|
-
return self.MODE
|
|
300
|
+
def ResetLEDs(self):
|
|
301
|
+
if self.DEVICE in (None, False): return
|
|
302
|
+
self._write(self.DEVICE_CMD["OFW_CART_MODE"]) # Reset LEDs
|
|
303
|
+
self._read(1)
|
|
625
304
|
|
|
626
|
-
def
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
self._write(self.DEVICE_CMD["SET_MODE_DMG"])
|
|
630
|
-
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
|
631
|
-
self._set_fw_variable("DMG_READ_METHOD", 1)
|
|
632
|
-
self._set_fw_variable("CART_MODE", 1)
|
|
633
|
-
self.MODE = "DMG"
|
|
634
|
-
elif mode == "AGB":
|
|
635
|
-
self._write(self.DEVICE_CMD["SET_MODE_AGB"])
|
|
636
|
-
self._write(self.DEVICE_CMD["SET_VOLTAGE_3_3V"])
|
|
637
|
-
self._set_fw_variable("AGB_READ_METHOD", 0)
|
|
638
|
-
self._set_fw_variable("CART_MODE", 2)
|
|
639
|
-
self.MODE = "AGB"
|
|
640
|
-
self._set_fw_variable(key="ADDRESS", value=0)
|
|
641
|
-
if self.FW["fw_ver"] >= 8: self._write(self.DEVICE_CMD["DISABLE_PULLUPS"], wait=True)
|
|
642
|
-
self.CartPowerOn()
|
|
643
|
-
|
|
644
|
-
def GetSupportedCartridgesDMG(self):
|
|
645
|
-
return (list(self.SUPPORTED_CARTS['DMG'].keys()), list(self.SUPPORTED_CARTS['DMG'].values()))
|
|
646
|
-
|
|
647
|
-
def GetSupportedCartridgesAGB(self):
|
|
648
|
-
return (list(self.SUPPORTED_CARTS['AGB'].keys()), list(self.SUPPORTED_CARTS['AGB'].values()))
|
|
649
|
-
|
|
650
|
-
def SetProgress(self, args):
|
|
651
|
-
if self.CANCEL and args["action"] not in ("ABORT", "FINISHED", "ERROR"): return
|
|
652
|
-
if "pos" in args: self.POS = args["pos"]
|
|
653
|
-
if args["action"] == "UPDATE_POS": self.INFO["transferred"] = args["pos"]
|
|
654
|
-
try:
|
|
655
|
-
self.SIGNAL.emit(args)
|
|
656
|
-
except AttributeError:
|
|
657
|
-
if self.SIGNAL is not None:
|
|
658
|
-
self.SIGNAL(args)
|
|
659
|
-
|
|
660
|
-
if args["action"] == "INITIALIZE":
|
|
661
|
-
self.POS = 0
|
|
662
|
-
elif args["action"] == "FINISHED":
|
|
663
|
-
self.POS = 0
|
|
664
|
-
self.SIGNAL = None
|
|
665
|
-
|
|
666
|
-
def ReadInfo(self, setPinsAsInputs=False, checkRtc=True):
|
|
667
|
-
if not self.IsConnected(): raise ConnectionError("Couldn’t access the the device.")
|
|
668
|
-
data = {}
|
|
669
|
-
self.SIGNAL = None
|
|
670
|
-
|
|
671
|
-
if self.FW["pcb_ver"] in (5, 6, 101):
|
|
672
|
-
self._write(self.DEVICE_CMD["OFW_CART_MODE"]) # Reset LEDs
|
|
673
|
-
self._read(1)
|
|
674
|
-
self.CartPowerOn()
|
|
675
|
-
|
|
676
|
-
if self.MODE == "DMG":
|
|
677
|
-
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
|
678
|
-
self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
|
|
679
|
-
self._set_fw_variable("DMG_READ_CS_PULSE", 0)
|
|
680
|
-
self._set_fw_variable("DMG_WRITE_CS_PULSE", 0)
|
|
681
|
-
elif self.MODE == "AGB":
|
|
682
|
-
if self.FW["pcb_ver"] in (5, 6, 101) and self.FW["fw_ver"] > 1:
|
|
683
|
-
self._write(self.DEVICE_CMD["AGB_BOOTUP_SEQUENCE"], wait=True)
|
|
305
|
+
def SupportsBootloaderReset(self):
|
|
306
|
+
if self.FW["fw_ver"] >= 12:
|
|
307
|
+
return self.FW["bootloader_reset"]
|
|
684
308
|
else:
|
|
685
|
-
print("{:s}Error: No mode was set.{:s}".format(ANSI.RED, ANSI.RESET))
|
|
686
|
-
return False
|
|
687
|
-
|
|
688
|
-
header = self.ReadROM(0, 0x180)
|
|
689
|
-
if Util.DEBUG:
|
|
690
|
-
with open("debug_header.bin", "wb") as f: f.write(header)
|
|
691
|
-
if header is False or len(header) != 0x180:
|
|
692
|
-
return False
|
|
693
|
-
|
|
694
|
-
# Parse ROM header
|
|
695
|
-
if self.MODE == "DMG":
|
|
696
|
-
data = RomFileDMG(header).GetHeader()
|
|
697
|
-
if "game_title" in data and data["game_title"] == "TETRIS" and hashlib.sha1(header).digest() != bytearray([0x1D, 0x69, 0x2A, 0x4B, 0x31, 0x7A, 0xA5, 0xE9, 0x67, 0xEE, 0xC2, 0x2F, 0xCC, 0x32, 0x43, 0x8C, 0xCB, 0xC5, 0x78, 0x0B]): # Sachen
|
|
698
|
-
header = self.ReadROM(0, 0x280)
|
|
699
|
-
data = RomFileDMG(header).GetHeader()
|
|
700
|
-
if "logo_correct" in data and data["logo_correct"] is False and not b"Future Console Design" in header: # workaround for strange bootlegs
|
|
701
|
-
self._cart_write(0, 0xFF)
|
|
702
|
-
time.sleep(0.1)
|
|
703
|
-
header = self.ReadROM(0, 0x280)
|
|
704
|
-
data = RomFileDMG(header).GetHeader()
|
|
705
|
-
if "mapper_raw" in data and data["mapper_raw"] == 0x203 or b"Future Console Design" in header: # Xploder GB version number
|
|
706
|
-
self._cart_write(0x0006, 0)
|
|
707
|
-
header[0:0x10] = self.ReadROM(0x4000, 0x10)
|
|
708
|
-
header[0xD0:0xE0] = self.ReadROM(0x40D0, 0x10)
|
|
709
|
-
data = RomFileDMG(header).GetHeader()
|
|
710
|
-
if data == {}: return False
|
|
711
|
-
|
|
712
|
-
data["has_rtc"] = False
|
|
713
|
-
data["rtc_string"] = "Not available"
|
|
714
|
-
if data["logo_correct"] is True:
|
|
715
|
-
_mbc = DMG_MBC().GetInstance(args={"mbc":data["mapper_raw"]}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle)
|
|
716
|
-
if checkRtc:
|
|
717
|
-
data["has_rtc"] = _mbc.HasRTC() is True
|
|
718
|
-
if data["has_rtc"] is True:
|
|
719
|
-
if _mbc.GetName() == "TAMA5": _mbc.EnableMapper()
|
|
720
|
-
_mbc.LatchRTC()
|
|
721
|
-
data["rtc_buffer"] = _mbc.ReadRTC()
|
|
722
|
-
if _mbc.GetName() == "TAMA5": self._set_fw_variable("DMG_READ_CS_PULSE", 0)
|
|
723
|
-
try:
|
|
724
|
-
data["rtc_string"] = _mbc.GetRTCString()
|
|
725
|
-
except:
|
|
726
|
-
data["rtc_string"] = "Invalid data"
|
|
727
|
-
if _mbc.GetName() == "G-MMC1":
|
|
728
|
-
try:
|
|
729
|
-
temp = bytearray([0] * 0x100000)
|
|
730
|
-
temp[0:0x180] = header
|
|
731
|
-
_mbc.SelectBankROM(7)
|
|
732
|
-
if data["game_title"] == "NP M-MENU MENU":
|
|
733
|
-
gbmem_menudata = self.ReadROM(0x4000, 0x1000)
|
|
734
|
-
temp[0x1C000:0x1C000+0x1000] = gbmem_menudata
|
|
735
|
-
elif data["game_title"] == "DMG MULTI MENU ":
|
|
736
|
-
gbmem_menudata = self.ReadROM(0x4000, 0x4000)
|
|
737
|
-
temp[0x1C000:0x1C000+0x4000] = gbmem_menudata
|
|
738
|
-
_mbc.SelectBankROM(0)
|
|
739
|
-
data["gbmem_parsed"] = (GBMemoryMap()).ParseMapData(buffer_map=_mbc.ReadHiddenSector(), buffer_rom=temp)
|
|
740
|
-
except:
|
|
741
|
-
print(traceback.format_exc())
|
|
742
|
-
print("{:s}An error occured while trying to read the hidden sector data of the NP GB-Memory cartridge.{:s}".format(ANSI.RED, ANSI.RESET))
|
|
743
|
-
|
|
744
|
-
elif self.MODE == "AGB":
|
|
745
|
-
# Unlock DACS carts on older firmware
|
|
746
|
-
if self.FW["pcb_ver"] not in (5, 6, 101) or self.FW["fw_ver"] == 1:
|
|
747
|
-
if header[0x04:0x04+0x9C] == bytearray([0x00] * 0x9C):
|
|
748
|
-
self.ReadROM(0x1FFFFE0, 20)
|
|
749
|
-
header = self.ReadROM(0, 0x180)
|
|
750
|
-
|
|
751
|
-
data = RomFileAGB(header).GetHeader()
|
|
752
|
-
if data["logo_correct"] is False: # workaround for strange bootlegs
|
|
753
|
-
self._cart_write(0, 0xFF)
|
|
754
|
-
time.sleep(0.1)
|
|
755
|
-
header = self.ReadROM(0, 0x180)
|
|
756
|
-
data = RomFileAGB(header).GetHeader()
|
|
757
|
-
|
|
758
|
-
if data["empty"] or data["empty_nocart"]:
|
|
759
|
-
data["rom_size"] = 0x2000000
|
|
760
|
-
else:
|
|
761
|
-
# Check where the ROM data repeats (for unlicensed carts)
|
|
762
|
-
size_check = header[0xA0:0xA0+16]
|
|
763
|
-
currAddr = 0x10000
|
|
764
|
-
while currAddr < 0x2000000:
|
|
765
|
-
buffer = self.ReadROM(currAddr + 0xA0, 64)[:16]
|
|
766
|
-
if buffer == size_check: break
|
|
767
|
-
currAddr *= 2
|
|
768
|
-
data["rom_size"] = currAddr
|
|
769
|
-
|
|
770
|
-
if (self.ReadROM(0x1FFE000, 0x0C) == b"AGBFLASHDACS"):
|
|
771
|
-
data["dacs_8m"] = True
|
|
772
|
-
|
|
773
|
-
data["rtc_string"] = "Not available"
|
|
774
|
-
if checkRtc and data["logo_correct"] is True and header[0xC5] == 0 and header[0xC7] == 0 and header[0xC9] == 0:
|
|
775
|
-
_agb_gpio = AGB_GPIO(args={"rtc":True}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle)
|
|
776
|
-
has_rtc = _agb_gpio.HasRTC()
|
|
777
|
-
data["has_rtc"] = has_rtc is True
|
|
778
|
-
if has_rtc is not True:
|
|
779
|
-
data["no_rtc_reason"] = has_rtc
|
|
780
|
-
data["has_rtc"] = False
|
|
781
|
-
else:
|
|
782
|
-
data["rtc_buffer"] = _agb_gpio.ReadRTC()
|
|
783
|
-
try:
|
|
784
|
-
data["rtc_string"] = _agb_gpio.GetRTCString()
|
|
785
|
-
except Exception as e:
|
|
786
|
-
dprint("RTC exception:", str(e.args[0]))
|
|
787
|
-
data["has_rtc"] = False
|
|
788
|
-
else:
|
|
789
|
-
data["has_rtc"] = False
|
|
790
|
-
data["no_rtc_reason"] = None
|
|
791
|
-
|
|
792
|
-
if data["ereader"] is True:
|
|
793
|
-
bank = 0
|
|
794
|
-
dprint("Switching to FLASH bank {:d}".format(bank))
|
|
795
|
-
cmds = [
|
|
796
|
-
[ 0x5555, 0xAA ],
|
|
797
|
-
[ 0x2AAA, 0x55 ],
|
|
798
|
-
[ 0x5555, 0xB0 ],
|
|
799
|
-
[ 0, bank ]
|
|
800
|
-
]
|
|
801
|
-
self._cart_write_flash(cmds)
|
|
802
|
-
temp = self.ReadRAM(address=0xD000, length=0x2000, command=self.DEVICE_CMD["AGB_CART_READ_SRAM"])
|
|
803
|
-
if temp[0:0x14] == b'Card-E Reader 2001\0\0':
|
|
804
|
-
data["ereader_calibration"] = temp
|
|
805
|
-
else:
|
|
806
|
-
data["ereader_calibration"] = None
|
|
807
|
-
del(data["ereader_calibration"])
|
|
808
|
-
|
|
809
|
-
dprint("Header data:", data)
|
|
810
|
-
data["raw"] = header
|
|
811
|
-
self.INFO = {**self.INFO, **data}
|
|
812
|
-
if "batteryless_sram" in self.INFO["dump_info"]: del(self.INFO["dump_info"]["batteryless_sram"])
|
|
813
|
-
self.INFO["dump_info"]["header"] = data
|
|
814
|
-
self.INFO["flash_type"] = 0
|
|
815
|
-
self.INFO["last_action"] = 0
|
|
816
|
-
|
|
817
|
-
if self.MODE == "DMG": #and setPinsAsInputs:
|
|
818
|
-
self._write(self.DEVICE_CMD["SET_ADDR_AS_INPUTS"])
|
|
819
|
-
|
|
820
|
-
return data
|
|
821
|
-
|
|
822
|
-
def DetectCartridge(self, mbc=None, limitVoltage=False, checkSaveType=True):
|
|
823
|
-
self.SIGNAL = None
|
|
824
|
-
cart_type_id = 0
|
|
825
|
-
save_type = None
|
|
826
|
-
save_chip = None
|
|
827
|
-
sram_unstable = None
|
|
828
|
-
save_size = None
|
|
829
|
-
checkBatterylessSRAM = False
|
|
830
|
-
|
|
831
|
-
# Header
|
|
832
|
-
has_rtc = self.INFO["has_rtc"]
|
|
833
|
-
info = self.ReadInfo(checkRtc=False)
|
|
834
|
-
self.INFO["has_rtc"] = has_rtc
|
|
835
|
-
|
|
836
|
-
if self.MODE == "DMG" and mbc is None:
|
|
837
|
-
mbc = info["mapper_raw"]
|
|
838
|
-
if mbc > 0x200: checkSaveType = False
|
|
839
|
-
|
|
840
|
-
ret = self.AutoDetectFlash(limitVoltage=limitVoltage)
|
|
841
|
-
if ret is False: return False
|
|
842
|
-
(cart_types, cart_type_id, flash_id, cfi_s, cfi) = ret
|
|
843
|
-
supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
|
|
844
|
-
cart_type = supported_carts[cart_type_id]
|
|
845
|
-
if self.MODE == "DMG" and "command_set" in cart_type and cart_type["command_set"] == "DMG-MBC5-32M-FLASH":
|
|
846
|
-
checkSaveType = False
|
|
847
|
-
elif self.MODE == "AGB" and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] == 1:
|
|
848
|
-
save_size == 65536
|
|
849
|
-
save_type = 7
|
|
850
|
-
checkSaveType = False
|
|
851
|
-
elif self.MODE == "AGB" and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] > 1:
|
|
852
|
-
checkSaveType = False
|
|
853
|
-
elif self.MODE == "DMG" and "mbc" in cart_type and cart_type["mbc"] == 0x105: # G-MMC1
|
|
854
|
-
header = self.ReadROM(0, 0x180)
|
|
855
|
-
data = RomFileDMG(header).GetHeader()
|
|
856
|
-
_mbc = DMG_MBC().GetInstance(args={"mbc":cart_type["mbc"]}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle)
|
|
857
|
-
temp = bytearray([0] * 0x100000)
|
|
858
|
-
temp[0:0x180] = header
|
|
859
|
-
_mbc.SelectBankROM(7)
|
|
860
|
-
if data["game_title"] == "NP M-MENU MENU":
|
|
861
|
-
gbmem_menudata = self.ReadROM(0x4000, 0x1000)
|
|
862
|
-
temp[0x1C000:0x1C000+0x1000] = gbmem_menudata
|
|
863
|
-
elif data["game_title"] == "DMG MULTI MENU ":
|
|
864
|
-
gbmem_menudata = self.ReadROM(0x4000, 0x4000)
|
|
865
|
-
temp[0x1C000:0x1C000+0x4000] = gbmem_menudata
|
|
866
|
-
_mbc.SelectBankROM(0)
|
|
867
|
-
info["gbmem"] = _mbc.ReadHiddenSector()
|
|
868
|
-
info["gbmem_parsed"] = (GBMemoryMap()).ParseMapData(buffer_map=info["gbmem"], buffer_rom=temp)
|
|
869
|
-
|
|
870
|
-
# Save Type and Size
|
|
871
|
-
if checkSaveType:
|
|
872
|
-
if self.MODE == "DMG":
|
|
873
|
-
save_size = 131072
|
|
874
|
-
save_type = 0x04
|
|
875
|
-
if mbc == 0x20: # MBC6
|
|
876
|
-
save_size = 1081344
|
|
877
|
-
save_type = 0x104
|
|
878
|
-
return (info, save_size, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, cfi, flash_id)
|
|
879
|
-
elif mbc == 0x22: # MBC7
|
|
880
|
-
save_type = 0x102
|
|
881
|
-
save_size = 512
|
|
882
|
-
elif mbc == 0xFD: # TAMA5
|
|
883
|
-
save_size = 32
|
|
884
|
-
save_type = 0x103
|
|
885
|
-
return (info, save_size, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, cfi, flash_id)
|
|
886
|
-
args = { 'mode':2, 'path':None, 'mbc':mbc, 'save_type':save_type, 'rtc':False }
|
|
887
|
-
elif self.MODE == "AGB":
|
|
888
|
-
args = { 'mode':2, 'path':None, 'mbc':mbc, 'save_type':8, 'rtc':False }
|
|
889
|
-
|
|
890
|
-
ret = self._BackupRestoreRAM(args=args)
|
|
891
|
-
|
|
892
|
-
if ret is not False and "data" in self.INFO:
|
|
893
|
-
save_size = Util.find_size(self.INFO["data"], len(self.INFO["data"]))
|
|
894
|
-
else:
|
|
895
|
-
save_size = 0
|
|
896
|
-
|
|
897
|
-
if self.MODE == "DMG":
|
|
898
|
-
try:
|
|
899
|
-
save_type = Util.DMG_Header_RAM_Sizes_Map[Util.DMG_Header_RAM_Sizes_Flasher_Map.index(save_size)]
|
|
900
|
-
except:
|
|
901
|
-
save_size = 0
|
|
902
|
-
save_type = 0
|
|
903
|
-
|
|
904
|
-
if save_size > 0x20:
|
|
905
|
-
if mbc == 0x22: # MBC7
|
|
906
|
-
if save_size == 256:
|
|
907
|
-
save_type = 0x101
|
|
908
|
-
elif save_size == 512:
|
|
909
|
-
save_type = 0x102
|
|
910
|
-
elif save_size > 0x10000: # MBC30+RTC?
|
|
911
|
-
check = True
|
|
912
|
-
for i in range(0x8000, 0x10000, 0x40):
|
|
913
|
-
if self.INFO["data"][i:i+3] != bytearray([self.INFO["data"][i]] * 3):
|
|
914
|
-
check = False
|
|
915
|
-
break
|
|
916
|
-
if check:
|
|
917
|
-
save_size = 32768
|
|
918
|
-
save_type = 0x03
|
|
919
|
-
else:
|
|
920
|
-
check = True
|
|
921
|
-
for i in range(0x1A000, 0x20000, 0x40):
|
|
922
|
-
if self.INFO["data"][i:i+3] != bytearray([self.INFO["data"][i]] * 3):
|
|
923
|
-
check = False
|
|
924
|
-
break
|
|
925
|
-
if check:
|
|
926
|
-
save_size = 65536
|
|
927
|
-
save_type = 0x05
|
|
928
|
-
|
|
929
|
-
elif self.MODE == "AGB":
|
|
930
|
-
if info["3d_memory"] is True:
|
|
931
|
-
save_type = None
|
|
932
|
-
save_size = 0
|
|
933
|
-
else:
|
|
934
|
-
# Check for FLASH
|
|
935
|
-
ret = self.ReadFlashSaveID()
|
|
936
|
-
if ret is not False:
|
|
937
|
-
(flash_save_id, _) = ret
|
|
938
|
-
try:
|
|
939
|
-
if flash_save_id != 0 and flash_save_id in Util.AGB_Flash_Save_Chips:
|
|
940
|
-
save_size = Util.AGB_Flash_Save_Chips_Sizes[list(Util.AGB_Flash_Save_Chips).index(flash_save_id)]
|
|
941
|
-
save_chip = Util.AGB_Flash_Save_Chips[flash_save_id]
|
|
942
|
-
if save_size == 131072:
|
|
943
|
-
save_type = 5
|
|
944
|
-
elif save_size == 65536:
|
|
945
|
-
save_type = 4
|
|
946
|
-
except:
|
|
947
|
-
pass
|
|
948
|
-
|
|
949
|
-
if save_type is None:
|
|
950
|
-
checkBatterylessSRAM = True
|
|
951
|
-
if info["dacs_8m"] is True:
|
|
952
|
-
save_size = 1048576
|
|
953
|
-
save_type = 6
|
|
954
|
-
elif save_size > 256: # SRAM
|
|
955
|
-
if save_size == 131072:
|
|
956
|
-
save_type = 8
|
|
957
|
-
checkBatterylessSRAM = True
|
|
958
|
-
elif save_size == 65536:
|
|
959
|
-
save_type = 7
|
|
960
|
-
checkBatterylessSRAM = True
|
|
961
|
-
elif save_size == 32768:
|
|
962
|
-
save_type = 3
|
|
963
|
-
elif save_size in Util.AGB_Header_Save_Sizes:
|
|
964
|
-
save_type = Util.AGB_Header_Save_Sizes.index(save_size)
|
|
965
|
-
else:
|
|
966
|
-
save_type = None
|
|
967
|
-
save_size = 0
|
|
968
|
-
else:
|
|
969
|
-
dprint("Testing EEPROM")
|
|
970
|
-
# Check for 4K EEPROM
|
|
971
|
-
self._BackupRestoreRAM(args={ 'mode':2, 'path':None, 'mbc':mbc, 'save_type':1, 'rtc':False })
|
|
972
|
-
save_size = Util.find_size(self.INFO["data"], len(self.INFO["data"]))
|
|
973
|
-
eeprom_4k = self.INFO["data"]
|
|
974
|
-
# Check for 64K EEPROM
|
|
975
|
-
self._BackupRestoreRAM(args={ 'mode':2, 'path':None, 'mbc':mbc, 'save_type':2, 'rtc':False })
|
|
976
|
-
save_size = Util.find_size(self.INFO["data"], len(self.INFO["data"]))
|
|
977
|
-
eeprom_64k = self.INFO["data"]
|
|
978
|
-
if eeprom_64k in (bytearray([0xFF] * len(eeprom_64k)), bytearray([0] * len(eeprom_64k))):
|
|
979
|
-
save_type = None
|
|
980
|
-
save_size = 0
|
|
981
|
-
elif (eeprom_4k == eeprom_64k[:len(eeprom_4k)]):
|
|
982
|
-
save_type = 2
|
|
983
|
-
save_size = 8192
|
|
984
|
-
checkBatterylessSRAM = False
|
|
985
|
-
else:
|
|
986
|
-
save_type = 1
|
|
987
|
-
save_size = 512
|
|
988
|
-
checkBatterylessSRAM = False
|
|
989
|
-
|
|
990
|
-
if checkBatterylessSRAM:
|
|
991
|
-
batteryless = self.CheckBatterylessSRAM()
|
|
992
|
-
if batteryless is not False:
|
|
993
|
-
save_type = 9
|
|
994
|
-
info["batteryless_sram"] = batteryless
|
|
995
|
-
self.INFO["dump_info"]["batteryless_sram"] = batteryless
|
|
996
|
-
|
|
997
|
-
self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
|
|
998
|
-
self.INFO["last_action"] = 0
|
|
999
|
-
self.INFO["action"] = None
|
|
1000
|
-
return (info, save_size, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, cfi, flash_id)
|
|
1001
|
-
|
|
1002
|
-
def CheckBatterylessSRAM(self):
|
|
1003
|
-
bl_size = None
|
|
1004
|
-
bl_offset = None
|
|
1005
|
-
if self.MODE == "AGB":
|
|
1006
|
-
buffer = self.ReadROM(0, 0x180)
|
|
1007
|
-
header = RomFileAGB(buffer).GetHeader()
|
|
1008
|
-
if header["game_code"] in ("GMBC", "PNES"):
|
|
1009
|
-
bl_size = 0x10000
|
|
1010
|
-
state_id1 = 0x57a731d7
|
|
1011
|
-
state_id2 = 0x57a731d8
|
|
1012
|
-
state_id3 = 0x57a731d9
|
|
1013
|
-
if struct.unpack("<I", self.ReadROM(0x400000-0x40000, 4))[0] in (state_id1, state_id2, state_id3):
|
|
1014
|
-
bl_offset = 0x400000-0x40000
|
|
1015
|
-
elif struct.unpack("<I", self.ReadROM(0x800000-0x40000, 4))[0] in (state_id1, state_id2, state_id3):
|
|
1016
|
-
bl_offset = 0x800000-0x40000
|
|
1017
|
-
elif struct.unpack("<I", self.ReadROM(0x1000000-0x40000, 4))[0] in (state_id1, state_id2, state_id3):
|
|
1018
|
-
bl_offset = 0x1000000-0x40000
|
|
1019
|
-
elif struct.unpack("<I", self.ReadROM(0x2000000-0x40000, 4))[0] in (state_id1, state_id2, state_id3):
|
|
1020
|
-
bl_offset = 0x2000000-0x40000
|
|
1021
|
-
dprint("Detected Goomba Color or PocketNES Batteryless ROM by Lesserkuma")
|
|
1022
|
-
else:
|
|
1023
|
-
boot_vector = (struct.unpack("<I", buffer[0:3] + bytearray([0]))[0] + 2) << 2
|
|
1024
|
-
batteryless_loader = self.ReadROM(boot_vector, 0x2000)
|
|
1025
|
-
if bytearray(b'<3 from Maniac') in batteryless_loader:
|
|
1026
|
-
payload_size = struct.unpack("<H", batteryless_loader[batteryless_loader.index(bytearray(b'<3 from Maniac')):][0x0E:0x10])[0]
|
|
1027
|
-
if payload_size == 0:
|
|
1028
|
-
payload_size = 0x414
|
|
1029
|
-
bl_offset = batteryless_loader.index(bytearray(b'<3 from Maniac')) + boot_vector + 0x10
|
|
1030
|
-
payload = self.ReadROM(bl_offset - payload_size, payload_size)
|
|
1031
|
-
bl_size = struct.unpack("<I", payload[0x8:0xC])[0]
|
|
1032
|
-
dprint("Detected Batteryless SRAM ROM made with the Automatic batteryless saving patcher for GBA by metroid-maniac")
|
|
1033
|
-
if bl_size not in (0x2000, 0x8000, 0x10000, 0x20000):
|
|
1034
|
-
print("{:s}Warning: Unsupported Batteryless SRAM size value detected: 0x{:X}{:s}".format(ANSI.YELLOW, bl_size, ANSI.RESET))
|
|
1035
|
-
elif (bytearray([0x02, 0x13, 0xA0, 0xE3]) in batteryless_loader):
|
|
1036
|
-
if (
|
|
1037
|
-
bytearray([0x09, 0x04, 0xA0, 0xE3]) in batteryless_loader or
|
|
1038
|
-
bytearray([0x09, 0x14, 0xA0, 0xE3]) in batteryless_loader or
|
|
1039
|
-
bytearray([0x09, 0x24, 0xA0, 0xE3]) in batteryless_loader or
|
|
1040
|
-
bytearray([0x09, 0x34, 0xA0, 0xE3]) in batteryless_loader
|
|
1041
|
-
):
|
|
1042
|
-
bl_size = 0x20000
|
|
1043
|
-
else:
|
|
1044
|
-
bl_size = 0x10000
|
|
1045
|
-
base_addr = batteryless_loader.index(bytearray([0x02, 0x13, 0xA0, 0xE3]))
|
|
1046
|
-
addr_value = batteryless_loader[base_addr - 8]
|
|
1047
|
-
addr_rotate_right = batteryless_loader[base_addr - 7] * 2
|
|
1048
|
-
addr_shift = batteryless_loader[base_addr - 3] << 1
|
|
1049
|
-
address = (addr_value >> addr_rotate_right) | (addr_value << (32 - addr_rotate_right)) & 0xFFFFFFFF
|
|
1050
|
-
address = (address << addr_shift)
|
|
1051
|
-
if address < 32*1024*1024 and address > 0x1000:
|
|
1052
|
-
bl_offset = address
|
|
1053
|
-
dprint("Detected Chinese bootleg Batteryless SRAM ROM")
|
|
1054
|
-
else:
|
|
1055
|
-
dprint("Bad offset with Chinese bootleg Batteryless SRAM ROM:", hex(address))
|
|
1056
|
-
else:
|
|
1057
|
-
bl_offset = None
|
|
1058
|
-
bl_size = None
|
|
1059
|
-
if bl_offset is None or bl_size is None:
|
|
1060
|
-
dprint("No Batteryless SRAM routine detected")
|
|
1061
309
|
return False
|
|
1062
|
-
dprint("bl_offset=0x{:X}, bl_size=0x{:X}".format(bl_offset, bl_size))
|
|
1063
|
-
return {"bl_offset":bl_offset, "bl_size":bl_size}
|
|
1064
|
-
|
|
1065
|
-
def ReadFlashSaveID(self):
|
|
1066
|
-
# Check if actually SRAM/FRAM
|
|
1067
|
-
test1 = self._cart_read(0x0004, agb_save_flash=True) >> 8
|
|
1068
|
-
self._cart_write_flash([[ 0x0004, test1 ^ 0xFF ]])
|
|
1069
|
-
test2 = self._cart_read(0x0004, agb_save_flash=True) >> 8
|
|
1070
|
-
if test1 != test2:
|
|
1071
|
-
self._cart_write_flash([[ 0x0004, test1 ]])
|
|
1072
|
-
dprint("Seems to be SRAM/FRAM, not FLASH")
|
|
1073
|
-
return False
|
|
1074
|
-
|
|
1075
|
-
# Read Chip ID
|
|
1076
|
-
temp5555 = self._cart_read(0x5555, agb_save_flash=True) >> 8
|
|
1077
|
-
temp2AAA = self._cart_read(0x2AAA, agb_save_flash=True) >> 8
|
|
1078
|
-
temp0000 = self._cart_read(0x0000, agb_save_flash=True) >> 8
|
|
1079
|
-
|
|
1080
|
-
cmds = [
|
|
1081
|
-
[ 0x5555, 0xAA ],
|
|
1082
|
-
[ 0x2AAA, 0x55 ],
|
|
1083
|
-
[ 0x5555, 0x90 ]
|
|
1084
|
-
]
|
|
1085
|
-
self._cart_write_flash(cmds)
|
|
1086
|
-
time.sleep(0.01)
|
|
1087
|
-
agb_flash_chip = self._cart_read(0, agb_save_flash=True)
|
|
1088
|
-
cmds = [
|
|
1089
|
-
[ 0x5555, 0xAA ],
|
|
1090
|
-
[ 0x2AAA, 0x55 ],
|
|
1091
|
-
[ 0x5555, 0xF0 ]
|
|
1092
|
-
]
|
|
1093
|
-
self._cart_write_flash(cmds)
|
|
1094
|
-
time.sleep(0.01)
|
|
1095
|
-
self._cart_write_flash([ [ 0, 0xF0 ] ])
|
|
1096
|
-
time.sleep(0.01)
|
|
1097
|
-
|
|
1098
|
-
if agb_flash_chip not in Util.AGB_Flash_Save_Chips:
|
|
1099
|
-
# Restore SRAM values
|
|
1100
|
-
cmds = [
|
|
1101
|
-
[ 0x5555, temp5555 ],
|
|
1102
|
-
[ 0x2AAA, temp2AAA ],
|
|
1103
|
-
[ 0x0000, temp0000 ]
|
|
1104
|
-
]
|
|
1105
|
-
self._cart_write_flash(cmds)
|
|
1106
|
-
agb_flash_chip_name = "Unknown flash chip ID (0x{:04X})".format(agb_flash_chip)
|
|
1107
|
-
else:
|
|
1108
|
-
agb_flash_chip_name = Util.AGB_Flash_Save_Chips[agb_flash_chip]
|
|
1109
|
-
|
|
1110
|
-
dprint(agb_flash_chip_name)
|
|
1111
|
-
return (agb_flash_chip, agb_flash_chip_name)
|
|
1112
|
-
|
|
1113
|
-
def ReadROM(self, address, length, skip_init=False, max_length=64):
|
|
1114
|
-
num = math.ceil(length / max_length)
|
|
1115
|
-
dprint("Reading 0x{:X} bytes from cartridge ROM at 0x{:X} in {:d} iteration(s)".format(length, address, num))
|
|
1116
|
-
if length > max_length: length = max_length
|
|
1117
|
-
|
|
1118
|
-
buffer = bytearray()
|
|
1119
|
-
if not skip_init:
|
|
1120
|
-
self._set_fw_variable("TRANSFER_SIZE", length)
|
|
1121
|
-
if self.MODE == "DMG":
|
|
1122
|
-
self._set_fw_variable("ADDRESS", address)
|
|
1123
|
-
self._set_fw_variable("DMG_ACCESS_MODE", 1) # MODE_ROM_READ
|
|
1124
|
-
elif self.MODE == "AGB":
|
|
1125
|
-
self._set_fw_variable("ADDRESS", address >> 1)
|
|
1126
|
-
|
|
1127
|
-
if self.MODE == "DMG":
|
|
1128
|
-
command = "DMG_CART_READ"
|
|
1129
|
-
elif self.MODE == "AGB":
|
|
1130
|
-
command = "AGB_CART_READ"
|
|
1131
|
-
|
|
1132
|
-
for n in range(0, num):
|
|
1133
|
-
self._write(self.DEVICE_CMD[command])
|
|
1134
|
-
temp = self._read(length)
|
|
1135
|
-
if temp is not False and isinstance(temp, int): temp = bytearray([temp])
|
|
1136
|
-
if temp is False or len(temp) != length:
|
|
1137
|
-
dprint("Error while trying to read 0x{:X} bytes from cartridge ROM at 0x{:X} in iteration {:d} of {:d} (response: {:s})".format(length, address, n, num, str(temp)))
|
|
1138
|
-
return bytearray()
|
|
1139
|
-
buffer += temp
|
|
1140
|
-
if self.INFO["action"] in (self.ACTIONS["ROM_READ"], self.ACTIONS["SAVE_READ"], self.ACTIONS["ROM_WRITE_VERIFY"]) and not self.NO_PROG_UPDATE:
|
|
1141
|
-
self.SetProgress({"action":"READ", "bytes_added":len(temp)})
|
|
1142
|
-
|
|
1143
|
-
return buffer
|
|
1144
|
-
|
|
1145
|
-
def ReadROM_3DMemory(self, address, length, max_length=64):
|
|
1146
|
-
buffer_size = 0x1000
|
|
1147
|
-
num = math.ceil(length / max_length)
|
|
1148
|
-
dprint("Reading 0x{:X} bytes from cartridge ROM in {:d} iteration(s)".format(length, num))
|
|
1149
|
-
if length > max_length: length = max_length
|
|
1150
|
-
|
|
1151
|
-
self._set_fw_variable("TRANSFER_SIZE", length)
|
|
1152
|
-
self._set_fw_variable("BUFFER_SIZE", buffer_size)
|
|
1153
|
-
self._set_fw_variable("ADDRESS", address >> 1)
|
|
1154
|
-
|
|
1155
|
-
buffer = bytearray()
|
|
1156
|
-
error = False
|
|
1157
|
-
for _ in range(0, int(num / (buffer_size / length))): #32
|
|
1158
|
-
for _ in range(0, int(buffer_size / length)): # 0x1000/0x200=8
|
|
1159
|
-
self._write(self.DEVICE_CMD["AGB_CART_READ_3D_MEMORY"])
|
|
1160
|
-
temp = self._read(length)
|
|
1161
|
-
if isinstance(temp, int): temp = bytearray([temp])
|
|
1162
|
-
if temp is False or len(temp) != length:
|
|
1163
|
-
error = True
|
|
1164
|
-
buffer += temp
|
|
1165
|
-
if self.INFO["action"] == self.ACTIONS["ROM_READ"] and not self.NO_PROG_UPDATE:
|
|
1166
|
-
self.SetProgress({"action":"READ", "bytes_added":length})
|
|
1167
|
-
self._write(0)
|
|
1168
|
-
|
|
1169
|
-
if error: return bytearray()
|
|
1170
|
-
return buffer
|
|
1171
|
-
|
|
1172
|
-
def ReadRAM(self, address, length, command=None, max_length=64):
|
|
1173
|
-
num = math.ceil(length / max_length)
|
|
1174
|
-
dprint("Reading 0x{:X} bytes from cartridge RAM in {:d} iteration(s)".format(length, num))
|
|
1175
|
-
if length > max_length: length = max_length
|
|
1176
|
-
buffer = bytearray()
|
|
1177
|
-
self._set_fw_variable("TRANSFER_SIZE", length)
|
|
1178
|
-
|
|
1179
|
-
if self.MODE == "DMG":
|
|
1180
|
-
self._set_fw_variable("ADDRESS", 0xA000 + address)
|
|
1181
|
-
self._set_fw_variable("DMG_ACCESS_MODE", 3) # MODE_RAM_READ
|
|
1182
|
-
self._set_fw_variable("DMG_READ_CS_PULSE", 1)
|
|
1183
|
-
if command is None: command = self.DEVICE_CMD["DMG_CART_READ"]
|
|
1184
|
-
elif self.MODE == "AGB":
|
|
1185
|
-
self._set_fw_variable("ADDRESS", address)
|
|
1186
|
-
if command is None: command = self.DEVICE_CMD["AGB_CART_READ_SRAM"]
|
|
1187
|
-
|
|
1188
|
-
for _ in range(0, num):
|
|
1189
|
-
self._write(command)
|
|
1190
|
-
temp = self._read(length)
|
|
1191
|
-
if isinstance(temp, int): temp = bytearray([temp])
|
|
1192
|
-
if temp is False or len(temp) != length: return bytearray()
|
|
1193
|
-
buffer += temp
|
|
1194
|
-
if not self.NO_PROG_UPDATE:
|
|
1195
|
-
self.SetProgress({"action":"READ", "bytes_added":len(temp)})
|
|
1196
|
-
|
|
1197
|
-
if self.MODE == "DMG":
|
|
1198
|
-
self._set_fw_variable("DMG_READ_CS_PULSE", 0)
|
|
1199
310
|
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
def ReadRAM_MBC7(self, address, length):
|
|
1203
|
-
max_length = 32
|
|
1204
|
-
num = math.ceil(length / max_length)
|
|
1205
|
-
dprint("Reading 0x{:X} bytes from cartridge EEPROM in {:d} iteration(s)".format(length, num))
|
|
1206
|
-
if length > max_length: length = max_length
|
|
1207
|
-
buffer = bytearray()
|
|
1208
|
-
self._set_fw_variable("TRANSFER_SIZE", length)
|
|
1209
|
-
self._set_fw_variable("ADDRESS", address)
|
|
1210
|
-
for _ in range(0, num):
|
|
1211
|
-
self._write(self.DEVICE_CMD["DMG_MBC7_READ_EEPROM"])
|
|
1212
|
-
temp = self._read(length)
|
|
1213
|
-
if isinstance(temp, int): temp = bytearray([temp])
|
|
1214
|
-
buffer += temp
|
|
1215
|
-
if not self.NO_PROG_UPDATE:
|
|
1216
|
-
self.SetProgress({"action":"READ", "bytes_added":len(temp)})
|
|
1217
|
-
|
|
1218
|
-
return buffer
|
|
1219
|
-
|
|
1220
|
-
def ReadRAM_TAMA5(self):
|
|
1221
|
-
dprint("Reading 0x20 bytes from cartridge RAM")
|
|
1222
|
-
buffer = bytearray()
|
|
1223
|
-
npu = self.NO_PROG_UPDATE
|
|
1224
|
-
self.NO_PROG_UPDATE = True
|
|
1225
|
-
self._set_fw_variable("DMG_READ_CS_PULSE", 1)
|
|
1226
|
-
self._set_fw_variable("DMG_WRITE_CS_PULSE", 1)
|
|
1227
|
-
|
|
1228
|
-
# Read save state
|
|
1229
|
-
for i in range(0, 0x20):
|
|
1230
|
-
self._cart_write(0xA001, 0x06, sram=True) # register select and address (high)
|
|
1231
|
-
self._cart_write(0xA000, i >> 4 | 0x01 << 1, sram=True) # bit 0 = higher ram address, rest = command
|
|
1232
|
-
self._cart_write(0xA001, 0x07, sram=True) # address (low)
|
|
1233
|
-
self._cart_write(0xA000, i & 0x0F, sram=True) # bits 0-3 = lower ram address
|
|
1234
|
-
self._cart_write(0xA001, 0x0D, sram=True) # data out (high)
|
|
1235
|
-
value1, value2 = None, None
|
|
1236
|
-
while value1 is None or value1 != value2:
|
|
1237
|
-
value2 = value1
|
|
1238
|
-
value1 = self._cart_read(0xA000)
|
|
1239
|
-
data_h = value1
|
|
1240
|
-
self._cart_write(0xA001, 0x0C, sram=True) # data out (low)
|
|
1241
|
-
|
|
1242
|
-
value1, value2 = None, None
|
|
1243
|
-
while value1 is None or value1 != value2:
|
|
1244
|
-
value2 = value1
|
|
1245
|
-
value1 = self._cart_read(0xA000)
|
|
1246
|
-
data_l = value1
|
|
1247
|
-
|
|
1248
|
-
data = ((data_h & 0xF) << 4) | (data_l & 0xF)
|
|
1249
|
-
buffer.append(data)
|
|
1250
|
-
self.SetProgress({"action":"UPDATE_POS", "abortable":False, "pos":i+1})
|
|
1251
|
-
|
|
1252
|
-
self._set_fw_variable("DMG_READ_CS_PULSE", 0)
|
|
1253
|
-
|
|
1254
|
-
self.NO_PROG_UPDATE = npu
|
|
1255
|
-
return buffer
|
|
1256
|
-
|
|
1257
|
-
def WriteRAM(self, address, buffer, command=None, max_length=256):
|
|
1258
|
-
length = len(buffer)
|
|
1259
|
-
num = math.ceil(length / max_length)
|
|
1260
|
-
dprint("Writing 0x{:X} bytes to cartridge RAM in {:d} iteration(s)".format(length, num))
|
|
1261
|
-
if length > max_length: length = max_length
|
|
1262
|
-
|
|
1263
|
-
self._set_fw_variable("TRANSFER_SIZE", length)
|
|
1264
|
-
if self.MODE == "DMG":
|
|
1265
|
-
self._set_fw_variable("ADDRESS", 0xA000 + address)
|
|
1266
|
-
self._set_fw_variable("DMG_ACCESS_MODE", 4) # MODE_RAM_WRITE
|
|
1267
|
-
self._set_fw_variable("DMG_WRITE_CS_PULSE", 1)
|
|
1268
|
-
if command is None: command = self.DEVICE_CMD["DMG_CART_WRITE_SRAM"]
|
|
1269
|
-
elif self.MODE == "AGB":
|
|
1270
|
-
self._set_fw_variable("ADDRESS", address)
|
|
1271
|
-
if command is None: command = self.DEVICE_CMD["AGB_CART_WRITE_SRAM"]
|
|
311
|
+
def BootloaderReset(self):
|
|
312
|
+
return False
|
|
1272
313
|
|
|
1273
|
-
|
|
1274
|
-
self._write(command)
|
|
1275
|
-
self._write(buffer[i*length:i*length+length])
|
|
1276
|
-
self._read(1)
|
|
1277
|
-
if self.INFO["action"] == self.ACTIONS["SAVE_WRITE"] and not self.NO_PROG_UPDATE:
|
|
1278
|
-
self.SetProgress({"action":"WRITE", "bytes_added":length})
|
|
1279
|
-
|
|
1280
|
-
if self.MODE == "DMG":
|
|
1281
|
-
self._set_fw_variable("ADDRESS", 0)
|
|
1282
|
-
self._set_fw_variable("DMG_WRITE_CS_PULSE", 0)
|
|
1283
|
-
|
|
314
|
+
def SupportsAudioAsWe(self):
|
|
1284
315
|
return True
|
|
1285
316
|
|
|
1286
|
-
def
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
skip_write = False
|
|
1294
|
-
for i in range(0, num):
|
|
1295
|
-
self._set_fw_variable("TRANSFER_SIZE", length)
|
|
1296
|
-
self._set_fw_variable("ADDRESS", address)
|
|
1297
|
-
dprint("Now in iteration {:d}".format(i))
|
|
1298
|
-
|
|
1299
|
-
if buffer[i*length:i*length+length] == bytearray([0xFF] * length):
|
|
1300
|
-
skip_write = True
|
|
1301
|
-
address += length
|
|
1302
|
-
continue
|
|
1303
|
-
|
|
1304
|
-
cmds = [
|
|
1305
|
-
[ 0x2000, 0x01 ],
|
|
1306
|
-
[ 0x3000, 0x02 ],
|
|
1307
|
-
[ 0x7555, 0xAA ],
|
|
1308
|
-
[ 0x4AAA, 0x55 ],
|
|
1309
|
-
[ 0x7555, 0xA0 ],
|
|
1310
|
-
]
|
|
1311
|
-
self._cart_write_flash(cmds)
|
|
1312
|
-
mapper.SelectBankFlash(mapper.GetROMBank())
|
|
1313
|
-
self._write(self.DEVICE_CMD["DMG_MBC6_MMSA_WRITE_FLASH"])
|
|
1314
|
-
self._write(buffer[i*length:i*length+length])
|
|
1315
|
-
ret = self._read(1)
|
|
1316
|
-
if ret not in (0x01, 0x03):
|
|
1317
|
-
dprint("Save write error (response = {:s}) in iteration {:d} while trying to write 0x{:X} bytes".format(str(ret), i, length))
|
|
1318
|
-
if "from_user" in self.CANCEL_ARGS and self.CANCEL_ARGS["from_user"]: return False
|
|
1319
|
-
self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"Save write error (response = {:s}) in iteration {:d} while trying to write 0x{:X} bytes".format(str(ret), i, length)})
|
|
1320
|
-
self.CANCEL = True
|
|
1321
|
-
self.ERROR = True
|
|
1322
|
-
return False
|
|
1323
|
-
self._cart_write(address + length - 1, 0x00)
|
|
1324
|
-
while True:
|
|
1325
|
-
sr = self._cart_read(address + length - 1)
|
|
1326
|
-
if sr == 0x80: break
|
|
1327
|
-
time.sleep(0.001)
|
|
1328
|
-
|
|
1329
|
-
address += length
|
|
1330
|
-
if self.INFO["action"] == self.ACTIONS["SAVE_WRITE"] and not self.NO_PROG_UPDATE:
|
|
1331
|
-
self.SetProgress({"action":"WRITE", "bytes_added":length})
|
|
1332
|
-
self._cart_write(address - 1, 0xF0)
|
|
1333
|
-
self.SKIPPING = skip_write
|
|
1334
|
-
|
|
1335
|
-
def WriteEEPROM_MBC7(self, address, buffer):
|
|
1336
|
-
length = len(buffer)
|
|
1337
|
-
max_length = 32
|
|
1338
|
-
num = math.ceil(length / max_length)
|
|
1339
|
-
if length > max_length: length = max_length
|
|
1340
|
-
dprint("Write 0x{:X} bytes to cartridge EEPROM in {:d} iteration(s)".format(length, num))
|
|
1341
|
-
self._set_fw_variable("TRANSFER_SIZE", length)
|
|
1342
|
-
self._set_fw_variable("ADDRESS", address)
|
|
1343
|
-
for i in range(0, num):
|
|
1344
|
-
self._write(self.DEVICE_CMD["DMG_MBC7_WRITE_EEPROM"])
|
|
1345
|
-
self._write(buffer[i*length:i*length+length])
|
|
1346
|
-
response = self._read(1)
|
|
1347
|
-
dprint("Response:", response)
|
|
1348
|
-
if self.INFO["action"] == self.ACTIONS["SAVE_WRITE"] and not self.NO_PROG_UPDATE:
|
|
1349
|
-
self.SetProgress({"action":"WRITE", "bytes_added":length})
|
|
1350
|
-
|
|
1351
|
-
def WriteRAM_TAMA5(self, buffer):
|
|
1352
|
-
npu = self.NO_PROG_UPDATE
|
|
1353
|
-
self.NO_PROG_UPDATE = True
|
|
1354
|
-
|
|
1355
|
-
for i in range(0, 0x20):
|
|
1356
|
-
self._cart_write(0xA001, 0x05, sram=True) # data in (high)
|
|
1357
|
-
self._cart_write(0xA000, buffer[i] >> 4, sram=True)
|
|
1358
|
-
self._cart_write(0xA001, 0x04, sram=True) # data in (low)
|
|
1359
|
-
self._cart_write(0xA000, buffer[i] & 0xF, sram=True)
|
|
1360
|
-
self._cart_write(0xA001, 0x06, sram=True) # register select and address (high)
|
|
1361
|
-
self._cart_write(0xA000, i >> 4 | 0x00 << 1, sram=True) # bit 0 = higher ram address, rest = command
|
|
1362
|
-
self._cart_write(0xA001, 0x07, sram=True) # address (low)
|
|
1363
|
-
self._cart_write(0xA000, i & 0x0F, sram=True) # bits 0-3 = lower ram address
|
|
1364
|
-
value1, value2 = None, None
|
|
1365
|
-
while value1 is None or value1 != value2:
|
|
1366
|
-
value2 = value1
|
|
1367
|
-
value1 = self._cart_read(0xA000)
|
|
1368
|
-
self.SetProgress({"action":"UPDATE_POS", "abortable":False, "pos":i+1})
|
|
1369
|
-
|
|
1370
|
-
self.NO_PROG_UPDATE = npu
|
|
1371
|
-
|
|
1372
|
-
def WriteROM(self, address, buffer, flash_buffer_size=False, skip_init=False, rumble_stop=False, max_length=MAX_BUFFER_WRITE):
|
|
1373
|
-
length = len(buffer)
|
|
1374
|
-
num = math.ceil(length / max_length)
|
|
1375
|
-
dprint("Writing 0x{:X} bytes to Flash ROM in {:d} iteration(s)".format(length, num))
|
|
1376
|
-
if length == 0:
|
|
1377
|
-
dprint("Length is zero?")
|
|
1378
|
-
return False
|
|
1379
|
-
elif length > max_length:
|
|
1380
|
-
length = max_length
|
|
1381
|
-
|
|
1382
|
-
skip_write = False
|
|
1383
|
-
ret = 0
|
|
1384
|
-
num_of_chunks = math.ceil(flash_buffer_size / length)
|
|
1385
|
-
pos = 0
|
|
1386
|
-
|
|
1387
|
-
if not skip_init:
|
|
1388
|
-
self._set_fw_variable("TRANSFER_SIZE", length)
|
|
1389
|
-
if flash_buffer_size is not False:
|
|
1390
|
-
self._set_fw_variable("BUFFER_SIZE", flash_buffer_size)
|
|
1391
|
-
|
|
1392
|
-
for i in range(0, num):
|
|
1393
|
-
data = bytearray(buffer[i*length:i*length+length])
|
|
1394
|
-
if (num_of_chunks == 1 or flash_buffer_size == 0) and (data == bytearray([0xFF] * len(data))):
|
|
1395
|
-
skip_init = False
|
|
1396
|
-
skip_write = True
|
|
1397
|
-
else:
|
|
1398
|
-
if skip_write:
|
|
1399
|
-
skip_write = False
|
|
1400
|
-
|
|
1401
|
-
if not skip_write:
|
|
1402
|
-
if not skip_init:
|
|
1403
|
-
if self.MODE == "DMG":
|
|
1404
|
-
self._set_fw_variable("ADDRESS", address)
|
|
1405
|
-
elif self.MODE == "AGB":
|
|
1406
|
-
self._set_fw_variable("ADDRESS", address >> 1)
|
|
1407
|
-
skip_init = True
|
|
1408
|
-
|
|
1409
|
-
if ret != 0x03: self._write(self.DEVICE_CMD["FLASH_PROGRAM"])
|
|
1410
|
-
ret = self._write(data, wait=True)
|
|
1411
|
-
|
|
1412
|
-
if ret not in (0x01, 0x03):
|
|
1413
|
-
dprint("Flash error at 0x{:X} in iteration {:d} of {:d} while trying to write a total of 0x{:X} bytes (response = {:s})".format(address, i, num, len(buffer), str(ret)))
|
|
1414
|
-
self.ERROR_ARGS = { "iteration":i }
|
|
1415
|
-
self.SKIPPING = False
|
|
1416
|
-
return False
|
|
1417
|
-
pos += len(data)
|
|
1418
|
-
|
|
1419
|
-
if rumble_stop and flash_buffer_size > 0 and pos % flash_buffer_size == 0:
|
|
1420
|
-
dprint("Sending rumble stop command")
|
|
1421
|
-
self._cart_write(address=0xC6, value=0x00, flashcart=True)
|
|
1422
|
-
rumble_stop = False
|
|
1423
|
-
|
|
1424
|
-
address += length
|
|
1425
|
-
if ((pos % length) * 10 == 0) and (self.INFO["action"] in (self.ACTIONS["ROM_WRITE"], self.ACTIONS["SAVE_WRITE"]) and not self.NO_PROG_UPDATE):
|
|
1426
|
-
self.SetProgress({"action":"WRITE", "bytes_added":length, "skipping":skip_write})
|
|
1427
|
-
|
|
1428
|
-
self.SKIPPING = skip_write
|
|
1429
|
-
|
|
1430
|
-
def WriteROM_GBMEMORY(self, address, buffer, bank):
|
|
1431
|
-
length = len(buffer)
|
|
1432
|
-
max_length = 128
|
|
1433
|
-
num = math.ceil(length / max_length)
|
|
1434
|
-
if length > max_length: length = max_length
|
|
1435
|
-
dprint("Writing 0x{:X} bytes to Flash ROM in {:d} iteration(s)".format(length, num))
|
|
1436
|
-
|
|
1437
|
-
skip_write = False
|
|
1438
|
-
for i in range(0, num):
|
|
1439
|
-
self._set_fw_variable("TRANSFER_SIZE", length)
|
|
1440
|
-
self._set_fw_variable("ADDRESS", address)
|
|
1441
|
-
dprint("Now in iteration {:d}".format(i))
|
|
1442
|
-
|
|
1443
|
-
if buffer[i*length:i*length+length] == bytearray([0xFF] * length):
|
|
1444
|
-
skip_write = True
|
|
1445
|
-
address += length
|
|
1446
|
-
continue
|
|
1447
|
-
|
|
1448
|
-
# Enable flash chip access
|
|
1449
|
-
self._cart_write_flash([
|
|
1450
|
-
[ 0x120, 0x09 ],
|
|
1451
|
-
[ 0x121, 0xAA ],
|
|
1452
|
-
[ 0x122, 0x55 ],
|
|
1453
|
-
[ 0x13F, 0xA5 ],
|
|
1454
|
-
])
|
|
1455
|
-
# Re-Enable writes to MBC registers
|
|
1456
|
-
self._cart_write_flash([
|
|
1457
|
-
[ 0x120, 0x11 ],
|
|
1458
|
-
[ 0x13F, 0xA5 ],
|
|
1459
|
-
])
|
|
1460
|
-
# Bank 1 for commands
|
|
1461
|
-
self._cart_write_flash([
|
|
1462
|
-
[ 0x2100, 0x01 ],
|
|
1463
|
-
])
|
|
1464
|
-
|
|
1465
|
-
# Write setup
|
|
1466
|
-
self._cart_write_flash([
|
|
1467
|
-
[ 0x120, 0x0F ],
|
|
1468
|
-
[ 0x125, 0x55 ],
|
|
1469
|
-
[ 0x126, 0x55 ],
|
|
1470
|
-
[ 0x127, 0xAA ],
|
|
1471
|
-
[ 0x13F, 0xA5 ],
|
|
1472
|
-
])
|
|
1473
|
-
self._cart_write_flash([
|
|
1474
|
-
[ 0x120, 0x0F ],
|
|
1475
|
-
[ 0x125, 0x2A ],
|
|
1476
|
-
[ 0x126, 0xAA ],
|
|
1477
|
-
[ 0x127, 0x55 ],
|
|
1478
|
-
[ 0x13F, 0xA5 ],
|
|
1479
|
-
])
|
|
1480
|
-
self._cart_write_flash([
|
|
1481
|
-
[ 0x120, 0x0F ],
|
|
1482
|
-
[ 0x125, 0x55 ],
|
|
1483
|
-
[ 0x126, 0x55 ],
|
|
1484
|
-
[ 0x127, 0xA0 ],
|
|
1485
|
-
[ 0x13F, 0xA5 ],
|
|
1486
|
-
])
|
|
1487
|
-
|
|
1488
|
-
# Set bank back
|
|
1489
|
-
self._cart_write_flash([
|
|
1490
|
-
[ 0x2100, bank ],
|
|
1491
|
-
])
|
|
1492
|
-
|
|
1493
|
-
# Disable writes to MBC registers
|
|
1494
|
-
self._cart_write_flash([
|
|
1495
|
-
[ 0x120, 0x10 ],
|
|
1496
|
-
[ 0x13F, 0xA5 ],
|
|
1497
|
-
])
|
|
1498
|
-
|
|
1499
|
-
# Undo Wakeup
|
|
1500
|
-
self._cart_write_flash([
|
|
1501
|
-
[ 0x120, 0x08 ],
|
|
1502
|
-
[ 0x13F, 0xA5 ],
|
|
1503
|
-
])
|
|
1504
|
-
|
|
1505
|
-
self._write(self.DEVICE_CMD["DMG_MBC6_MMSA_WRITE_FLASH"])
|
|
1506
|
-
self._write(buffer[i*length:i*length+length])
|
|
1507
|
-
ret = self._read(1)
|
|
1508
|
-
if ret not in (0x01, 0x03):
|
|
1509
|
-
self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"Flash write error (response = {:s}) in iteration {:d} while trying to write 0x{:X} bytes".format(str(ret), i, length)})
|
|
1510
|
-
self.CANCEL = True
|
|
1511
|
-
self.ERROR = True
|
|
1512
|
-
return False
|
|
1513
|
-
|
|
1514
|
-
self._cart_write(address + length - 1, 0xFF)
|
|
1515
|
-
while True:
|
|
1516
|
-
sr = self._cart_read(address + length - 1)
|
|
1517
|
-
if sr == 0x80: break
|
|
1518
|
-
time.sleep(0.001)
|
|
1519
|
-
|
|
1520
|
-
address += length
|
|
1521
|
-
if self.INFO["action"] == self.ACTIONS["ROM_WRITE"] and not self.NO_PROG_UPDATE:
|
|
1522
|
-
self.SetProgress({"action":"WRITE", "bytes_added":length})
|
|
1523
|
-
|
|
1524
|
-
self._cart_write(address - 1, 0xF0)
|
|
1525
|
-
self.SKIPPING = skip_write
|
|
1526
|
-
|
|
1527
|
-
def WriteROM_DMG_MBC5_32M_FLASH(self, address, buffer, bank):
|
|
1528
|
-
length = len(buffer)
|
|
1529
|
-
max_length = 128
|
|
1530
|
-
num = math.ceil(length / max_length)
|
|
1531
|
-
if length > max_length: length = max_length
|
|
1532
|
-
dprint("Writing 0x{:X} bytes to Flash ROM in {:d} iteration(s)".format(length, num))
|
|
1533
|
-
|
|
1534
|
-
skip_write = False
|
|
1535
|
-
for i in range(0, num):
|
|
1536
|
-
self._set_fw_variable("TRANSFER_SIZE", length)
|
|
1537
|
-
self._set_fw_variable("ADDRESS", address)
|
|
1538
|
-
dprint("Now in iteration {:d}".format(i))
|
|
1539
|
-
|
|
1540
|
-
if buffer[i*length:i*length+length] == bytearray([0xFF] * length):
|
|
1541
|
-
skip_write = True
|
|
1542
|
-
address += length
|
|
1543
|
-
continue
|
|
1544
|
-
|
|
1545
|
-
self._cart_write(0x4000, 0xFF)
|
|
1546
|
-
self._cart_write_flash([
|
|
1547
|
-
[ address, 0xE0 ],
|
|
1548
|
-
[ address, length - 1 ],
|
|
1549
|
-
[ address, 0x00 ],
|
|
1550
|
-
])
|
|
1551
|
-
self._write(self.DEVICE_CMD["DMG_MBC6_MMSA_WRITE_FLASH"])
|
|
1552
|
-
self._write(buffer[i*length:i*length+length])
|
|
1553
|
-
ret = self._read(1)
|
|
1554
|
-
if ret not in (0x01, 0x03):
|
|
1555
|
-
self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"Flash write error (response = {:s}) in iteration {:d} while trying to write 0x{:X} bytes".format(str(ret), i, length)})
|
|
1556
|
-
self.CANCEL = True
|
|
1557
|
-
self.ERROR = True
|
|
1558
|
-
return False
|
|
1559
|
-
|
|
1560
|
-
self._cart_write_flash([
|
|
1561
|
-
[ address, 0x0C ],
|
|
1562
|
-
[ address, length - 1 ],
|
|
1563
|
-
[ address, 0x00 ],
|
|
1564
|
-
])
|
|
1565
|
-
lives = 100
|
|
1566
|
-
while lives > 0:
|
|
1567
|
-
self._cart_write(0x4000, 0x70)
|
|
1568
|
-
sr = self._cart_read(address + length - 1)
|
|
1569
|
-
dprint("sr=0x{:X}".format(sr))
|
|
1570
|
-
if sr & 0x80 == 0x80: break
|
|
1571
|
-
time.sleep(0.001)
|
|
1572
|
-
lives -= 1
|
|
1573
|
-
self._cart_write(0x4000, 0xFF)
|
|
1574
|
-
|
|
1575
|
-
if lives == 0:
|
|
1576
|
-
self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"Flash write error (response = {:s}) in iteration {:d} while trying to write 0x{:X} bytes".format(str(sr), i, length)})
|
|
1577
|
-
self.CANCEL = True
|
|
1578
|
-
self.ERROR = True
|
|
1579
|
-
return False
|
|
1580
|
-
|
|
1581
|
-
address += length
|
|
1582
|
-
if self.INFO["action"] == self.ACTIONS["ROM_WRITE"] and not self.NO_PROG_UPDATE:
|
|
1583
|
-
self.SetProgress({"action":"WRITE", "bytes_added":length})
|
|
1584
|
-
|
|
1585
|
-
self._cart_write(address - 1, 0xFF)
|
|
1586
|
-
self.SKIPPING = skip_write
|
|
1587
|
-
|
|
1588
|
-
def WriteROM_DMG_DatelOrbitV2(self, address, buffer, bank):
|
|
1589
|
-
length = len(buffer)
|
|
1590
|
-
dprint("Writing 0x{:X} bytes to Datel Orbit V2 cartridge".format(length))
|
|
1591
|
-
for i in range(0, length):
|
|
1592
|
-
self._cart_write(0x7FE1, 2)
|
|
1593
|
-
self._cart_write(0x5555, 0xAA)
|
|
1594
|
-
self._cart_write(0x2AAA, 0x55)
|
|
1595
|
-
self._cart_write(0x5555, 0xA0)
|
|
1596
|
-
self._cart_write(0x7FE1, bank)
|
|
1597
|
-
self._cart_write(address + i, buffer[i])
|
|
1598
|
-
if self.INFO["action"] == self.ACTIONS["ROM_WRITE"] and not self.NO_PROG_UPDATE:
|
|
1599
|
-
self.SetProgress({"action":"WRITE", "bytes_added":1})
|
|
1600
|
-
return True
|
|
1601
|
-
|
|
1602
|
-
def WriteROM_DMG_EEPROM(self, address, buffer, bank, eeprom_buffer_size=0x80):
|
|
1603
|
-
length = len(buffer)
|
|
1604
|
-
if self.FW["pcb_ver"] not in (5, 6, 101) or self.BAUDRATE == 1000000:
|
|
1605
|
-
max_length = 256
|
|
1606
|
-
else:
|
|
1607
|
-
max_length = 1024
|
|
1608
|
-
num = math.ceil(length / max_length)
|
|
1609
|
-
if length > max_length: length = max_length
|
|
1610
|
-
dprint("Writing 0x{:X} bytes to EEPROM in {:d} iteration(s)".format(length, num))
|
|
1611
|
-
|
|
1612
|
-
for i in range(0, num):
|
|
1613
|
-
self._set_fw_variable("BUFFER_SIZE", eeprom_buffer_size)
|
|
1614
|
-
self._set_fw_variable("TRANSFER_SIZE", length)
|
|
1615
|
-
self._set_fw_variable("ADDRESS", address)
|
|
1616
|
-
dprint("Now in iteration {:d}".format(i))
|
|
1617
|
-
|
|
1618
|
-
self._write(self.DEVICE_CMD["DMG_EEPROM_WRITE"])
|
|
1619
|
-
self._write(buffer[i*length:i*length+length])
|
|
1620
|
-
ret = self._read(1)
|
|
1621
|
-
if ret not in (0x01, 0x03):
|
|
1622
|
-
self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"EEPROM write error (response = {:s}) in iteration {:d} while trying to write 0x{:X} bytes".format(str(ret), i, length)})
|
|
1623
|
-
self.CANCEL = True
|
|
1624
|
-
self.ERROR = True
|
|
1625
|
-
return False
|
|
1626
|
-
|
|
1627
|
-
address += length
|
|
1628
|
-
if self.INFO["action"] == self.ACTIONS["ROM_WRITE"] and not self.NO_PROG_UPDATE:
|
|
1629
|
-
self.SetProgress({"action":"WRITE", "bytes_added":length})
|
|
1630
|
-
|
|
1631
|
-
self._cart_write_flash([
|
|
1632
|
-
[0x0006, 0x01],
|
|
1633
|
-
[0x5555, 0xAA],
|
|
1634
|
-
[0x2AAA, 0x55],
|
|
1635
|
-
[0x5555, 0xF0],
|
|
1636
|
-
[0x0006, bank]
|
|
1637
|
-
])
|
|
1638
|
-
|
|
1639
|
-
def CheckROMStable(self):
|
|
1640
|
-
if not self.IsConnected(): raise ConnectionError("Couldn’t access the the device.")
|
|
1641
|
-
buffer1 = self.ReadROM(0x80, 0x40)
|
|
1642
|
-
time.sleep(0.05)
|
|
1643
|
-
buffer2 = self.ReadROM(0x80, 0x40)
|
|
1644
|
-
return buffer1 == buffer2
|
|
1645
|
-
|
|
1646
|
-
def AutoDetectFlash(self, limitVoltage=False):
|
|
1647
|
-
flash_types = []
|
|
1648
|
-
flash_type = 0
|
|
1649
|
-
flash_id = None
|
|
1650
|
-
flash_id_found = False
|
|
1651
|
-
|
|
1652
|
-
supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
|
|
1653
|
-
|
|
1654
|
-
if self.MODE == "DMG":
|
|
1655
|
-
if limitVoltage:
|
|
1656
|
-
self._write(self.DEVICE_CMD["SET_VOLTAGE_3_3V"])
|
|
1657
|
-
else:
|
|
1658
|
-
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
|
1659
|
-
time.sleep(0.1)
|
|
1660
|
-
self._write(self.DEVICE_CMD["SET_MODE_DMG"])
|
|
1661
|
-
elif self.MODE == "AGB":
|
|
1662
|
-
self._write(self.DEVICE_CMD["SET_MODE_AGB"])
|
|
1663
|
-
|
|
1664
|
-
for f in range(1, len(supported_carts)):
|
|
1665
|
-
if self.CANCEL or self.ERROR:
|
|
1666
|
-
cancel_args = {"action":"ABORT", "abortable":False}
|
|
1667
|
-
cancel_args.update(self.CANCEL_ARGS)
|
|
1668
|
-
self.CANCEL_ARGS = {}
|
|
1669
|
-
self.ERROR_ARGS = {}
|
|
1670
|
-
self.SetProgress(cancel_args)
|
|
1671
|
-
return False
|
|
1672
|
-
|
|
1673
|
-
flashcart_meta = supported_carts[f]
|
|
1674
|
-
if flash_id is not None:
|
|
1675
|
-
if ("flash_ids" not in flashcart_meta) or (flash_id not in flashcart_meta["flash_ids"]):
|
|
1676
|
-
continue
|
|
1677
|
-
dprint("*** Now checking: {:s}\n".format(flashcart_meta["names"][0]))
|
|
1678
|
-
|
|
1679
|
-
if self.MODE == "DMG":
|
|
1680
|
-
we = flashcart_meta["write_pin"]
|
|
1681
|
-
if we == "WR":
|
|
1682
|
-
self._set_fw_variable("FLASH_WE_PIN", 0x01) # FLASH_WE_PIN_WR
|
|
1683
|
-
elif we in ("AUDIO", "VIN"):
|
|
1684
|
-
self._set_fw_variable("FLASH_WE_PIN", 0x02) # FLASH_WE_PIN_AUDIO
|
|
1685
|
-
elif we == "WR+RESET":
|
|
1686
|
-
self._set_fw_variable("FLASH_WE_PIN", 0x03) # FLASH_WE_PIN_WR_RESET
|
|
1687
|
-
|
|
1688
|
-
fc_fncptr = {
|
|
1689
|
-
"cart_write_fncptr":self._cart_write,
|
|
1690
|
-
"cart_write_fast_fncptr":self._cart_write_flash,
|
|
1691
|
-
"cart_read_fncptr":self.ReadROM,
|
|
1692
|
-
"cart_powercycle_fncptr":self.CartPowerCycle,
|
|
1693
|
-
"progress_fncptr":self.SetProgress,
|
|
1694
|
-
"set_we_pin_wr":self._set_we_pin_wr,
|
|
1695
|
-
"set_we_pin_audio":self._set_we_pin_audio,
|
|
1696
|
-
}
|
|
1697
|
-
flashcart = Flashcart(config=flashcart_meta, fncptr=fc_fncptr)
|
|
1698
|
-
flashcart.Reset(full_reset=False)
|
|
1699
|
-
if flashcart.Unlock() is False: return False
|
|
1700
|
-
if "flash_ids" in flashcart_meta and len(flashcart_meta["flash_ids"]) > 0:
|
|
1701
|
-
vfid = flashcart.VerifyFlashID()
|
|
1702
|
-
if vfid is not False:
|
|
1703
|
-
(verified, cart_flash_id) = vfid
|
|
1704
|
-
if verified and cart_flash_id in flashcart_meta["flash_ids"]:
|
|
1705
|
-
flash_id = cart_flash_id
|
|
1706
|
-
flash_id_found = True
|
|
1707
|
-
flash_type = f
|
|
1708
|
-
flash_types.append(flash_type)
|
|
1709
|
-
flashcart.Reset(full_reset=False)
|
|
1710
|
-
dprint("Found the correct cartridge type!")
|
|
1711
|
-
|
|
1712
|
-
#if self.CanPowerCycleCart(): self.CartPowerCycle()
|
|
1713
|
-
|
|
1714
|
-
# Check flash size
|
|
1715
|
-
flash_type_id = 0
|
|
1716
|
-
cfi_s = ""
|
|
1717
|
-
cfi = None
|
|
1718
|
-
if len(flash_types) > 0:
|
|
1719
|
-
flash_type_id = flash_types[0]
|
|
1720
|
-
if self.MODE == "DMG":
|
|
1721
|
-
supp_flash_types = self.GetSupportedCartridgesDMG()
|
|
1722
|
-
elif self.MODE == "AGB":
|
|
1723
|
-
supp_flash_types = self.GetSupportedCartridgesAGB()
|
|
1724
|
-
|
|
1725
|
-
(flash_id, cfi_s, cfi) = self.CheckFlashChip(limitVoltage=limitVoltage, cart_type=supp_flash_types[1][flash_type_id])
|
|
1726
|
-
if "flash_size" in supp_flash_types[1][flash_types[0]]:
|
|
1727
|
-
size = supp_flash_types[1][flash_types[0]]["flash_size"]
|
|
1728
|
-
size_undetected = False
|
|
1729
|
-
for i in range(0, len(flash_types)):
|
|
1730
|
-
if "flash_size" in supp_flash_types[1][flash_types[i]]:
|
|
1731
|
-
if size != supp_flash_types[1][flash_types[i]]["flash_size"]:
|
|
1732
|
-
size_undetected = True
|
|
1733
|
-
|
|
1734
|
-
if size_undetected:
|
|
1735
|
-
if isinstance(cfi, dict) and "device_size" in cfi:
|
|
1736
|
-
for i in range(0, len(flash_types)):
|
|
1737
|
-
if "flash_size" in supp_flash_types[1][flash_types[i]] and cfi['device_size'] == supp_flash_types[1][flash_types[i]]["flash_size"]:
|
|
1738
|
-
flash_type_id = flash_types[i]
|
|
1739
|
-
size_undetected = False
|
|
1740
|
-
break
|
|
1741
|
-
else:
|
|
1742
|
-
if self.MODE == "AGB":
|
|
1743
|
-
# Check where the ROM data repeats (for unlicensed carts)
|
|
1744
|
-
header = self.ReadROM(0, 0x180)
|
|
1745
|
-
size_check = header[0xA0:0xA0+16]
|
|
1746
|
-
currAddr = 0x10000
|
|
1747
|
-
while currAddr < 0x2000000:
|
|
1748
|
-
buffer = self.ReadROM(currAddr + 0xA0, 64)[:16]
|
|
1749
|
-
if buffer == size_check: break
|
|
1750
|
-
currAddr *= 2
|
|
1751
|
-
rom_size = currAddr
|
|
1752
|
-
|
|
1753
|
-
for i in range(0, len(flash_types)):
|
|
1754
|
-
if "flash_size" in supp_flash_types[1][flash_types[i]] and rom_size == supp_flash_types[1][flash_types[i]]["flash_size"]:
|
|
1755
|
-
flash_type_id = flash_types[i]
|
|
1756
|
-
size_undetected = False
|
|
1757
|
-
break
|
|
1758
|
-
|
|
1759
|
-
else:
|
|
1760
|
-
(flash_id, cfi_s, cfi) = self.CheckFlashChip(limitVoltage=limitVoltage)
|
|
1761
|
-
|
|
1762
|
-
if self.MODE == "DMG" and not flash_id_found:
|
|
1763
|
-
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
|
1764
|
-
time.sleep(0.1)
|
|
1765
|
-
|
|
1766
|
-
return (flash_types, flash_type_id, flash_id, cfi_s, cfi)
|
|
1767
|
-
|
|
1768
|
-
def CheckFlashChip(self, limitVoltage=False, cart_type=None): # aka. the most horribly written function
|
|
1769
|
-
if cart_type is not None:
|
|
1770
|
-
if self.MODE == "DMG":
|
|
1771
|
-
if cart_type["write_pin"] == "WR":
|
|
1772
|
-
we = 0x01 # FLASH_WE_PIN_WR
|
|
1773
|
-
elif cart_type["write_pin"] in ("AUDIO", "VIN"):
|
|
1774
|
-
we = 0x02 # FLASH_WE_PIN_AUDIO
|
|
1775
|
-
elif cart_type["write_pin"] == "WR+RESET":
|
|
1776
|
-
we = 0x03 # FLASH_WE_PIN_WR_RESET
|
|
1777
|
-
self._set_fw_variable("FLASH_WE_PIN", we)
|
|
1778
|
-
|
|
1779
|
-
if self.FW["pcb_ver"] in (5, 6, 101):
|
|
1780
|
-
self._write(self.DEVICE_CMD["OFW_CART_MODE"])
|
|
1781
|
-
self._read(1)
|
|
1782
|
-
self.CartPowerOn()
|
|
1783
|
-
|
|
1784
|
-
flashcart = None
|
|
1785
|
-
flash_id_lines = []
|
|
1786
|
-
flash_commands = [
|
|
1787
|
-
{ 'read_cfi':[[0x555, 0x98]], 'read_identifier':[[ 0x555, 0xAA ], [ 0x2AA, 0x55 ], [ 0x555, 0x90 ]], 'reset':[[ 0x0, 0xF0 ]] },
|
|
1788
|
-
{ 'read_cfi':[[0x5555, 0x98]], 'read_identifier':[[ 0x5555, 0xAA ], [ 0x2AAA, 0x55 ], [ 0x5555, 0x90 ]], 'reset':[[ 0x0, 0xF0 ]] },
|
|
1789
|
-
{ 'read_cfi':[[0xAA, 0x98]], 'read_identifier':[[ 0xAAA, 0xAA ], [ 0x555, 0x55 ], [ 0xAAA, 0x90 ]], 'reset':[[ 0x0, 0xF0 ]] },
|
|
1790
|
-
{ 'read_cfi':[[0xAAA, 0x98]], 'read_identifier':[[ 0xAAA, 0xAA ], [ 0x555, 0x55 ], [ 0xAAA, 0x90 ]], 'reset':[[ 0x0, 0xF0 ]] },
|
|
1791
|
-
{ 'read_cfi':[[0xAAAA, 0x98]], 'read_identifier':[[ 0xAAAA, 0xAA ], [ 0x5555, 0x55 ], [ 0xAAAA, 0x90 ]], 'reset':[[ 0x0, 0xF0 ]] },
|
|
1792
|
-
{ 'read_cfi':[[0x4555, 0x98]], 'read_identifier':[[ 0x4555, 0xAA ], [ 0x4AAA, 0x55 ], [ 0x4555, 0x90 ]], 'reset':[[ 0x4000, 0xF0 ]] },
|
|
1793
|
-
{ 'read_cfi':[[0x7555, 0x98]], 'read_identifier':[[ 0x7555, 0xAA ], [ 0x7AAA, 0x55 ], [ 0x7555, 0x90 ]], 'reset':[[ 0x7000, 0xF0 ]] },
|
|
1794
|
-
{ 'read_cfi':[[0x4AAA, 0x98]], 'read_identifier':[[ 0x4AAA, 0xAA ], [ 0x4555, 0x55 ], [ 0x4AAA, 0x90 ]], 'reset':[[ 0x4000, 0xF0 ]] },
|
|
1795
|
-
{ 'read_cfi':[[0x7AAA, 0x98]], 'read_identifier':[[ 0x7AAA, 0xAA ], [ 0x7555, 0x55 ], [ 0x7AAA, 0x90 ]], 'reset':[[ 0x7000, 0xF0 ]] },
|
|
1796
|
-
{ 'read_cfi':[[0, 0x98]], 'read_identifier':[[ 0, 0x90 ]], 'reset':[[ 0, 0xFF ]] },
|
|
1797
|
-
]
|
|
1798
|
-
|
|
1799
|
-
if self.MODE == "DMG":
|
|
1800
|
-
del(flash_commands[4]) # 0xAAAA is in SRAM space on DMG
|
|
1801
|
-
if limitVoltage:
|
|
1802
|
-
self._write(self.DEVICE_CMD["SET_VOLTAGE_3_3V"])
|
|
1803
|
-
else:
|
|
1804
|
-
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
|
1805
|
-
time.sleep(0.1)
|
|
1806
|
-
|
|
1807
|
-
check_buffer = self.ReadROM(0, 0x400)
|
|
1808
|
-
d_swap = None
|
|
1809
|
-
cfi_info = ""
|
|
1810
|
-
rom_string = ""
|
|
1811
|
-
for j in range(0, 8):
|
|
1812
|
-
rom_string += "{:02X} ".format(check_buffer[j])
|
|
1813
|
-
rom_string += "\n"
|
|
1814
|
-
cfi = {'raw':b''}
|
|
1815
|
-
|
|
1816
|
-
if self.MODE == "DMG":
|
|
1817
|
-
rom_string = "[ ROM ] " + rom_string
|
|
1818
|
-
we_pins = [ "WR", "AUDIO" ]
|
|
1819
|
-
else:
|
|
1820
|
-
rom_string = "[ ROM ] " + rom_string
|
|
1821
|
-
we_pins = [ None ]
|
|
1822
|
-
|
|
1823
|
-
for we in we_pins:
|
|
1824
|
-
if "method" in cfi: break
|
|
1825
|
-
for method in flash_commands:
|
|
1826
|
-
if self.MODE == "DMG":
|
|
1827
|
-
if we == "WR":
|
|
1828
|
-
self._set_fw_variable("FLASH_WE_PIN", 0x01) # FLASH_WE_PIN_WR
|
|
1829
|
-
elif we in ("AUDIO", "VIN"):
|
|
1830
|
-
self._set_fw_variable("FLASH_WE_PIN", 0x02) # FLASH_WE_PIN_AUDIO
|
|
1831
|
-
elif we == "WR+RESET":
|
|
1832
|
-
self._set_fw_variable("FLASH_WE_PIN", 0x03) # FLASH_WE_PIN_WR_RESET
|
|
1833
|
-
|
|
1834
|
-
for i in range(0, len(method['reset'])):
|
|
1835
|
-
self._cart_write(method['reset'][i][0], method['reset'][i][1], flashcart=True)
|
|
1836
|
-
for i in range(0, len(method['read_cfi'])):
|
|
1837
|
-
self._cart_write(method['read_cfi'][i][0], method["read_cfi"][i][1], flashcart=True)
|
|
1838
|
-
buffer = self.ReadROM(0, 0x400)
|
|
1839
|
-
for i in range(0, len(method['reset'])):
|
|
1840
|
-
self._cart_write(method['reset'][i][0], method['reset'][i][1], flashcart=True)
|
|
1841
|
-
#if buffer == check_buffer: continue
|
|
1842
|
-
if buffer == bytearray([0x00] * len(buffer)): continue
|
|
1843
|
-
|
|
1844
|
-
magic = "{:s}{:s}{:s}".format(chr(buffer[0x20]), chr(buffer[0x22]), chr(buffer[0x24]))
|
|
1845
|
-
if magic == "QRY": # nothing swapped
|
|
1846
|
-
d_swap = ( 0, 0 )
|
|
1847
|
-
elif magic == "RQZ": # D0D1 swapped
|
|
1848
|
-
d_swap = ( 0, 1 )
|
|
1849
|
-
if d_swap is not None and d_swap != ( 0, 0 ):
|
|
1850
|
-
for i in range(0, len(buffer)):
|
|
1851
|
-
buffer[i] = bitswap(buffer[i], d_swap)
|
|
1852
|
-
|
|
1853
|
-
cfi_parsed = ParseCFI(buffer)
|
|
1854
|
-
try:
|
|
1855
|
-
if d_swap is not None:
|
|
1856
|
-
dprint("CFI @ {:s}/{:X}/{:X}/{:s}".format(str(we), method['read_identifier'][0][0], bitswap(method['read_identifier'][0][1], d_swap), str(d_swap)))
|
|
1857
|
-
else:
|
|
1858
|
-
dprint("CFI @ {:s}/{:X}/{:X}/{:s}".format(str(we), method['read_identifier'][0][0], method['read_identifier'][0][1], str(d_swap)))
|
|
1859
|
-
dprint("└", cfi_parsed)
|
|
1860
|
-
except:
|
|
1861
|
-
pass
|
|
1862
|
-
|
|
1863
|
-
if cfi_parsed != False:
|
|
1864
|
-
cfi = cfi_parsed
|
|
1865
|
-
cfi["raw"] = buffer
|
|
1866
|
-
if Util.DEBUG:
|
|
1867
|
-
with open("debug_cfi.bin", "wb") as f: f.write(buffer)
|
|
1868
|
-
|
|
1869
|
-
cfi["bytes"] = ""
|
|
1870
|
-
for i in range(0, 0x400):
|
|
1871
|
-
cfi["bytes"] += "{:02X}".format(buffer[i])
|
|
1872
|
-
if self.MODE == "DMG": cfi["we"] = we
|
|
1873
|
-
cfi["method_id"] = flash_commands.index(method)
|
|
1874
|
-
|
|
1875
|
-
if d_swap is not None and d_swap != ( 0, 0 ):
|
|
1876
|
-
for k in method.keys():
|
|
1877
|
-
for c in range(0, len(method[k])):
|
|
1878
|
-
if isinstance(method[k][c][1], int):
|
|
1879
|
-
method[k][c][1] = bitswap(method[k][c][1], d_swap)
|
|
1880
|
-
|
|
1881
|
-
# Flash ID
|
|
1882
|
-
for i in range(0, len(method['read_identifier'])):
|
|
1883
|
-
self._cart_write(method['read_identifier'][i][0], method["read_identifier"][i][1], flashcart=True)
|
|
1884
|
-
flash_id = self.ReadROM(0, 8)
|
|
1885
|
-
|
|
1886
|
-
if self.MODE == "DMG":
|
|
1887
|
-
method_string = "[" + we.ljust(5) + "/{:4X}/{:2X}]".format(method['read_identifier'][0][0], method['read_identifier'][0][1])
|
|
1888
|
-
else:
|
|
1889
|
-
method_string = "[{:6X}/{:2X}]".format(method['read_identifier'][0][0], method['read_identifier'][0][1])
|
|
1890
|
-
line_exists = False
|
|
1891
|
-
for i in range(0, len(flash_id_lines)):
|
|
1892
|
-
if method_string == flash_id_lines[i][0]: line_exists = True
|
|
1893
|
-
if not line_exists: flash_id_lines.append([method_string, flash_id])
|
|
1894
|
-
for i in range(0, len(method['reset'])):
|
|
1895
|
-
self._cart_write(method['reset'][i][0], method['reset'][i][1], flashcart=True)
|
|
1896
|
-
|
|
1897
|
-
cfi["method"] = method
|
|
1898
|
-
else:
|
|
1899
|
-
for j in range(0, 2):
|
|
1900
|
-
if j == 1:
|
|
1901
|
-
#d_swap = ( 0, 1 )
|
|
1902
|
-
for k in method.keys():
|
|
1903
|
-
for c in range(0, len(method[k])):
|
|
1904
|
-
if isinstance(method[k][c][1], int):
|
|
1905
|
-
method[k][c][1] = bitswap(method[k][c][1], ( 0, 1 ))
|
|
1906
|
-
for i in range(0, len(method['read_identifier'])):
|
|
1907
|
-
self._cart_write(method['read_identifier'][i][0], method["read_identifier"][i][1], flashcart=True)
|
|
1908
|
-
flash_id = self.ReadROM(0, 8)
|
|
1909
|
-
if flash_id == check_buffer[:len(flash_id)]: continue
|
|
1910
|
-
if flash_id == bytearray([0x00] * len(flash_id)): continue
|
|
1911
|
-
|
|
1912
|
-
if self.MODE == "DMG":
|
|
1913
|
-
method_string = "[" + we.ljust(5) + "/{:4X}/{:2X}]".format(method['read_identifier'][0][0], method['read_identifier'][0][1])
|
|
1914
|
-
else:
|
|
1915
|
-
method_string = "[{:6X}/{:2X}]".format(method['read_identifier'][0][0], method['read_identifier'][0][1])
|
|
1916
|
-
line_exists = False
|
|
1917
|
-
for i in range(0, len(flash_id_lines)):
|
|
1918
|
-
if method_string == flash_id_lines[i][0]: line_exists = True
|
|
1919
|
-
if not line_exists: flash_id_lines.append([method_string, flash_id])
|
|
1920
|
-
for i in range(0, len(method['reset'])):
|
|
1921
|
-
self._cart_write(method['reset'][i][0], method['reset'][i][1], flashcart=True)
|
|
1922
|
-
|
|
1923
|
-
if cart_type is not None: # reset cartridge if method is known
|
|
1924
|
-
fc_fncptr = {
|
|
1925
|
-
"cart_write_fncptr":self._cart_write,
|
|
1926
|
-
"cart_write_fast_fncptr":self._cart_write_flash,
|
|
1927
|
-
"cart_read_fncptr":self.ReadROM,
|
|
1928
|
-
"cart_powercycle_fncptr":self.CartPowerCycle,
|
|
1929
|
-
"progress_fncptr":self.SetProgress,
|
|
1930
|
-
"set_we_pin_wr":self._set_we_pin_wr,
|
|
1931
|
-
"set_we_pin_audio":self._set_we_pin_audio,
|
|
1932
|
-
}
|
|
1933
|
-
flashcart = Flashcart(config=cart_type, fncptr=fc_fncptr)
|
|
1934
|
-
flashcart.Reset(full_reset=False)
|
|
1935
|
-
|
|
1936
|
-
if "method" in cfi:
|
|
1937
|
-
s = ""
|
|
1938
|
-
if d_swap is not None and d_swap != ( 0, 0 ): s += "Swapped pins: {:s}\n".format(str(d_swap))
|
|
1939
|
-
s += "Device size: 0x{:07X} ({:.2f} MiB)\n".format(cfi["device_size"], cfi["device_size"] / 1024 / 1024)
|
|
1940
|
-
s += "Voltage: {:.1f}–{:.1f} V\n".format(cfi["vdd_min"], cfi["vdd_max"])
|
|
1941
|
-
s += "Single write: {:s}\n".format(str(cfi["single_write"]))
|
|
1942
|
-
if "buffer_size" in cfi:
|
|
1943
|
-
s += "Buffered write: {:s} ({:d} Bytes)\n".format(str(cfi["buffer_write"]), cfi["buffer_size"])
|
|
1944
|
-
else:
|
|
1945
|
-
s += "Buffered write: {:s}\n".format(str(cfi["buffer_write"]))
|
|
1946
|
-
if cfi["chip_erase"]: s += "Chip erase: {:d}–{:d} ms\n".format(cfi["chip_erase_time_avg"], cfi["chip_erase_time_max"])
|
|
1947
|
-
if cfi["sector_erase"]: s += "Sector erase: {:d}–{:d} ms\n".format(cfi["sector_erase_time_avg"], cfi["sector_erase_time_max"])
|
|
1948
|
-
if cfi["tb_boot_sector"] is not False: s += "Sector flags: {:s}\n".format(str(cfi["tb_boot_sector"]))
|
|
1949
|
-
pos = 0
|
|
1950
|
-
oversize = False
|
|
1951
|
-
s = s[:-1]
|
|
1952
|
-
for i in range(0, cfi['erase_sector_regions']):
|
|
1953
|
-
esb = cfi['erase_sector_blocks'][i]
|
|
1954
|
-
s += "\nRegion {:d}: 0x{:07X}–0x{:07X} @ 0x{:X} Bytes × {:d}".format(i+1, pos, pos+esb[2]-1, esb[0], esb[1])
|
|
1955
|
-
if oversize: s += " (alt)"
|
|
1956
|
-
pos += esb[2]
|
|
1957
|
-
if pos >= cfi['device_size']:
|
|
1958
|
-
pos = 0
|
|
1959
|
-
oversize = True
|
|
1960
|
-
#s += "\nSHA-1: {:s}".format(cfi["sha1"])
|
|
1961
|
-
cfi_info = s
|
|
1962
|
-
|
|
1963
|
-
if cfi['raw'] == b'':
|
|
1964
|
-
for we in we_pins:
|
|
1965
|
-
if we == "WR":
|
|
1966
|
-
self._set_fw_variable("FLASH_WE_PIN", 0x01) # FLASH_WE_PIN_WR
|
|
1967
|
-
elif we in ("AUDIO", "VIN"):
|
|
1968
|
-
self._set_fw_variable("FLASH_WE_PIN", 0x02) # FLASH_WE_PIN_AUDIO
|
|
1969
|
-
elif we == "WR+RESET":
|
|
1970
|
-
self._set_fw_variable("FLASH_WE_PIN", 0x03) # FLASH_WE_PIN_WR_RESET
|
|
1971
|
-
|
|
1972
|
-
for method in flash_commands:
|
|
1973
|
-
for i in range(0, len(method['reset'])):
|
|
1974
|
-
self._cart_write(method['reset'][i][0], method["reset"][i][1], flashcart=True)
|
|
1975
|
-
for i in range(0, len(method['read_identifier'])):
|
|
1976
|
-
self._cart_write(method['read_identifier'][i][0], method["read_identifier"][i][1], flashcart=True)
|
|
1977
|
-
if "start_addr" in method:
|
|
1978
|
-
flash_id = self.ReadROM(method["start_addr"], 8)
|
|
1979
|
-
else:
|
|
1980
|
-
flash_id = self.ReadROM(0, 8)
|
|
1981
|
-
if flash_id == bytearray([0x00] * len(flash_id)): continue
|
|
1982
|
-
|
|
1983
|
-
for i in range(0, len(method['reset'])):
|
|
1984
|
-
self._cart_write(method['reset'][i][0], method["reset"][i][1], flashcart=True)
|
|
1985
|
-
|
|
1986
|
-
if flash_id != check_buffer[0:8]:
|
|
1987
|
-
if self.MODE == "DMG":
|
|
1988
|
-
method_string = "[" + we.ljust(5) + "/{:4X}/{:2X}]".format(method['read_identifier'][0][0], method['read_identifier'][0][1])
|
|
1989
|
-
else:
|
|
1990
|
-
method_string = "[{:6X}/{:2X}]".format(method['read_identifier'][0][0], method['read_identifier'][0][1])
|
|
1991
|
-
line_exists = False
|
|
1992
|
-
for i in range(0, len(flash_id_lines)):
|
|
1993
|
-
if method_string == flash_id_lines[i][0]: line_exists = True
|
|
1994
|
-
if not line_exists: flash_id_lines.append([method_string, flash_id])
|
|
1995
|
-
for i in range(0, len(method['reset'])):
|
|
1996
|
-
self._cart_write(method['reset'][i][0], method['reset'][i][1], flashcart=True)
|
|
1997
|
-
|
|
1998
|
-
if self.MODE == "DMG":
|
|
1999
|
-
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
|
2000
|
-
time.sleep(0.2)
|
|
2001
|
-
|
|
2002
|
-
if cart_type is not None: # reset cartridge if method is known
|
|
2003
|
-
fc_fncptr = {
|
|
2004
|
-
"cart_write_fncptr":self._cart_write,
|
|
2005
|
-
"cart_write_fast_fncptr":self._cart_write_flash,
|
|
2006
|
-
"cart_read_fncptr":self.ReadROM,
|
|
2007
|
-
"cart_powercycle_fncptr":self.CartPowerCycle,
|
|
2008
|
-
"progress_fncptr":self.SetProgress,
|
|
2009
|
-
"set_we_pin_wr":self._set_we_pin_wr,
|
|
2010
|
-
"set_we_pin_audio":self._set_we_pin_audio,
|
|
2011
|
-
}
|
|
2012
|
-
flashcart = Flashcart(config=cart_type, fncptr=fc_fncptr)
|
|
2013
|
-
flashcart.Reset(full_reset=True)
|
|
2014
|
-
|
|
2015
|
-
flash_id = ""
|
|
2016
|
-
for i in range(0, len(flash_id_lines)):
|
|
2017
|
-
flash_id += flash_id_lines[i][0] + " "
|
|
2018
|
-
for j in range(0, 8):
|
|
2019
|
-
flash_id += "{:02X} ".format(flash_id_lines[i][1][j])
|
|
2020
|
-
flash_id += "\n"
|
|
2021
|
-
|
|
2022
|
-
flash_id = rom_string + flash_id
|
|
2023
|
-
#self._set_fw_variable("FLASH_WE_PIN", 0x02) # Set AUDIO back to high
|
|
2024
|
-
return (flash_id, cfi_info, cfi)
|
|
2025
|
-
|
|
2026
|
-
def GetDumpReport(self):
|
|
2027
|
-
return Util.GetDumpReport(self.INFO["dump_info"], self)
|
|
2028
|
-
|
|
2029
|
-
def GetReadErrors(self):
|
|
2030
|
-
return self.READ_ERRORS
|
|
2031
|
-
|
|
2032
|
-
#################################################################
|
|
2033
|
-
|
|
2034
|
-
def DoTransfer(self, mode, fncSetProgress, args):
|
|
2035
|
-
from . import DataTransfer
|
|
2036
|
-
args['mode'] = mode
|
|
2037
|
-
args['port'] = self
|
|
2038
|
-
if self.WORKER is None:
|
|
2039
|
-
self.WORKER = DataTransfer.DataTransfer(args)
|
|
2040
|
-
if fncSetProgress not in (False, None):
|
|
2041
|
-
self.WORKER.updateProgress.connect(fncSetProgress)
|
|
2042
|
-
else:
|
|
2043
|
-
self.WORKER.setConfig(args)
|
|
2044
|
-
self.WORKER.start()
|
|
2045
|
-
|
|
2046
|
-
def BackupROM(self, fncSetProgress=None, args=None):
|
|
2047
|
-
self.DoTransfer(1, fncSetProgress, args)
|
|
2048
|
-
|
|
2049
|
-
def BackupRAM(self, fncSetProgress=None, args=None):
|
|
2050
|
-
if fncSetProgress is False:
|
|
2051
|
-
args['mode'] = 2
|
|
2052
|
-
args['port'] = self
|
|
2053
|
-
self._BackupRestoreRAM(args=args)
|
|
2054
|
-
else:
|
|
2055
|
-
self.DoTransfer(2, fncSetProgress, args)
|
|
2056
|
-
|
|
2057
|
-
def RestoreRAM(self, fncSetProgress=None, args=None):
|
|
2058
|
-
self.DoTransfer(3, fncSetProgress, args)
|
|
2059
|
-
|
|
2060
|
-
def FlashROM(self, fncSetProgress=None, args=None):
|
|
2061
|
-
self.DoTransfer(4, fncSetProgress, args)
|
|
2062
|
-
|
|
2063
|
-
#################################################################
|
|
2064
|
-
|
|
2065
|
-
def _BackupROM(self, args):
|
|
2066
|
-
file = None
|
|
2067
|
-
if len(args["path"]) > 0:
|
|
2068
|
-
file = open(args["path"], "wb")
|
|
2069
|
-
|
|
2070
|
-
self.FAST_READ = True
|
|
2071
|
-
|
|
2072
|
-
flashcart = False
|
|
2073
|
-
#is_3dmemory = False
|
|
2074
|
-
supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
|
|
2075
|
-
cart_type = copy.deepcopy(supported_carts[args["cart_type"]])
|
|
2076
|
-
if not isinstance(cart_type, str):
|
|
2077
|
-
cart_type["_index"] = 0
|
|
2078
|
-
for i in range(0, len(list(self.SUPPORTED_CARTS[self.MODE].keys()))):
|
|
2079
|
-
if i == args["cart_type"]:
|
|
2080
|
-
try:
|
|
2081
|
-
cart_type["_index"] = cart_type["names"].index(list(self.SUPPORTED_CARTS[self.MODE].keys())[i])
|
|
2082
|
-
|
|
2083
|
-
fc_fncptr = {
|
|
2084
|
-
"cart_write_fncptr":self._cart_write,
|
|
2085
|
-
"cart_write_fast_fncptr":self._cart_write_flash,
|
|
2086
|
-
"cart_read_fncptr":self.ReadROM,
|
|
2087
|
-
"cart_powercycle_fncptr":self.CartPowerCycle,
|
|
2088
|
-
"progress_fncptr":self.SetProgress,
|
|
2089
|
-
"set_we_pin_wr":self._set_we_pin_wr,
|
|
2090
|
-
"set_we_pin_audio":self._set_we_pin_audio,
|
|
2091
|
-
}
|
|
2092
|
-
flashcart = Flashcart(config=cart_type, fncptr=fc_fncptr)
|
|
2093
|
-
except:
|
|
2094
|
-
pass
|
|
2095
|
-
|
|
2096
|
-
# Firmware check L8
|
|
2097
|
-
if self.FW["fw_ver"] < 8 and flashcart and "enable_pullups" in cart_type:
|
|
2098
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L8 on the GBxCart RW v1.4 hardware revision or newer. It may also work with older hardware revisions using the official firmware and insideGadgets flasher software available from <a href=\"https://www.gbxcart.com/\">https://www.gbxcart.com/</a>.", "abortable":False})
|
|
2099
|
-
return False
|
|
2100
|
-
# Firmware check L8
|
|
2101
|
-
|
|
2102
|
-
buffer_len = 0x4000
|
|
2103
|
-
|
|
2104
|
-
self.INFO["dump_info"]["timestamp"] = datetime.datetime.now().astimezone().replace(microsecond=0).isoformat()
|
|
2105
|
-
self.INFO["dump_info"]["file_name"] = args["path"]
|
|
2106
|
-
self.INFO["dump_info"]["file_size"] = args["rom_size"]
|
|
2107
|
-
self.INFO["dump_info"]["cart_type"] = args["cart_type"]
|
|
2108
|
-
self.INFO["dump_info"]["system"] = self.MODE
|
|
2109
|
-
if self.MODE == "DMG":
|
|
2110
|
-
self.INFO["dump_info"]["rom_size"] = args["rom_size"]
|
|
2111
|
-
self.INFO["dump_info"]["mapper_type"] = args["mbc"]
|
|
2112
|
-
|
|
2113
|
-
self.INFO["mapper_raw"] = args["mbc"]
|
|
2114
|
-
if not self.IsSupportedMbc(args["mbc"]):
|
|
2115
|
-
msg = "This cartridge uses a mapper that is not supported by {:s} using your {:s} device. An updated hardware revision is required.".format(Util.APPNAME, self.GetFullName())
|
|
2116
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":msg, "abortable":False})
|
|
2117
|
-
return False
|
|
2118
|
-
|
|
2119
|
-
if "verify_mbc" in args and args["verify_mbc"] is not None:
|
|
2120
|
-
_mbc = args["verify_mbc"]
|
|
2121
|
-
else:
|
|
2122
|
-
_mbc = DMG_MBC().GetInstance(args=args, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle)
|
|
2123
|
-
self._write(self.DEVICE_CMD["SET_MODE_DMG"])
|
|
2124
|
-
|
|
2125
|
-
self._set_fw_variable("DMG_WRITE_CS_PULSE", 0)
|
|
2126
|
-
self._set_fw_variable("DMG_READ_CS_PULSE", 0)
|
|
2127
|
-
if _mbc.GetName() == "TAMA5":
|
|
2128
|
-
self._set_fw_variable("DMG_WRITE_CS_PULSE", 1)
|
|
2129
|
-
self._set_fw_variable("DMG_READ_CS_PULSE", 1)
|
|
2130
|
-
_mbc.EnableMapper()
|
|
2131
|
-
self._set_fw_variable("DMG_READ_CS_PULSE", 0)
|
|
2132
|
-
elif _mbc.GetName() == "Sachen":
|
|
2133
|
-
start_bank = int(args["rom_size"] / 0x4000)
|
|
2134
|
-
_mbc.SetStartBank(start_bank)
|
|
2135
|
-
else:
|
|
2136
|
-
_mbc.EnableMapper()
|
|
2137
|
-
|
|
2138
|
-
rom_size = args["rom_size"]
|
|
2139
|
-
rom_banks = _mbc.GetROMBanks(rom_size)
|
|
2140
|
-
rom_bank_size = _mbc.GetROMBankSize()
|
|
2141
|
-
size = _mbc.GetROMSize()
|
|
2142
|
-
|
|
2143
|
-
#if _mbc.GetName() == "Datel MegaMem":
|
|
2144
|
-
# args["rom_size"] = self.INFO["dump_info"]["file_size"] = rom_size = size = _mbc.GetMaxROMSize()
|
|
2145
|
-
|
|
2146
|
-
elif self.MODE == "AGB":
|
|
2147
|
-
self.INFO["dump_info"]["mapper_type"] = None
|
|
2148
|
-
self._write(self.DEVICE_CMD["SET_MODE_AGB"])
|
|
2149
|
-
buffer_len = 0x10000
|
|
2150
|
-
size = 32 * 1024 * 1024
|
|
2151
|
-
if "agb_rom_size" in args: size = args["agb_rom_size"]
|
|
2152
|
-
self.INFO["dump_info"]["rom_size"] = size
|
|
2153
|
-
|
|
2154
|
-
if flashcart and "flash_bank_size" in cart_type:
|
|
2155
|
-
if "verify_write" in args:
|
|
2156
|
-
rom_banks = math.ceil(len(args["verify_write"]) / cart_type["flash_bank_size"])
|
|
2157
|
-
else:
|
|
2158
|
-
rom_banks = math.ceil(size / cart_type["flash_bank_size"])
|
|
2159
|
-
rom_bank_size = cart_type["flash_bank_size"]
|
|
2160
|
-
else:
|
|
2161
|
-
rom_banks = 1
|
|
2162
|
-
rom_bank_size = 0x2000000
|
|
2163
|
-
|
|
2164
|
-
is_3dmemory = (self.MODE == "AGB" and "command_set" in cart_type and cart_type["command_set"] == "3DMEMORY")
|
|
2165
|
-
|
|
2166
|
-
if "verify_write" in args:
|
|
2167
|
-
size = len(args["verify_write"])
|
|
2168
|
-
buffer_len = min(buffer_len, size)
|
|
2169
|
-
else:
|
|
2170
|
-
if "bl_offset" in args:
|
|
2171
|
-
method = "SAVE_READ"
|
|
2172
|
-
else:
|
|
2173
|
-
method = "ROM_READ"
|
|
2174
|
-
pos = 0
|
|
2175
|
-
self.SetProgress({"action":"INITIALIZE", "method":method, "size":size})
|
|
2176
|
-
self.INFO["action"] = self.ACTIONS[method]
|
|
2177
|
-
if self.FW["fw_ver"] >= 8:
|
|
2178
|
-
if flashcart and "enable_pullups" in cart_type:
|
|
2179
|
-
self._write(self.DEVICE_CMD["ENABLE_PULLUPS"], wait=True)
|
|
2180
|
-
dprint("Pullups enabled")
|
|
2181
|
-
else:
|
|
2182
|
-
self._write(self.DEVICE_CMD["DISABLE_PULLUPS"], wait=True)
|
|
2183
|
-
dprint("Pullups disabled")
|
|
2184
|
-
|
|
2185
|
-
buffer = bytearray(size)
|
|
2186
|
-
max_length = self.MAX_BUFFER_READ
|
|
2187
|
-
dprint("Max buffer size: 0x{:X}".format(max_length))
|
|
2188
|
-
if self.FAST_READ is True:
|
|
2189
|
-
if is_3dmemory:
|
|
2190
|
-
max_length = min(max_length, 0x1000)
|
|
2191
|
-
else:
|
|
2192
|
-
max_length = min(max_length, 0x2000)
|
|
2193
|
-
self.INFO["dump_info"]["transfer_size"] = max_length
|
|
2194
|
-
pos_total = 0
|
|
2195
|
-
start_address = 0
|
|
2196
|
-
end_address = size
|
|
2197
|
-
# dprint("ROM banks:", rom_banks)
|
|
2198
|
-
|
|
2199
|
-
start_bank = 0
|
|
2200
|
-
if "verify_write" in args:
|
|
2201
|
-
buffer_pos = args["verify_from"]
|
|
2202
|
-
start_address = buffer_pos
|
|
2203
|
-
end_address = args["verify_from"] + args["verify_len"]
|
|
2204
|
-
start_bank = math.floor(buffer_pos / rom_bank_size)
|
|
2205
|
-
end_bank = math.ceil((buffer_pos + args["verify_len"]) / rom_bank_size)
|
|
2206
|
-
rom_banks = end_bank
|
|
2207
|
-
elif "bl_offset" in args:
|
|
2208
|
-
buffer_pos = args["bl_offset"]
|
|
2209
|
-
start_address = buffer_pos
|
|
2210
|
-
end_address = args["bl_offset"] + args["bl_size"]
|
|
2211
|
-
start_bank = math.floor(buffer_pos / rom_bank_size)
|
|
2212
|
-
end_bank = math.ceil((buffer_pos + args["bl_size"]) / rom_bank_size)
|
|
2213
|
-
rom_banks = end_bank
|
|
2214
|
-
|
|
2215
|
-
dprint("start_address=0x{:X}, end_address=0x{:X}, start_bank=0x{:X}, rom_banks=0x{:X}, buffer_len=0x{:X}, max_length=0x{:X}".format(start_address, end_address, start_bank, rom_banks, buffer_len, max_length))
|
|
2216
|
-
bank = start_bank
|
|
2217
|
-
while bank < rom_banks:
|
|
2218
|
-
# ↓↓↓ Switch ROM bank
|
|
2219
|
-
if self.MODE == "DMG":
|
|
2220
|
-
if _mbc.ResetBeforeBankChange(bank) is True:
|
|
2221
|
-
dprint("Resetting the MBC")
|
|
2222
|
-
self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
|
|
2223
|
-
(start_address, bank_size) = _mbc.SelectBankROM(bank)
|
|
2224
|
-
end_address = start_address + bank_size
|
|
2225
|
-
buffer_len = min(buffer_len, _mbc.GetROMBankSize())
|
|
2226
|
-
if "verify_write" in args:
|
|
2227
|
-
buffer_len = min(buffer_len, bank_size, len(args["verify_write"]))
|
|
2228
|
-
end_address = start_address + bank_size
|
|
2229
|
-
start_address += (buffer_pos % rom_bank_size)
|
|
2230
|
-
if end_address > start_address + args["verify_len"]:
|
|
2231
|
-
end_address = start_address + args["verify_len"]
|
|
2232
|
-
elif self.MODE == "AGB":
|
|
2233
|
-
if "verify_write" in args:
|
|
2234
|
-
buffer_len = min(buffer_len, len(args["verify_write"]))
|
|
2235
|
-
if "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] > 0:
|
|
2236
|
-
flashcart.SelectBankROM(bank)
|
|
2237
|
-
temp = end_address - start_address
|
|
2238
|
-
start_address %= cart_type["flash_bank_size"]
|
|
2239
|
-
end_address = min(cart_type["flash_bank_size"], start_address + temp)
|
|
2240
|
-
# ↑↑↑ Switch ROM bank
|
|
2241
|
-
|
|
2242
|
-
skip_init = False
|
|
2243
|
-
pos = start_address
|
|
2244
|
-
lives = 20
|
|
2245
|
-
|
|
2246
|
-
while pos < end_address:
|
|
2247
|
-
temp = bytearray()
|
|
2248
|
-
if self.CANCEL:
|
|
2249
|
-
cancel_args = {"action":"ABORT", "abortable":False}
|
|
2250
|
-
cancel_args.update(self.CANCEL_ARGS)
|
|
2251
|
-
self.CANCEL_ARGS = {}
|
|
2252
|
-
self.ERROR_ARGS = {}
|
|
2253
|
-
self.SetProgress(cancel_args)
|
|
2254
|
-
try:
|
|
2255
|
-
if file is not None: file.close()
|
|
2256
|
-
except:
|
|
2257
|
-
pass
|
|
2258
|
-
if self.CanPowerCycleCart(): self.CartPowerCycle()
|
|
2259
|
-
return
|
|
2260
|
-
|
|
2261
|
-
if is_3dmemory:
|
|
2262
|
-
temp = self.ReadROM_3DMemory(address=pos, length=buffer_len, max_length=max_length)
|
|
2263
|
-
else:
|
|
2264
|
-
if self.FW["fw_ver"] >= 10 and "verify_write" in args and (self.MODE != "AGB" or args["verify_base_pos"] > 0xC9):
|
|
2265
|
-
# Verify mode (by CRC32)
|
|
2266
|
-
dprint("CRC32 verification (verify_base_pos=0x{:X}, pos=0x{:X}, pos_total=0x{:X}, buffer_len=0x{:X})".format(args["verify_base_pos"], pos, pos_total, buffer_len))
|
|
2267
|
-
if self.MODE == "DMG":
|
|
2268
|
-
self._set_fw_variable("ADDRESS", pos)
|
|
2269
|
-
elif self.MODE == "AGB":
|
|
2270
|
-
self._set_fw_variable("ADDRESS", pos >> 1)
|
|
2271
|
-
self._write(self.DEVICE_CMD["CALC_CRC32"])
|
|
2272
|
-
self._write(bytearray(struct.pack(">I", buffer_len)))
|
|
2273
|
-
crc32_expected = zlib.crc32(args["verify_write"][pos_total:pos_total+buffer_len])
|
|
2274
|
-
crc32_calculated = struct.unpack(">I", self._read(4))[0]
|
|
2275
|
-
dprint("Expected CRC32: 0x{:X}".format(crc32_expected))
|
|
2276
|
-
dprint("Calculated CRC32: 0x{:X}".format(crc32_calculated))
|
|
2277
|
-
if crc32_expected == crc32_calculated:
|
|
2278
|
-
pos += buffer_len
|
|
2279
|
-
pos_total += buffer_len
|
|
2280
|
-
dprint("CRC32 verification successful between 0x{:X} and 0x{:X}".format(pos_total-buffer_len, pos_total))
|
|
2281
|
-
self.SetProgress({"action":"UPDATE_POS", "pos":args["verify_from"]+pos_total})
|
|
2282
|
-
continue
|
|
2283
|
-
else:
|
|
2284
|
-
dprint("Mismatch during CRC32 verification between 0x{:X} and 0x{:X}".format(pos_total, pos_total+buffer_len))
|
|
2285
|
-
temp = self.ReadROM(address=pos, length=buffer_len, skip_init=skip_init, max_length=max_length)
|
|
2286
|
-
else:
|
|
2287
|
-
# Normal read
|
|
2288
|
-
temp = self.ReadROM(address=pos, length=buffer_len, skip_init=skip_init, max_length=max_length)
|
|
2289
|
-
skip_init = True
|
|
2290
|
-
|
|
2291
|
-
if len(temp) != buffer_len:
|
|
2292
|
-
pos_temp = pos_total
|
|
2293
|
-
if "verify_write" in args:
|
|
2294
|
-
pos_temp += args["verify_base_pos"]
|
|
2295
|
-
self.SetProgress({"action":"UPDATE_POS", "pos":args["verify_from"]+pos_total})
|
|
2296
|
-
else:
|
|
2297
|
-
self.SetProgress({"action":"UPDATE_POS", "pos":pos_total})
|
|
2298
|
-
|
|
2299
|
-
err_text = "Note: Incomplete transfer detected. Resuming from 0x{:X}...".format(pos_temp)
|
|
2300
|
-
if (max_length >> 1) < 64:
|
|
2301
|
-
dprint("Failed to receive 0x{:X} bytes from the device at position 0x{:X}.".format(buffer_len, pos_temp))
|
|
2302
|
-
max_length = 64
|
|
2303
|
-
elif lives > 20:
|
|
2304
|
-
dprint("Failed to receive 0x{:X} bytes from the device at position 0x{:X}.".format(buffer_len, pos_temp))
|
|
2305
|
-
else:
|
|
2306
|
-
dprint("Failed to receive 0x{:X} bytes from the device at position 0x{:X}. Decreasing maximum transfer buffer size to 0x{:X}.".format(buffer_len, pos_temp, max_length >> 1))
|
|
2307
|
-
max_length >>= 1
|
|
2308
|
-
self.MAX_BUFFER_READ = max_length
|
|
2309
|
-
err_text += "\nBuffer size adjusted to {:d} bytes...".format(max_length)
|
|
2310
|
-
if ".dev" in Util.VERSION_PEP440 and not Util.DEBUG: print(err_text)
|
|
2311
|
-
|
|
2312
|
-
self.INFO["dump_info"]["transfer_size"] = max_length
|
|
2313
|
-
skip_init = False
|
|
2314
|
-
self.DEVICE.reset_input_buffer()
|
|
2315
|
-
self.DEVICE.reset_output_buffer()
|
|
2316
|
-
lives -= 1
|
|
2317
|
-
if lives == 0:
|
|
2318
|
-
self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"An error occured while reading from the cartridge. When connecting the device, avoid passive USB hubs and try different USB ports/cables."})
|
|
2319
|
-
self.CANCEL = True
|
|
2320
|
-
self.ERROR = True
|
|
2321
|
-
if "verify_write" in args: return False
|
|
2322
|
-
continue
|
|
2323
|
-
elif lives < 20:
|
|
2324
|
-
lives = 20
|
|
2325
|
-
|
|
2326
|
-
if file is not None: file.write(temp)
|
|
2327
|
-
buffer[pos_total:pos_total+len(temp)] = temp
|
|
2328
|
-
pos_total += len(temp)
|
|
2329
|
-
|
|
2330
|
-
if "verify_write" in args:
|
|
2331
|
-
#if pos_total >= len(args["verify_write"]): break
|
|
2332
|
-
check = args["verify_write"][pos_total-len(temp):pos_total]
|
|
2333
|
-
if Util.DEBUG:
|
|
2334
|
-
dprint("Writing 0x{:X} bytes to debug_verify.bin".format(len(temp)))
|
|
2335
|
-
with open("debug_verify.bin", "ab") as f: f.write(temp)
|
|
2336
|
-
|
|
2337
|
-
if temp[:len(check)] != check:
|
|
2338
|
-
for i in range(0, pos_total):
|
|
2339
|
-
if (i < len(args["verify_write"]) - 1) and (i < pos_total - 1) and args["verify_write"][i] != buffer[i]:
|
|
2340
|
-
if args["rtc_area"] is True and i in (0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9):
|
|
2341
|
-
dprint("Skipping RTC area at 0x{:X}".format(i))
|
|
2342
|
-
else:
|
|
2343
|
-
dprint("Mismatch during verification at 0x{:X}".format(i))
|
|
2344
|
-
return i
|
|
2345
|
-
else:
|
|
2346
|
-
dprint("Verification successful between 0x{:X} and 0x{:X}".format(pos_total-len(temp), pos_total))
|
|
2347
|
-
self.SetProgress({"action":"UPDATE_POS", "pos":args["verify_from"]+pos_total})
|
|
2348
|
-
else:
|
|
2349
|
-
self.SetProgress({"action":"UPDATE_POS", "pos":pos_total})
|
|
2350
|
-
|
|
2351
|
-
pos += buffer_len
|
|
2352
|
-
|
|
2353
|
-
bank += 1
|
|
2354
|
-
|
|
2355
|
-
if "verify_write" in args:
|
|
2356
|
-
return min(pos_total, len(args["verify_write"]))
|
|
2357
|
-
|
|
2358
|
-
if not "bl_offset" in args:
|
|
2359
|
-
# Hidden sector (GB-Memory)
|
|
2360
|
-
if self.MODE == "DMG" and len(args["path"]) > 0 and _mbc.HasHiddenSector():
|
|
2361
|
-
file = open(os.path.splitext(args["path"])[0] + ".map", "wb")
|
|
2362
|
-
temp = _mbc.ReadHiddenSector()
|
|
2363
|
-
self.INFO["hidden_sector"] = temp
|
|
2364
|
-
self.INFO["dump_info"]["gbmem"] = temp
|
|
2365
|
-
self.INFO["dump_info"]["gbmem_parsed"] = (GBMemoryMap()).ParseMapData(buffer_map=temp, buffer_rom=buffer)
|
|
2366
|
-
file.write(temp)
|
|
2367
|
-
file.close()
|
|
2368
|
-
gbmp = self.INFO["dump_info"]["gbmem_parsed"]
|
|
2369
|
-
if (isinstance(gbmp, list)) and len(args["path"]) > 2:
|
|
2370
|
-
for i in range(1, len(gbmp)):
|
|
2371
|
-
if gbmp[i]["header"] == {} or gbmp[i]["header"]["logo_correct"] is False: continue
|
|
2372
|
-
settings = None
|
|
2373
|
-
if "settings" in args: settings = args["settings"]
|
|
2374
|
-
gbmp_n = Util.GenerateFileName(mode="DMG", header=gbmp[i]["header"], settings=settings)
|
|
2375
|
-
gbmp_p = "{:s} - {:s}".format(os.path.splitext(args["path"])[0], gbmp_n)
|
|
2376
|
-
with open(gbmp_p, "wb") as f:
|
|
2377
|
-
f.write(buffer[gbmp[i]["rom_offset"]:gbmp[i]["rom_offset"]+gbmp[i]["rom_size"]])
|
|
2378
|
-
else:
|
|
2379
|
-
if "hidden_sector" in self.INFO: del(self.INFO["hidden_sector"])
|
|
2380
|
-
if "gbmem" in self.INFO["dump_info"]: del(self.INFO["dump_info"]["gbmem"])
|
|
2381
|
-
if "gbmem_parsed" in self.INFO["dump_info"]: del(self.INFO["dump_info"]["gbmem_parsed"])
|
|
2382
|
-
|
|
2383
|
-
# Calculate Global Checksum
|
|
2384
|
-
if self.MODE == "DMG":
|
|
2385
|
-
if _mbc.GetName() == "MMM01":
|
|
2386
|
-
self.INFO["dump_info"]["header"] = RomFileDMG(buffer[-0x8000:-0x8000+0x180]).GetHeader(unchanged=True)
|
|
2387
|
-
else:
|
|
2388
|
-
self.INFO["dump_info"]["header"] = RomFileDMG(buffer[:0x180]).GetHeader()
|
|
2389
|
-
#chk = _mbc.CalcChecksum(buffer)
|
|
2390
|
-
self.INFO["rom_checksum_calc"] = _mbc.CalcChecksum(buffer)
|
|
2391
|
-
elif self.MODE == "AGB":
|
|
2392
|
-
self.INFO["dump_info"]["header"] = RomFileAGB(buffer[:0x180]).GetHeader()
|
|
2393
|
-
#chk = self.INFO["file_crc32"]
|
|
2394
|
-
|
|
2395
|
-
temp_ver = "N/A"
|
|
2396
|
-
ids = [ b"SRAM_", b"EEPROM_V", b"FLASH_V", b"FLASH512_V", b"FLASH1M_V", b"AGB_8MDACS_DL_V" ]
|
|
2397
|
-
for id in ids:
|
|
2398
|
-
temp_pos = buffer.find(id)
|
|
2399
|
-
if temp_pos > 0:
|
|
2400
|
-
temp_ver = buffer[temp_pos:temp_pos+0x20]
|
|
2401
|
-
temp_ver = temp_ver[:temp_ver.index(0x00)].decode("ascii", "replace")
|
|
2402
|
-
break
|
|
2403
|
-
self.INFO["dump_info"]["agb_savelib"] = temp_ver
|
|
2404
|
-
self.INFO["dump_info"]["agb_save_flash_id"] = None
|
|
2405
|
-
if "FLASH" in temp_ver:
|
|
2406
|
-
try:
|
|
2407
|
-
agb_save_flash_id = self.ReadFlashSaveID()
|
|
2408
|
-
if agb_save_flash_id is not False and len(agb_save_flash_id) == 2:
|
|
2409
|
-
self.INFO["dump_info"]["agb_save_flash_id"] = agb_save_flash_id
|
|
2410
|
-
except:
|
|
2411
|
-
print("Error querying the flash save chip.")
|
|
2412
|
-
self.DEVICE.reset_input_buffer()
|
|
2413
|
-
self.DEVICE.reset_output_buffer()
|
|
2414
|
-
|
|
2415
|
-
if "eeprom_data" in self.INFO["dump_info"]: del(self.INFO["dump_info"]["eeprom_data"])
|
|
2416
|
-
if "EEPROM" in temp_ver and len(buffer) == 0x2000000:
|
|
2417
|
-
padding_byte = buffer[0x1FFFEFF]
|
|
2418
|
-
dprint("Replacing unmapped ROM data of cartridge (32 MiB ROM + EEPROM save type) with the original padding byte of 0x{:02X}.".format(padding_byte))
|
|
2419
|
-
self.INFO["dump_info"]["eeprom_data"] = buffer[0x1FFFF00:0x1FFFF10]
|
|
2420
|
-
buffer[0x1FFFF00:0x2000000] = bytearray([padding_byte] * 0x100)
|
|
2421
|
-
file.seek(0x1FFFF00)
|
|
2422
|
-
file.write(buffer[0x1FFFF00:0x2000000])
|
|
2423
|
-
|
|
2424
|
-
self.INFO["file_crc32"] = zlib.crc32(buffer) & 0xFFFFFFFF
|
|
2425
|
-
self.INFO["file_sha1"] = hashlib.sha1(buffer).hexdigest()
|
|
2426
|
-
self.INFO["file_sha256"] = hashlib.sha256(buffer).hexdigest()
|
|
2427
|
-
self.INFO["file_md5"] = hashlib.md5(buffer).hexdigest()
|
|
2428
|
-
self.INFO["dump_info"]["hash_crc32"] = self.INFO["file_crc32"]
|
|
2429
|
-
self.INFO["dump_info"]["hash_sha1"] = self.INFO["file_sha1"]
|
|
2430
|
-
self.INFO["dump_info"]["hash_sha256"] = self.INFO["file_sha256"]
|
|
2431
|
-
self.INFO["dump_info"]["hash_md5"] = self.INFO["file_md5"]
|
|
2432
|
-
|
|
2433
|
-
# Check for ROM loops
|
|
2434
|
-
self.INFO["loop_detected"] = False
|
|
2435
|
-
temp = min(0x2000000, len(buffer))
|
|
2436
|
-
while temp > 0x4000:
|
|
2437
|
-
temp = temp >> 1
|
|
2438
|
-
if (buffer[0:0x4000] == buffer[temp:temp+0x4000]):
|
|
2439
|
-
if buffer[0:temp] == buffer[temp:temp*2]:
|
|
2440
|
-
self.INFO["loop_detected"] = temp
|
|
2441
|
-
else:
|
|
2442
|
-
break
|
|
2443
|
-
|
|
2444
|
-
if file is not None: file.close()
|
|
2445
|
-
|
|
2446
|
-
# ↓↓↓ Switch to first ROM bank
|
|
2447
|
-
if self.MODE == "DMG":
|
|
2448
|
-
if _mbc.ResetBeforeBankChange(0) is True:
|
|
2449
|
-
dprint("Resetting the MBC")
|
|
2450
|
-
self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
|
|
2451
|
-
_mbc.SelectBankROM(0)
|
|
2452
|
-
elif self.MODE == "AGB":
|
|
2453
|
-
if "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] > 0:
|
|
2454
|
-
flashcart.SelectBankROM(0)
|
|
2455
|
-
# ↑↑↑ Switch to first ROM bank
|
|
2456
|
-
|
|
2457
|
-
# Clean up
|
|
2458
|
-
self.INFO["last_action"] = self.INFO["action"]
|
|
2459
|
-
self.INFO["action"] = None
|
|
2460
|
-
self.INFO["last_path"] = args["path"]
|
|
2461
|
-
self.SetProgress({"action":"FINISHED"})
|
|
2462
|
-
return True
|
|
2463
|
-
|
|
2464
|
-
def _BackupRestoreRAM(self, args):
|
|
2465
|
-
self.FAST_READ = False
|
|
2466
|
-
if "rtc" not in args: args["rtc"] = False
|
|
2467
|
-
|
|
2468
|
-
# Prepare some stuff
|
|
2469
|
-
command = None
|
|
2470
|
-
empty_data_byte = 0x00
|
|
2471
|
-
extra_size = 0
|
|
2472
|
-
audio_low = False
|
|
2473
|
-
|
|
2474
|
-
cart_type = None
|
|
2475
|
-
if "cart_type" in args:
|
|
2476
|
-
supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
|
|
2477
|
-
cart_type = copy.deepcopy(supported_carts[args["cart_type"]])
|
|
2478
|
-
|
|
2479
|
-
if self.MODE == "DMG":
|
|
2480
|
-
_mbc = DMG_MBC().GetInstance(args=args, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle)
|
|
2481
|
-
if not self.IsSupportedMbc(args["mbc"]):
|
|
2482
|
-
msg = "This cartridge uses a mapper that is not supported by {:s} using your {:s} device. An updated hardware revision is required.".format(Util.APPNAME, self.GetFullName())
|
|
2483
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":msg, "abortable":False})
|
|
2484
|
-
return False
|
|
2485
|
-
if "save_size" in args:
|
|
2486
|
-
save_size = args["save_size"]
|
|
2487
|
-
else:
|
|
2488
|
-
save_size = Util.DMG_Header_RAM_Sizes_Flasher_Map[Util.DMG_Header_RAM_Sizes_Map.index(args["save_type"])]
|
|
2489
|
-
ram_banks = _mbc.GetRAMBanks(save_size)
|
|
2490
|
-
buffer_len = min(0x200, _mbc.GetRAMBankSize())
|
|
2491
|
-
self._write(self.DEVICE_CMD["SET_MODE_DMG"])
|
|
2492
|
-
self._set_fw_variable("DMG_WRITE_CS_PULSE", 0)
|
|
2493
|
-
self._set_fw_variable("DMG_READ_CS_PULSE", 0)
|
|
2494
|
-
|
|
2495
|
-
# Enable mappers
|
|
2496
|
-
if _mbc.GetName() == "TAMA5":
|
|
2497
|
-
self._set_fw_variable("DMG_WRITE_CS_PULSE", 1)
|
|
2498
|
-
self._set_fw_variable("DMG_READ_CS_PULSE", 1)
|
|
2499
|
-
_mbc.EnableMapper()
|
|
2500
|
-
self._set_fw_variable("DMG_READ_CS_PULSE", 0)
|
|
2501
|
-
buffer_len = 0x20
|
|
2502
|
-
elif _mbc.GetName() == "MBC7":
|
|
2503
|
-
buffer_len = save_size
|
|
2504
|
-
elif _mbc.GetName() == "MBC6":
|
|
2505
|
-
empty_data_byte = 0xFF
|
|
2506
|
-
audio_low = True
|
|
2507
|
-
self._set_fw_variable("FLASH_METHOD", 0x04) # FLASH_METHOD_DMG_MBC6
|
|
2508
|
-
self._set_fw_variable("FLASH_WE_PIN", 0x01) # WR
|
|
2509
|
-
_mbc.EnableFlash(enable=True, enable_write=True if (args["mode"] == 3) else False)
|
|
2510
|
-
elif _mbc.GetName() == "Xploder GB":
|
|
2511
|
-
empty_data_byte = 0xFF
|
|
2512
|
-
self._set_fw_variable("FLASH_PULSE_RESET", 0)
|
|
2513
|
-
self._set_fw_variable("FLASH_DOUBLE_DIE", 0)
|
|
2514
|
-
self._write(self.DEVICE_CMD["SET_FLASH_CMD"])
|
|
2515
|
-
self._write(0x00) # FLASH_COMMAND_SET_NONE
|
|
2516
|
-
self._write(0x01) # FLASH_METHOD_UNBUFFERED
|
|
2517
|
-
self._write(0x01) # FLASH_WE_PIN_WR
|
|
2518
|
-
commands = [
|
|
2519
|
-
[ 0x5555, 0xAA ],
|
|
2520
|
-
[ 0x2AAA, 0x55 ],
|
|
2521
|
-
[ 0x5555, 0xA0 ],
|
|
2522
|
-
]
|
|
2523
|
-
for i in range(0, 6):
|
|
2524
|
-
if i >= len(commands):
|
|
2525
|
-
self._write(bytearray(struct.pack(">I", 0)) + bytearray(struct.pack(">H", 0)))
|
|
2526
|
-
else:
|
|
2527
|
-
self._write(bytearray(struct.pack(">I", commands[i][0])) + bytearray(struct.pack(">H", commands[i][1])))
|
|
2528
|
-
self._set_fw_variable("FLASH_COMMANDS_BANK_1", 1)
|
|
2529
|
-
self._write(self.DEVICE_CMD["DMG_SET_BANK_CHANGE_CMD"])
|
|
2530
|
-
self._write(1) # number of commands
|
|
2531
|
-
self._write(bytearray(struct.pack(">I", 0x0006))) # address/value
|
|
2532
|
-
self._write(0) # type = address
|
|
2533
|
-
ret = self._read(1)
|
|
2534
|
-
if ret != 0x01:
|
|
2535
|
-
print("Error in DMG_SET_BANK_CHANGE_CMD:", ret)
|
|
2536
|
-
else:
|
|
2537
|
-
_mbc.EnableMapper()
|
|
2538
|
-
|
|
2539
|
-
if args["rtc"] is True:
|
|
2540
|
-
extra_size = _mbc.GetRTCBufferSize()
|
|
2541
|
-
|
|
2542
|
-
# Check for DMG-MBC5-32M-FLASH
|
|
2543
|
-
self._cart_write(0x2000, 0x00)
|
|
2544
|
-
self._cart_write(0x4000, 0x90)
|
|
2545
|
-
flash_id = self._cart_read(0x4000, 2)
|
|
2546
|
-
if flash_id == bytearray([0xB0, 0x88]): audio_low = True
|
|
2547
|
-
self._cart_write(0x4000, 0xF0)
|
|
2548
|
-
self._cart_write(0x4000, 0xFF)
|
|
2549
|
-
self._cart_write(0x2000, 0x01)
|
|
2550
|
-
if audio_low:
|
|
2551
|
-
dprint("DMG-MBC5-32M-FLASH Development Cartridge detected")
|
|
2552
|
-
self._set_fw_variable("FLASH_WE_PIN", 0x01)
|
|
2553
|
-
self._cart_write(0x4000, 0x00)
|
|
2554
|
-
|
|
2555
|
-
_mbc.EnableRAM(enable=True)
|
|
2556
|
-
|
|
2557
|
-
elif self.MODE == "AGB":
|
|
2558
|
-
self._write(self.DEVICE_CMD["SET_MODE_AGB"])
|
|
2559
|
-
self._write(self.DEVICE_CMD["SET_VOLTAGE_3_3V"])
|
|
2560
|
-
buffer_len = 0x2000
|
|
2561
|
-
if "save_size" in args:
|
|
2562
|
-
save_size = args["save_size"]
|
|
2563
|
-
else:
|
|
2564
|
-
save_size = Util.AGB_Header_Save_Sizes[args["save_type"]]
|
|
2565
|
-
ram_banks = math.ceil(save_size / 0x10000)
|
|
2566
|
-
agb_flash_chip = 0
|
|
2567
|
-
|
|
2568
|
-
if args["save_type"] in (1, 2): # EEPROM
|
|
2569
|
-
if args["mode"] == 3:
|
|
2570
|
-
buffer_len = 0x40
|
|
2571
|
-
else:
|
|
2572
|
-
buffer_len = 0x100
|
|
2573
|
-
elif args["save_type"] in (4, 5): # FLASH
|
|
2574
|
-
empty_data_byte = 0xFF
|
|
2575
|
-
ret = self.ReadFlashSaveID()
|
|
2576
|
-
if ret is False:
|
|
2577
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Couldn’t detect the save data flash chip.", "abortable":False})
|
|
2578
|
-
return False
|
|
2579
|
-
buffer_len = 0x1000
|
|
2580
|
-
(agb_flash_chip, _) = ret
|
|
2581
|
-
if agb_flash_chip in (0xBF5B, 0xFFFF): # Bootlegs
|
|
2582
|
-
buffer_len = 0x800
|
|
2583
|
-
elif args["save_type"] == 6: # DACS
|
|
2584
|
-
self._write(self.DEVICE_CMD["AGB_BOOTUP_SEQUENCE"], wait=True)
|
|
2585
|
-
empty_data_byte = 0xFF
|
|
2586
|
-
# Read Chip ID
|
|
2587
|
-
ram_banks = 1
|
|
2588
|
-
self._cart_write(0, 0x90)
|
|
2589
|
-
flash_id = self._cart_read(0, 4)
|
|
2590
|
-
self._cart_write(0, 0x50)
|
|
2591
|
-
self._cart_write(0, 0xFF)
|
|
2592
|
-
if flash_id != bytearray([ 0xB0, 0x00, 0x9F, 0x00 ]):
|
|
2593
|
-
dprint("Warning: Unknown DACS flash chip ID ({:s})".format(' '.join(format(x, '02X') for x in flash_id)))
|
|
2594
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Couldn’t detect the DACS flash chip.\nUnknown Flash ID: {:s}".format(' '.join(format(x, '02X') for x in flash_id)), "abortable":False})
|
|
2595
|
-
return False
|
|
2596
|
-
buffer_len = 0x2000
|
|
2597
|
-
|
|
2598
|
-
# ↓↓↓ Load commands into firmware
|
|
2599
|
-
flash_cmds = [
|
|
2600
|
-
[ "PA", 0x70 ],
|
|
2601
|
-
[ "PA", 0x10 ],
|
|
2602
|
-
[ "PA", "PD" ]
|
|
2603
|
-
]
|
|
2604
|
-
self._set_fw_variable("FLASH_SHARP_VERIFY_SR", 1)
|
|
2605
|
-
self._write(self.DEVICE_CMD["SET_FLASH_CMD"])
|
|
2606
|
-
self._write(0x02) # FLASH_COMMAND_SET_INTEL
|
|
2607
|
-
self._write(0x01) # FLASH_METHOD_UNBUFFERED
|
|
2608
|
-
self._write(0x00) # unset
|
|
2609
|
-
for i in range(0, 6):
|
|
2610
|
-
if i > len(flash_cmds) - 1: # skip
|
|
2611
|
-
self._write(bytearray(struct.pack(">I", 0)) + bytearray(struct.pack(">H", 0)))
|
|
2612
|
-
else:
|
|
2613
|
-
address = flash_cmds[i][0]
|
|
2614
|
-
value = flash_cmds[i][1]
|
|
2615
|
-
if not isinstance(address, int): address = 0
|
|
2616
|
-
if not isinstance(value, int): value = 0
|
|
2617
|
-
address >>= 1
|
|
2618
|
-
dprint("Setting command #{:d} to 0x{:X}=0x{:X}".format(i, address, value))
|
|
2619
|
-
self._write(bytearray(struct.pack(">I", address)) + bytearray(struct.pack(">H", value)))
|
|
2620
|
-
# ↑↑↑ Load commands into firmware
|
|
2621
|
-
|
|
2622
|
-
# Bootleg mapper
|
|
2623
|
-
if cart_type is not None and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] == 1:
|
|
2624
|
-
sram_5 = struct.unpack("B", bytes(self._cart_read(address=5, length=1, agb_save_flash=True)))[0]
|
|
2625
|
-
self._cart_write(address=5, value=1, sram=True)
|
|
2626
|
-
|
|
2627
|
-
commands = [ # save type commands
|
|
2628
|
-
[ [None], [None] ], # No save
|
|
2629
|
-
[ bytearray([ self.DEVICE_CMD["AGB_CART_READ_EEPROM"], 1]), bytearray([ self.DEVICE_CMD["AGB_CART_WRITE_EEPROM"], 1]) ], # 4K EEPROM
|
|
2630
|
-
[ bytearray([ self.DEVICE_CMD["AGB_CART_READ_EEPROM"], 2]), bytearray([ self.DEVICE_CMD["AGB_CART_WRITE_EEPROM"], 2]) ], # 64K EEPROM
|
|
2631
|
-
[ self.DEVICE_CMD["AGB_CART_READ_SRAM"], self.DEVICE_CMD["AGB_CART_WRITE_SRAM"] ], # 256K SRAM
|
|
2632
|
-
[ self.DEVICE_CMD["AGB_CART_READ_SRAM"], bytearray([ self.DEVICE_CMD["AGB_CART_WRITE_FLASH_DATA"], 2 if agb_flash_chip == 0x1F3D else 1]) ], # 512K FLASH
|
|
2633
|
-
[ self.DEVICE_CMD["AGB_CART_READ_SRAM"], bytearray([ self.DEVICE_CMD["AGB_CART_WRITE_FLASH_DATA"], 1]) ], # 1M FLASH
|
|
2634
|
-
[ False, False ], # 8M DACS
|
|
2635
|
-
[ self.DEVICE_CMD["AGB_CART_READ_SRAM"], self.DEVICE_CMD["AGB_CART_WRITE_SRAM"] ], # 512K SRAM
|
|
2636
|
-
[ self.DEVICE_CMD["AGB_CART_READ_SRAM"], self.DEVICE_CMD["AGB_CART_WRITE_SRAM"] ], # 1M SRAM
|
|
2637
|
-
]
|
|
2638
|
-
command = commands[args["save_type"]][args["mode"] - 2]
|
|
2639
|
-
|
|
2640
|
-
if args["rtc"] is True:
|
|
2641
|
-
extra_size = 0x10
|
|
2642
|
-
|
|
2643
|
-
if args["mode"] == 2: # Backup
|
|
2644
|
-
action = "SAVE_READ"
|
|
2645
|
-
buffer = bytearray()
|
|
2646
|
-
elif args["mode"] == 3: # Restore
|
|
2647
|
-
action = "SAVE_WRITE"
|
|
2648
|
-
self.INFO["save_erase"] = args["erase"]
|
|
2649
|
-
if args["erase"] == True:
|
|
2650
|
-
buffer = bytearray([ empty_data_byte ] * save_size)
|
|
2651
|
-
if self.MODE == "DMG" and _mbc.GetName() == "Xploder GB":
|
|
2652
|
-
buffer[0] = 0x00
|
|
2653
|
-
else:
|
|
2654
|
-
if args["path"] is None:
|
|
2655
|
-
if "buffer" in args:
|
|
2656
|
-
buffer = args["buffer"]
|
|
2657
|
-
else:
|
|
2658
|
-
buffer = self.INFO["data"]
|
|
2659
|
-
else:
|
|
2660
|
-
with open(args["path"], "rb") as f:
|
|
2661
|
-
buffer = bytearray(f.read())
|
|
2662
|
-
|
|
2663
|
-
# Fill too small file
|
|
2664
|
-
if not (self.MODE == "AGB" and args["save_type"] == 6): # Not DACS
|
|
2665
|
-
if args["mode"] == 3:
|
|
2666
|
-
while len(buffer) < save_size:
|
|
2667
|
-
buffer += bytearray(buffer)
|
|
2668
|
-
|
|
2669
|
-
if self.MODE == "AGB" and "ereader" in self.INFO and self.INFO["ereader"] is True: # e-Reader
|
|
2670
|
-
buffer[0xFF80:0x10000] = bytearray([0] * 0x80)
|
|
2671
|
-
buffer[0x1FF80:0x20000] = bytearray([0] * 0x80)
|
|
2672
|
-
|
|
2673
|
-
# Main loop
|
|
2674
|
-
if not (args["mode"] == 2 and "verify_write" in args and args["verify_write"]):
|
|
2675
|
-
self.INFO["action"] = self.ACTIONS[action]
|
|
2676
|
-
self.SetProgress({"action":"INITIALIZE", "method":action, "size":save_size+extra_size})
|
|
2677
|
-
|
|
2678
|
-
buffer_offset = 0
|
|
2679
|
-
for bank in range(0, ram_banks):
|
|
2680
|
-
if self.MODE == "DMG":
|
|
2681
|
-
if _mbc.GetName() == "MBC6" and bank > 7:
|
|
2682
|
-
self._set_fw_variable("DMG_ROM_BANK", bank - 8)
|
|
2683
|
-
(start_address, bank_size) = _mbc.SelectBankFlash(bank - 8)
|
|
2684
|
-
end_address = start_address + bank_size
|
|
2685
|
-
buffer_len = 0x2000
|
|
2686
|
-
if args["mode"] == 3: # Restore
|
|
2687
|
-
if ((buffer_offset - 0x8000) % 0x20000) == 0:
|
|
2688
|
-
dprint("Erasing flash sector at position 0x{:X}".format(buffer_offset))
|
|
2689
|
-
_mbc.EraseFlashSector()
|
|
2690
|
-
elif _mbc.GetName() == "Xploder GB":
|
|
2691
|
-
self._set_fw_variable("DMG_ROM_BANK", bank + 8)
|
|
2692
|
-
(start_address, bank_size) = _mbc.SelectBankRAM(bank)
|
|
2693
|
-
end_address = min(save_size, start_address + bank_size)
|
|
2694
|
-
else:
|
|
2695
|
-
self._set_fw_variable("DMG_WRITE_CS_PULSE", 1 if _mbc.WriteWithCSPulse() else 0)
|
|
2696
|
-
(start_address, bank_size) = _mbc.SelectBankRAM(bank)
|
|
2697
|
-
end_address = min(save_size, start_address + bank_size)
|
|
2698
|
-
elif self.MODE == "AGB":
|
|
2699
|
-
start_address = 0
|
|
2700
|
-
bank_size = 0x10000
|
|
2701
|
-
if args["save_type"] == 6: # DACS
|
|
2702
|
-
bank_size = min(save_size, 0x100000)
|
|
2703
|
-
buffer_len = 0x2000
|
|
2704
|
-
|
|
2705
|
-
end_address = min(save_size, bank_size)
|
|
2706
|
-
|
|
2707
|
-
if save_size > bank_size:
|
|
2708
|
-
if args["save_type"] == 8 or agb_flash_chip in (0xBF5B, 0xFFFF): # Bootleg 1M
|
|
2709
|
-
dprint("Switching to bootleg save bank {:d}".format(bank))
|
|
2710
|
-
self._cart_write(0x1000000, bank)
|
|
2711
|
-
elif args["save_type"] == 5: # FLASH 1M
|
|
2712
|
-
dprint("Switching to FLASH bank {:d}".format(bank))
|
|
2713
|
-
cmds = [
|
|
2714
|
-
[ 0x5555, 0xAA ],
|
|
2715
|
-
[ 0x2AAA, 0x55 ],
|
|
2716
|
-
[ 0x5555, 0xB0 ],
|
|
2717
|
-
[ 0, bank ]
|
|
2718
|
-
]
|
|
2719
|
-
self._cart_write_flash(cmds)
|
|
2720
|
-
else:
|
|
2721
|
-
dprint("Unknown bank switching method")
|
|
2722
|
-
time.sleep(0.05)
|
|
2723
|
-
|
|
2724
|
-
max_length = 64
|
|
2725
|
-
dprint("start_address=0x{:X}, end_address=0x{:X}, buffer_len=0x{:X}, buffer_offset=0x{:X}".format(start_address, end_address, buffer_len, buffer_offset))
|
|
2726
|
-
pos = start_address
|
|
2727
|
-
while pos < end_address:
|
|
2728
|
-
if self.CANCEL:
|
|
2729
|
-
cancel_args = {"action":"ABORT", "abortable":False}
|
|
2730
|
-
cancel_args.update(self.CANCEL_ARGS)
|
|
2731
|
-
self.CANCEL_ARGS = {}
|
|
2732
|
-
self.ERROR_ARGS = {}
|
|
2733
|
-
self.SetProgress(cancel_args)
|
|
2734
|
-
if self.CanPowerCycleCart(): self.CartPowerCycle()
|
|
2735
|
-
return
|
|
2736
|
-
|
|
2737
|
-
if args["mode"] == 2: # Backup
|
|
2738
|
-
in_temp = [None] * 2
|
|
2739
|
-
if "verify_read" in args and args["verify_read"]: # Read twice for detecting instabilities
|
|
2740
|
-
xe = 2
|
|
2741
|
-
else:
|
|
2742
|
-
xe = 1
|
|
2743
|
-
for x in range(0, xe):
|
|
2744
|
-
if x == 1:
|
|
2745
|
-
self.NO_PROG_UPDATE = True
|
|
2746
|
-
else:
|
|
2747
|
-
self.NO_PROG_UPDATE = False
|
|
2748
|
-
|
|
2749
|
-
if self.MODE == "DMG" and _mbc.GetName() == "MBC7":
|
|
2750
|
-
in_temp[x] = self.ReadRAM_MBC7(address=pos, length=buffer_len)
|
|
2751
|
-
elif self.MODE == "DMG" and _mbc.GetName() == "MBC6" and bank > 7: # MBC6 flash save memory
|
|
2752
|
-
in_temp[x] = self.ReadROM(address=pos, length=buffer_len, skip_init=False, max_length=max_length)
|
|
2753
|
-
elif self.MODE == "DMG" and _mbc.GetName() == "TAMA5":
|
|
2754
|
-
in_temp[x] = self.ReadRAM_TAMA5()
|
|
2755
|
-
elif self.MODE == "DMG" and _mbc.GetName() == "Xploder GB":
|
|
2756
|
-
in_temp[x] = self.ReadROM(address=0x20000+pos, length=buffer_len, skip_init=False, max_length=max_length)
|
|
2757
|
-
elif self.MODE == "AGB" and args["save_type"] in (1, 2): # EEPROM
|
|
2758
|
-
in_temp[x] = self.ReadRAM(address=int(pos/8), length=buffer_len, command=command, max_length=max_length)
|
|
2759
|
-
elif self.MODE == "AGB" and args["save_type"] == 6: # DACS
|
|
2760
|
-
in_temp[x] = self.ReadROM(address=0x1F00000+pos, length=buffer_len, skip_init=False, max_length=max_length)
|
|
2761
|
-
elif self.MODE == "DMG" and _mbc.GetName() == "MBC2":
|
|
2762
|
-
in_temp[x] = self.ReadRAM(address=pos, length=buffer_len, command=command, max_length=max_length)
|
|
2763
|
-
for i in range(0, len(in_temp[x])):
|
|
2764
|
-
in_temp[x][i] = in_temp[x][i] & 0x0F
|
|
2765
|
-
else:
|
|
2766
|
-
in_temp[x] = self.ReadRAM(address=pos, length=buffer_len, command=command, max_length=max_length)
|
|
2767
|
-
|
|
2768
|
-
if len(in_temp[x]) != buffer_len:
|
|
2769
|
-
if (max_length >> 1) < 64:
|
|
2770
|
-
dprint("Received 0x{:X} bytes instead of 0x{:X} bytes from the device at position 0x{:X}!".format(len(in_temp[x]), buffer_len, len(buffer)))
|
|
2771
|
-
max_length = 64
|
|
2772
|
-
else:
|
|
2773
|
-
dprint("Received 0x{:X} bytes instead of 0x{:X} bytes from the device at position 0x{:X}! Decreasing maximum transfer buffer length to 0x{:X}.".format(len(in_temp[x]), buffer_len, len(buffer), max_length >> 1))
|
|
2774
|
-
max_length >>= 1
|
|
2775
|
-
self.DEVICE.reset_input_buffer()
|
|
2776
|
-
self.DEVICE.reset_output_buffer()
|
|
2777
|
-
continue
|
|
2778
|
-
|
|
2779
|
-
if xe == 2 and in_temp[0] != in_temp[1]:
|
|
2780
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Failed to read save data consistently. Please ensure that the cartridge contacts are clean.", "abortable":False})
|
|
2781
|
-
return False
|
|
2782
|
-
|
|
2783
|
-
temp = in_temp[0]
|
|
2784
|
-
buffer += temp
|
|
2785
|
-
self.SetProgress({"action":"UPDATE_POS", "pos":len(buffer)})
|
|
2786
|
-
|
|
2787
|
-
elif args["mode"] == 3: # Restore
|
|
2788
|
-
if self.MODE == "DMG" and _mbc.GetName() == "MBC7":
|
|
2789
|
-
self.WriteEEPROM_MBC7(address=pos, buffer=buffer[buffer_offset:buffer_offset+buffer_len])
|
|
2790
|
-
elif self.MODE == "DMG" and _mbc.GetName() == "MBC6" and bank > 7: # MBC6 flash save memory
|
|
2791
|
-
if self.FW["pcb_ver"] in (5, 6, 101):
|
|
2792
|
-
self.WriteROM(address=pos, buffer=buffer[buffer_offset:buffer_offset+buffer_len])
|
|
2793
|
-
self._cart_write(pos + buffer_len - 1, 0xF0)
|
|
2794
|
-
else:
|
|
2795
|
-
self.WriteFlash_MBC6(address=pos, buffer=buffer[buffer_offset:buffer_offset+buffer_len], mapper=_mbc)
|
|
2796
|
-
elif self.MODE == "DMG" and _mbc.GetName() == "TAMA5":
|
|
2797
|
-
self.WriteRAM_TAMA5(buffer=buffer[buffer_offset:buffer_offset+buffer_len])
|
|
2798
|
-
elif self.MODE == "DMG" and _mbc.GetName() == "Xploder GB":
|
|
2799
|
-
self.WriteROM_DMG_EEPROM(address=pos, buffer=buffer[buffer_offset:buffer_offset+buffer_len], bank=bank+8)
|
|
2800
|
-
elif self.MODE == "AGB" and args["save_type"] in (1, 2): # EEPROM
|
|
2801
|
-
self.WriteRAM(address=int(pos/8), buffer=buffer[buffer_offset:buffer_offset+buffer_len], command=command)
|
|
2802
|
-
elif self.MODE == "AGB" and args["save_type"] in (4, 5): # FLASH
|
|
2803
|
-
sector_address = pos % 0x10000
|
|
2804
|
-
if agb_flash_chip == 0x1F3D: # Atmel AT29LV512
|
|
2805
|
-
self.WriteRAM(address=int(pos/128), buffer=buffer[buffer_offset:buffer_offset+buffer_len], command=command)
|
|
2806
|
-
else:
|
|
2807
|
-
dprint("pos=0x{:X}, sector_address={:d}".format(pos, sector_address))
|
|
2808
|
-
cmds = [
|
|
2809
|
-
[ 0x5555, 0xAA ],
|
|
2810
|
-
[ 0x2AAA, 0x55 ],
|
|
2811
|
-
[ 0x5555, 0x80 ],
|
|
2812
|
-
[ 0x5555, 0xAA ],
|
|
2813
|
-
[ 0x2AAA, 0x55 ],
|
|
2814
|
-
[ sector_address, 0x30 ]
|
|
2815
|
-
]
|
|
2816
|
-
self._cart_write_flash(cmds)
|
|
2817
|
-
sr = 0
|
|
2818
|
-
lives = 50
|
|
2819
|
-
while True:
|
|
2820
|
-
time.sleep(0.01)
|
|
2821
|
-
sr = self._cart_read(sector_address, agb_save_flash=True)
|
|
2822
|
-
dprint("Data Check: 0x{:X} == 0xFFFF? {:s}".format(sr, str(sr == 0xFFFF)))
|
|
2823
|
-
if sr == 0xFFFF: break
|
|
2824
|
-
lives -= 1
|
|
2825
|
-
if lives == 0:
|
|
2826
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"Accessing the save data flash chip failed. Please make sure you selected the correct save type. If you are using a reproduction cartridge, check if it really is equipped with a flash chip for save data, or if it uses SRAM for save data instead.", "abortable":False})
|
|
2827
|
-
return False
|
|
2828
|
-
if buffer[buffer_offset:buffer_offset+buffer_len] != bytearray([0xFF] * buffer_len):
|
|
2829
|
-
if ("ereader" in self.INFO and self.INFO["ereader"] is True and sector_address == 0xF000):
|
|
2830
|
-
self.WriteRAM(address=pos, buffer=buffer[buffer_offset:buffer_offset+0xF80], command=command, max_length=0x80)
|
|
2831
|
-
else:
|
|
2832
|
-
self.WriteRAM(address=pos, buffer=buffer[buffer_offset:buffer_offset+buffer_len], command=command)
|
|
2833
|
-
elif self.MODE == "AGB" and args["save_type"] == 6: # DACS
|
|
2834
|
-
sector_address = pos + 0x1F00000
|
|
2835
|
-
if sector_address in (0x1F00000, 0x1F10000, 0x1F20000, 0x1F30000, 0x1F40000, 0x1F50000, 0x1F60000, 0x1F70000, 0x1F80000, 0x1F90000, 0x1FA0000, 0x1FB0000, 0x1FC0000, 0x1FD0000, 0x1FE0000, 0x1FF0000, 0x1FF2000, 0x1FF4000, 0x1FF6000, 0x1FF8000, 0x1FFA000, 0x1FFC000):
|
|
2836
|
-
dprint("DACS: Now at sector 0x{:X}".format(sector_address))
|
|
2837
|
-
cmds = [
|
|
2838
|
-
[ # Erase Sector
|
|
2839
|
-
[ 0, 0x50 ],
|
|
2840
|
-
[ sector_address, 0x20 ],
|
|
2841
|
-
[ sector_address, 0xD0 ],
|
|
2842
|
-
]
|
|
2843
|
-
]
|
|
2844
|
-
if sector_address == 0x1F00000: # First write
|
|
2845
|
-
temp = [
|
|
2846
|
-
[ # Unlock
|
|
2847
|
-
[ 0, 0x50 ],
|
|
2848
|
-
[ 0, 0x60 ],
|
|
2849
|
-
[ 0, 0xD0 ],
|
|
2850
|
-
]
|
|
2851
|
-
]
|
|
2852
|
-
temp.extend(cmds)
|
|
2853
|
-
cmds = temp
|
|
2854
|
-
elif sector_address == 0x1FFC000: # Boot sector
|
|
2855
|
-
temp = [
|
|
2856
|
-
[ # Unlock 1
|
|
2857
|
-
[ 0, 0x50 ],
|
|
2858
|
-
[ 0, 0x60 ],
|
|
2859
|
-
[ 0, 0xD0 ],
|
|
2860
|
-
],
|
|
2861
|
-
[ # Unlock 2
|
|
2862
|
-
[ 0, 0x50 ],
|
|
2863
|
-
[ sector_address, 0x60 ],
|
|
2864
|
-
[ sector_address, 0xDC ],
|
|
2865
|
-
]
|
|
2866
|
-
]
|
|
2867
|
-
temp.extend(cmds)
|
|
2868
|
-
cmds = temp
|
|
2869
|
-
|
|
2870
|
-
for cmd in cmds:
|
|
2871
|
-
dprint("Executing DACS commands:", cmd)
|
|
2872
|
-
self._cart_write_flash(commands=cmd, flashcart=True)
|
|
2873
|
-
sr = 0
|
|
2874
|
-
lives = 20
|
|
2875
|
-
while True:
|
|
2876
|
-
time.sleep(0.1)
|
|
2877
|
-
sr = struct.unpack("<H", self._cart_read(sector_address, 2))[0]
|
|
2878
|
-
dprint("DACS: Status Register Check: 0x{:X} == 0x80? {:s}".format(sr, str(sr & 0xE0 == 0x80)))
|
|
2879
|
-
if sr & 0xE0 == 0x80: break
|
|
2880
|
-
lives -= 1
|
|
2881
|
-
if lives == 0:
|
|
2882
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"An error occured while writing to the DACS cartridge. Please make sure that the cartridge contacts are clean, re-connect the device and try again from the beginning.", "abortable":False})
|
|
2883
|
-
return False
|
|
2884
|
-
if sector_address < 0x1FFE000:
|
|
2885
|
-
dprint("DACS: Writing to area 0x{:X}–0x{:X}".format(0x1F00000+pos, 0x1F00000+pos+buffer_len-1))
|
|
2886
|
-
self.WriteROM(address=0x1F00000+pos, buffer=buffer[buffer_offset:buffer_offset+buffer_len])
|
|
2887
|
-
else:
|
|
2888
|
-
dprint("DACS: Skipping read-only area 0x{:X}–0x{:X}".format(0x1F00000+pos, 0x1F00000+pos+buffer_len-1))
|
|
2889
|
-
else:
|
|
2890
|
-
self.WriteRAM(address=pos, buffer=buffer[buffer_offset:buffer_offset+buffer_len], command=command)
|
|
2891
|
-
self.SetProgress({"action":"UPDATE_POS", "pos":buffer_offset+buffer_len})
|
|
2892
|
-
|
|
2893
|
-
pos += buffer_len
|
|
2894
|
-
buffer_offset += buffer_len
|
|
2895
|
-
|
|
2896
|
-
verified = False
|
|
2897
|
-
if args["mode"] == 2: # Backup
|
|
2898
|
-
self.INFO["transferred"] = len(buffer)
|
|
2899
|
-
rtc_buffer = None
|
|
2900
|
-
# Real Time Clock
|
|
2901
|
-
if args["rtc"] is True:
|
|
2902
|
-
self.NO_PROG_UPDATE = True
|
|
2903
|
-
if self.MODE == "DMG" and args["rtc"] is True:
|
|
2904
|
-
_mbc.LatchRTC()
|
|
2905
|
-
rtc_buffer = _mbc.ReadRTC()
|
|
2906
|
-
elif self.MODE == "AGB":
|
|
2907
|
-
_agb_gpio = AGB_GPIO(args={"rtc":True}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle)
|
|
2908
|
-
rtc_buffer = _agb_gpio.ReadRTC()
|
|
2909
|
-
self.NO_PROG_UPDATE = False
|
|
2910
|
-
self.SetProgress({"action":"UPDATE_POS", "pos":len(buffer)+len(rtc_buffer)})
|
|
2911
|
-
|
|
2912
|
-
# Bootleg mapper
|
|
2913
|
-
if self.MODE == "AGB" and cart_type is not None and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] == 1:
|
|
2914
|
-
buffer[5] = sram_5
|
|
2915
|
-
|
|
2916
|
-
if args["path"] is not None:
|
|
2917
|
-
if self.MODE == "DMG" and _mbc.GetName() == "MBC2":
|
|
2918
|
-
for i in range(0, len(buffer)):
|
|
2919
|
-
buffer[i] = buffer[i] & 0x0F
|
|
2920
|
-
file = open(args["path"], "wb")
|
|
2921
|
-
file.write(buffer)
|
|
2922
|
-
if rtc_buffer is not None:
|
|
2923
|
-
file.write(rtc_buffer)
|
|
2924
|
-
file.close()
|
|
2925
|
-
else:
|
|
2926
|
-
self.INFO["data"] = buffer
|
|
2927
|
-
|
|
2928
|
-
self.INFO["file_crc32"] = zlib.crc32(buffer) & 0xFFFFFFFF
|
|
2929
|
-
self.INFO["file_sha1"] = hashlib.sha1(buffer).hexdigest()
|
|
2930
|
-
|
|
2931
|
-
if "verify_write" in args and args["verify_write"] not in (None, False):
|
|
2932
|
-
return True
|
|
2933
|
-
|
|
2934
|
-
elif args["mode"] == 3: # Restore
|
|
2935
|
-
self.INFO["transferred"] = len(buffer)
|
|
2936
|
-
if args["rtc"] is True:
|
|
2937
|
-
advance = "rtc_advance" in args and args["rtc_advance"]
|
|
2938
|
-
self.SetProgress({"action":"UPDATE_RTC", "method":"write"})
|
|
2939
|
-
if self.MODE == "DMG" and args["rtc"] is True:
|
|
2940
|
-
_mbc.WriteRTC(buffer[-_mbc.GetRTCBufferSize():], advance=advance)
|
|
2941
|
-
elif self.MODE == "AGB":
|
|
2942
|
-
_agb_gpio = AGB_GPIO(args={"rtc":True}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle)
|
|
2943
|
-
_agb_gpio.WriteRTC(buffer[-0x10:], advance=advance)
|
|
2944
|
-
self.SetProgress({"action":"UPDATE_POS", "pos":len(buffer)})
|
|
2945
|
-
|
|
2946
|
-
# ↓↓↓ Write verify
|
|
2947
|
-
if "verify_write" in args and args["verify_write"] is True and args["erase"] is not True:
|
|
2948
|
-
if self.MODE == "DMG": _mbc.SelectBankRAM(0)
|
|
2949
|
-
self.SetProgress({"action":"INITIALIZE", "method":"SAVE_WRITE_VERIFY", "size":buffer_offset})
|
|
2950
|
-
|
|
2951
|
-
verify_args = copy.copy(args)
|
|
2952
|
-
start_address = 0
|
|
2953
|
-
end_address = buffer_offset
|
|
2954
|
-
if self.MODE == "AGB" and args["save_type"] == 6: # DACS
|
|
2955
|
-
end_address = min(len(buffer), 0xFE000)
|
|
2956
|
-
|
|
2957
|
-
path = args["path"] # backup path
|
|
2958
|
-
verify_args.update({"mode":2, "verify_write":buffer, "path":None})
|
|
2959
|
-
self.ReadROM(0, 4) # dummy read
|
|
2960
|
-
self.INFO["data"] = None
|
|
2961
|
-
if self._BackupRestoreRAM(verify_args) is False: return
|
|
2962
|
-
|
|
2963
|
-
if args["mbc"] == 6: # MBC2
|
|
2964
|
-
for i in range(0, len(self.INFO["data"])):
|
|
2965
|
-
self.INFO["data"][i] &= 0x0F
|
|
2966
|
-
buffer[i] &= 0x0F
|
|
2967
|
-
|
|
2968
|
-
args["path"] = path # restore path
|
|
2969
|
-
if self.CANCEL is True:
|
|
2970
|
-
pass
|
|
2971
|
-
elif (self.INFO["data"][:end_address] != buffer[:end_address]):
|
|
2972
|
-
msg = ""
|
|
2973
|
-
count = 0
|
|
2974
|
-
for i in range(0, len(self.INFO["data"])):
|
|
2975
|
-
if i >= len(buffer): break
|
|
2976
|
-
data1 = self.INFO["data"][i]
|
|
2977
|
-
data2 = buffer[:end_address][i]
|
|
2978
|
-
if data1 != data2:
|
|
2979
|
-
count += 1
|
|
2980
|
-
if len(msg.split("\n")) <= 10:
|
|
2981
|
-
msg += "- 0x{:06X}: {:02X}≠{:02X}\n".format(i, data1, data2)
|
|
2982
|
-
elif len(msg.split("\n")) == 11:
|
|
2983
|
-
msg += "(more than 10 differences found)\n"
|
|
2984
|
-
else:
|
|
2985
|
-
pass
|
|
2986
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"The save data was written completely, but {:d} byte(s) ({:.2f}%) didn’t pass the verification check.\n\n{:s}".format(count, (count / len(self.INFO["data"]) * 100), msg[:-1]), "abortable":False})
|
|
2987
|
-
return False
|
|
2988
|
-
else:
|
|
2989
|
-
verified = True
|
|
2990
|
-
# ↑↑↑ Write verify
|
|
2991
|
-
|
|
2992
|
-
if self.MODE == "DMG":
|
|
2993
|
-
_mbc.SelectBankRAM(0)
|
|
2994
|
-
_mbc.EnableRAM(enable=False)
|
|
2995
|
-
self._set_fw_variable("DMG_READ_CS_PULSE", 0)
|
|
2996
|
-
if audio_low: self._set_fw_variable("FLASH_WE_PIN", 0x02)
|
|
2997
|
-
self._write(self.DEVICE_CMD["SET_ADDR_AS_INPUTS"]) # Prevent hotplugging corruptions on rare occasions
|
|
2998
|
-
|
|
2999
|
-
# Bootleg mapper
|
|
3000
|
-
elif self.MODE == "AGB" and cart_type is not None and "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] == 1:
|
|
3001
|
-
self._cart_write(address=5, value=0, sram=True)
|
|
3002
|
-
self._cart_write(address=5, value=sram_5, sram=True)
|
|
3003
|
-
|
|
3004
|
-
# Clean up
|
|
3005
|
-
self.INFO["last_action"] = self.INFO["action"]
|
|
3006
|
-
self.INFO["action"] = None
|
|
3007
|
-
self.INFO["last_path"] = args["path"]
|
|
3008
|
-
self.SetProgress({"action":"FINISHED", "verified":verified})
|
|
3009
|
-
return True
|
|
3010
|
-
|
|
3011
|
-
def _FlashROM(self, args):
|
|
3012
|
-
self.FAST_READ = True
|
|
3013
|
-
if "buffer" in args:
|
|
3014
|
-
data_import = args["buffer"]
|
|
3015
|
-
else:
|
|
3016
|
-
with open(args["path"], "rb") as file: data_import = bytearray(file.read())
|
|
3017
|
-
|
|
3018
|
-
flash_offset = 0 # Batteryless SRAM or Transfer Resume
|
|
3019
|
-
if "flash_offset" in args:
|
|
3020
|
-
flash_offset = args["flash_offset"]
|
|
3021
|
-
if "start_addr" in args and args["start_addr"] > 0:
|
|
3022
|
-
data_import = bytearray(b'\xFF' * args["start_addr"]) + data_import
|
|
3023
|
-
|
|
3024
|
-
# Pad data
|
|
3025
|
-
if len(data_import) > 0:
|
|
3026
|
-
if len(data_import) < 0x400:
|
|
3027
|
-
data_import += bytearray([0xFF] * (0x400 - len(data_import)))
|
|
3028
|
-
if len(data_import) % 0x8000 > 0:
|
|
3029
|
-
data_import += bytearray([0xFF] * (0x8000 - len(data_import) % 0x8000))
|
|
3030
|
-
|
|
3031
|
-
# Skip writing the last 256 bytes of 32 MiB ROMs with EEPROM save type
|
|
3032
|
-
if self.MODE == "AGB" and len(data_import) == 0x2000000:
|
|
3033
|
-
temp_ver = "N/A"
|
|
3034
|
-
ids = [ b"SRAM_", b"EEPROM_V", b"FLASH_V", b"FLASH512_V", b"FLASH1M_V", b"AGB_8MDACS_DL_V" ]
|
|
3035
|
-
for id in ids:
|
|
3036
|
-
temp_pos = data_import.find(id)
|
|
3037
|
-
if temp_pos > 0:
|
|
3038
|
-
temp_ver = data_import[temp_pos:temp_pos+0x20]
|
|
3039
|
-
temp_ver = temp_ver[:temp_ver.index(0x00)].decode("ascii", "replace")
|
|
3040
|
-
break
|
|
3041
|
-
if "EEPROM" in temp_ver:
|
|
3042
|
-
print("Note: The last 256 bytes of this 32 MiB ROM will not be written as this area is reserved by the EEPROM save type.")
|
|
3043
|
-
data_import = data_import[:0x1FFFF00]
|
|
3044
|
-
|
|
3045
|
-
# Fix bootlogo and header
|
|
3046
|
-
if "fix_bootlogo" in args and isinstance(args["fix_bootlogo"], bytearray):
|
|
3047
|
-
dstr = ''.join(format(x, '02X') for x in args["fix_bootlogo"])
|
|
3048
|
-
dprint("Replacing bootlogo data with", dstr)
|
|
3049
|
-
if self.MODE == "DMG":
|
|
3050
|
-
data_import[0x104:0x134] = args["fix_bootlogo"]
|
|
3051
|
-
elif self.MODE == "AGB":
|
|
3052
|
-
data_import[0x04:0xA0] = args["fix_bootlogo"]
|
|
3053
|
-
if "fix_header" in args and args["fix_header"]:
|
|
3054
|
-
dprint("Fixing header checksums")
|
|
3055
|
-
if self.MODE == "DMG":
|
|
3056
|
-
temp = RomFileDMG(data_import[0:0x200]).FixHeader()
|
|
3057
|
-
elif self.MODE == "AGB":
|
|
3058
|
-
temp = RomFileAGB(data_import[0:0x200]).FixHeader()
|
|
3059
|
-
data_import[0:0x200] = temp
|
|
3060
|
-
|
|
3061
|
-
supported_carts = list(self.SUPPORTED_CARTS[self.MODE].values())
|
|
3062
|
-
cart_type = copy.deepcopy(supported_carts[args["cart_type"]])
|
|
3063
|
-
if cart_type == "RETAIL": return False # Generic ROM Cartridge is not flashable
|
|
3064
|
-
|
|
3065
|
-
# Special carts
|
|
3066
|
-
if "Retrostage GameBoy Blaster" in cart_type["names"]:
|
|
3067
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"The Retrostage GameBoy Blaster cartridge is currently not fully supported by FlashGBX. However, you can use the insideGadgets “Flasher” software available from <a href=\"https://www.gbxcart.com/\">https://www.gbxcart.com/</a> to flash this cartridge.", "abortable":False})
|
|
3068
|
-
return False
|
|
3069
|
-
elif "insideGadgets Power Cart 1 MB, 128 KB SRAM" in cart_type["names"]:
|
|
3070
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"The insideGadgets Power Cart is currently not fully supported by FlashGBX. However, you can use the dedicated insideGadgets “iG Power Cart Programs” software available from <a href=\"https://www.gbxcart.com/\">https://www.gbxcart.com/</a> to flash this cartridge.", "abortable":False})
|
|
3071
|
-
return False
|
|
3072
|
-
elif "power_cycle" in cart_type and cart_type["power_cycle"] is True and not self.CanPowerCycleCart():
|
|
3073
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type is not flashable using FlashGBX and your GBxCart RW hardware revision due to missing cartridge power cycling support.", "abortable":False})
|
|
3074
|
-
return False
|
|
3075
|
-
# Special carts
|
|
3076
|
-
# Firmware check L1
|
|
3077
|
-
if cart_type["type"] == "DMG" and "write_pin" in cart_type and cart_type["write_pin"] == "WR+RESET":
|
|
3078
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type is currently not supported by FlashGBX. Please try the insideGadgets GBxCart RW flasher software available from <a href=\"https://www.gbxcart.com/\">https://www.gbxcart.com/</a> instead.", "abortable":False})
|
|
3079
|
-
return False
|
|
3080
|
-
if (self.FW["pcb_ver"] not in (5, 6, 101) or self.FW["fw_ver"] < 2) and ("pulse_reset_after_write" in cart_type and cart_type["pulse_reset_after_write"] is True):
|
|
3081
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type is not supported by FlashGBX using your GBxCart RW hardware revision and/or firmware version. You can also try the official GBxCart RW firmware and interface software available from <a href=\"https://www.gbxcart.com/\">https://www.gbxcart.com/</a> instead.", "abortable":False})
|
|
3082
|
-
return False
|
|
3083
|
-
# Firmware check L1
|
|
3084
|
-
# Firmware check L2
|
|
3085
|
-
if (self.FW["pcb_ver"] not in (5, 6, 101) or self.FW["fw_ver"] < 3) and ("command_set" in cart_type and cart_type["command_set"] == "SHARP") and ("buffer_write" in cart_type["commands"]):
|
|
3086
|
-
if self.FW["pcb_ver"] in (5, 6):
|
|
3087
|
-
print("Note: Update your GBxCart RW firmware to version L3 or higher for a better transfer rate with this cartridge.")
|
|
3088
|
-
del(cart_type["commands"]["buffer_write"])
|
|
3089
|
-
# Firmware check L2
|
|
3090
|
-
# Firmware check L5
|
|
3091
|
-
if (self.FW["pcb_ver"] not in (5, 6, 101) or self.FW["fw_ver"] < 5) and ("flash_commands_on_bank_1" in cart_type and cart_type["flash_commands_on_bank_1"] is True):
|
|
3092
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L5 on the GBxCart RW v1.4 hardware revision or newer. It may also work with older hardware revisions using the official firmware and insideGadgets flasher software available from <a href=\"https://www.gbxcart.com/\">https://www.gbxcart.com/</a>.", "abortable":False})
|
|
3093
|
-
return False
|
|
3094
|
-
if (self.FW["pcb_ver"] not in (5, 6, 101) or self.FW["fw_ver"] < 5) and ("double_die" in cart_type and cart_type["double_die"] is True):
|
|
3095
|
-
if self.FW["pcb_ver"] in (5, 6):
|
|
3096
|
-
print("Note: Update your GBxCart RW firmware to version L5 or higher for a better transfer rate with this cartridge.")
|
|
3097
|
-
del(cart_type["commands"]["buffer_write"])
|
|
3098
|
-
# Firmware check L5
|
|
3099
|
-
# Firmware check L8
|
|
3100
|
-
if self.FW["fw_ver"] < 8 and "enable_pullups" in cart_type and cart_type["enable_pullups"] is True:
|
|
3101
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type requires at least firmware version L8 on the GBxCart RW v1.4 hardware revision or newer. It may also work with older hardware revisions using the official firmware and insideGadgets flasher software available from <a href=\"https://www.gbxcart.com/\">https://www.gbxcart.com/</a>.", "abortable":False})
|
|
3102
|
-
return False
|
|
3103
|
-
# Firmware check L8
|
|
3104
|
-
|
|
3105
|
-
cart_type["_index"] = 0
|
|
3106
|
-
for i in range(0, len(list(self.SUPPORTED_CARTS[self.MODE].keys()))):
|
|
3107
|
-
if i == args["cart_type"]:
|
|
3108
|
-
try:
|
|
3109
|
-
cart_type["_index"] = cart_type["names"].index(list(self.SUPPORTED_CARTS[self.MODE].keys())[i])
|
|
3110
|
-
except:
|
|
3111
|
-
pass
|
|
3112
|
-
|
|
3113
|
-
fc_fncptr = {
|
|
3114
|
-
"cart_write_fncptr":self._cart_write,
|
|
3115
|
-
"cart_write_fast_fncptr":self._cart_write_flash,
|
|
3116
|
-
"cart_read_fncptr":self.ReadROM,
|
|
3117
|
-
"cart_powercycle_fncptr":self.CartPowerCycle,
|
|
3118
|
-
"progress_fncptr":self.SetProgress,
|
|
3119
|
-
"set_we_pin_wr":self._set_we_pin_wr,
|
|
3120
|
-
"set_we_pin_audio":self._set_we_pin_audio,
|
|
3121
|
-
}
|
|
3122
|
-
if cart_type["command_set"] == "GBMEMORY":
|
|
3123
|
-
flashcart = Flashcart_DMG_MMSA(config=cart_type, fncptr=fc_fncptr)
|
|
3124
|
-
else:
|
|
3125
|
-
flashcart = Flashcart(config=cart_type, fncptr=fc_fncptr)
|
|
3126
|
-
|
|
3127
|
-
rumble = "rumble" in flashcart.CONFIG and flashcart.CONFIG["rumble"] is True
|
|
3128
|
-
|
|
3129
|
-
# ↓↓↓ Set Voltage
|
|
3130
|
-
if args["override_voltage"] is not False:
|
|
3131
|
-
if args["override_voltage"] == 5:
|
|
3132
|
-
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
|
3133
|
-
else:
|
|
3134
|
-
self._write(self.DEVICE_CMD["SET_VOLTAGE_3_3V"])
|
|
3135
|
-
elif flashcart.GetVoltage() == 3.3:
|
|
3136
|
-
self._write(self.DEVICE_CMD["SET_VOLTAGE_3_3V"])
|
|
3137
|
-
elif flashcart.GetVoltage() == 5:
|
|
3138
|
-
self._write(self.DEVICE_CMD["SET_VOLTAGE_5V"])
|
|
3139
|
-
# ↑↑↑ Set Voltage
|
|
3140
|
-
|
|
3141
|
-
# ↓↓↓ Pad data for full chip erase on sector erase mode
|
|
3142
|
-
if not flashcart.SupportsChipErase() and flashcart.SupportsSectorErase() and args["prefer_chip_erase"]:
|
|
3143
|
-
print("{:s}Note: Chip erase mode is not supported for this flash cartridge type. Sector erase mode will be used.{:s}\n".format(ANSI.YELLOW, ANSI.RESET))
|
|
3144
|
-
flash_size = flashcart.GetFlashSize()
|
|
3145
|
-
if flash_size is not False and len(data_import) < flash_size:
|
|
3146
|
-
# Pad with FF till the end (with MemoryError fix)
|
|
3147
|
-
pad_len = flash_size - len(data_import)
|
|
3148
|
-
while pad_len > 0x2000000:
|
|
3149
|
-
data_import += bytearray([0xFF] * 0x2000000)
|
|
3150
|
-
pad_len -= 0x2000000
|
|
3151
|
-
data_import += bytearray([0xFF] * (flash_size - len(data_import)))
|
|
3152
|
-
# ↑↑↑ Pad data for full chip erase on sector erase mode
|
|
3153
|
-
|
|
3154
|
-
# ↓↓↓ Flashcart configuration
|
|
3155
|
-
_mbc = None
|
|
3156
|
-
errmsg_mbc_selection = ""
|
|
3157
|
-
if self.MODE == "DMG":
|
|
3158
|
-
self._write(self.DEVICE_CMD["SET_MODE_DMG"])
|
|
3159
|
-
mbc = flashcart.GetMBC()
|
|
3160
|
-
if mbc is not False and isinstance(mbc, int):
|
|
3161
|
-
args["mbc"] = mbc
|
|
3162
|
-
dprint("Using forced mapper type 0x{:02X} for flashing".format(mbc))
|
|
3163
|
-
elif mbc == "manual":
|
|
3164
|
-
dprint("Using manually selected mapper type 0x{:02X} for flashing".format(args["mbc"]))
|
|
3165
|
-
else:
|
|
3166
|
-
args["mbc"] = 0
|
|
3167
|
-
dprint("Using default mapper type 0x{:02X} for flashing".format(args["mbc"]))
|
|
3168
|
-
if args["mbc"] == 0: args["mbc"] = 0x19 # MBC5 default
|
|
3169
|
-
|
|
3170
|
-
if not self.IsSupportedMbc(args["mbc"]):
|
|
3171
|
-
msg = "This cartridge uses a mapper that is not supported by {:s} using your {:s} device. An updated hardware revision is required.".format(Util.APPNAME, self.GetFullName())
|
|
3172
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":msg, "abortable":False})
|
|
3173
|
-
return False
|
|
3174
|
-
|
|
3175
|
-
_mbc = DMG_MBC().GetInstance(args=args, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycle, clk_toggle_fncptr=self._clk_toggle)
|
|
3176
|
-
|
|
3177
|
-
self._set_fw_variable("FLASH_PULSE_RESET", 1 if flashcart.PulseResetAfterWrite() else 0)
|
|
3178
|
-
|
|
3179
|
-
end_bank = math.ceil(len(data_import) / _mbc.GetROMBankSize())
|
|
3180
|
-
rom_bank_size = _mbc.GetROMBankSize()
|
|
3181
|
-
|
|
3182
|
-
_mbc.EnableMapper()
|
|
3183
|
-
|
|
3184
|
-
if flashcart.GetMBC() == "manual":
|
|
3185
|
-
errmsg_mbc_selection += "\n- Check mapper type used: {:s} (manual selection)".format(_mbc.GetName())
|
|
3186
|
-
else:
|
|
3187
|
-
errmsg_mbc_selection += "\n- Check mapper type used: {:s} (forced by selected cartridge type)".format(_mbc.GetName())
|
|
3188
|
-
if len(data_import) > _mbc.GetMaxROMSize():
|
|
3189
|
-
errmsg_mbc_selection += "\n- Check mapper type ROM size limit: likely up to {:s}".format(Util.formatFileSize(size=_mbc.GetMaxROMSize()))
|
|
3190
|
-
|
|
3191
|
-
elif self.MODE == "AGB":
|
|
3192
|
-
self._write(self.DEVICE_CMD["SET_MODE_AGB"])
|
|
3193
|
-
if flashcart and "flash_bank_size" in cart_type:
|
|
3194
|
-
end_bank = math.ceil(len(data_import) / cart_type["flash_bank_size"])
|
|
3195
|
-
else:
|
|
3196
|
-
end_bank = 1
|
|
3197
|
-
if flashcart and "flash_bank_size" in cart_type:
|
|
3198
|
-
rom_bank_size = cart_type["flash_bank_size"]
|
|
3199
|
-
else:
|
|
3200
|
-
rom_bank_size = 0x2000000
|
|
3201
|
-
|
|
3202
|
-
flash_buffer_size = flashcart.GetBufferSize()
|
|
3203
|
-
|
|
3204
|
-
if self.FW["fw_ver"] >= 8:
|
|
3205
|
-
if "enable_pullups" in cart_type:
|
|
3206
|
-
self._write(self.DEVICE_CMD["ENABLE_PULLUPS"], wait=True)
|
|
3207
|
-
dprint("Pullups enabled")
|
|
3208
|
-
else:
|
|
3209
|
-
self._write(self.DEVICE_CMD["DISABLE_PULLUPS"], wait=True)
|
|
3210
|
-
dprint("Pullups disabled")
|
|
3211
|
-
# ↑↑↑ Flashcart configuration
|
|
3212
|
-
|
|
3213
|
-
# ↓↓↓ DMG-MMSA-JPN hidden sector
|
|
3214
|
-
if self.MODE == "DMG" and _mbc.GetName() == "G-MMC1":
|
|
3215
|
-
if "buffer_map" not in args:
|
|
3216
|
-
if os.path.exists(os.path.splitext(args["path"])[0] + ".map"):
|
|
3217
|
-
with open(os.path.splitext(args["path"])[0] + ".map", "rb") as file: args["buffer_map"] = file.read()
|
|
3218
|
-
else:
|
|
3219
|
-
temp = data_import
|
|
3220
|
-
if len(temp) == 0: temp = bytearray([0xFF] * 0x180)
|
|
3221
|
-
try:
|
|
3222
|
-
gbmem = GBMemoryMap(rom=temp, oldmap=_mbc.ReadHiddenSector())
|
|
3223
|
-
args["buffer_map"] = gbmem.GetMapData()
|
|
3224
|
-
except Exception:
|
|
3225
|
-
print(traceback.format_exc())
|
|
3226
|
-
print("{:s}An error occured while trying to generate the hidden sector data for the NP GB-Memory cartridge.{:s}".format(ANSI.RED, ANSI.RESET))
|
|
3227
|
-
args["buffer_map"] = False
|
|
3228
|
-
|
|
3229
|
-
if args["buffer_map"] is False:
|
|
3230
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"The NP GB-Memory Cartridge requires extra hidden sector data. As it couldn’t be auto-generated, please provide your own at the following path: {:s}".format(os.path.splitext(args["path"])[0] + ".map"), "abortable":False})
|
|
3231
|
-
return False
|
|
3232
|
-
else:
|
|
3233
|
-
dprint("Generated hidden sector data:", args["buffer_map"])
|
|
3234
|
-
if Util.DEBUG:
|
|
3235
|
-
with open("debug_mmsa_map.bin", "wb") as f: f.write(args["buffer_map"])
|
|
3236
|
-
data_map_import = copy.copy(args["buffer_map"])
|
|
3237
|
-
data_map_import = bytearray(data_map_import)
|
|
3238
|
-
dprint("Hidden sector data loaded")
|
|
3239
|
-
# ↑↑↑ DMG-MMSA-JPN hidden sector
|
|
3240
|
-
|
|
3241
|
-
# ↓↓↓ Load commands into firmware
|
|
3242
|
-
flash_cmds = []
|
|
3243
|
-
command_set_type = flashcart.GetCommandSetType()
|
|
3244
|
-
temp = 0
|
|
3245
|
-
if command_set_type == "AMD":
|
|
3246
|
-
temp = 0x01
|
|
3247
|
-
#self._write(0x01) # FLASH_COMMAND_SET_AMD
|
|
3248
|
-
#self._set_fw_variable("FLASH_SHARP_VERIFY_SR", 0)
|
|
3249
|
-
dprint("Using AMD command set")
|
|
3250
|
-
elif command_set_type == "INTEL":
|
|
3251
|
-
temp = 0x02
|
|
3252
|
-
#self._write(0x02) # FLASH_COMMAND_SET_INTEL
|
|
3253
|
-
self._set_fw_variable("FLASH_SHARP_VERIFY_SR", 0)
|
|
3254
|
-
dprint("Using Intel command set")
|
|
3255
|
-
elif command_set_type == "SHARP":
|
|
3256
|
-
temp = 0x02
|
|
3257
|
-
#self._write(0x02) # FLASH_COMMAND_SET_INTEL
|
|
3258
|
-
self._set_fw_variable("FLASH_SHARP_VERIFY_SR", 1)
|
|
3259
|
-
dprint("Using Sharp/Intel command set")
|
|
3260
|
-
elif command_set_type in ("GBMEMORY", "DMG-MBC5-32M-FLASH"):
|
|
3261
|
-
temp = 0x00
|
|
3262
|
-
dprint("Using GB-Memory command set")
|
|
3263
|
-
elif command_set_type in ("BLAZE_XPLODER", "DATEL_ORBITV2"):
|
|
3264
|
-
temp = 0x00
|
|
3265
|
-
else:
|
|
3266
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type is currently not supported for ROM flashing.", "abortable":False})
|
|
3267
|
-
return False
|
|
3268
|
-
|
|
3269
|
-
if flashcart.HasDoubleDie() and self.FW["pcb_ver"] in (5, 6, 101) and self.FW["fw_ver"] >= 5:
|
|
3270
|
-
self._set_fw_variable("FLASH_DOUBLE_DIE", 1)
|
|
3271
|
-
else:
|
|
3272
|
-
self._set_fw_variable("FLASH_DOUBLE_DIE", 0)
|
|
3273
|
-
|
|
3274
|
-
if command_set_type == "GBMEMORY" and self.FW["pcb_ver"] not in (5, 6, 101):
|
|
3275
|
-
self._set_fw_variable("FLASH_WE_PIN", 0x01)
|
|
3276
|
-
dprint("Using GB-Memory mode on GBxCart RW v1.3")
|
|
3277
|
-
elif command_set_type == "DMG-MBC5-32M-FLASH":
|
|
3278
|
-
self._set_fw_variable("FLASH_WE_PIN", 0x02)
|
|
3279
|
-
else:
|
|
3280
|
-
self._write(self.DEVICE_CMD["SET_FLASH_CMD"])
|
|
3281
|
-
self._write(temp)
|
|
3282
|
-
|
|
3283
|
-
if flashcart.IsF2A():
|
|
3284
|
-
self._write(0x05) # FLASH_METHOD_AGB_FLASH2ADVANCE
|
|
3285
|
-
flash_cmds = [
|
|
3286
|
-
[ "SA", 0xE8 ],
|
|
3287
|
-
[ "SA", "BS" ],
|
|
3288
|
-
[ "PA", "PD" ],
|
|
3289
|
-
[ "SA", 0xD0 ],
|
|
3290
|
-
[ "SA", 0xFF ]
|
|
3291
|
-
]
|
|
3292
|
-
dprint("Using Flash2Advance mode with a buffer of {:d} bytes".format(flash_buffer_size))
|
|
3293
|
-
elif command_set_type == "GBMEMORY" and self.FW["pcb_ver"] in (5, 6, 101):
|
|
3294
|
-
self._write(0x03) # FLASH_METHOD_DMG_MMSA
|
|
3295
|
-
dprint("Using GB-Memory mode on GBxCart RW v1.4")
|
|
3296
|
-
elif flashcart.SupportsBufferWrite() and flash_buffer_size > 0:
|
|
3297
|
-
self._write(0x02) # FLASH_METHOD_BUFFERED
|
|
3298
|
-
flash_cmds = flashcart.GetCommands("buffer_write")
|
|
3299
|
-
dprint("Using buffered writing with a buffer of {:d} bytes".format(flash_buffer_size))
|
|
3300
|
-
elif flashcart.SupportsSingleWrite():
|
|
3301
|
-
self._write(0x01) # FLASH_METHOD_UNBUFFERED
|
|
3302
|
-
flash_cmds = flashcart.GetCommands("single_write")
|
|
3303
|
-
dprint("Using single writing")
|
|
3304
|
-
else:
|
|
3305
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"This cartridge type is currently not supported for ROM flashing.", "abortable":False})
|
|
3306
|
-
return False
|
|
3307
|
-
|
|
3308
|
-
if flashcart.WEisWR():
|
|
3309
|
-
we = 0x01 # FLASH_WE_PIN_WR
|
|
3310
|
-
self._write(we)
|
|
3311
|
-
dprint("Using WR as WE")
|
|
3312
|
-
elif flashcart.WEisAUDIO():
|
|
3313
|
-
we = 0x02 # FLASH_WE_PIN_AUDIO
|
|
3314
|
-
self._write(we)
|
|
3315
|
-
dprint("Using AUDIO as WE")
|
|
3316
|
-
elif flashcart.WEisWR_RESET():
|
|
3317
|
-
we = 0x03 # FLASH_WE_PIN_WR_RESET
|
|
3318
|
-
self._write(we) # FLASH_WE_PIN_WR_RESET
|
|
3319
|
-
dprint("Using WR+RESET as WE")
|
|
3320
|
-
else:
|
|
3321
|
-
we = 0x00
|
|
3322
|
-
self._write(we) # unset
|
|
3323
|
-
|
|
3324
|
-
for i in range(0, 6):
|
|
3325
|
-
if i > len(flash_cmds) - 1: # skip
|
|
3326
|
-
self._write(bytearray(struct.pack(">I", 0)) + bytearray(struct.pack(">H", 0)))
|
|
3327
|
-
else:
|
|
3328
|
-
address = flash_cmds[i][0]
|
|
3329
|
-
value = flash_cmds[i][1]
|
|
3330
|
-
if not isinstance(address, int): address = 0
|
|
3331
|
-
if not isinstance(value, int): value = 0
|
|
3332
|
-
if self.MODE == "AGB": address >>= 1
|
|
3333
|
-
dprint("Setting command #{:d} to 0x{:X}=0x{:X}".format(i, address, value))
|
|
3334
|
-
self._write(bytearray(struct.pack(">I", address)) + bytearray(struct.pack(">H", value)))
|
|
3335
|
-
|
|
3336
|
-
if self.FW["fw_ver"] >= 6:
|
|
3337
|
-
if "flash_commands_on_bank_1" in cart_type and cart_type["flash_commands_on_bank_1"] is True:
|
|
3338
|
-
self._set_fw_variable("FLASH_COMMANDS_BANK_1", 1)
|
|
3339
|
-
self._write(self.DEVICE_CMD["DMG_SET_BANK_CHANGE_CMD"])
|
|
3340
|
-
if "bank_switch" in cart_type["commands"]:
|
|
3341
|
-
self._write(len(cart_type["commands"]["bank_switch"])) # number of commands
|
|
3342
|
-
for command in cart_type["commands"]["bank_switch"]:
|
|
3343
|
-
address = command[0]
|
|
3344
|
-
value = command[1]
|
|
3345
|
-
if value == "ID":
|
|
3346
|
-
self._write(bytearray(struct.pack(">I", address))) # address
|
|
3347
|
-
self._write(0) # type = address
|
|
3348
|
-
else:
|
|
3349
|
-
self._write(bytearray(struct.pack(">I", value))) # value
|
|
3350
|
-
self._write(1) # type = value
|
|
3351
|
-
ret = self._read(1)
|
|
3352
|
-
if ret != 0x01:
|
|
3353
|
-
print("Error in DMG_SET_BANK_CHANGE_CMD:", ret)
|
|
3354
|
-
else:
|
|
3355
|
-
self._write(0, wait=True)
|
|
3356
|
-
else:
|
|
3357
|
-
self._set_fw_variable("FLASH_COMMANDS_BANK_1", 0)
|
|
3358
|
-
self._write(self.DEVICE_CMD["DMG_SET_BANK_CHANGE_CMD"])
|
|
3359
|
-
self._write(0, wait=True)
|
|
3360
|
-
else:
|
|
3361
|
-
if "flash_commands_on_bank_1" in cart_type and cart_type["flash_commands_on_bank_1"] is True:
|
|
3362
|
-
self._set_fw_variable("FLASH_COMMANDS_BANK_1", 1)
|
|
3363
|
-
else:
|
|
3364
|
-
self._set_fw_variable("FLASH_COMMANDS_BANK_1", 0)
|
|
3365
|
-
# ↑↑↑ Load commands into firmware
|
|
3366
|
-
|
|
3367
|
-
# ↓↓↓ Preparations
|
|
3368
|
-
if self.MODE == "DMG" and "flash_commands_on_bank_1" in cart_type and cart_type["flash_commands_on_bank_1"] is True:
|
|
3369
|
-
dprint("Setting ROM bank 1")
|
|
3370
|
-
_mbc.SelectBankROM(1)
|
|
3371
|
-
# ↑↑↑ Preparations
|
|
3372
|
-
|
|
3373
|
-
# ↓↓↓ Read Flash ID
|
|
3374
|
-
if "flash_ids" in cart_type:
|
|
3375
|
-
(verified, flash_id) = flashcart.VerifyFlashID()
|
|
3376
|
-
if not verified and not command_set_type == "BLAZE_XPLODER":
|
|
3377
|
-
print("Note: This cartridge’s Flash ID ({:s}) doesn’t match the cartridge type selection.".format(' '.join(format(x, '02X') for x in flash_id)))
|
|
3378
|
-
else:
|
|
3379
|
-
if flashcart.Unlock() is False: return False
|
|
3380
|
-
# ↑↑↑ Read Flash ID
|
|
3381
|
-
|
|
3382
|
-
# ↓↓↓ Read Sector Map
|
|
3383
|
-
sector_map = flashcart.GetSectorMap()
|
|
3384
|
-
smallest_sector_size = 0x2000
|
|
3385
|
-
sector_offsets = []
|
|
3386
|
-
write_sectors = None
|
|
3387
|
-
sector_pos = 0
|
|
3388
|
-
delta_state_new = None
|
|
3389
|
-
flash_capacity = len(data_import)
|
|
3390
|
-
if sector_map is not None and sector_map is not False:
|
|
3391
|
-
smallest_sector_size = flashcart.GetSmallestSectorSize()
|
|
3392
|
-
sector_offsets = flashcart.GetSectorOffsets(rom_size=flashcart.GetFlashSize(default=len(data_import)), rom_bank_size=rom_bank_size)
|
|
3393
|
-
if len(sector_offsets) > 0:
|
|
3394
|
-
flash_capacity = sector_offsets[-1][0] + sector_offsets[-1][1]
|
|
3395
|
-
if flash_capacity < len(data_import) and not (flashcart.SupportsChipErase() and args["prefer_chip_erase"]):
|
|
3396
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"There are not enough flash sectors available to write this ROM. The maximum capacity is {:s}.".format(Util.formatFileSize(size=flash_capacity, asInt=False)), "abortable":False})
|
|
3397
|
-
return False
|
|
3398
|
-
|
|
3399
|
-
sector_offsets_hash = base64.urlsafe_b64encode(hashlib.sha1(str(sector_offsets).encode("UTF-8")).digest()).decode("ASCII", "ignore")[:4]
|
|
3400
|
-
|
|
3401
|
-
# Delta flashing
|
|
3402
|
-
if len(sector_offsets) > 1:
|
|
3403
|
-
splitext = os.path.splitext(args["path"])
|
|
3404
|
-
if splitext[0].endswith(".delta") and os.path.exists(splitext[0][:-6] + splitext[1]):
|
|
3405
|
-
delta_state_new = []
|
|
3406
|
-
with open(splitext[0][:-6] + splitext[1], "rb") as f:
|
|
3407
|
-
for i in range(0, len(sector_offsets)):
|
|
3408
|
-
s_from = sector_offsets[i][0]
|
|
3409
|
-
s_size = sector_offsets[i][1]
|
|
3410
|
-
s_to = s_from + s_size
|
|
3411
|
-
if data_import[s_from:s_to] != f.read(s_to - s_from):
|
|
3412
|
-
x = [ s_from, s_size, zlib.crc32(data_import[s_from:s_to]) & 0xFFFFFFFF ]
|
|
3413
|
-
delta_state_new.append(x)
|
|
3414
|
-
dprint("Sector differs:", x)
|
|
3415
|
-
write_sectors = copy.copy(delta_state_new)
|
|
3416
|
-
json_file = "{:s}_{:s}.json".format(splitext[0], sector_offsets_hash)
|
|
3417
|
-
if os.path.exists(json_file):
|
|
3418
|
-
with open(json_file, "rb") as f:
|
|
3419
|
-
try:
|
|
3420
|
-
delta_state_old = json.loads(f.read().decode("UTF-8-SIG"))
|
|
3421
|
-
except:
|
|
3422
|
-
delta_state_old = []
|
|
3423
|
-
if len(delta_state_old) > 0:
|
|
3424
|
-
for x in delta_state_old:
|
|
3425
|
-
if x in write_sectors:
|
|
3426
|
-
del(write_sectors[write_sectors.index(x)])
|
|
3427
|
-
dprint("Skipping sector:", x)
|
|
3428
|
-
else:
|
|
3429
|
-
write_sectors2 = []
|
|
3430
|
-
for y in write_sectors: write_sectors2.append(y[:-1])
|
|
3431
|
-
if x[:-1] not in write_sectors2:
|
|
3432
|
-
write_sectors.append(x)
|
|
3433
|
-
dprint("Forcing sector:", x)
|
|
3434
|
-
|
|
3435
|
-
if len(write_sectors) == 0:
|
|
3436
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"No flash sectors were found that would need to be updated for delta flashing.", "abortable":False})
|
|
3437
|
-
return False
|
|
3438
|
-
|
|
3439
|
-
elif "flash_sectors" in args and len(args["flash_sectors"]) > 0:
|
|
3440
|
-
write_sectors = args["flash_sectors"]
|
|
3441
|
-
|
|
3442
|
-
elif flash_offset > 0: # Batteryless SRAM
|
|
3443
|
-
if "flash_size" not in args:
|
|
3444
|
-
flash_size = len(data_import) - flash_offset
|
|
3445
|
-
else:
|
|
3446
|
-
flash_size = args["flash_size"]
|
|
3447
|
-
data_import = data_import[:flash_size]
|
|
3448
|
-
bl_sectors = []
|
|
3449
|
-
for sector in sector_offsets:
|
|
3450
|
-
if flash_offset > sector[0]: continue
|
|
3451
|
-
#if len(bl_sectors) > 0 and (flash_offset + flash_size) < (sector[0] + sector[1]): break
|
|
3452
|
-
if (flash_offset + flash_size) < sector[0]: break
|
|
3453
|
-
bl_sectors.append(sector)
|
|
3454
|
-
write_sectors = bl_sectors
|
|
3455
|
-
if "bl_save" in args:
|
|
3456
|
-
data_import = bytearray([0] * bl_sectors[0][0]) + data_import
|
|
3457
|
-
else:
|
|
3458
|
-
write_sectors = []
|
|
3459
|
-
for item in sector_offsets:
|
|
3460
|
-
if item[0] < len(data_import):
|
|
3461
|
-
write_sectors.append(item)
|
|
3462
|
-
|
|
3463
|
-
dprint("Sectors to update:", write_sectors)
|
|
3464
|
-
# ↑↑↑ Read Sector Map
|
|
3465
|
-
|
|
3466
|
-
# ↓↓↓ Chip erase
|
|
3467
|
-
chip_erase = False
|
|
3468
|
-
if flashcart.SupportsChipErase() and not flash_offset > 0:
|
|
3469
|
-
if flashcart.SupportsSectorErase() and args["prefer_chip_erase"] is False and sector_map is not False:
|
|
3470
|
-
chip_erase = False
|
|
3471
|
-
else:
|
|
3472
|
-
chip_erase = True
|
|
3473
|
-
dprint("Erasing the entire flash chip")
|
|
3474
|
-
if flashcart.ChipErase() is False:
|
|
3475
|
-
return False
|
|
3476
|
-
elif flashcart.SupportsSectorErase() is False:
|
|
3477
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"No erase method available.", "abortable":False})
|
|
3478
|
-
return False
|
|
3479
|
-
# ↑↑↑ Chip erase
|
|
3480
|
-
|
|
3481
|
-
# ↓↓↓ Flash Write
|
|
3482
|
-
self.SetProgress({"action":"INITIALIZE", "method":"ROM_WRITE", "size":len(data_import), "flash_offset":flash_offset})
|
|
3483
|
-
self.SetProgress({"action":"UPDATE_POS", "pos":flash_offset})
|
|
3484
|
-
self.INFO["action"] = self.ACTIONS["ROM_WRITE"]
|
|
3485
|
-
|
|
3486
|
-
if smallest_sector_size is not False:
|
|
3487
|
-
buffer_len = smallest_sector_size
|
|
3488
|
-
elif self.MODE == "DMG":
|
|
3489
|
-
buffer_len = _mbc.GetROMBankSize()
|
|
3490
|
-
if _mbc.HasFlashBanks(): _mbc.SelectBankFlash(0)
|
|
3491
|
-
else:
|
|
3492
|
-
buffer_len = 0x2000
|
|
3493
|
-
dprint("Transfer buffer length is 0x{:X}".format(buffer_len))
|
|
3494
|
-
|
|
3495
|
-
start_bank = 0
|
|
3496
|
-
start_address = 0
|
|
3497
|
-
buffer_pos = 0
|
|
3498
|
-
retry_hp = 100
|
|
3499
|
-
end_address = len(data_import)
|
|
3500
|
-
dprint("ROM banks:", end_bank)
|
|
3501
|
-
|
|
3502
|
-
if chip_erase:
|
|
3503
|
-
write_sectors = [[ 0, len(data_import) ]]
|
|
3504
|
-
elif write_sectors is None or len(write_sectors) == 0:
|
|
3505
|
-
write_sectors = sector_offsets
|
|
3506
|
-
|
|
3507
|
-
for sector in write_sectors:
|
|
3508
|
-
if chip_erase is False:
|
|
3509
|
-
retry_hp = 100
|
|
3510
|
-
if self.MODE == "AGB":
|
|
3511
|
-
dprint("Writing sector:", hex(sector[0]), hex(sector[1]))
|
|
3512
|
-
buffer_pos = sector[0]
|
|
3513
|
-
start_address = buffer_pos
|
|
3514
|
-
end_address = sector[0] + sector[1]
|
|
3515
|
-
if sector[:2] not in sector_offsets:
|
|
3516
|
-
dprint("Sector not found for delta writing:", sector)
|
|
3517
|
-
continue
|
|
3518
|
-
sector_pos = sector_offsets.index(sector[:2])
|
|
3519
|
-
start_bank = math.floor(buffer_pos / rom_bank_size)
|
|
3520
|
-
end_bank = math.ceil((buffer_pos + sector[1]) / rom_bank_size)
|
|
3521
|
-
# print(hex(start_address), hex(end_address), start_bank, end_bank)
|
|
3522
|
-
# print(hex(sector_offsets[sector_pos][0]), sector_pos)
|
|
3523
|
-
# print("")
|
|
3524
|
-
elif self.MODE == "DMG":
|
|
3525
|
-
dprint("Writing sector:", hex(sector[0]), hex(sector[1]))
|
|
3526
|
-
buffer_pos = sector[0]
|
|
3527
|
-
#end_address = sector[0] + sector[1]
|
|
3528
|
-
if sector[:2] not in sector_offsets:
|
|
3529
|
-
print("Sector not found for delta writing:", sector)
|
|
3530
|
-
continue
|
|
3531
|
-
sector_pos = sector_offsets.index(sector[:2])
|
|
3532
|
-
start_bank = math.floor(buffer_pos / rom_bank_size)
|
|
3533
|
-
end_bank = math.ceil((buffer_pos + sector[1]) / rom_bank_size)
|
|
3534
|
-
# print(hex(start_address), hex(end_address), start_bank, end_bank)
|
|
3535
|
-
# print(hex(sector_offsets[sector_pos][0]), sector_pos)
|
|
3536
|
-
# print("")
|
|
3537
|
-
|
|
3538
|
-
#for bank in range(start_bank, end_bank):
|
|
3539
|
-
bank = start_bank
|
|
3540
|
-
while bank < end_bank:
|
|
3541
|
-
if self.CANCEL:
|
|
3542
|
-
cancel_args = {"action":"ABORT", "abortable":False}
|
|
3543
|
-
cancel_args.update(self.CANCEL_ARGS)
|
|
3544
|
-
self.CANCEL_ARGS = {}
|
|
3545
|
-
self.ERROR_ARGS = {}
|
|
3546
|
-
self.SetProgress(cancel_args)
|
|
3547
|
-
if self.CanPowerCycleCart(): self.CartPowerCycle()
|
|
3548
|
-
return
|
|
3549
|
-
|
|
3550
|
-
status = None
|
|
3551
|
-
#print("Bank:", bank, "...")
|
|
3552
|
-
# ↓↓↓ Switch ROM bank
|
|
3553
|
-
if self.MODE == "DMG":
|
|
3554
|
-
if _mbc.ResetBeforeBankChange(bank) is True:
|
|
3555
|
-
dprint("Resetting the MBC")
|
|
3556
|
-
self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
|
|
3557
|
-
(start_address, bank_size) = _mbc.SelectBankROM(bank)
|
|
3558
|
-
if flashcart.PulseResetAfterWrite():
|
|
3559
|
-
if bank == 0:
|
|
3560
|
-
self._write(self.DEVICE_CMD["OFW_GB_CART_MODE"])
|
|
3561
|
-
self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
|
|
3562
|
-
else:
|
|
3563
|
-
self._write(self.DEVICE_CMD["OFW_GB_FLASH_BANK_1_COMMAND_WRITES"])
|
|
3564
|
-
self._set_fw_variable("DMG_ROM_BANK", bank)
|
|
3565
|
-
|
|
3566
|
-
buffer_len = min(buffer_len, bank_size)
|
|
3567
|
-
if "start_addr" in flashcart.CONFIG and bank == 0: start_address = flashcart.CONFIG["start_addr"]
|
|
3568
|
-
end_address = start_address + bank_size
|
|
3569
|
-
start_address += (buffer_pos % rom_bank_size)
|
|
3570
|
-
if end_address > start_address + sector[1]:
|
|
3571
|
-
end_address = start_address + sector[1]
|
|
3572
|
-
|
|
3573
|
-
elif self.MODE == "AGB":
|
|
3574
|
-
if "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] > 0:
|
|
3575
|
-
flashcart.Reset(full_reset=True)
|
|
3576
|
-
flashcart.SelectBankROM(bank)
|
|
3577
|
-
temp = end_address - start_address
|
|
3578
|
-
start_address %= cart_type["flash_bank_size"]
|
|
3579
|
-
#end_address = start_address + temp
|
|
3580
|
-
end_address = min(cart_type["flash_bank_size"], start_address + temp)
|
|
3581
|
-
# ↑↑↑ Switch ROM bank
|
|
3582
|
-
|
|
3583
|
-
skip_init = False
|
|
3584
|
-
pos = start_address
|
|
3585
|
-
dprint("buffer_pos=0x{:X}, start_address=0x{:X}, end_address=0x{:X}".format(buffer_pos, start_address, end_address))
|
|
3586
|
-
|
|
3587
|
-
while pos < end_address:
|
|
3588
|
-
if self.CANCEL:
|
|
3589
|
-
cancel_args = {"action":"ABORT", "abortable":False}
|
|
3590
|
-
cancel_args.update(self.CANCEL_ARGS)
|
|
3591
|
-
self.CANCEL_ARGS = {}
|
|
3592
|
-
self.ERROR_ARGS = {}
|
|
3593
|
-
self.SetProgress(cancel_args)
|
|
3594
|
-
if self.CanPowerCycleCart(): self.CartPowerCycle()
|
|
3595
|
-
return
|
|
3596
|
-
|
|
3597
|
-
if buffer_pos >= len(data_import): break
|
|
3598
|
-
|
|
3599
|
-
# ↓↓↓ Sector erase
|
|
3600
|
-
se_ret = None
|
|
3601
|
-
if chip_erase is False:
|
|
3602
|
-
if sector_pos < len(sector_offsets) and buffer_pos == sector_offsets[sector_pos][0]:
|
|
3603
|
-
self.SetProgress({"action":"UPDATE_POS", "pos":buffer_pos})
|
|
3604
|
-
dprint("Erasing sector #{:d} at position 0x{:X} (0x{:X})".format(sector_pos, buffer_pos, pos))
|
|
3605
|
-
sector_pos += 1
|
|
3606
|
-
if flashcart.FlashCommandsOnBank1(): _mbc.SelectBankROM(bank)
|
|
3607
|
-
se_ret = flashcart.SectorErase(pos=pos, buffer_pos=buffer_pos)
|
|
3608
|
-
if se_ret:
|
|
3609
|
-
self.SetProgress({"action":"UPDATE_POS", "pos":buffer_pos, "pos_sector":buffer_pos})
|
|
3610
|
-
sector_size = se_ret
|
|
3611
|
-
dprint("Next sector size: 0x{:X}".format(sector_size))
|
|
3612
|
-
skip_init = False
|
|
3613
|
-
if "from_user" in self.CANCEL_ARGS and self.CANCEL_ARGS["from_user"]:
|
|
3614
|
-
continue
|
|
3615
|
-
# ↑↑↑ Sector erase
|
|
3616
|
-
|
|
3617
|
-
if se_ret is not False:
|
|
3618
|
-
if command_set_type == "GBMEMORY" and self.FW["pcb_ver"] < 5:
|
|
3619
|
-
status = self.WriteROM_GBMEMORY(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], bank=bank)
|
|
3620
|
-
elif command_set_type == "GBMEMORY" and self.FW["pcb_ver"] in (5, 6, 101):
|
|
3621
|
-
status = self.WriteROM(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], flash_buffer_size=flash_buffer_size, skip_init=(skip_init and not self.SKIPPING))
|
|
3622
|
-
self._cart_write(pos + buffer_len - 1, 0xF0)
|
|
3623
|
-
elif command_set_type == "DMG-MBC5-32M-FLASH":
|
|
3624
|
-
status = self.WriteROM_DMG_MBC5_32M_FLASH(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], bank=bank)
|
|
3625
|
-
elif command_set_type == "BLAZE_XPLODER":
|
|
3626
|
-
status = self.WriteROM_DMG_EEPROM(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], bank=bank)
|
|
3627
|
-
elif command_set_type == "DATEL_ORBITV2":
|
|
3628
|
-
status = self.WriteROM_DMG_DatelOrbitV2(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], bank=bank)
|
|
3629
|
-
else:
|
|
3630
|
-
max_buffer_write = self.MAX_BUFFER_WRITE
|
|
3631
|
-
if (len(data_import) == 0x1FFFF00) and (buffer_pos+buffer_len > len(data_import)):
|
|
3632
|
-
# 32 MiB ROM + EEPROM cart
|
|
3633
|
-
max_buffer_write = 256
|
|
3634
|
-
buffer_len = (buffer_pos+buffer_len - len(data_import))
|
|
3635
|
-
status = self.WriteROM(address=pos, buffer=data_import[buffer_pos:buffer_pos+buffer_len], flash_buffer_size=flash_buffer_size, skip_init=(skip_init and not self.SKIPPING), rumble_stop=rumble, max_length=max_buffer_write)
|
|
3636
|
-
|
|
3637
|
-
if status is False or se_ret is False:
|
|
3638
|
-
self.CANCEL = True
|
|
3639
|
-
self.ERROR = True
|
|
3640
|
-
if "from_user" in self.CANCEL_ARGS and self.CANCEL_ARGS["from_user"]:
|
|
3641
|
-
break
|
|
3642
|
-
elif buffer_pos == 0 or not self.DEVICE.is_open or self.DEVICE is None:
|
|
3643
|
-
self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"An error occured while writing 0x{:X} bytes at position 0x{:X} ({:s}). Please re-connect the device and try again from the beginning.\n\nTroubleshooting advice:\n- Clean cartridge contacts\n- Avoid passive USB hubs and try different USB ports/cables\n- Check cartridge type selection{:s}".format(buffer_len, buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False), errmsg_mbc_selection)})
|
|
3644
|
-
break
|
|
3645
|
-
else:
|
|
3646
|
-
if chip_erase: retry_hp = 0
|
|
3647
|
-
if "iteration" in self.ERROR_ARGS and self.ERROR_ARGS["iteration"] > 0:
|
|
3648
|
-
retry_hp -= 5
|
|
3649
|
-
if retry_hp <= 0:
|
|
3650
|
-
self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"Unstable connection detected while writing 0x{:X} bytes in iteration {:d} at position 0x{:X} ({:s}). Please re-connect the device and try again from the beginning.\n\nTroubleshooting advice:\n- Clean cartridge contacts\n- Avoid passive USB hubs and try different USB ports/cables".format(buffer_len, self.ERROR_ARGS["iteration"], buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False))})
|
|
3651
|
-
continue
|
|
3652
|
-
else:
|
|
3653
|
-
retry_hp -= 10
|
|
3654
|
-
if retry_hp <= 0:
|
|
3655
|
-
self.CANCEL_ARGS.update({"info_type":"msgbox_critical", "info_msg":"An error occured while writing 0x{:X} bytes at position 0x{:X} ({:s}). Please re-connect the device and try again from the beginning.\n\nTroubleshooting advice:\n- Clean cartridge contacts\n- Avoid passive USB hubs and try different USB ports/cables\n- Check cartridge type selection\n- Check cartridge ROM storage size (at least {:s} is required){:s}".format(buffer_len, buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False), Util.formatFileSize(size=len(data_import), asInt=False), errmsg_mbc_selection), "abortable":False})
|
|
3656
|
-
continue
|
|
3657
|
-
|
|
3658
|
-
rev_buffer_pos = sector_offsets[sector_pos - 1][0]
|
|
3659
|
-
buffer_pos = rev_buffer_pos
|
|
3660
|
-
bank = start_bank
|
|
3661
|
-
sector_pos -= 1
|
|
3662
|
-
err_text = "Write error! Retrying from 0x{:X}...".format(rev_buffer_pos)
|
|
3663
|
-
print(err_text)
|
|
3664
|
-
dprint(err_text, "Bank {:d} | HP: {:d}/100".format(bank, retry_hp))
|
|
3665
|
-
pos = end_address
|
|
3666
|
-
status = False
|
|
3667
|
-
|
|
3668
|
-
self.SetProgress({"action":"ERROR", "abortable":True, "pos":buffer_pos, "text":"Write error! Retrying from 0x{:X}...".format(rev_buffer_pos)})
|
|
3669
|
-
delay = 0.5 + (100-retry_hp)/50
|
|
3670
|
-
if self.CanPowerCycleCart():
|
|
3671
|
-
self.CartPowerOff()
|
|
3672
|
-
time.sleep(delay)
|
|
3673
|
-
self.CartPowerOn()
|
|
3674
|
-
if self.MODE == "DMG" and _mbc.HasFlashBanks(): _mbc.SelectBankFlash(bank)
|
|
3675
|
-
time.sleep(delay)
|
|
3676
|
-
if self.DEVICE is None:
|
|
3677
|
-
raise ConnectionAbortedError("A critical connection error occured while writing 0x{:X} bytes at position 0x{:X} ({:s}). Please re-connect the device and try again from the beginning.".format(buffer_len, buffer_pos, Util.formatFileSize(size=buffer_pos, asInt=False)))
|
|
3678
|
-
|
|
3679
|
-
self.CANCEL_ARGS = {}
|
|
3680
|
-
self.CANCEL = False
|
|
3681
|
-
self.ERROR = False
|
|
3682
|
-
self.DEVICE.reset_input_buffer()
|
|
3683
|
-
self.DEVICE.reset_output_buffer()
|
|
3684
|
-
self._cart_write(pos, 0xF0)
|
|
3685
|
-
self._cart_write(pos, 0xFF)
|
|
3686
|
-
flashcart.Unlock()
|
|
3687
|
-
continue
|
|
3688
|
-
|
|
3689
|
-
self.CANCEL = True
|
|
3690
|
-
self.ERROR = True
|
|
3691
|
-
continue
|
|
3692
|
-
|
|
3693
|
-
skip_init = True
|
|
3694
|
-
|
|
3695
|
-
buffer_pos += buffer_len
|
|
3696
|
-
pos += buffer_len
|
|
3697
|
-
self.SetProgress({"action":"UPDATE_POS", "pos":buffer_pos})
|
|
3698
|
-
|
|
3699
|
-
if status is not False:
|
|
3700
|
-
bank += 1
|
|
3701
|
-
|
|
3702
|
-
self.SetProgress({"action":"UPDATE_POS", "pos":len(data_import)})
|
|
3703
|
-
# ↑↑↑ Flash write
|
|
3704
|
-
|
|
3705
|
-
# ↓↓↓ GB-Memory Hidden Sector
|
|
3706
|
-
if command_set_type == "GBMEMORY":
|
|
3707
|
-
flashcart.EraseHiddenSector(buffer=data_map_import)
|
|
3708
|
-
status = self.WriteROM_GBMEMORY(address=0, buffer=data_map_import[0:128], bank=1)
|
|
3709
|
-
if status is False:
|
|
3710
|
-
self.SetProgress({"action":"ABORT", "info_type":"msgbox_critical", "info_msg":"An error occured while writing the hidden sector. Please make sure that the cartridge contacts are clean, re-connect the device and try again from the beginning.", "abortable":False})
|
|
3711
|
-
return False
|
|
3712
|
-
# ↑↑↑ GB-Memory Hidden Sector
|
|
3713
|
-
|
|
3714
|
-
# ↓↓↓ Reset flash
|
|
3715
|
-
flashcart.Reset(full_reset=True)
|
|
3716
|
-
# ↑↑↑ Reset flash
|
|
3717
|
-
|
|
3718
|
-
# ↓↓↓ Flash verify
|
|
3719
|
-
verified = False
|
|
3720
|
-
if "broken_sectors" in self.INFO: del(self.INFO["broken_sectors"])
|
|
3721
|
-
if "verify_write" in args and args["verify_write"] is True:
|
|
3722
|
-
self.SetProgress({"action":"INITIALIZE", "method":"ROM_WRITE_VERIFY", "size":len(data_import), "flash_offset":flash_offset})
|
|
3723
|
-
if Util.DEBUG:
|
|
3724
|
-
with open("debug_verify.bin", "wb") as f: pass
|
|
3725
|
-
|
|
3726
|
-
broken_sectors = []
|
|
3727
|
-
for sector in write_sectors:
|
|
3728
|
-
verified = True
|
|
3729
|
-
if sector[0] >= len(data_import): break
|
|
3730
|
-
verify_args = copy.copy(args)
|
|
3731
|
-
verify_args.update({"verify_write":data_import[sector[0]:sector[0]+sector[1]], "rom_size":len(data_import), "verify_from":sector[0], "path":"", "rtc_area":flashcart.HasRTC(), "verify_mbc":_mbc})
|
|
3732
|
-
verify_args["verify_base_pos"] = sector[0]
|
|
3733
|
-
verify_args["verify_len"] = len(verify_args["verify_write"])
|
|
3734
|
-
verify_args["rom_size"] = len(verify_args["verify_write"])
|
|
3735
|
-
self.SetProgress({"action":"UPDATE_POS", "pos":sector[0]})
|
|
3736
|
-
|
|
3737
|
-
self.ReadROM(0, 4) # dummy read
|
|
3738
|
-
start_address = 0
|
|
3739
|
-
end_address = buffer_pos
|
|
3740
|
-
|
|
3741
|
-
verified_size = self._BackupROM(verify_args)
|
|
3742
|
-
|
|
3743
|
-
if isinstance(verified_size, int): dprint("args[\"verify_len\"]=0x{:X}, verified_size=0x{:X}".format(verify_args["verify_len"], verified_size))
|
|
3744
|
-
if self.CANCEL or self.ERROR:
|
|
3745
|
-
cancel_args = {"action":"ABORT", "abortable":False}
|
|
3746
|
-
cancel_args.update(self.CANCEL_ARGS)
|
|
3747
|
-
self.CANCEL_ARGS = {}
|
|
3748
|
-
self.ERROR_ARGS = {}
|
|
3749
|
-
self.SetProgress(cancel_args)
|
|
3750
|
-
if self.CanPowerCycleCart(): self.CartPowerCycle()
|
|
3751
|
-
verified = False
|
|
3752
|
-
return
|
|
3753
|
-
elif (verified_size is not True) and (verify_args["verify_len"] != verified_size):
|
|
3754
|
-
print("Verification failed at 0x{:X}! Sector: {:s}".format(sector[0]+verified_size, str(sector)))
|
|
3755
|
-
broken_sectors.append(sector)
|
|
3756
|
-
continue
|
|
3757
|
-
|
|
3758
|
-
if len(broken_sectors) > 0:
|
|
3759
|
-
self.INFO["broken_sectors"] = broken_sectors
|
|
3760
|
-
self.INFO["verify_error_params"] = {}
|
|
3761
|
-
self.INFO["verify_error_params"]["rom_size"] = len(data_import)
|
|
3762
|
-
if self.MODE == "DMG":
|
|
3763
|
-
self.INFO["verify_error_params"]["mapper_name"] = _mbc.GetName()
|
|
3764
|
-
if flashcart.GetMBC() == "manual":
|
|
3765
|
-
self.INFO["verify_error_params"]["mapper_selection_type"] = 1 # manual
|
|
3766
|
-
else:
|
|
3767
|
-
self.INFO["verify_error_params"]["mapper_selection_type"] = 2 # forced by cart type
|
|
3768
|
-
self.INFO["verify_error_params"]["mapper_max_size"] = _mbc.GetMaxROMSize()
|
|
3769
|
-
verified = False
|
|
3770
|
-
# ↑↑↑ Flash verify
|
|
3771
|
-
|
|
3772
|
-
if delta_state_new is not None and not chip_erase and not "broken_sectors" in self.INFO:
|
|
3773
|
-
try:
|
|
3774
|
-
with open(json_file, "wb") as f:
|
|
3775
|
-
f.write(json.dumps(delta_state_new).encode("UTF-8-SIG"))
|
|
3776
|
-
except PermissionError:
|
|
3777
|
-
print("Error: Couldn’t update write-protected file “{:s}”".format(json_file))
|
|
3778
|
-
|
|
3779
|
-
# ↓↓↓ Switch to first ROM bank
|
|
3780
|
-
if self.MODE == "DMG":
|
|
3781
|
-
if _mbc.ResetBeforeBankChange(0) is True:
|
|
3782
|
-
dprint("Resetting the MBC")
|
|
3783
|
-
self._write(self.DEVICE_CMD["DMG_MBC_RESET"], wait=True)
|
|
3784
|
-
_mbc.SelectBankROM(0)
|
|
3785
|
-
self._set_fw_variable("DMG_ROM_BANK", 0)
|
|
3786
|
-
elif self.MODE == "AGB":
|
|
3787
|
-
if "flash_bank_select_type" in cart_type and cart_type["flash_bank_select_type"] > 0:
|
|
3788
|
-
flashcart.SelectBankROM(0)
|
|
3789
|
-
# ↑↑↑ Switch to first ROM bank
|
|
3790
|
-
|
|
3791
|
-
# Power Cycle Cartridge
|
|
3792
|
-
self.SetMode(self.MODE)
|
|
3793
|
-
|
|
3794
|
-
self.INFO["last_action"] = self.INFO["action"]
|
|
3795
|
-
self.INFO["action"] = None
|
|
3796
|
-
self.SetProgress({"action":"FINISHED", "verified":verified})
|
|
3797
|
-
return True
|
|
3798
|
-
|
|
3799
|
-
#################################################################
|
|
3800
|
-
|
|
3801
|
-
def TransferData(self, args, signal):
|
|
3802
|
-
self.ERROR = False
|
|
3803
|
-
self.CANCEL = False
|
|
3804
|
-
self.CANCEL_ARGS = {}
|
|
3805
|
-
self.READ_ERRORS = 0
|
|
3806
|
-
if self.IsConnected():
|
|
3807
|
-
if self.FW["pcb_ver"] in (5, 6, 101):
|
|
3808
|
-
self._write(self.DEVICE_CMD["OFW_CART_MODE"])
|
|
3809
|
-
self._read(1)
|
|
3810
|
-
self.CartPowerOn()
|
|
3811
|
-
|
|
3812
|
-
ret = False
|
|
3813
|
-
self.SIGNAL = signal
|
|
3814
|
-
try:
|
|
3815
|
-
temp = copy.copy(args)
|
|
3816
|
-
if "buffer" in temp: temp["buffer"] = "(data)"
|
|
3817
|
-
dprint("args:", temp)
|
|
3818
|
-
del(temp)
|
|
3819
|
-
self.NO_PROG_UPDATE = False
|
|
3820
|
-
self.READ_ERRORS = 0
|
|
3821
|
-
if args['mode'] == 1: ret = self._BackupROM(args)
|
|
3822
|
-
elif args['mode'] == 2: ret = self._BackupRestoreRAM(args)
|
|
3823
|
-
elif args['mode'] == 3: ret = self._BackupRestoreRAM(args)
|
|
3824
|
-
elif args['mode'] == 4: ret = self._FlashROM(args)
|
|
3825
|
-
if self.FW is None: return False
|
|
3826
|
-
if self.FW["pcb_ver"] in (5, 6, 101):
|
|
3827
|
-
if ret is True:
|
|
3828
|
-
self._write(self.DEVICE_CMD["OFW_DONE_LED_ON"])
|
|
3829
|
-
elif self.ERROR is True:
|
|
3830
|
-
self._write(self.DEVICE_CMD["OFW_ERROR_LED_ON"])
|
|
3831
|
-
return True
|
|
3832
|
-
except serial.serialutil.SerialTimeoutException as _:
|
|
3833
|
-
print("Connection timed out. Please reconnect the device.")
|
|
3834
|
-
return False
|
|
3835
|
-
except serial.serialutil.PortNotOpenError as _:
|
|
3836
|
-
print("Connection closed.")
|
|
3837
|
-
return False
|
|
317
|
+
def Close(self, cartPowerOff=False):
|
|
318
|
+
try:
|
|
319
|
+
self.ResetLEDs()
|
|
320
|
+
except SerialException:
|
|
321
|
+
pass
|
|
322
|
+
return super().Close(cartPowerOff)
|