mirror of
https://github.com/spicyjpeg/573in1.git
synced 2025-01-22 19:52:05 +01:00
Get the tool into an almost working state
This commit is contained in:
parent
6b33d4edd1
commit
b0f269eb6e
@ -10,12 +10,15 @@
|
||||
"read": "Dumping cartridge...",
|
||||
"identifyGame": "Attempting to identify game..."
|
||||
},
|
||||
"hddDumpWorker": {
|
||||
"save": "Saving cartridge dump..."
|
||||
},
|
||||
"qrCodeWorker": {
|
||||
"compress": "Compressing cartridge dump...",
|
||||
"generate": "Generating QR code..."
|
||||
},
|
||||
"rebootWorker": {
|
||||
"reboot": "Resetting system..."
|
||||
"reboot": "Rebooting system..."
|
||||
}
|
||||
},
|
||||
|
||||
@ -41,7 +44,8 @@
|
||||
},
|
||||
"hddDump": {
|
||||
"name": "Dump cartridge to hard drive",
|
||||
"prompt": "Save the contents of the cartridge's EEPROM to a file on the IDE hard drive."
|
||||
"prompt": "Save the contents of the cartridge's EEPROM to a file on the IDE hard drive or CF card connected as secondary drive (if any).",
|
||||
"error": "An error occurred while saving the dump to the hard drive. Turn off the system and make sure the drive is connected to the IDE bus properly, set as secondary and formatted with a single FAT16, FAT32 or exFAT partition.\n\nPress the Test button to view debug logs."
|
||||
},
|
||||
"hexdump": {
|
||||
"name": "View cartridge hexdump",
|
||||
@ -60,8 +64,9 @@
|
||||
"prompt": "Wipe all data and flash the cartridge with another game's identifiers. All cartridges can be converted for use with any other game that uses the same cartridge type."
|
||||
},
|
||||
"erase": {
|
||||
"name": "Erase cartridge",
|
||||
"prompt": "Wipe all data including game identifiers. Erased cartridges can be flashed for use with games unsupported by this tool using a master calendar."
|
||||
"name": "Erase cartridge",
|
||||
"prompt": "Wipe all data including game identifiers. Erased cartridges can be flashed for use with games unsupported by this tool using a master calendar.",
|
||||
"confirm": "The contents of the cartridge's EEPROM will be erased, making it unusable until reflashed and resetting the unlocking key to 00-00-00-00-00-00-00-00. The cartridge ID will not be altered.\n\nDo you wish to proceed?"
|
||||
}
|
||||
},
|
||||
|
||||
@ -86,13 +91,19 @@
|
||||
|
||||
"locked": {
|
||||
"unidentified": "This cartridge contains data for an unsupported game.\n\nAs the cartridge is currently locked, you will have to manually select which game it belongs to in order to dump its contents or reflash it for use with a supported game, as each game has a different unlocking key.",
|
||||
"identified": "This cartridge has been identified as:\n %s\n\nIf this is correct, you may proceed to unlock the cartridge using the appropriate key for this game in order to access and modify its contents.",
|
||||
"identified": "This cartridge has been identified as:\n %s\n %s\n\nIf this is correct, you may proceed to unlock the cartridge using the appropriate key for this game in order to access and modify its contents.",
|
||||
"unknown": "This cartridge cannot be identified without unlocking it first.\n\nYou will have to manually select which game it belongs to in order to dump its contents or reflash it for use with a supported game, as each game has a different unlocking key."
|
||||
},
|
||||
"unlocked": {
|
||||
"unidentified": "This cartridge contains data for an unsupported game.\n\nThe system identifier (if any) cannot be reset or edited, however you may still dump the cartridge's contents or reflash it for use with a supported game.",
|
||||
"identified": "This cartridge has been identified as:\n %s\n\nYou may now proceed to reset the system identifier, edit it or erase and reflash the cartridge for use with another game.",
|
||||
"identified": "This cartridge has been identified as:\n %s\n %s\n\nYou may now proceed to reset the system identifier, edit it or erase and reflash the cartridge for use with another game.",
|
||||
"blank": "This cartridge has been previously erased and is now blank.\n\nIt must be flashed and optionally initialized with a system identifier in order to be used with a supported game."
|
||||
},
|
||||
"pairing": {
|
||||
"unsupported": "This game does not pair to I/O boards",
|
||||
"unpaired": "Not paired to any digital I/O board",
|
||||
"thisSystem": "Paired to this system's I/O board",
|
||||
"otherSystem": "Paired to %s"
|
||||
}
|
||||
},
|
||||
"prompt": {
|
||||
@ -118,6 +129,17 @@
|
||||
}
|
||||
},
|
||||
|
||||
"ConfirmScreen": {
|
||||
"title": "Confirm",
|
||||
"no": "No, go back",
|
||||
"yes": "Yes, continue"
|
||||
},
|
||||
|
||||
"ErrorScreen": {
|
||||
"title": "Error",
|
||||
"ok": "Continue"
|
||||
},
|
||||
|
||||
"QRCodeScreen": {
|
||||
"title": "{CART_ICON} Cartridge dump",
|
||||
"prompt": "Scan this code and paste the resulting string into the decodeDump.py script provided alongside this tool to obtain a dump of the cartridge. Press {START_BUTTON} to go back."
|
||||
@ -128,18 +150,6 @@
|
||||
"prompt": "Press {START_BUTTON} to go back."
|
||||
},
|
||||
|
||||
"UnlockConfirmScreen": {
|
||||
"title": "Warning",
|
||||
"no": "No, go back",
|
||||
"yes": "Yes, continue"
|
||||
},
|
||||
|
||||
"UnlockErrorScreen": {
|
||||
"title": "Error",
|
||||
|
||||
"ok": "Back to cartridge info"
|
||||
},
|
||||
|
||||
"UnlockKeyScreen": {
|
||||
"title": "{CART_ICON} Select unlocking key",
|
||||
"prompt": "If the cartridge has been converted before, select the game it was last converted to.",
|
||||
@ -147,8 +157,8 @@
|
||||
|
||||
"autoUnlock": "Use key from identified game (recommended)",
|
||||
"useCustomKey": "Enter key manually...",
|
||||
"useNullKey1": "Use null key (00-00-00-00-00-00-00-00)",
|
||||
"useNullKey2": "Use null key (FF-FF-FF-FF-FF-FF-FF-FF)"
|
||||
"use00Key": "Use null key (00-00-00-00-00-00-00-00)",
|
||||
"useFFKey": "Use null key (FF-FF-FF-FF-FF-FF-FF-FF)"
|
||||
},
|
||||
|
||||
"WarningScreen": {
|
||||
|
@ -58,6 +58,10 @@ void CartActionsScreen::qrDump(ui::Context &ctx) {
|
||||
}
|
||||
|
||||
void CartActionsScreen::hddDump(ui::Context &ctx) {
|
||||
APP->_errorScreen.setMessage(*this, STR("CartActionsScreen.hddDump.error"));
|
||||
|
||||
APP->_setupWorker(&App::_hddDumpWorker);
|
||||
ctx.show(APP->_workerStatusScreen, false, true);
|
||||
}
|
||||
|
||||
void CartActionsScreen::hexdump(ui::Context &ctx) {
|
||||
@ -67,6 +71,16 @@ void CartActionsScreen::reflash(ui::Context &ctx) {
|
||||
}
|
||||
|
||||
void CartActionsScreen::erase(ui::Context &ctx) {
|
||||
APP->_confirmScreen.setMessage(
|
||||
*this,
|
||||
[](ui::Context &ctx) {
|
||||
//APP->_setupWorker(&App::_eraseWorker);
|
||||
//ctx.show(APP->_workerStatusScreen, false, true);
|
||||
},
|
||||
STR("CartActionsScreen.erase.confirm")
|
||||
);
|
||||
|
||||
ctx.show(APP->_confirmScreen, false, true);
|
||||
}
|
||||
|
||||
void CartActionsScreen::resetSystemID(ui::Context &ctx) {
|
||||
@ -75,15 +89,18 @@ void CartActionsScreen::resetSystemID(ui::Context &ctx) {
|
||||
void CartActionsScreen::editSystemID(ui::Context &ctx) {
|
||||
}
|
||||
|
||||
void CartActionsScreen::show(ui::Context &ctx, bool goBack) {
|
||||
void CartActionsScreen::show(ui::Context &ctx, bool goBack) {
|
||||
_title = STR("CartActionsScreen.title");
|
||||
_prompt = STRH(_ACTIONS[0].prompt);
|
||||
_itemPrompt = STR("CartActionsScreen.itemPrompt");
|
||||
|
||||
_listLength = 1;
|
||||
/*_listLength = APP->_identified
|
||||
#if 0 // TODO
|
||||
_listLength = APP->_identified
|
||||
? _NUM_IDENTIFIED_ACTIONS
|
||||
: _NUM_UNIDENTIFIED_ACTIONS;*/
|
||||
: _NUM_UNIDENTIFIED_ACTIONS;
|
||||
#else
|
||||
_listLength = 2;
|
||||
#endif
|
||||
|
||||
ListScreen::show(ctx, goBack);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "app/app.hpp"
|
||||
#include "ps1/system.h"
|
||||
#include "cart.hpp"
|
||||
@ -65,9 +66,13 @@ void App::_cartDetectWorker(void) {
|
||||
_unloadCartData();
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (_provider->loadStruct(_dump, "data/test.dump")) {
|
||||
if (
|
||||
_resourceProvider->loadStruct(_dump, "data/test.573")
|
||||
== sizeof(cart::Dump)
|
||||
) {
|
||||
LOG("using dummy cart driver");
|
||||
_driver = new cart::DummyDriver(_dump);
|
||||
_driver->readSystemID();
|
||||
} else {
|
||||
_driver = cart::newCartDriver(_dump);
|
||||
}
|
||||
@ -87,7 +92,7 @@ void App::_cartDetectWorker(void) {
|
||||
LOG("cart parser @ 0x%08x", _parser);
|
||||
_workerStatus.update(2, 4, WSTR("App.cartDetectWorker.identifyGame"));
|
||||
|
||||
if (!_provider->loadData(_db, _CARTDB_PATHS[_dump.chipType])) {
|
||||
if (!_resourceProvider->loadData(_db, _CARTDB_PATHS[_dump.chipType])) {
|
||||
LOG("%s not found", _CARTDB_PATHS[_dump.chipType]);
|
||||
goto _cartInitDone;
|
||||
}
|
||||
@ -107,8 +112,8 @@ _cartInitDone:
|
||||
util::Data bitstream;
|
||||
bool ready;
|
||||
|
||||
if (!_provider->loadData(bitstream, "data/fpga.bit")) {
|
||||
LOG("failed to load bitstream");
|
||||
if (!_resourceProvider->loadData(bitstream, "data/fpga.bit")) {
|
||||
LOG("bitstream unavailable");
|
||||
goto _initDone;
|
||||
}
|
||||
|
||||
@ -134,7 +139,7 @@ void App::_cartUnlockWorker(void) {
|
||||
_workerStatus.update(0, 2, WSTR("App.cartUnlockWorker.read"));
|
||||
|
||||
if (_driver->readPrivateData()) {
|
||||
_workerStatus.finish(_unlockErrorScreen);
|
||||
_workerStatus.finish(_errorScreen);
|
||||
_dummyWorker();
|
||||
return;
|
||||
}
|
||||
@ -172,6 +177,26 @@ void App::_qrCodeWorker(void) {
|
||||
_dummyWorker();
|
||||
}
|
||||
|
||||
void App::_hddDumpWorker(void) {
|
||||
_workerStatus.update(0, 1, WSTR("App.hddDumpWorker.save"));
|
||||
|
||||
char code[8], region[8], path[32];
|
||||
|
||||
if (_parser->getCode(code) && _parser->getRegion(region))
|
||||
snprintf(path, sizeof(path), "%s%s.573", code, region);
|
||||
else
|
||||
__builtin_strcpy(path, "unknown_cart.573");
|
||||
|
||||
LOG("saving dump as %s", path);
|
||||
|
||||
if (_fileProvider->saveStruct(_dump, path) == sizeof(cart::Dump))
|
||||
_workerStatus.finish(_cartInfoScreen);
|
||||
else
|
||||
_workerStatus.finish(_errorScreen);
|
||||
|
||||
_dummyWorker();
|
||||
}
|
||||
|
||||
void App::_rebootWorker(void) {
|
||||
int startTime = _ctx->time;
|
||||
int duration = _ctx->gpuCtx.refreshRate * 3;
|
||||
@ -200,19 +225,21 @@ void App::_interruptHandler(void) {
|
||||
|
||||
if (_allowWatchdogClear)
|
||||
io::clearWatchdog();
|
||||
if (gpu::isIdle())
|
||||
if (gpu::isIdle() && (_workerStatus.status != WORKER_BUSY_SUSPEND))
|
||||
switchThread(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void App::run(
|
||||
ui::Context &ctx, file::Provider &provider, file::StringTable &strings
|
||||
ui::Context &ctx, file::Provider &resourceProvider,
|
||||
file::Provider &fileProvider, file::StringTable &stringTable
|
||||
) {
|
||||
LOG("starting app @ 0x%08x", this);
|
||||
|
||||
_ctx = &ctx;
|
||||
_provider = &provider;
|
||||
_strings = &strings;
|
||||
_ctx = &ctx;
|
||||
_resourceProvider = &resourceProvider;
|
||||
_fileProvider = &fileProvider;
|
||||
_stringTable = &stringTable;
|
||||
|
||||
ctx.screenData = this;
|
||||
|
||||
|
@ -14,46 +14,58 @@
|
||||
|
||||
/* Worker status class */
|
||||
|
||||
enum WorkerStatusType {
|
||||
WORKER_IDLE = 0,
|
||||
WORKER_BUSY = 1,
|
||||
WORKER_BUSY_SUSPEND = 2, // Prevent main thread from running
|
||||
WORKER_NEXT = 3, // Go to next screen (goBack=false)
|
||||
WORKER_NEXT_BACK = 4 // Go to next screen (goBack=true)
|
||||
};
|
||||
|
||||
// This class is used by the worker thread to report its current status back to
|
||||
// the main thread and the WorkerStatusScreen.
|
||||
class WorkerStatus {
|
||||
public:
|
||||
volatile int progress, progressTotal;
|
||||
volatile bool nextGoBack;
|
||||
volatile WorkerStatusType status;
|
||||
|
||||
volatile int progress, progressTotal;
|
||||
|
||||
const char *volatile message;
|
||||
ui::Screen *volatile nextScreen;
|
||||
|
||||
inline void update(int part, int total) {
|
||||
inline void reset(void) {
|
||||
status = WORKER_IDLE;
|
||||
progress = 0;
|
||||
progressTotal = 1;
|
||||
message = nullptr;
|
||||
nextScreen = nullptr;
|
||||
}
|
||||
inline void update(int part, int total, const char *text = nullptr) {
|
||||
auto mask = setInterruptMask(0);
|
||||
status = WORKER_BUSY;
|
||||
progress = part;
|
||||
progressTotal = total;
|
||||
|
||||
if (text)
|
||||
message = text;
|
||||
if (mask)
|
||||
setInterruptMask(mask);
|
||||
}
|
||||
inline void update(int part, int total, const char *text) {
|
||||
auto mask = setInterruptMask(0);
|
||||
progress = part;
|
||||
progressTotal = total;
|
||||
message = text;
|
||||
inline void suspendMainThread(void) {
|
||||
auto mask = setInterruptMask(0);
|
||||
status = WORKER_BUSY_SUSPEND;
|
||||
|
||||
if (mask)
|
||||
setInterruptMask(mask);
|
||||
}
|
||||
inline void finish(ui::Screen &next, bool goBack = false) {
|
||||
auto mask = setInterruptMask(0);
|
||||
auto mask = setInterruptMask(0);
|
||||
status = goBack ? WORKER_NEXT_BACK : WORKER_NEXT;
|
||||
nextScreen = &next;
|
||||
nextGoBack = goBack;
|
||||
|
||||
if (mask)
|
||||
setInterruptMask(mask);
|
||||
}
|
||||
inline void reset(void) {
|
||||
progress = 0;
|
||||
progressTotal = 1;
|
||||
nextScreen = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/* App class */
|
||||
@ -64,10 +76,11 @@ class App {
|
||||
friend class WorkerStatusScreen;
|
||||
friend class WarningScreen;
|
||||
friend class ButtonMappingScreen;
|
||||
friend class ErrorScreen;
|
||||
friend class ConfirmScreen;
|
||||
friend class CartInfoScreen;
|
||||
friend class UnlockKeyScreen;
|
||||
friend class UnlockConfirmScreen;
|
||||
friend class UnlockErrorScreen;
|
||||
friend class KeyEntryScreen;
|
||||
friend class CartActionsScreen;
|
||||
friend class QRCodeScreen;
|
||||
|
||||
@ -75,16 +88,17 @@ private:
|
||||
WorkerStatusScreen _workerStatusScreen;
|
||||
WarningScreen _warningScreen;
|
||||
ButtonMappingScreen _buttonMappingScreen;
|
||||
ErrorScreen _errorScreen;
|
||||
ConfirmScreen _confirmScreen;
|
||||
CartInfoScreen _cartInfoScreen;
|
||||
UnlockKeyScreen _unlockKeyScreen;
|
||||
UnlockConfirmScreen _unlockConfirmScreen;
|
||||
UnlockErrorScreen _unlockErrorScreen;
|
||||
KeyEntryScreen _keyEntryScreen;
|
||||
CartActionsScreen _cartActionsScreen;
|
||||
QRCodeScreen _qrCodeScreen;
|
||||
|
||||
ui::Context *_ctx;
|
||||
file::Provider *_provider;
|
||||
file::StringTable *_strings;
|
||||
file::Provider *_resourceProvider, *_fileProvider;
|
||||
file::StringTable *_stringTable;
|
||||
|
||||
cart::Dump _dump;
|
||||
cart::CartDB _db;
|
||||
@ -106,6 +120,7 @@ private:
|
||||
void _cartDetectWorker(void);
|
||||
void _cartUnlockWorker(void);
|
||||
void _qrCodeWorker(void);
|
||||
void _hddDumpWorker(void);
|
||||
void _rebootWorker(void);
|
||||
|
||||
void _interruptHandler(void);
|
||||
@ -123,14 +138,15 @@ public:
|
||||
}
|
||||
|
||||
void run(
|
||||
ui::Context &ctx, file::Provider &provider, file::StringTable &strings
|
||||
ui::Context &ctx, file::Provider &resourceProvider,
|
||||
file::Provider &fileProvider, file::StringTable &stringTable
|
||||
);
|
||||
};
|
||||
|
||||
#define APP (reinterpret_cast<App *>(ctx.screenData))
|
||||
#define STR(id) (APP->_strings->get(id ## _h))
|
||||
#define STRH(id) (APP->_strings->get(id))
|
||||
#define STR(id) (APP->_stringTable->get(id ## _h))
|
||||
#define STRH(id) (APP->_stringTable->get(id))
|
||||
|
||||
#define WAPP (reinterpret_cast<App *>(_ctx->screenData))
|
||||
#define WSTR(id) (WAPP->_strings->get(id ## _h))
|
||||
#define WSTRH(id) (WAPP->_strings->get(id))
|
||||
#define WSTR(id) (WAPP->_stringTable->get(id ## _h))
|
||||
#define WSTRH(id) (WAPP->_stringTable->get(id))
|
||||
|
@ -1,4 +1,5 @@
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include "app/app.hpp"
|
||||
#include "app/misc.hpp"
|
||||
@ -15,18 +16,19 @@ void WorkerStatusScreen::show(ui::Context &ctx, bool goBack) {
|
||||
}
|
||||
|
||||
void WorkerStatusScreen::update(ui::Context &ctx) {
|
||||
auto &status = APP->_workerStatus;
|
||||
auto nextScreen = status.nextScreen;
|
||||
auto &worker = APP->_workerStatus;
|
||||
auto nextScreen = worker.nextScreen;
|
||||
|
||||
if (worker.status >= WORKER_NEXT) {
|
||||
worker.reset();
|
||||
ctx.show(*nextScreen, worker.status == WORKER_NEXT_BACK);
|
||||
|
||||
if (nextScreen) {
|
||||
LOG("worker finished, next=0x%08x", nextScreen);
|
||||
|
||||
ctx.show(*nextScreen, status.nextGoBack);
|
||||
return;
|
||||
}
|
||||
|
||||
_setProgress(ctx, status.progress, status.progressTotal);
|
||||
_body = status.message;
|
||||
_setProgress(ctx, worker.progress, worker.progressTotal);
|
||||
_body = worker.message;
|
||||
}
|
||||
|
||||
static constexpr int WARNING_COOLDOWN = 15;
|
||||
@ -99,3 +101,68 @@ void ButtonMappingScreen::update(ui::Context &ctx) {
|
||||
ctx.show(APP->_workerStatusScreen, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
void ErrorScreen::setMessage(ui::Screen &prev, const char *format, ...) {
|
||||
_prevScreen = &prev;
|
||||
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
vsnprintf(_bodyText, sizeof(_bodyText), format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void ErrorScreen::show(ui::Context &ctx, bool goBack) {
|
||||
_title = STR("ErrorScreen.title");
|
||||
_body = _bodyText;
|
||||
_buttons[0] = STR("ErrorScreen.ok");
|
||||
|
||||
_numButtons = 1;
|
||||
_locked = _prevScreen ? false : true;
|
||||
|
||||
MessageScreen::show(ctx, goBack);
|
||||
}
|
||||
|
||||
void ErrorScreen::update(ui::Context &ctx) {
|
||||
MessageScreen::update(ctx);
|
||||
|
||||
if (ctx.buttons.pressed(ui::BTN_START))
|
||||
ctx.show(*_prevScreen, true, true);
|
||||
}
|
||||
|
||||
void ConfirmScreen::setMessage(
|
||||
ui::Screen &prev, void (*callback)(ui::Context &ctx), const char *format,
|
||||
...
|
||||
) {
|
||||
_prevScreen = &prev;
|
||||
_callback = callback;
|
||||
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
vsnprintf(_bodyText, sizeof(_bodyText), format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void ConfirmScreen::show(ui::Context &ctx, bool goBack) {
|
||||
_title = STR("ConfirmScreen.title");
|
||||
_body = _bodyText;
|
||||
_buttons[0] = STR("ConfirmScreen.no");
|
||||
_buttons[1] = STR("ConfirmScreen.yes");
|
||||
|
||||
_numButtons = 2;
|
||||
|
||||
MessageScreen::show(ctx, goBack);
|
||||
ctx.sounds[ui::SOUND_ERROR].play();
|
||||
}
|
||||
|
||||
void ConfirmScreen::update(ui::Context &ctx) {
|
||||
MessageScreen::update(ctx);
|
||||
|
||||
if (ctx.buttons.pressed(ui::BTN_START)) {
|
||||
if (_activeButton)
|
||||
_callback(ctx);
|
||||
else
|
||||
ctx.show(*_prevScreen, true, true);
|
||||
}
|
||||
}
|
||||
|
@ -30,3 +30,31 @@ public:
|
||||
void show(ui::Context &ctx, bool goBack = false);
|
||||
void update(ui::Context &ctx);
|
||||
};
|
||||
|
||||
class ErrorScreen : public ui::MessageScreen {
|
||||
private:
|
||||
char _bodyText[512];
|
||||
ui::Screen *_prevScreen;
|
||||
|
||||
public:
|
||||
void setMessage(ui::Screen &prev, const char *format, ...);
|
||||
|
||||
void show(ui::Context &ctx, bool goBack = false);
|
||||
void update(ui::Context &ctx);
|
||||
};
|
||||
|
||||
class ConfirmScreen : public ui::MessageScreen {
|
||||
private:
|
||||
char _bodyText[512];
|
||||
ui::Screen *_prevScreen;
|
||||
void (*_callback)(ui::Context &ctx);
|
||||
|
||||
public:
|
||||
void setMessage(
|
||||
ui::Screen &prev, void (*callback)(ui::Context &ctx),
|
||||
const char *format, ...
|
||||
);
|
||||
|
||||
void show(ui::Context &ctx, bool goBack = false);
|
||||
void update(ui::Context &ctx);
|
||||
};
|
||||
|
@ -134,11 +134,12 @@ void CartInfoScreen::show(ui::Context &ctx, bool goBack) {
|
||||
// - unlocked, no private data, blank
|
||||
// => only dumping/flashing available
|
||||
IdentifyState state;
|
||||
char name[96];
|
||||
char name[96], pairStatus[64];
|
||||
|
||||
if (APP->_identified) {
|
||||
state = IDENTIFIED;
|
||||
APP->_identified->getDisplayName(name, sizeof(name));
|
||||
pairStatus[0] = 0; // TODO
|
||||
} else if (dump.flags & cart::DUMP_PUBLIC_DATA_OK) {
|
||||
state = APP->_dump.isReadableDataEmpty() ? BLANK_CART : UNIDENTIFIED;
|
||||
} else {
|
||||
@ -146,11 +147,15 @@ void CartInfoScreen::show(ui::Context &ctx, bool goBack) {
|
||||
}
|
||||
|
||||
if (dump.flags & cart::DUMP_PRIVATE_DATA_OK) {
|
||||
ptr += snprintf(ptr, end - ptr, STRH(_UNLOCKED_PROMPTS[state]), name);
|
||||
ptr += snprintf(
|
||||
ptr, end - ptr, STRH(_UNLOCKED_PROMPTS[state]), name, pairStatus
|
||||
);
|
||||
|
||||
_prompt = STR("CartInfoScreen.prompt.unlocked");
|
||||
} else {
|
||||
ptr += snprintf(ptr, end - ptr, STRH(_LOCKED_PROMPTS[state]), name);
|
||||
ptr += snprintf(
|
||||
ptr, end - ptr, STRH(_LOCKED_PROMPTS[state]), name, pairStatus
|
||||
);
|
||||
|
||||
_prompt = STR("CartInfoScreen.prompt.locked");
|
||||
}
|
||||
@ -188,11 +193,11 @@ static const SpecialEntry _SPECIAL_ENTRIES[]{
|
||||
.name = 0,
|
||||
.target = nullptr
|
||||
}, {
|
||||
.name = "UnlockKeyScreen.useNullKey2"_h,
|
||||
.target = &UnlockKeyScreen::useNullKey2
|
||||
.name = "UnlockKeyScreen.useFFKey"_h,
|
||||
.target = &UnlockKeyScreen::useFFKey
|
||||
}, {
|
||||
.name = "UnlockKeyScreen.useNullKey1"_h,
|
||||
.target = &UnlockKeyScreen::useNullKey1
|
||||
.name = "UnlockKeyScreen.use00Key"_h,
|
||||
.target = &UnlockKeyScreen::use00Key
|
||||
}, {
|
||||
.name = "UnlockKeyScreen.useCustomKey"_h,
|
||||
.target = &UnlockKeyScreen::useCustomKey
|
||||
@ -223,25 +228,25 @@ void UnlockKeyScreen::autoUnlock(ui::Context &ctx) {
|
||||
APP->_dump.dataKey, APP->_identified->dataKey,
|
||||
sizeof(APP->_dump.dataKey)
|
||||
);
|
||||
ctx.show(APP->_unlockConfirmScreen, false, true);
|
||||
ctx.show(APP->_confirmScreen, false, true);
|
||||
}
|
||||
|
||||
void UnlockKeyScreen::useCustomKey(ui::Context &ctx) {
|
||||
//ctx.show(APP->_unlockKeyEntryScreen, false, true);
|
||||
ctx.show(APP->_keyEntryScreen, false, true);
|
||||
}
|
||||
|
||||
void UnlockKeyScreen::useNullKey1(ui::Context &ctx) {
|
||||
void UnlockKeyScreen::use00Key(ui::Context &ctx) {
|
||||
__builtin_memset(
|
||||
APP->_dump.dataKey, 0x00, sizeof(APP->_dump.dataKey)
|
||||
);
|
||||
ctx.show(APP->_unlockConfirmScreen, false, true);
|
||||
ctx.show(APP->_confirmScreen, false, true);
|
||||
}
|
||||
|
||||
void UnlockKeyScreen::useNullKey2(ui::Context &ctx) {
|
||||
void UnlockKeyScreen::useFFKey(ui::Context &ctx) {
|
||||
__builtin_memset(
|
||||
APP->_dump.dataKey, 0xff, sizeof(APP->_dump.dataKey)
|
||||
);
|
||||
ctx.show(APP->_unlockConfirmScreen, false, true);
|
||||
ctx.show(APP->_confirmScreen, false, true);
|
||||
}
|
||||
|
||||
void UnlockKeyScreen::show(ui::Context &ctx, bool goBack) {
|
||||
@ -260,6 +265,20 @@ void UnlockKeyScreen::update(ui::Context &ctx) {
|
||||
if (ctx.buttons.pressed(ui::BTN_START)) {
|
||||
int index = _activeItem + _getSpecialEntryOffset(ctx);
|
||||
|
||||
APP->_confirmScreen.setMessage(
|
||||
APP->_unlockKeyScreen,
|
||||
[](ui::Context &ctx) {
|
||||
APP->_setupWorker(&App::_cartUnlockWorker);
|
||||
ctx.show(APP->_workerStatusScreen, false, true);
|
||||
},
|
||||
STRH(_CART_TYPES[APP->_dump.chipType].warning)
|
||||
);
|
||||
|
||||
APP->_errorScreen.setMessage(
|
||||
APP->_cartInfoScreen,
|
||||
STRH(_CART_TYPES[APP->_dump.chipType].error)
|
||||
);
|
||||
|
||||
if (index < 0) {
|
||||
(this->*_SPECIAL_ENTRIES[-index].target)(ctx);
|
||||
} else {
|
||||
@ -267,50 +286,15 @@ void UnlockKeyScreen::update(ui::Context &ctx) {
|
||||
APP->_dump.dataKey, APP->_db.get(index)->dataKey,
|
||||
sizeof(APP->_dump.dataKey)
|
||||
);
|
||||
ctx.show(APP->_unlockConfirmScreen, false, true);
|
||||
ctx.show(APP->_confirmScreen, false, true);
|
||||
}
|
||||
} else if (ctx.buttons.held(ui::BTN_LEFT) && ctx.buttons.held(ui::BTN_RIGHT)) {
|
||||
ctx.show(APP->_cartInfoScreen, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
void UnlockConfirmScreen::show(ui::Context &ctx, bool goBack) {
|
||||
_title = STR("UnlockConfirmScreen.title");
|
||||
_body = STRH(_CART_TYPES[APP->_dump.chipType].warning);
|
||||
_buttons[0] = STR("UnlockConfirmScreen.no");
|
||||
_buttons[1] = STR("UnlockConfirmScreen.yes");
|
||||
|
||||
_numButtons = 2;
|
||||
|
||||
MessageScreen::show(ctx, goBack);
|
||||
void KeyEntryScreen::show(ui::Context &ctx, bool goBack) {
|
||||
}
|
||||
|
||||
void UnlockConfirmScreen::update(ui::Context &ctx) {
|
||||
MessageScreen::update(ctx);
|
||||
|
||||
if (ctx.buttons.pressed(ui::BTN_START)) {
|
||||
if (_activeButton) {
|
||||
APP->_setupWorker(&App::_cartUnlockWorker);
|
||||
ctx.show(APP->_workerStatusScreen, false, true);
|
||||
} else {
|
||||
ctx.show(APP->_unlockKeyScreen, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UnlockErrorScreen::show(ui::Context &ctx, bool goBack) {
|
||||
_title = STR("UnlockErrorScreen.title");
|
||||
_body = STRH(_CART_TYPES[APP->_dump.chipType].error);
|
||||
_buttons[0] = STR("UnlockErrorScreen.ok");
|
||||
|
||||
_numButtons = 1;
|
||||
|
||||
MessageScreen::show(ctx, goBack);
|
||||
}
|
||||
|
||||
void UnlockErrorScreen::update(ui::Context &ctx) {
|
||||
MessageScreen::update(ctx);
|
||||
|
||||
if (ctx.buttons.pressed(ui::BTN_START))
|
||||
ctx.show(APP->_cartInfoScreen, true, true);
|
||||
void KeyEntryScreen::update(ui::Context &ctx) {
|
||||
}
|
||||
|
@ -25,20 +25,14 @@ protected:
|
||||
public:
|
||||
void autoUnlock(ui::Context &ctx);
|
||||
void useCustomKey(ui::Context &ctx);
|
||||
void useNullKey1(ui::Context &ctx);
|
||||
void useNullKey2(ui::Context &ctx);
|
||||
void use00Key(ui::Context &ctx);
|
||||
void useFFKey(ui::Context &ctx);
|
||||
|
||||
void show(ui::Context &ctx, bool goBack = false);
|
||||
void update(ui::Context &ctx);
|
||||
};
|
||||
|
||||
class UnlockConfirmScreen : public ui::MessageScreen {
|
||||
public:
|
||||
void show(ui::Context &ctx, bool goBack = false);
|
||||
void update(ui::Context &ctx);
|
||||
};
|
||||
|
||||
class UnlockErrorScreen : public ui::MessageScreen {
|
||||
class KeyEntryScreen : public ui::HexEntryScreen {
|
||||
public:
|
||||
void show(ui::Context &ctx, bool goBack = false);
|
||||
void update(ui::Context &ctx);
|
||||
|
@ -154,6 +154,7 @@ class [[gnu::packed]] Dump {
|
||||
public:
|
||||
ChipType chipType;
|
||||
uint8_t flags;
|
||||
uint8_t _reserved[2];
|
||||
|
||||
Identifier systemID, cartID, zsID;
|
||||
|
||||
|
@ -142,7 +142,7 @@ public:
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
static constexpr int _NUM_KNOWN_FORMATS = 12;
|
||||
static constexpr int _NUM_KNOWN_FORMATS = 11;
|
||||
|
||||
static const KnownFormat _KNOWN_FORMATS[_NUM_KNOWN_FORMATS]{
|
||||
{
|
||||
@ -216,7 +216,7 @@ bool isValidRegion(const char *region) {
|
||||
if (region[2] == 'Z') {
|
||||
if (!__builtin_isdigit(region[3]) || !__builtin_isdigit(region[4]))
|
||||
return false;
|
||||
|
||||
|
||||
region += 2;
|
||||
}
|
||||
if (region[3])
|
||||
|
@ -20,6 +20,8 @@ DriverError DummyDriver::readSystemID(void) {
|
||||
}
|
||||
|
||||
DriverError DummyDriver::readCartID(void) {
|
||||
if (_privateDump.flags & DUMP_ZS_ID_OK)
|
||||
_dump.flags |= DUMP_ZS_ID_OK;
|
||||
if (_privateDump.flags & DUMP_CART_ID_OK) {
|
||||
_dump.flags |= DUMP_CART_ID_OK;
|
||||
return NO_ERROR;
|
||||
@ -195,7 +197,7 @@ enum X76F041ConfigOp : uint8_t {
|
||||
_X76F041_CFG_SET_DATA_KEY = 0x20,
|
||||
_X76F041_CFG_READ_CONFIG = 0x60,
|
||||
_X76F041_CFG_WRITE_CONFIG = 0x50,
|
||||
_X76F041_CFG_ERASE = 0x70
|
||||
_X76F041_CFG_MASS_PROGRAM = 0x70
|
||||
};
|
||||
|
||||
DriverError X76F041Driver::readPrivateData(void) {
|
||||
@ -282,7 +284,7 @@ DriverError X76F041Driver::writeData(void) {
|
||||
|
||||
DriverError X76F041Driver::erase(void) {
|
||||
auto error = _x76Command(
|
||||
_X76F041_CONFIG, _X76F041_CFG_ERASE, _X76F041_ACK_POLL
|
||||
_X76F041_CONFIG, _X76F041_CFG_MASS_PROGRAM, _X76F041_ACK_POLL
|
||||
);
|
||||
if (error)
|
||||
return error;
|
||||
|
@ -51,9 +51,8 @@ private:
|
||||
public:
|
||||
inline DummyDriver(Dump &dump)
|
||||
: Driver(dump), _privateDump(dump) {
|
||||
_dump.flags = _privateDump.flags & (
|
||||
DUMP_HAS_SYSTEM_ID | DUMP_HAS_CART_ID
|
||||
);
|
||||
_dump.flags = _privateDump.flags &
|
||||
(DUMP_HAS_SYSTEM_ID | DUMP_HAS_CART_ID);
|
||||
}
|
||||
|
||||
DriverError readSystemID(void);
|
||||
|
16
src/file.cpp
16
src/file.cpp
@ -108,7 +108,7 @@ void FATFile::close(void) {
|
||||
uint32_t currentSPUOffset = spu::DUMMY_BLOCK_END;
|
||||
|
||||
size_t Provider::loadData(util::Data &output, const char *path) {
|
||||
auto file = openFile(path);
|
||||
auto file = openFile(path, READ);
|
||||
|
||||
if (!file)
|
||||
return 0;
|
||||
@ -124,7 +124,7 @@ size_t Provider::loadData(util::Data &output, const char *path) {
|
||||
}
|
||||
|
||||
size_t Provider::loadData(void *output, size_t length, const char *path) {
|
||||
auto file = openFile(path);
|
||||
auto file = openFile(path, READ);
|
||||
|
||||
if (!file)
|
||||
return 0;
|
||||
@ -135,7 +135,17 @@ size_t Provider::loadData(void *output, size_t length, const char *path) {
|
||||
|
||||
return actualLength;
|
||||
}
|
||||
size_t Provider::saveData(const void *input, size_t length, const char *path) {
|
||||
auto file = openFile(path, WRITE | ALLOW_CREATE);
|
||||
|
||||
if (!file)
|
||||
return 0;
|
||||
|
||||
size_t actualLength = file->write(input, length);
|
||||
file->close();
|
||||
|
||||
return actualLength;
|
||||
}
|
||||
size_t Provider::loadTIM(gpu::Image &output, const char *path) {
|
||||
util::Data data;
|
||||
|
||||
@ -332,7 +342,7 @@ size_t ZIPProvider::loadData(void *output, size_t length, const char *path) {
|
||||
|
||||
/* String table parser */
|
||||
|
||||
static const char _ERROR_STRING[] = "missingno";
|
||||
static const char _ERROR_STRING[]{ "missingno" };
|
||||
|
||||
const char *StringTable::get(util::Hash id) const {
|
||||
if (!ptr)
|
||||
|
@ -77,12 +77,16 @@ public:
|
||||
template<class T> inline size_t loadStruct(T &output, const char *path) {
|
||||
return loadData(&output, sizeof(output), path);
|
||||
}
|
||||
template<class T> inline size_t saveStruct(const T &input, const char *path) {
|
||||
return saveData(&input, sizeof(input), path);
|
||||
}
|
||||
|
||||
virtual void close(void) {}
|
||||
|
||||
virtual File *openFile(const char *path) { return nullptr; }
|
||||
virtual File *openFile(const char *path, uint32_t flags) { return nullptr; }
|
||||
virtual size_t loadData(util::Data &output, const char *path);
|
||||
virtual size_t loadData(void *output, size_t length, const char *path);
|
||||
virtual size_t saveData(const void *input, size_t length, const char *path);
|
||||
size_t loadTIM(gpu::Image &output, const char *path);
|
||||
size_t loadVAG(spu::Sound &output, const char *path);
|
||||
};
|
||||
|
@ -20,7 +20,7 @@ size_t upload(const RectWH &rect, const void *data, bool wait) {
|
||||
size_t length = (rect.w * rect.h) / 2;
|
||||
|
||||
util::assertAligned<uint32_t>(data);
|
||||
assert(!(length % _DMA_CHUNK_SIZE));
|
||||
//assert(!(length % _DMA_CHUNK_SIZE));
|
||||
length = (length + _DMA_CHUNK_SIZE - 1) / _DMA_CHUNK_SIZE;
|
||||
|
||||
if (!waitForDMATransfer(DMA_GPU, _DMA_TIMEOUT))
|
||||
|
15
src/ide.cpp
15
src/ide.cpp
@ -8,6 +8,19 @@
|
||||
#include "io.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
/*
|
||||
* Based on the following specifications:
|
||||
*
|
||||
* - "AT Attachment with Packet Interface - 6", 2001-06-26
|
||||
* - "CF+ and CompactFlash Specification Revision 3.0", 2004-12-23
|
||||
* - SFF-8020i "ATA Packet Interface for CD-ROMs 2.6", 1996-01-22 (seems to be
|
||||
* rather inaccurate about the IDE side of things, but some drives actually
|
||||
* implement those inaccuracies!)
|
||||
*
|
||||
* https://www.cs.utexas.edu/~dahlin/Classes/UGOS/reading/ide.html
|
||||
* https://web.archive.org/web/20060427142409/http://www.stanford.edu/~csapuntz/blackmagic.html
|
||||
*/
|
||||
|
||||
namespace ide {
|
||||
|
||||
static constexpr int _STATUS_TIMEOUT = 100000;
|
||||
@ -83,6 +96,8 @@ DeviceError Device::_waitForStatus(uint8_t mask, uint8_t value, int timeout) {
|
||||
return NO_ERROR;
|
||||
|
||||
delayMicroseconds(1);
|
||||
if (acknowledgeInterrupt(IRQ_VBLANK))
|
||||
io::clearWatchdog();
|
||||
}
|
||||
|
||||
LOG("IDE timeout, stat=0x%02x, err=0x%02x", _read(CS0_STATUS), _read(CS0_ERROR));
|
||||
|
@ -15,10 +15,11 @@
|
||||
extern "C" const uint8_t _resources[];
|
||||
extern "C" const size_t _resourcesSize;
|
||||
|
||||
static const char _DEFAULT_RESOURCE_ZIP_PATH[] = "cartToolResources.zip";
|
||||
static const char _DEFAULT_RESOURCE_ZIP_PATH[]{ "cart_tool_resources.zip" };
|
||||
|
||||
static const char *const _UI_SOUND_PATHS[ui::NUM_UI_SOUNDS]{
|
||||
"assets/sounds/startup.vag", // ui::SOUND_STARTUP
|
||||
"assets/sounds/error.vag", // ui::SOUND_ERROR
|
||||
"assets/sounds/move.vag", // ui::SOUND_MOVE
|
||||
"assets/sounds/enter.vag", // ui::SOUND_ENTER
|
||||
"assets/sounds/exit.vag", // ui::SOUND_EXIT
|
||||
@ -175,6 +176,6 @@ _resourceInitDone:
|
||||
io::setMiscOutput(io::MISC_SPU_ENABLE, true);
|
||||
io::clearWatchdog();
|
||||
|
||||
app.run(uiCtx, resourceProvider, strings);
|
||||
app.run(uiCtx, resourceProvider, fileProvider, strings);
|
||||
return 0;
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ size_t upload(uint32_t ramOffset, const void *data, size_t length, bool wait) {
|
||||
length /= 4;
|
||||
|
||||
util::assertAligned<uint32_t>(data);
|
||||
assert(!(length % _DMA_CHUNK_SIZE));
|
||||
//assert(!(length % _DMA_CHUNK_SIZE));
|
||||
length = (length + _DMA_CHUNK_SIZE - 1) / _DMA_CHUNK_SIZE;
|
||||
|
||||
if (!waitForDMATransfer(DMA_SPU, _DMA_TIMEOUT))
|
||||
|
@ -31,14 +31,15 @@ enum Color : gpu::Color {
|
||||
COLOR_SUBTITLE = 0x4078a0
|
||||
};
|
||||
|
||||
static constexpr int NUM_UI_SOUNDS = 5;
|
||||
static constexpr int NUM_UI_SOUNDS = 6;
|
||||
|
||||
enum Sound {
|
||||
SOUND_STARTUP = 0,
|
||||
SOUND_MOVE = 1,
|
||||
SOUND_ENTER = 2,
|
||||
SOUND_EXIT = 3,
|
||||
SOUND_CLICK = 4
|
||||
SOUND_ERROR = 1,
|
||||
SOUND_MOVE = 2,
|
||||
SOUND_ENTER = 3,
|
||||
SOUND_EXIT = 4,
|
||||
SOUND_CLICK = 5
|
||||
};
|
||||
|
||||
enum AnimationSpeed {
|
||||
|
@ -89,6 +89,7 @@ void MessageScreen::update(Context &ctx) {
|
||||
if (ctx.buttons.pressed(ui::BTN_RIGHT)) {
|
||||
if (_activeButton < (_numButtons - 1)) {
|
||||
_activeButton++;
|
||||
|
||||
_buttonAnim.setValue(ctx.time, 0, _getButtonWidth(), SPEED_FASTEST);
|
||||
ctx.sounds[SOUND_MOVE].play();
|
||||
} else {
|
||||
@ -183,11 +184,8 @@ void ImageScreen::draw(Context &ctx, bool active) const {
|
||||
}
|
||||
|
||||
// Image
|
||||
width += 1;
|
||||
height += 1;
|
||||
|
||||
_image.drawScaled(
|
||||
ctx.gpuCtx, x - width, y - height, width * 2, height * 2
|
||||
ctx.gpuCtx, x - width - 1, y - height - 1, width * 2, height * 2
|
||||
);
|
||||
}
|
||||
|
||||
@ -357,7 +355,13 @@ void ListScreen::update(Context &ctx) {
|
||||
}
|
||||
|
||||
HexEntryScreen::HexEntryScreen(void)
|
||||
: _title(nullptr), _prompt(nullptr) {}
|
||||
: _numDigits(0), _title(nullptr), _prompt(nullptr) {}
|
||||
|
||||
void HexEntryScreen::show(Context &ctx, bool goBack) {
|
||||
AnimatedScreen::show(ctx, goBack);
|
||||
|
||||
_activeItem = 0;
|
||||
}
|
||||
|
||||
void HexEntryScreen::draw(Context &ctx, bool active) const {
|
||||
int screenWidth = ctx.gpuCtx.width - SCREEN_MARGIN_X * 2;
|
||||
@ -381,6 +385,35 @@ void HexEntryScreen::draw(Context &ctx, bool active) const {
|
||||
}
|
||||
|
||||
void HexEntryScreen::update(Context &ctx) {
|
||||
if (ctx.buttons.held(ui::BTN_START)) {
|
||||
} else {
|
||||
if (
|
||||
ctx.buttons.pressed(ui::BTN_LEFT) ||
|
||||
(ctx.buttons.repeating(ui::BTN_LEFT) && (_activeItem > 0))
|
||||
) {
|
||||
if (_activeItem > 0) {
|
||||
_activeItem--;
|
||||
|
||||
//_buttonAnim.setValue(ctx.time, 0, _getButtonWidth(), SPEED_FASTEST);
|
||||
ctx.sounds[SOUND_MOVE].play();
|
||||
} else {
|
||||
ctx.sounds[SOUND_CLICK].play();
|
||||
}
|
||||
}
|
||||
if (
|
||||
ctx.buttons.pressed(ui::BTN_RIGHT) ||
|
||||
(ctx.buttons.repeating(ui::BTN_RIGHT) && (_activeItem < (_numDigits - 1)))
|
||||
) {
|
||||
if (_activeItem < (_numDigits + _numButtons - 1)) {
|
||||
_activeItem++;
|
||||
|
||||
//_buttonAnim.setValue(ctx.time, 0, _getButtonWidth(), SPEED_FASTEST);
|
||||
ctx.sounds[SOUND_MOVE].play();
|
||||
} else {
|
||||
ctx.sounds[SOUND_CLICK].play();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -107,12 +107,16 @@ public:
|
||||
|
||||
class HexEntryScreen : public AnimatedScreen {
|
||||
protected:
|
||||
uint8_t _buffer[16];
|
||||
uint8_t _buffer[32];
|
||||
|
||||
int _numDigits, _numButtons, _activeItem;
|
||||
|
||||
const char *_title, *_prompt;
|
||||
const char *_buttons[3];
|
||||
|
||||
public:
|
||||
HexEntryScreen(void);
|
||||
virtual void show(Context &ctx, bool goBack = false);
|
||||
virtual void draw(Context &ctx, bool active = true) const;
|
||||
virtual void update(Context &ctx);
|
||||
};
|
||||
|
@ -108,7 +108,7 @@ uint16_t zsCRC16(const uint8_t *data, size_t length) {
|
||||
|
||||
/* String manipulation */
|
||||
|
||||
static const char _HEX_CHARSET[] = "0123456789ABCDEF";
|
||||
static const char _HEX_CHARSET[]{ "0123456789ABCDEF" };
|
||||
|
||||
size_t hexToString(char *output, const uint8_t *input, size_t length, char sep) {
|
||||
size_t outLength = 0;
|
||||
@ -139,7 +139,7 @@ size_t serialNumberToString(char *output, const uint8_t *input) {
|
||||
|
||||
// This format is used by Konami's tools to display trace IDs in the TID_81
|
||||
// format.
|
||||
static const char _TRACE_ID_CHECKSUM_CHARSET[] = "0X987654321";
|
||||
static const char _TRACE_ID_CHECKSUM_CHARSET[]{ "0X987654321" };
|
||||
|
||||
size_t traceIDToString(char *output, const uint8_t *input) {
|
||||
uint16_t high = (input[0] << 8) | input[1];
|
||||
@ -163,7 +163,7 @@ size_t traceIDToString(char *output, const uint8_t *input) {
|
||||
|
||||
// This encoding is similar to standard base45, but with some problematic
|
||||
// characters (' ', '$', '%', '*') excluded.
|
||||
static const char _BASE41_CHARSET[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./:";
|
||||
static const char _BASE41_CHARSET[]{ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./:"};
|
||||
|
||||
size_t encodeBase41(char *output, const uint8_t *input, size_t length) {
|
||||
size_t outLength = 0;
|
||||
|
@ -116,7 +116,7 @@ class IdentifierSet:
|
||||
|
||||
## Cartridge dump structure
|
||||
|
||||
_DUMP_HEADER_STRUCT: Struct = Struct("< 2B 8s 8s 8s 8s 8s")
|
||||
_DUMP_HEADER_STRUCT: Struct = Struct("< 2B 2x 8s 8s 8s 8s 8s")
|
||||
|
||||
_CHIP_SIZES: Mapping[ChipType, tuple[int, int, int]] = {
|
||||
ChipType.X76F041: ( 512, 384, 128 ),
|
||||
|
Loading…
x
Reference in New Issue
Block a user