This commit is contained in:
Lesserkuma 2025-12-06 15:17:01 +01:00
parent 37ca12a4f1
commit c694ca8b1e
42 changed files with 198 additions and 180 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1,82 +0,0 @@
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppName "FlashGBX"
#define MyAppVersion "<APP_VERSION>"
#define MyAppPublisher "Lesserkuma"
#define MyAppURL "https://github.com/lesserkuma/FlashGBX"
#define MyAppExeName "FlashGBX-app.exe"
#define MyFilesDir "<FILES_DIR>"
#define MyCH341Dir "<CH341_DIR>"
#define MyOutputDir "<OUTPUT_DIR>"
[Setup]
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{060FD4CF-E72F-497D-812E-EB5599D59A0A}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppVerName={#MyAppName} v{#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={autopf}\{#MyAppName}
DisableProgramGroupPage=yes
;PrivilegesRequiredOverridesAllowed=dialog
OutputDir={#MyOutputDir}
OutputBaseFilename=Setup_FlashGBX
Compression=lzma2/ultra64
SolidCompression=yes
WizardStyle=modern
UninstallDisplayIcon={app}\FlashGBX-app.exe
UninstallDisplayName={#MyAppName} v{#MyAppVersion}
PrivilegesRequired=lowest
ArchitecturesAllowed=x64compatible
ArchitecturesInstallIn64BitMode=x64compatible
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
[Types]
Name: "full"; Description: "Install Application and Drivers"
Name: "custom"; Description: "Custom Installation"; Flags: iscustom
[Components]
Name: "program"; Description: "FlashGBX application"; Types: full custom; Flags: fixed
Name: "driver_ch341"; Description: "CH340/CH341 driver v3.8.2023.02 for GBxCart RW and GBFlash (install/re-install)"; Types: full
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"
[Files]
Source: "{#MyFilesDir}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: program
Source: "{#MyCH341Dir}\*.*"; DestDir: "{app}\Drivers\CH341"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: driver_ch341
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}";
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Run]
Filename: "{app}\Drivers\CH341\CH341SER.EXE"; Description: "Install CH340/CH341 driver"; Flags: waituntilterminated; Components: driver_ch341
Filename: "{app}\Python_3.8.10\python.exe"; Parameters: "-m pip install PySide2==5.15.2.1 --no-warn-script-location --isolated --cache-dir {app}\Python_3.8.10\cache"; StatusMsg: "Setting up prerequisites in embedded Python runtime (only used for FlashGBX)..."; Flags: waituntilterminated runminimized; Components: program
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
[InstallDelete]
Type: filesandordirs; Name: "{app}\Python";
Type: filesandordirs; Name: "{app}\Python_3.8.10";
Type: filesandordirs; Name: "{app}\*.pyd";
[Code]
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
if CurUninstallStep = usPostUninstall then
begin
DelTree(ExpandConstant('{app}'), True, True, True);
if MsgBox('Delete all config files of FlashGBX as well?', mbConfirmation, MB_YESNO or MB_DEFBUTTON2) = IDYES then
begin
DelTree(ExpandConstant('{localappdata}\{#MyAppName}'), True, True, True);
end;
end;
end;

Binary file not shown.

View File

@ -49,7 +49,7 @@ jobs:
run: |
python -m venv ${{ github.workspace }}/venv
source ${{ github.workspace }}/venv/bin/activate
python -m pip install pyinstaller==6.11.0 Pillow==10.3.0 PySide6==6.7.2 pyserial==3.5 python-dateutil==2.9.0.post0 requests==2.32.3
python -m pip install pyinstaller==6.11.0 Pillow==10.3.0 PySide6==6.7.2 pyserial==3.5 python-dateutil==2.9.0.post0 requests==2.32.3 packaging==25.0
- name: Build FlashGBX
run: |

View File

@ -1,4 +1,12 @@
# Release notes
### v4.5 (released 2025-12-06)
- Added support for SD007_T40_6401B\*CD_71_TS28 with 39VF6401B
- Added support for ModRetro Chromatic Cartridge with IS29GL032 *(thanks Jayro)*
- Added support for MXP54_16D_ERATH with MSP54LV256
- Added support for a new Game Boy Advance bootleg flash save chip
- Confirmed support for MXP54_16D_046 with MSP54LV256
- Updated the Game Boy and Game Boy Advance lookup databases for save types, ROM sizes and checksums
### v4.4 (released 2025-05-23)
- Added a few workarounds for instabilities with some GBxCart RW devices
- Fixed save data access for “Korokoro Puzzle - Happy Panecchu!” (AGB-KHPJ-JPN)
@ -423,7 +431,7 @@
- Confirmed support for SD007_TSOP_48BALL_V10 with GL032M10BFIR3 *(thanks Mr_V)*
- Added support for 2006_TSOP_64BALL_6106 with W29GL128SH9B *(thanks marv17)*
- Fixed support for insideGadgets 1 MB, 128 KB SRAM *(thanks AlexiG)*
- The [setup package](https://github.com/lesserkuma/FlashGBX/releases) now includes the CH341 USB serial driver for insideGadgets GBxCart RW devices
- The [setup package](https://github.com/Lesserkuma/FlashGBX/releases) now includes the CH341 USB serial driver for insideGadgets GBxCart RW devices
### v2.2 (released 2021-06-03)
- Added support for insideGadgets 2 MB, 32 KB FRAM, v1.0 *(thanks t5b6_de)*

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
import traceback
from serial import SerialException

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
import sys, os, glob, re, json, zlib, argparse, zipfile, traceback, platform, datetime, copy
from . import Util
@ -88,7 +88,7 @@ def main(portableMode=False):
os.environ['QT_MAC_WANTS_LAYER'] = '1'
print("{:s} {:s} by Lesserkuma".format(Util.APPNAME, Util.VERSION))
print("https://github.com/lesserkuma/FlashGBX")
print("https://github.com/Lesserkuma/FlashGBX")
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
app_path = os.path.dirname(sys.executable)
@ -139,6 +139,7 @@ def main(portableMode=False):
ap_cli2.add_argument("--agb-romsize", choices=["auto", "32kb", "64kb", "128kb", "256kb", "512kb", "1mb", "2mb", "4mb", "8mb", "16mb", "32mb", "64mb", "128mb", "256mb", "512mb"], type=str.lower, default="auto", help="set size of Game Boy Advance cartridge ROM data")
ap_cli2.add_argument("--agb-savetype", choices=["auto", "eeprom4k", "eeprom64k", "sram256k", "flash512k", "flash1m", "dacs8m", "sram512k", "sram1m"], type=str.lower, default="auto", help="set type of Game Boy Advance cartridge save data")
ap_cli2.add_argument("--store-rtc", action="store_true", default=False, help="store RTC register values if supported")
ap_cli2.add_argument("--keep-calibration", action="store_true", default=True, help="keep existing calibration data of the e-Reader when writing save data")
ap_cli2.add_argument("--ignore-bad-header", action="store_true", help="dont stop if invalid data found in cartridge header data")
ap_cli2.add_argument("--flashcart-type", type=str, default="autodetect", help="name of flash cart; see txt files in config directory")
ap_cli2.add_argument("--prefer-chip-erase", action="store_true", help="prefer full chip erase over sector erase when both available")

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
import datetime, shutil, platform, os, math, traceback, re, time, serial, zipfile
try:
@ -485,6 +485,9 @@ class FlashGBX_CLI():
bad_read = False
s = ""
if self.CONN.GetMode() == "DMG":
if data["db"]:
s += "Game Name: {:s}\n".format(os.path.splitext(Util.GenerateFileName(mode=self.CONN.GetMode(), header=self.CONN.INFO, settings=None))[0])
s += "Game Title: {:s}\n".format(data["game_title"])
if len(data['game_code']) > 0:
s += "Game Code: {:s}\n".format(data['game_code'])
@ -554,6 +557,9 @@ class FlashGBX_CLI():
self.ARGS["argparsed"].flashcart_type = cart_types[0][i]
elif self.CONN.GetMode() == "AGB":
if data["db"]:
s += "Game Name: {:s}\n".format(os.path.splitext(Util.GenerateFileName(mode=self.CONN.GetMode(), header=self.CONN.INFO, settings=None))[0])
s += "Game Title: {:s}\n".format(data["game_title"])
s += "Game Code: {:s}\n".format(data["game_code"])
s += "Revision: {:d}\n".format(data["version"])
@ -1160,7 +1166,7 @@ class FlashGBX_CLI():
if "ereader_calibration" in self.CONN.INFO:
with open(path, "rb") as f: buffer = bytearray(f.read())
if buffer[0xD000:0xF000] != self.CONN.INFO["ereader_calibration"]:
if not args.overwrite:
if args.keep_calibration:
if args.action == "erase-save": args.action = "restore-save"
print("Note: Keeping existing e-Reader calibration data.")
buffer[0xD000:0xF000] = self.CONN.INFO["ereader_calibration"]

View File

@ -1,12 +1,13 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
import sys, os, time, datetime, json, platform, subprocess, requests, webbrowser, pkg_resources, threading, calendar, queue
import sys, os, time, datetime, json, platform, subprocess, requests, webbrowser, threading, calendar, queue
from .pyside import QtCore, QtWidgets, QtGui, QApplication
from PIL.ImageQt import ImageQt
from PIL import Image
from serial import SerialException
from packaging import version
from .RomFileDMG import RomFileDMG
from .RomFileAGB import RomFileAGB
from .PocketCameraWindow import PocketCameraWindow
@ -204,7 +205,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.mnuConfig.addAction("Use &No-Intro file names", lambda: self.SETTINGS.setValue("UseNoIntroFilenames", str(self.mnuConfig.actions()[7].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
self.mnuConfig.addAction("Automatic cartridge &power off", lambda: [ self.SETTINGS.setValue("AutoPowerOff", str(self.mnuConfig.actions()[8].isChecked()).lower().replace("true", "350").replace("false", "0")), self.SetAutoPowerOff() ])
self.mnuConfig.addAction("Skip writing matching ROM chunk&s", lambda: self.SETTINGS.setValue("CompareSectors", str(self.mnuConfig.actions()[9].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
self.mnuConfig.addAction("Alternative address set mode (can fix or cause write errors)", lambda: self.SETTINGS.setValue("ForceWrPullup", str(self.mnuConfig.actions()[10].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
self.mnuConfig.addAction("Alternate address set mode (can fix or cause write errors)", lambda: self.SETTINGS.setValue("ForceWrPullup", str(self.mnuConfig.actions()[10].isChecked()).lower().replace("true", "enabled").replace("false", "disabled")))
self.mnuConfig.addSeparator()
self.mnuConfigReadModeAGB = QtWidgets.QMenu("&Read Method (Game Boy Advance)")
self.mnuConfigReadModeAGB.addAction("S&tream", lambda: [ self.SETTINGS.setValue("AGBReadMethod", str(self.mnuConfigReadModeAGB.actions()[1].isChecked()).lower().replace("true", "2")), self.SetAGBReadMethod() ])
@ -629,8 +630,8 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.SETTINGS.setValue("UpdateCheck", "disabled")
if update_check and update_check.lower() == "enabled":
print("")
url = "https://api.github.com/repos/lesserkuma/FlashGBX/releases/latest"
site = "https://github.com/lesserkuma/FlashGBX/releases/latest"
url = "https://api.github.com/repos/Lesserkuma/FlashGBX/releases/latest"
site = "https://github.com/Lesserkuma/FlashGBX/releases/latest"
try:
ret = requests.get(url, allow_redirects=True, timeout=1.5)
except requests.exceptions.ConnectTimeout as e:
@ -648,9 +649,9 @@ class FlashGBX_GUI(QtWidgets.QWidget):
ret = json.loads(ret)
if 'tag_name' in ret:
latest_version = str(ret['tag_name'])
if pkg_resources.parse_version(latest_version) == pkg_resources.parse_version(VERSION_PEP440):
if version.parse(latest_version) == version.parse(VERSION_PEP440):
print("You are using the latest version of {:s}.".format(APPNAME))
elif pkg_resources.parse_version(latest_version) > pkg_resources.parse_version(VERSION_PEP440):
elif version.parse(latest_version) > version.parse(VERSION_PEP440):
msg_text = "A new version of {:s} has been released!\nVersion {:s} is now available.".format(APPNAME, latest_version)
print(msg_text)
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Question, windowTitle="{:s} Update Check".format(APPNAME), text=msg_text)
@ -725,14 +726,14 @@ class FlashGBX_GUI(QtWidgets.QWidget):
def AboutFlashGBX(self):
msg = "This software is being developed by Lesserkuma as a hobby project. There is no affiliation with Nintendo or any other company. This software is provided as-is and the developer is not responsible for any damage that is caused by the use of it. Use at your own risk!<br><br>"
msg += f"© 2020{datetime.datetime.now().year} Lesserkuma<br>"
msg += "• Website: <a href=\"https://github.com/lesserkuma/FlashGBX\">https://github.com/lesserkuma/FlashGBX</a><br><br>"
msg += "Acknowledgments and Contributors:<br>2358, 90sFlav, AcoVanConis, AdmirtheSableye, AlexiG, ALXCO-Hardware, AndehX, antPL, aronson, Ausar, bbsan, BennVenn, ccs21, chobby, ClassicOldSong, Cliffback, CodyWick13, Corborg, Cristóbal, crizzlycruz, Crystal, Därk, Davidish, delibird_deals, DevDavisNunez, Diddy_Kong, djedditt, Dr-InSide, dyf2007, easthighNerd, EchelonPrime, edo999, Eldram, Ell, EmperorOfTigers, endrift, Erba Verde, ethanstrax, eveningmoose, Falknör, FerrantePescara, frarees, Frost Clock, Gahr, gandalf1980, gboh, gekkio, Godan, Grender, HDR, Herax, Hiccup, hiks, howie0210, iamevn, Icesythe7, ide, infinest, inYourBackline, iyatemu, Jayro, Jenetrix, JFox, joyrider3774, jrharbort, JS7457, julgr, Kaede, kane159, KOOORAY, kscheel, kyokohunter, Leitplanke, litlemoran, LovelyA72, Lu, Luca DS, LucentW, luxkiller65, manuelcm1, marv17, Merkin, metroid-maniac, Mr_V, Mufsta, olDirdey, orangeglo, paarongiroux, Paradoxical, Rairch, Raphaël BOICHOT, redalchemy, RetroGorek, RevZ, RibShark, s1cp, Satumox, Sgt.DoudouMiel, SH, Shinichi999, Sillyhatday, simonK, Sithdown, skite2001, Smelly-Ghost, Sonikks, Squiddy, Stitch, Super Maker, t5b6_de, Tauwasser, TheNFCookie, Timville, twitnic, velipso, Veund, voltagex, Voultar, Warez Waldo, wickawack, Winter1760, Wkr, x7l7j8cc, xactoes, xukkorz, yosoo, Zeii, Zelante, zipplet, Zoo, zvxr"
msg += "• Website: <a href=\"https://github.com/Lesserkuma/FlashGBX\">https://github.com/Lesserkuma/FlashGBX</a><br><br>"
msg += "Acknowledgments and Contributors:<br>2358, 90sFlav, AcoVanConis, AdmirtheSableye, AlexiG, ALXCO-Hardware, AndehX, antPL, aronson, Ausar, bbsan, BennVenn, ccs21, chobby, ClassicOldSong, Cliffback, CodyWick13, Corborg, Cristóbal, crizzlycruz, Crystal, Därk, Davidish, delibird_deals, DevDavisNunez, Diddy_Kong, djedditt, Dr-InSide, dyf2007, easthighNerd, EchelonPrime, edo999, Eldram, Ell, EmperorOfTigers, endrift, Erba Verde, ethanstrax, eveningmoose, Falknör, FerrantePescara, frarees, Frost Clock, Gahr, gandalf1980, gboh, gekkio, Godan, Grender, HDR, Herax, Hiccup, hiks, howie0210, iamevn, Icesythe7, ide, infinest, inYourBackline, iyatemu, Jayro, Jenetrix, JFox, joyrider3774, jrharbort, JS7457, julgr, Kaede, kane159, KOOORAY, kscheel, kyokohunter, Leif, Leitplanke, litlemoran, LovelyA72, Lu, Luca DS, LucentW, luxkiller65, manuelcm1, marv17, Merkin, metroid-maniac, Mr_V, Mufsta, olDirdey, orangeglo, paarongiroux, Paradoxical, Rairch, Raphaël BOICHOT, redalchemy, RetroGorek, RevZ, RibShark, s1cp, Satumox, Sgt.DoudouMiel, SH, Shinichi999, Sillyhatday, simonK, Sithdown, skite2001, Smelly-Ghost, Sonikks, Squiddy, Stitch, Super Maker, t5b6_de, Tauwasser, TheNFCookie, Timville, twitnic, velipso, Veund, voltagex, Voultar, Warez Waldo, wickawack, Winter1760, Wkr, x7l7j8cc, xactoes, xukkorz, yosoo, Zeii, Zelante, zipplet, Zoo, zvxr"
QtWidgets.QMessageBox.information(self, "{:s} {:s}".format(APPNAME, VERSION), msg, QtWidgets.QMessageBox.Ok)
def AboutGameDB(self):
msg = f"{APPNAME} uses a game database that is based on the ongoing efforts of the No-Intro project. Visit <a href=\"https://no-intro.org/\">https://no-intro.org/</a> for more information.<br><br>"
msg += f"No-Intro databases referenced for this version of {APPNAME}:<br>"
msg += "• Nintendo - Game Boy (20250427-010043)<br>• Nintendo - Game Boy Advance (20250516-204815)<br>• Nintendo - Game Boy Advance (Video) (20241213-211743)<br>• Nintendo - Game Boy Color (20250516-032234)" # No-Intro DBs
msg += "• Nintendo - Game Boy (20251203-165423)<br>• Nintendo - Game Boy Advance (20251202-225320)<br>• Nintendo - Game Boy Advance (Video) (20251114-101831)<br>• Nintendo - Game Boy Color (20251127-150925)" # No-Intro DBs
QtWidgets.QMessageBox.information(self, "{:s} {:s}".format(APPNAME, VERSION), msg, QtWidgets.QMessageBox.Ok)
def OpenPath(self, path=None):
@ -887,7 +888,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
text = "A firmware update for your {:s} device is required to use this software. Do you want to update now?".format(dev.GetFullName())
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=text, standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, defaultButton=QtWidgets.QMessageBox.Yes)
elif dev.FW_UPDATE_REQ == 2:
text = "Your {:s} device is no longer supported in this version of FlashGBX due to technical limitations. The last supported version is <a href=\"https://github.com/lesserkuma/FlashGBX/releases/tag/3.37\">FlashGBX v3.37</a>.\n\nYou can still use the Firmware Updater, however any other functions are no longer available.\n\nDo you want to run the Firmware Updater now?".format(dev.GetFullName()).replace("\n", "<br>")
text = "Your {:s} device is no longer supported in this version of FlashGBX due to technical limitations. The last supported version is <a href=\"https://github.com/Lesserkuma/FlashGBX/releases/tag/3.37\">FlashGBX v3.37</a>.\n\nYou can still use the Firmware Updater, however any other functions are no longer available.\n\nDo you want to run the Firmware Updater now?".format(dev.GetFullName()).replace("\n", "<br>")
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Warning, windowTitle="{:s} {:s}".format(APPNAME, VERSION), text=text, standardButtons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, defaultButton=QtWidgets.QMessageBox.Yes)
else:
text = "A firmware update for your {:s} device is available. Do you want to update now?".format(dev.GetFullName())
@ -988,7 +989,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
"- Check if the operating system detects the device (if not, reboot your machine)\n" \
"- Update the firmware through Options → Tools → Firmware Updater"
if platform.system() == "Darwin":
msg += "\n   - <b>For Joey Jr on macOS:</b> Use the dedicated <a href=\"https://github.com/lesserkuma/JoeyJr_FWUpdater\">Firmware Updater for Joey Jr</a>"
msg += "\n   - <b>For Joey Jr on macOS:</b> Use the dedicated <a href=\"https://github.com/Lesserkuma/JoeyJr_FWUpdater\">Firmware Updater for Joey Jr</a>"
elif platform.system() == "Linux":
msg += "\n- <b>For Linux users:</b> Ensure your user account has permissions to use the device (try adding yourself to user groups “dialout” or “uucp”)"
@ -2273,11 +2274,11 @@ class FlashGBX_GUI(QtWidgets.QWidget):
detected = False
if detected is False:
intro_msg = "In order to access Batteryless SRAM save data, its ROM location and size must be specified.\n\n"
if mode == "AGB":
max_size = self.cmbAGBHeaderROMSizeResult.currentText().replace(" ", " ")
elif mode == "DMG":
max_size = self.cmbDMGHeaderROMSizeResult.currentText().replace(" ", " ")
intro_msg2 = "⚠️ The required parameters could not be auto-detected. Please enter the ROM location and size manually below. Note that wrong values can corrupt your game upon writing, so having a full " + max_size + " ROM backup is recommended."
# if mode == "AGB":
# max_size = self.cmbAGBHeaderROMSizeResult.currentText().replace(" ", " ")
# elif mode == "DMG":
# max_size = self.cmbDMGHeaderROMSizeResult.currentText().replace(" ", " ")
intro_msg2 = "⚠️ The required parameters could not be auto-detected. Please enter the ROM location and size manually below. Note that wrong values can corrupt your game upon writing, so having a full ROM backup is recommended."
if mode == "DMG":
# Load database of observed configurations from various bootlegs
@ -2600,7 +2601,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
self.CONN.SetMode("DMG")
elif self.optAGB.isChecked() and (mode == "DMG" or mode == None):
self.CONN.SetMode("AGB")
except BrokenPipeError:
except (BrokenPipeError, SerialException):
msg = "Failed to turn on the cartridge power.\n\nThe “Automatic cartridge power off” setting has therefore been disabled. Please re-connect the device and try again."
self.mnuConfig.actions()[5].setChecked(False)
self.SETTINGS.setValue("AutoPowerOff", "0")
@ -2635,7 +2636,7 @@ class FlashGBX_GUI(QtWidgets.QWidget):
try:
data = self.CONN.ReadInfo(setPinsAsInputs=True)
except SerialException:
except (BrokenPipeError, SerialException):
self.LimitBaudRateGBxCartRW()
self.DisconnectDevice()
QtWidgets.QMessageBox.critical(self, "{:s} {:s}".format(APPNAME, VERSION), "The connection to the device was lost while trying to read the ROM header. This may happen if the inserted cartridge issues a short circuit or its peak power draw is too high.\n\nAs a potential workaround for the latter, you can try hotswapping the cartridge:\n1. Remove the cartridge from the device.\n2. Reconnect the device and select mode.\n3. Then insert the cartridge and click “{:s}”.".format(self.btnHeaderRefresh.text().replace("&", "")), QtWidgets.QMessageBox.Ok)

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
import time, copy, math, struct
from .Util import dprint, bitswap

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
import datetime, struct, copy, zlib, hashlib
from . import Util

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
import time, math, struct, traceback, zlib, copy, hashlib, os, datetime, platform, json, base64
import serial, serial.tools.list_ports
@ -147,6 +147,7 @@ class LK_Device(ABC):
AGB_READ_METHODS = ["Single", "MemCpy", "Stream"]
LAST_CHECK_ACTIVE = 0
USER_ANSWER = None
SKIP_POWERCYCLE = False
def __init__(self):
pass
@ -721,7 +722,7 @@ class LK_Device(ABC):
continue
def CartPowerCycle(self, delay=0.1):
if self.CanPowerCycleCart():
if self.CanPowerCycleCart() and not self.SKIP_POWERCYCLE:
dprint("Power cycling cartridge with a delay of {:.1f} seconds".format(delay))
self.CartPowerOff(delay=delay)
self.CartPowerOn(delay=delay)
@ -868,6 +869,7 @@ class LK_Device(ABC):
dprint(f"Setting automatic power off time value to {value}")
self._set_fw_variable("AUTO_POWEROFF_TIME", value)
self._set_fw_variable("AUTO_POWEROFF_ENABLED", 1 if value != 0 else 0)
self.SKIP_POWERCYCLE = False if value != 0 else True
def GetSupportedCartridgesDMG(self):
return (list(self.SUPPORTED_CARTS['DMG'].keys()), list(self.SUPPORTED_CARTS['DMG'].values()))
@ -1104,7 +1106,7 @@ class LK_Device(ABC):
if self.MODE == "DMG": #and setPinsAsInputs:
self._write(self.DEVICE_CMD["SET_ADDR_AS_INPUTS"], wait=self.FW["fw_ver"] >= 12)
self.Debug()
#self.Debug()
return data
def _DetectCartridge(self, args): # Wrapper for thread call
@ -1140,7 +1142,7 @@ class LK_Device(ABC):
# Disable Auto Power Off
_apoe = False
if self.FW["fw_ver"] >= 12:
if self.FW["fw_ver"] >= 12 and self.SKIP_POWERCYCLE is False:
if self.CanPowerCycleCart():
self.CartPowerCycle()
_apoe = self._get_fw_variable("AUTO_POWEROFF_ENABLED") == 1
@ -1266,7 +1268,7 @@ class LK_Device(ABC):
save_size = Util.AGB_Flash_Save_Chips_Sizes[list(Util.AGB_Flash_Save_Chips).index(flash_save_id)]
save_chip = Util.AGB_Flash_Save_Chips[flash_save_id]
if flash_save_id in (0xBF5B, 0xFFFF): # Bootlegs
if flash_save_id in (0xBF4B, 0xBF5B, 0xFFFF): # Bootlegs
if self.INFO["data"][0:0x20000] == bytearray([0xFF] * 0x20000):
save_type = 5
elif self.INFO["data"][0:0x10000] == self.INFO["data"][0x10000:0x20000]:
@ -1332,7 +1334,7 @@ class LK_Device(ABC):
self.INFO["last_action"] = 0
self.INFO["action"] = None
if self.CanPowerCycleCart() and _apoe is True:
if self.CanPowerCycleCart() and _apoe is True and self.SKIP_POWERCYCLE is False:
self._set_fw_variable("AUTO_POWEROFF_TIME", _apot)
return (info, save_size, save_type, save_chip, sram_unstable, cart_types, cart_type_id, cfi_s, cfi, flash_id, detected_size)
@ -2882,6 +2884,7 @@ class LK_Device(ABC):
# Calculate Global Checksum
if self.MODE == "DMG":
self.INFO["dump_info"]["header"].update(RomFileDMG(buffer[:0x180]).GetHeader(unchanged=True))
if _mbc.GetName() == "MMM01":
self.INFO["dump_info"]["header"].update(RomFileDMG(buffer[-0x8000:-0x8000+0x180]).GetHeader(unchanged=True))
elif _mbc.GetName() == "Sachen":
@ -3118,7 +3121,7 @@ class LK_Device(ABC):
return False
buffer_len = 0x1000
(agb_flash_chip, _) = ret
if agb_flash_chip in (0xBF5B, 0xFFFF): # Bootlegs
if agb_flash_chip in (0xBF4B, 0xBF5B, 0xFFFF): # Bootlegs
buffer_len = 0x800
elif args["save_type"] == 6: # DACS
# self._write(self.DEVICE_CMD["AGB_BOOTUP_SEQUENCE"], wait=self.FW["fw_ver"] >= 12)
@ -3255,7 +3258,7 @@ class LK_Device(ABC):
end_address = min(save_size, bank_size)
if save_size > bank_size:
if args["save_type"] == 8 or agb_flash_chip in (0xBF5B, 0xFFFF): # Bootleg 1M
if args["save_type"] == 8 or agb_flash_chip in (0xBF4B, 0xBF5B, 0xFFFF): # Bootleg 1M
dprint("Switching to bootleg save bank {:d}".format(bank))
self._cart_write(0x1000000, bank)
elif args["save_type"] == 5: # FLASH 1M
@ -3563,7 +3566,7 @@ class LK_Device(ABC):
buffer[0xFF80:0x10000] = self.INFO["data"][0xFF80:0x10000]
buffer[0x1FF80:0x20000] = self.INFO["data"][0xFF80:0x10000]
elif (self.INFO["data"][:end_address] != buffer[:end_address]):
if (self.INFO["data"][:end_address] != buffer[:end_address]):
msg = ""
count = 0
time_start = time.time()

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
import time, datetime, struct, math, hashlib
from dateutil.relativedelta import relativedelta
@ -260,13 +260,6 @@ class DMG_MBC3(DMG_MBC):
def GetName(self):
return "MBC3"
def EnableRAM(self, enable=True):
dprint(self.GetName(), "|", enable)
commands = [
[ 0x0000, 0x0A if enable else 0x00 ],
]
self.CartWrite(commands)
def HasRTC(self):
dprint("Checking for RTC")
if self.MBC_ID not in (0x0F, 0x10, 0x110, 0x206):
@ -472,20 +465,6 @@ class DMG_MBC5(DMG_MBC):
def GetName(self):
return "MBC5"
def EnableRAM(self, enable=True):
dprint(self.GetName(), "|", enable)
if enable:
commands = [
[ 0x6000, 0x01 ],
[ 0x0000, 0x0A ],
]
else:
commands = [
[ 0x0000, 0x00 ],
[ 0x6000, 0x00 ],
]
self.CartWrite(commands)
def SelectBankROM(self, index):
dprint(self.GetName(), "|", index)
@ -701,7 +680,7 @@ class DMG_MMM01(DMG_MBC):
class DMG_GBD(DMG_MBC5):
def GetName(self):
return "MAC-GBD"
def SelectBankROM(self, index):
dprint(self.GetName(), "|", index)
commands = [

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
from PIL import Image
from PIL.PngImagePlugin import PngInfo

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
import functools, os, json, platform, shutil
from PIL.ImageQt import ImageQt

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
import hashlib, re, zlib, string, os, json, copy, struct
from . import Util

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
import hashlib, re, string, struct, os, json, copy
from . import Util

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
from .pyside import QtCore, QtWidgets, QtGui

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
import math, time, datetime, copy, configparser, threading, os, platform, traceback, io, struct, re, statistics, random, sys
from io import StringIO
@ -8,9 +8,9 @@ from enum import Enum
# Common constants
APPNAME = "FlashGBX"
VERSION_PEP440 = "4.4"
VERSION_PEP440 = "4.5"
VERSION = "v{:s}".format(VERSION_PEP440)
VERSION_TIMESTAMP = 1748007939
VERSION_TIMESTAMP = 1765034084
DEBUG = False
DEBUG_LOG = []
APP_PATH = ""
@ -20,8 +20,8 @@ AGB_Header_ROM_Sizes = [ "32 KiB", "64 KiB", "128 KiB", "256 KiB", "512 KiB", "1
AGB_Header_ROM_Sizes_Map = [ 0x8000, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000, 0x200000, 0x400000, 0x800000, 0x1000000, 0x2000000, 0x4000000, 0x8000000, 0x10000000, 0x20000000 ]
AGB_Header_Save_Types = [ "None", "4K EEPROM (512 Bytes)", "64K EEPROM (8 KiB)", "256K SRAM/FRAM (32 KiB)", "512K FLASH (64 KiB)", "1M FLASH (128 KiB)", "8M DACS (1 MiB)", "Unlicensed 512K SRAM (64 KiB)", "Unlicensed 1M SRAM (128 KiB)", "Unlicensed Batteryless SRAM" ]
AGB_Header_Save_Sizes = [ 0, 512, 8192, 32768, 65536, 131072, 1048576, 65536, 131072, 0 ]
AGB_Flash_Save_Chips = { 0xBFD4:"SST 39VF512", 0x1F3D:"Atmel AT29LV512", 0xC21C:"Macronix MX29L512", 0x321B:"Panasonic MN63F805MNP", 0xC209:"Macronix MX29L010", 0x6213:"SANYO LE26FV10N1TS", 0xBF5B:"Unlicensed SST49LF080A", 0xFFFF:"Unlicensed 0xFFFF" }
AGB_Flash_Save_Chips_Sizes = [ 0x10000, 0x10000, 0x10000, 0x10000, 0x20000, 0x20000, 0x20000, 0x20000 ]
AGB_Flash_Save_Chips = { 0xBFD4:"SST 39VF512", 0x1F3D:"Atmel AT29LV512", 0xC21C:"Macronix MX29L512", 0x321B:"Panasonic MN63F805MNP", 0xC209:"Macronix MX29L010", 0x6213:"SANYO LE26FV10N1TS", 0xBF4B:"Unlicensed SST25VF064C", 0xBF5B:"Unlicensed SST49LF080A", 0xFFFF:"Unlicensed 0xFFFF" }
AGB_Flash_Save_Chips_Sizes = [ 0x10000, 0x10000, 0x10000, 0x10000, 0x20000, 0x20000, 0x20000, 0x20000, 0x20000 ]
DMG_Header_Mapper = { 0x00:'None', 0x01:'MBC1', 0x02:'MBC1+SRAM', 0x03:'MBC1+SRAM+BATTERY', 0x06:'MBC2+SRAM+BATTERY', 0x0F:'MBC3+RTC+BATTERY', 0x10:'MBC3+RTC+SRAM+BATTERY', 0x110:'MBC30+RTC+SRAM+BATTERY', 0x12:'MBC3+SRAM', 0x13:'MBC3+SRAM+BATTERY', 0x19:'MBC5', 0x1A:'MBC5+SRAM', 0x1B:'MBC5+SRAM+BATTERY', 0x1C:'MBC5+RUMBLE', 0x1E:'MBC5+RUMBLE+SRAM+BATTERY', 0x20:'MBC6+SRAM+FLASH+BATTERY', 0x22:'MBC7+ACCELEROMETER+EEPROM', 0x101:'MBC1M', 0x103:'MBC1M+SRAM+BATTERY', 0x0B:'MMM01', 0x0D:'MMM01+SRAM+BATTERY', 0xFC:'MAC-GBD+SRAM+BATTERY', 0x105:'G-MMC1+SRAM+BATTERY', 0x104:'M161', 0xFF:'HuC-1+IR+SRAM+BATTERY', 0xFE:'HuC-3+RTC+SRAM+BATTERY', 0xFD:'TAMA5+RTC+EEPROM', 0x201:'Unlicensed 256M Mapper', 0x202:'Unlicensed Wisdom Tree Mapper', 0x203:'Unlicensed Xploder GB Mapper', 0x204:'Unlicensed Sachen Mapper', 0x205:'Unlicensed Datel Orbit V2 Mapper', 0x206:'Unlicensed MBCX Mapper' }
DMG_Mapper_Types = { "None":[ 0x00, 0x08, 0x09 ], "MBC1":[ 0x01, 0x02, 0x03 ], "MBC2":[ 0x05, 0x06 ], "MBC3":[ 0x0F, 0x10, 0x11, 0x12, 0x13 ], "MBC30":[ 0x110 ], "MBC5":[ 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E ], "MBC6":[ 0x20 ], "MBC7":[ 0x22 ], "MBC1M":[ 0x101, 0x103 ], "MMM01":[ 0x0B, 0x0D ], "MAC-GBD":[ 0xFC ], "G-MMC1":[ 0x105 ], "M161":[ 0x104 ], "HuC-1":[ 0xFF ], "HuC-3":[ 0xFE ], "TAMA5":[ 0xFD ], "Unlicensed 256M Multi Cart Mapper":[ 0x201 ], "Unlicensed Wisdom Tree Mapper":[ 0x202 ], "Unlicensed Xploder GB Mapper":[ 0x203 ], "Unlicensed Sachen Mapper":[ 0x204 ], "Unlicensed Datel Orbit V2 Mapper":[ 0x205 ], "Unlicensed MBCX Mapper":[ 0x206 ] }

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
from . import FlashGBX
FlashGBX.main()

View File

@ -3,11 +3,13 @@
"names":[
"BX2006_TSOP_64BALL with GL256S",
"BGA64B-71-TV-DEEP with 256M29EML",
"FunnyPlaying MidnightTrace 32 MiB Flash Cart"
"FunnyPlaying MidnightTrace 32 MiB Flash Cart",
"MXP54_16D_046 with MSP54LV256"
],
"flash_ids":[
[ 0x02, 0x00, 0x7D, 0x22 ],
[ 0x8A, 0x00, 0x7D, 0x22 ],
[ 0x02, 0x00, 0x7D, 0x22 ],
[ 0x02, 0x00, 0x7D, 0x22 ]
],
"voltage":3.3,

View File

@ -0,0 +1,90 @@
{
"type":"AGB",
"names":[
"MXP54_16D_ERATH with MSP54LV256"
],
"flash_ids":[
[ 0x02, 0x00, 0xBD, 0x22 ]
],
"voltage":3.3,
"flash_size":0x2000000,
"sector_size_from_cfi":true,
"_reset_every":0x100000,
"status_register_mask":0x40,
"status_register_value":0x40,
"chip_erase_timeout":300,
"command_set":"AMD",
"commands":{
"reset":[
[ 0, 0xF0 ]
],
"read_identifier":[
[ 0xAAA, 0x69 ],
[ 0x555, 0x96 ],
[ 0xAAA, 0x50 ]
],
"read_cfi":[
[ 0xAA, 0x58 ]
],
"sector_erase":[
[ 0xAAA, 0x69 ],
[ 0x555, 0x96 ],
[ 0xAAA, 0x40 ],
[ 0xAAA, 0x69 ],
[ 0x555, 0x96 ],
[ "SA", 0x30 ]
],
"sector_erase_wait_for":[
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ "SA", 0xFFFF, 0xFFFF ]
],
"chip_erase":[
[ 0xAAA, 0x69 ],
[ 0x555, 0x96 ],
[ 0xAAA, 0x40 ],
[ 0xAAA, 0x69 ],
[ 0x555, 0x96 ],
[ 0xAAA, 0x10 ]
],
"chip_erase_wait_for":[
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ 0, 0xFFFF, 0xFFFF ]
],
"buffer_write":[
[ 0xAAA, 0x69 ],
[ 0x555, 0x96 ],
[ "SA", 0x26 ],
[ "SA", "BS" ],
[ "PA", "PD" ],
[ "SA", 0x2A ]
],
"buffer_write_wait_for":[
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ "SA", "PD", 0xFFFF ]
],
"single_write":[
[ 0xAAA, 0x69 ],
[ 0x555, 0x96 ],
[ 0xAAA, 0x60 ],
[ "PA", "PD" ]
],
"single_write_wait_for":[
[ null, null, null ],
[ null, null, null ],
[ null, null, null ],
[ null, null, null ]
]
}
}

View File

@ -11,6 +11,7 @@
[ 0x20, 0x00, 0x7D, 0x00 ]
],
"voltage":3.3,
"voltage_variants":true,
"flash_size":0x400000,
"start_addr":0,
"first_bank":1,

View File

@ -2,11 +2,13 @@
"type":"DMG",
"names":[
"GBFlash RTC with MX29LV320EB",
"GBFlash RTC with MX29LV320EB"
"GBFlash RTC with MX29LV320EB",
"ModRetro Chromatic Cartridge with S29JL032"
],
"flash_ids":[
[ 0x01, 0x01, 0x7E, 0x7E ],
[ 0xC2, 0xC2, 0xA8, 0xA8 ]
[ 0xC2, 0xC2, 0xA8, 0xA8 ],
[ 0x01, 0x01, 0x53, 0x53 ]
],
"voltage":5,
"flash_size":0x400000,

View File

@ -1,10 +1,12 @@
{
"type":"DMG",
"names":[
"insideGadgets 4 MiB (S29GL032M)"
"insideGadgets 4 MiB (S29GL032M)",
"ModRetro Chromatic Cartridge with IS29GL032"
],
"flash_ids":[
[ 0x01, 0x01, 0x7E, 0x7E ]
[ 0x01, 0x01, 0x7E, 0x7E ],
[ 0x9D, 0x9D, 0x7E, 0x7E ]
],
"voltage":5,
"flash_size":0x400000,

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
import zipfile, serial, struct, time, datetime
try:

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
import zipfile, os, serial, struct, time, re, math
from .pyside import QtCore, QtWidgets, QtGui, QDesktopWidget

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
import zipfile, serial, struct, time, random, hashlib, datetime
try:

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
import zipfile, time, os, struct, serial, platform
from serial import SerialException
@ -422,7 +422,7 @@ try:
verified = False
if verified is False:
text = "The firmware update file is corrupted."
text = "The firmware update file is corrupted or invalid."
self.btnUpdate.setEnabled(True)
self.btnClose.setEnabled(True)
msgbox = QtWidgets.QMessageBox(parent=self, icon=QtWidgets.QMessageBox.Critical, windowTitle="FlashGBX", text=text, standardButtons=QtWidgets.QMessageBox.Ok)

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
# pylint: disable=wildcard-import, unused-wildcard-import
from .LK_Device import *

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
# pylint: disable=wildcard-import, unused-wildcard-import
from .LK_Device import *

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
# pylint: disable=wildcard-import, unused-wildcard-import
from .LK_Device import *

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
#
# PySide abstraction layer contributed by J-Fox
#

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,8 +1,8 @@
# FlashGBX (by Lesserkuma)
for Windows, Linux, macOS (→ [Download](https://github.com/lesserkuma/FlashGBX/releases))
for Windows, Linux, macOS (→ [Download](https://github.com/Lesserkuma/FlashGBX/releases))
<img src="https://raw.githubusercontent.com/lesserkuma/FlashGBX/master/.github/01.png" alt="FlashGBX on Windows 11" width="500"><br><img src="https://raw.githubusercontent.com/lesserkuma/FlashGBX/master/.github/02.png" alt="GB Camera Album Viewer" width="500">
<img src="https://raw.githubusercontent.com/Lesserkuma/FlashGBX/master/.github/01.png" alt="FlashGBX on Windows 11" width="500"><br><img src="https://raw.githubusercontent.com/Lesserkuma/FlashGBX/master/.github/02.png" alt="GB Camera Album Viewer" width="500">
## Introduction
@ -108,6 +108,7 @@ for Windows, Linux, macOS (→ [Download](https://github.com/lesserkuma/FlashGBX
- insideGadgets 4 MiB (2× 2 MiB), 32 KiB FRAM, MBC5
- insideGadgets MegaDuck 32K
- ModRetro Chromatic Cartridge with 39VF1681
- ModRetro Chromatic Cartridge with IS29GL032
- Mr Flash 64M
- Sillyhatday MBC5-DUAL-FLASH-4/8MB
- Squareboi 4 MiB (2× 2 MiB)
@ -185,6 +186,7 @@ for Windows, Linux, macOS (→ [Download](https://github.com/lesserkuma/FlashGBX
- SD007_BV5_V3 with AM29LV160MB
- SD007_K8D3216_32M with MX29LV160CT
- SD007_T40_48BALL_71_TV_TS28 with M29W640
- SD007_T40_6401B\*CD_71_TS28 with 39VF6401B
- SD007_T40_64BALL_S71_TV_TS28 with TC58FVB016FT-85
- SD007_T40_64BALL_SOJ28 with 29LV016T
- SD007_T40_64BALL_TSOP28 with 29LV016T
@ -276,6 +278,8 @@ for Windows, Linux, macOS (→ [Download](https://github.com/lesserkuma/FlashGBX
- M6MGJ927 (no PCB text)
- MSP54LV512 (no PCB text)
- MX29GL128EHT2I and ALTERA CPLD
- MXP54_16D_046 with MSP54LV256
- MXP54_16D_ERATH with MSP54LV256
- SUN100S_MSP54XXX with MSP54LV100
- Unknown 29LV320 variant (no PCB text)
@ -287,19 +291,20 @@ Many different reproduction cartridges share their flash chip command set, so ev
### Pre-compiled binaries and packages
Available in the GitHub [Releases](https://github.com/lesserkuma/FlashGBX/releases) section are pre-compiled downloads available for:
Available in the GitHub [Releases](https://github.com/Lesserkuma/FlashGBX/releases) section are pre-compiled downloads available for:
* **Windows (64-bit)** *(Windows 8 or newer)*
* Setup: An installer that will add the application to the start menu and optionally create a desktop icon
* Portable: Have everything in one place including the config files
* **Linux**
* Pre-made packages are contributed by JJ-Fox [here](https://github.com/JJ-Fox/FlashGBX-Linux-builds/releases/latest).
* Ubuntu (.deb file): Install using `dpkg -i /path/to/FlashGBX_x.x_Ubuntu-all.deb`.
* Other distributions: Pre-made Linux packages are available at [JJ-Foxs repository](https://github.com/JJ-Fox/FlashGBX-Linux-builds/releases/latest).
* **macOS** *(Monterey 12 or newer)*
* x86-64/arm64 (.dmg file): Install by opening the .dmg file and copying over the “FlashGBX” application to the desktop.<br>If it doesnt run, it probably got quarantined during download. Run the following command in a Terminal window to unquarantine it: `xattr -d com.apple.quarantine /path/to/FlashGBX.app`.<br>*(Based on a contribution by [Cliffback](https://github.com/Cliffback))*
* x86-64/arm64 (.dmg file): Install by opening the .dmg file and copying over the “FlashGBX” application to the desktop.<br>If it doesnt run, it probably got quarantined due to the lack of a Apple Developer Program certificate. Right-click the extracted FlashGBX icon, choose “Open Terminal at Folder” and enter this command to unquarantine it: `xattr -d com.apple.quarantine ../FlashGBX.app`.
*(If you have a Joey Jr and use macOS, please run the [Joey Jr Firmware Updater](https://github.com/lesserkuma/JoeyJr_FWUpdater) before using FlashGBX.)*
*(If you have a Joey Jr and use macOS, please run the [Joey Jr Firmware Updater](https://github.com/Lesserkuma/JoeyJr_FWUpdater) before using FlashGBX.)*
### Run via Python
@ -385,7 +390,7 @@ Thanks to the No-Intro project for their game databases which FlashGBXs datab
## Third Party Notices and Licenses
Please view the <a href="https://github.com/lesserkuma/FlashGBX/blob/master/Third%20Party%20Notices.md">Third Party Notices</a>.
Please view the <a href="https://github.com/Lesserkuma/FlashGBX/blob/master/Third%20Party%20Notices.md">Third Party Notices</a>.
## DISCLAIMER

2
run.py
View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
# Note: This file runs FlashGBX in portable mode.

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# FlashGBX
# Author: Lesserkuma (github.com/lesserkuma)
# Author: Lesserkuma (github.com/Lesserkuma)
import setuptools
@ -8,10 +8,10 @@ with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read(
setuptools.setup(
name="FlashGBX",
version="4.4",
version="4.5",
author="Lesserkuma",
description="Reads and writes Game Boy and Game Boy Advance cartridge data",
url="https://github.com/lesserkuma/FlashGBX",
url="https://github.com/Lesserkuma/FlashGBX",
packages=setuptools.find_packages(),
install_requires=['pyserial>=3.5', 'Pillow', 'setuptools', 'requests', 'python-dateutil'],
extras_require={
@ -30,8 +30,8 @@ setuptools.setup(
'Intended Audience :: Developers',
],
project_urls={
'Source': 'https://github.com/lesserkuma/FlashGBX/',
'Tracker': 'https://github.com/lesserkuma/FlashGBX/issues',
'Source': 'https://github.com/Lesserkuma/FlashGBX/',
'Tracker': 'https://github.com/Lesserkuma/FlashGBX/issues',
},
entry_points={
'console_scripts': (