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