mirror of
https://github.com/spicyjpeg/573in1.git
synced 2025-03-01 15:30:31 +01:00
More cart identification bugfixes
This commit is contained in:
parent
912ecb8d8d
commit
25f5f67840
@ -7,7 +7,7 @@
|
||||
"identifyGame": "Attempting to identify game..."
|
||||
},
|
||||
"cartEraseWorker": {
|
||||
"erase": "Performing cartridge erase...",
|
||||
"erase": "Erasing cartridge...",
|
||||
"error": "An error occurred while erasing the cartridge's EEPROM. The unlocking key may or may not have been successfully changed.\n\nPress the Test button to view debug logs."
|
||||
},
|
||||
"cartReflashWorker": {
|
||||
@ -96,7 +96,7 @@
|
||||
"CartInfoScreen": {
|
||||
"title": "{CART_ICON} Cartridge information",
|
||||
"digitalIOInfo": "Digital I/O ID:\t%s\nDigital I/O SN:\t%s\n\n",
|
||||
"cartInfo": "Cartridge type:\t%s\nUnlock status:\t%s\nDS2401 identifier:\t%s\nZS01 identifier:\t%s\n\n",
|
||||
"cartInfo": "Cartridge type:\t%s\nUnlock status:\t%s\nDS2401 identifier:\t%s\nZS01 identifier:\t%s\nConfiguration:\t%s\n\n",
|
||||
|
||||
"id": {
|
||||
"error": "read failure",
|
||||
@ -104,13 +104,17 @@
|
||||
"noCartID": "cartridge has no DS2401",
|
||||
"noZSID": "not a ZS01 cartridge"
|
||||
},
|
||||
"config": {
|
||||
"error": "read failure",
|
||||
"locked": "unlock required"
|
||||
},
|
||||
"unlockStatus": {
|
||||
"locked": "{CLOSED_LOCK} locked, game key required",
|
||||
"unlocked": "{OPEN_LOCK} unlocked"
|
||||
},
|
||||
"pairing": {
|
||||
"unsupported": "This game does not pair to I/O boards",
|
||||
"unknown": "Unlock cartridge to view pairing status",
|
||||
"unknown": "Unlock required to view pairing status",
|
||||
"unpaired": "Not paired to any digital I/O board",
|
||||
"thisSystem": "Paired to this system's I/O board",
|
||||
"otherSystem": "Paired to %s (%s)"
|
||||
@ -201,7 +205,7 @@
|
||||
|
||||
"UnlockKeyScreen": {
|
||||
"title": "{CART_ICON} Select unlocking key",
|
||||
"prompt": "If the cartridge has been converted before, select the game it was last converted to.",
|
||||
"prompt": "If the cartridge has been converted before, select the game it was last converted to. If it is currently blank, select 00-00-00-00-00-00-00-00.",
|
||||
"itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to confirm, hold {LEFT_BUTTON} + {RIGHT_BUTTON} to go back",
|
||||
|
||||
"autoUnlock": "Use key from identified game (recommended)",
|
||||
|
132
src/app/app.cpp
132
src/app/app.cpp
@ -29,10 +29,10 @@ void App::_unloadCartData(void) {
|
||||
_dump.clearData();
|
||||
|
||||
_identified = nullptr;
|
||||
_db.destroy();
|
||||
//_selectedEntry = nullptr;
|
||||
}
|
||||
|
||||
void App::_setupWorker(void (App::*func)(void)) {
|
||||
void App::_setupWorker(bool (App::*func)(void)) {
|
||||
LOG("restarting worker, func=0x%08x", func);
|
||||
|
||||
auto mask = setInterruptMask(0);
|
||||
@ -66,7 +66,7 @@ static const char *const _CARTDB_PATHS[cart::NUM_CHIP_TYPES]{
|
||||
"data/zs01.cartdb"
|
||||
};
|
||||
|
||||
void App::_cartDetectWorker(void) {
|
||||
bool App::_cartDetectWorker(void) {
|
||||
_workerStatus.setNextScreen(_cartInfoScreen);
|
||||
_workerStatus.update(0, 4, WSTR("App.cartDetectWorker.identifyCart"));
|
||||
_unloadCartData();
|
||||
@ -105,10 +105,14 @@ void App::_cartDetectWorker(void) {
|
||||
LOG("cart parser @ 0x%08x", _parser);
|
||||
_workerStatus.update(2, 4, WSTR("App.cartDetectWorker.identifyGame"));
|
||||
|
||||
if (!_resourceProvider->loadData(_db, _CARTDB_PATHS[_dump.chipType])) {
|
||||
if (!_db.ptr) {
|
||||
if (!_resourceProvider->loadData(
|
||||
_db, _CARTDB_PATHS[_dump.chipType])
|
||||
) {
|
||||
LOG("%s not found", _CARTDB_PATHS[_dump.chipType]);
|
||||
goto _cartInitDone;
|
||||
}
|
||||
}
|
||||
|
||||
char code[8], region[8];
|
||||
|
||||
@ -116,6 +120,17 @@ void App::_cartDetectWorker(void) {
|
||||
goto _cartInitDone;
|
||||
if (_parser->getCode(code) && _parser->getRegion(region))
|
||||
_identified = _db.lookup(code, region);
|
||||
if (!_identified)
|
||||
goto _cartInitDone;
|
||||
|
||||
// Force the parser to use correct format for the game (to prevent
|
||||
// ambiguity between different formats).
|
||||
delete _parser;
|
||||
_parser = cart::newCartParser(
|
||||
_dump, _identified->formatType, _identified->flags
|
||||
);
|
||||
|
||||
LOG("new cart parser @ 0x%08x", _parser);
|
||||
}
|
||||
|
||||
_cartInitDone:
|
||||
@ -127,7 +142,7 @@ _cartInitDone:
|
||||
|
||||
if (!_resourceProvider->loadData(bitstream, "data/fpga.bit")) {
|
||||
LOG("bitstream unavailable");
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
ready = io::loadBitstream(bitstream.as<uint8_t>(), bitstream.length);
|
||||
@ -135,7 +150,7 @@ _cartInitDone:
|
||||
|
||||
if (!ready) {
|
||||
LOG("bitstream upload failed");
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
delayMicroseconds(5000); // Probably not necessary
|
||||
@ -148,9 +163,11 @@ _cartInitDone:
|
||||
|
||||
if (error)
|
||||
LOG("system ID error, code=%d", error);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void App::_cartUnlockWorker(void) {
|
||||
bool App::_cartUnlockWorker(void) {
|
||||
_workerStatus.setNextScreen(_cartInfoScreen, true);
|
||||
_workerStatus.update(0, 2, WSTR("App.cartUnlockWorker.read"));
|
||||
|
||||
@ -163,7 +180,7 @@ void App::_cartUnlockWorker(void) {
|
||||
_cartInfoScreen, WSTR("App.cartUnlockWorker.error")
|
||||
);*/
|
||||
_workerStatus.setNextScreen(_errorScreen);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_parser)
|
||||
@ -172,7 +189,7 @@ void App::_cartUnlockWorker(void) {
|
||||
_parser = cart::newCartParser(_dump);
|
||||
|
||||
if (!_parser)
|
||||
return;
|
||||
return true;
|
||||
|
||||
LOG("cart parser @ 0x%08x", _parser);
|
||||
_workerStatus.update(1, 2, WSTR("App.cartUnlockWorker.identifyGame"));
|
||||
@ -181,9 +198,28 @@ void App::_cartUnlockWorker(void) {
|
||||
|
||||
if (_parser->getCode(code) && _parser->getRegion(region))
|
||||
_identified = _db.lookup(code, region);
|
||||
|
||||
// If auto-identification failed (e.g. because the format has no game code),
|
||||
// use the game whose unlocking key was selected as a hint.
|
||||
if (!_identified) {
|
||||
if (_selectedEntry) {
|
||||
LOG("identify failed, using key as hint");
|
||||
_identified = _selectedEntry;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void App::_qrCodeWorker(void) {
|
||||
delete _parser;
|
||||
_parser = cart::newCartParser(
|
||||
_dump, _identified->formatType, _identified->flags
|
||||
);
|
||||
|
||||
LOG("new cart parser @ 0x%08x", _parser);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool App::_qrCodeWorker(void) {
|
||||
char qrString[cart::MAX_QR_STRING_LENGTH];
|
||||
|
||||
_workerStatus.setNextScreen(_qrCodeScreen);
|
||||
@ -192,9 +228,11 @@ void App::_qrCodeWorker(void) {
|
||||
|
||||
_workerStatus.update(1, 2, WSTR("App.qrCodeWorker.generate"));
|
||||
_qrCodeScreen.generateCode(qrString);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void App::_hddDumpWorker(void) {
|
||||
bool App::_hddDumpWorker(void) {
|
||||
_workerStatus.update(0, 1, WSTR("App.hddDumpWorker.save"));
|
||||
|
||||
char code[8], region[8], path[32];
|
||||
@ -211,23 +249,23 @@ void App::_hddDumpWorker(void) {
|
||||
_cartInfoScreen, WSTR("App.hddDumpWorker.error")
|
||||
);
|
||||
_workerStatus.setNextScreen(_errorScreen);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
_workerStatus.setNextScreen(_cartInfoScreen);
|
||||
return true;
|
||||
}
|
||||
|
||||
void App::_cartWriteWorker(void) {
|
||||
bool App::_cartWriteWorker(void) {
|
||||
_workerStatus.update(0, 1, WSTR("App.cartWriteWorker.write"));
|
||||
|
||||
uint8_t key[8];
|
||||
auto error = _driver->writeData();
|
||||
|
||||
_cartDetectWorker();
|
||||
if (!error)
|
||||
_identified->copyKeyTo(key);
|
||||
|
||||
if (!error && _identified) {
|
||||
_identified->copyKeyTo(_dump.dataKey);
|
||||
_cartUnlockWorker();
|
||||
}
|
||||
_cartDetectWorker();
|
||||
|
||||
if (error) {
|
||||
LOG("write error, code=%d", error);
|
||||
@ -236,60 +274,63 @@ void App::_cartWriteWorker(void) {
|
||||
_cartInfoScreen, WSTR("App.cartWriteWorker.error")
|
||||
);
|
||||
_workerStatus.setNextScreen(_errorScreen);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void App::_cartReflashWorker(void) {
|
||||
_workerStatus.update(0, 1, WSTR("App.cartWriteWorker.flash"));
|
||||
_dump.copyKeyFrom(key);
|
||||
return _cartUnlockWorker();
|
||||
}
|
||||
|
||||
bool App::_cartReflashWorker(void) {
|
||||
if (!_cartEraseWorker())
|
||||
return false;
|
||||
|
||||
_workerStatus.update(1, 2, WSTR("App.cartReflashWorker.flash"));
|
||||
|
||||
if (_parser)
|
||||
delete _parser;
|
||||
|
||||
_parser = cart::newCartParser(
|
||||
_dump, _reflashEntry->formatType, _reflashEntry->flags
|
||||
_dump, _selectedEntry->formatType, _selectedEntry->flags
|
||||
);
|
||||
auto pri = _parser->getIdentifiers();
|
||||
auto pub = _parser->getPublicIdentifiers();
|
||||
|
||||
_dump.clearData();
|
||||
_dump.initConfig(9, _selectedEntry->flags & cart::DATA_HAS_PUBLIC_SECTION);
|
||||
|
||||
if (pri) {
|
||||
if (_reflashEntry->flags & cart::DATA_HAS_CART_ID)
|
||||
if (_selectedEntry->flags & cart::DATA_HAS_CART_ID)
|
||||
pri->cartID.copyFrom(_dump.cartID.data);
|
||||
if (_reflashEntry->flags & cart::DATA_HAS_TRACE_ID)
|
||||
if (_selectedEntry->flags & cart::DATA_HAS_TRACE_ID)
|
||||
pri->updateTraceID(
|
||||
_reflashEntry->traceIDType, _reflashEntry->traceIDParam
|
||||
_selectedEntry->traceIDType, _selectedEntry->traceIDParam
|
||||
);
|
||||
if (_reflashEntry->flags & cart::DATA_HAS_INSTALL_ID) {
|
||||
if (_selectedEntry->flags & cart::DATA_HAS_INSTALL_ID) {
|
||||
// The private installation ID seems to be unused on carts with a
|
||||
// public data section.
|
||||
if (pub)
|
||||
pub->setInstallID(_reflashEntry->installIDPrefix);
|
||||
pub->setInstallID(_selectedEntry->installIDPrefix);
|
||||
else
|
||||
pri->setInstallID(_reflashEntry->installIDPrefix);
|
||||
pri->setInstallID(_selectedEntry->installIDPrefix);
|
||||
}
|
||||
}
|
||||
|
||||
_parser->setCode(_reflashEntry->code);
|
||||
_parser->setRegion(_reflashEntry->region);
|
||||
_parser->setYear(_reflashEntry->year);
|
||||
_parser->setCode(_selectedEntry->code);
|
||||
_parser->setRegion(_selectedEntry->region);
|
||||
_parser->setYear(_selectedEntry->year);
|
||||
_parser->flush();
|
||||
|
||||
uint8_t key[8];
|
||||
auto error = _driver->writeData();
|
||||
|
||||
if (!error) {
|
||||
_reflashEntry->copyKeyTo(key);
|
||||
_selectedEntry->copyKeyTo(key);
|
||||
error = _driver->setDataKey(key);
|
||||
}
|
||||
|
||||
_cartDetectWorker();
|
||||
|
||||
if (!error) {
|
||||
_dump.copyKeyFrom(key);
|
||||
_cartUnlockWorker();
|
||||
}
|
||||
|
||||
if (error) {
|
||||
LOG("write error, code=%d", error);
|
||||
|
||||
@ -297,21 +338,20 @@ void App::_cartReflashWorker(void) {
|
||||
_cartInfoScreen, WSTR("App.cartReflashWorker.error")
|
||||
);
|
||||
_workerStatus.setNextScreen(_errorScreen);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void App::_cartEraseWorker(void) {
|
||||
_dump.copyKeyFrom(key);
|
||||
return _cartUnlockWorker();
|
||||
}
|
||||
|
||||
bool App::_cartEraseWorker(void) {
|
||||
_workerStatus.update(0, 1, WSTR("App.cartEraseWorker.erase"));
|
||||
|
||||
auto error = _driver->erase();
|
||||
|
||||
_cartDetectWorker();
|
||||
|
||||
if (!error) {
|
||||
_dump.clearKey();
|
||||
_cartUnlockWorker();
|
||||
}
|
||||
|
||||
if (error) {
|
||||
LOG("erase error, code=%d", error);
|
||||
|
||||
@ -319,7 +359,11 @@ void App::_cartEraseWorker(void) {
|
||||
_cartInfoScreen, WSTR("App.cartEraseWorker.error")
|
||||
);
|
||||
_workerStatus.setNextScreen(_errorScreen);
|
||||
return false;
|
||||
}
|
||||
|
||||
_dump.clearKey();
|
||||
return _cartUnlockWorker();
|
||||
}
|
||||
|
||||
/* Misc. functions */
|
||||
|
@ -122,24 +122,24 @@ private:
|
||||
cart::CartDB _db;
|
||||
Thread _workerThread;
|
||||
WorkerStatus _workerStatus;
|
||||
void (App::*_workerFunction)(void);
|
||||
bool (App::*_workerFunction)(void);
|
||||
|
||||
uint8_t *_workerStack;
|
||||
cart::Driver *_driver;
|
||||
cart::Parser *_parser;
|
||||
const cart::DBEntry *_identified, *_reflashEntry;
|
||||
const cart::DBEntry *_identified, *_selectedEntry;
|
||||
|
||||
void _unloadCartData(void);
|
||||
void _setupWorker(void (App::*func)(void));
|
||||
void _setupWorker(bool (App::*func)(void));
|
||||
void _setupInterrupts(void);
|
||||
|
||||
void _cartDetectWorker(void);
|
||||
void _cartUnlockWorker(void);
|
||||
void _qrCodeWorker(void);
|
||||
void _hddDumpWorker(void);
|
||||
void _cartWriteWorker(void);
|
||||
void _cartReflashWorker(void);
|
||||
void _cartEraseWorker(void);
|
||||
bool _cartDetectWorker(void);
|
||||
bool _cartUnlockWorker(void);
|
||||
bool _qrCodeWorker(void);
|
||||
bool _hddDumpWorker(void);
|
||||
bool _cartWriteWorker(void);
|
||||
bool _cartReflashWorker(void);
|
||||
bool _cartEraseWorker(void);
|
||||
|
||||
void _worker(void);
|
||||
void _interruptHandler(void);
|
||||
|
@ -163,8 +163,8 @@ void CartActionsScreen::show(ui::Context &ctx, bool goBack) {
|
||||
|
||||
_listLength = _NUM_NO_SYSTEM_ID_ACTIONS;
|
||||
|
||||
if (APP->_identified) {
|
||||
if (APP->_identified->flags & cart::DATA_HAS_SYSTEM_ID)
|
||||
if (APP->_parser) {
|
||||
if (APP->_parser->flags & cart::DATA_HAS_SYSTEM_ID)
|
||||
_listLength = _NUM_SYSTEM_ID_ACTIONS;
|
||||
}
|
||||
|
||||
@ -214,7 +214,7 @@ void HexdumpScreen::show(ui::Context &ctx, bool goBack) {
|
||||
char *ptr = _bodyText, *end = &_bodyText[sizeof(_bodyText)];
|
||||
|
||||
for (size_t i = 0; i < length; i += 16) {
|
||||
ptr += snprintf(ptr, end - ptr, "%04x: ", i);
|
||||
ptr += snprintf(ptr, end - ptr, "%04X: ", i);
|
||||
ptr += util::hexToString(ptr, &APP->_dump.data[i], 16, ' ');
|
||||
|
||||
*(ptr++) = '\n';
|
||||
@ -258,7 +258,7 @@ void ReflashGameScreen::update(ui::Context &ctx) {
|
||||
STR("CartActionsScreen.reflash.confirm")
|
||||
);
|
||||
|
||||
APP->_reflashEntry = APP->_db.get(_activeItem);
|
||||
APP->_selectedEntry = APP->_db.get(_activeItem);
|
||||
ctx.show(APP->_confirmScreen, false, true);
|
||||
} else if (
|
||||
(ctx.buttons.held(ui::BTN_LEFT) && ctx.buttons.pressed(ui::BTN_RIGHT)) ||
|
||||
|
@ -62,7 +62,7 @@ void CartInfoScreen::show(ui::Context &ctx, bool goBack) {
|
||||
|
||||
auto &dump = APP->_dump;
|
||||
|
||||
char id1[32], id2[32];
|
||||
char id1[32], id2[32], config[32];
|
||||
char *ptr = _bodyText, *end = &_bodyText[sizeof(_bodyText)];
|
||||
|
||||
// Digital I/O board info
|
||||
@ -113,25 +113,36 @@ void CartInfoScreen::show(ui::Context &ctx, bool goBack) {
|
||||
else
|
||||
__builtin_strcpy(id2, STR("CartInfoScreen.id.noZSID"));
|
||||
|
||||
if (dump.flags & cart::DUMP_CONFIG_OK)
|
||||
util::hexToString(config, dump.config, sizeof(dump.config), '-');
|
||||
else if (dump.flags & cart::DUMP_PRIVATE_DATA_OK)
|
||||
__builtin_strcpy(config, STR("CartInfoScreen.config.error"));
|
||||
else
|
||||
__builtin_strcpy(config, STR("CartInfoScreen.config.locked"));
|
||||
|
||||
auto unlockStatus = (dump.flags & cart::DUMP_PRIVATE_DATA_OK)
|
||||
? STR("CartInfoScreen.unlockStatus.unlocked")
|
||||
: STR("CartInfoScreen.unlockStatus.locked");
|
||||
|
||||
ptr += snprintf(
|
||||
ptr, end - ptr, STR("CartInfoScreen.cartInfo"),
|
||||
STRH(_CART_TYPES[dump.chipType].name), unlockStatus, id1, id2
|
||||
STRH(_CART_TYPES[dump.chipType].name), unlockStatus, id1, id2, config
|
||||
);
|
||||
|
||||
// At this point the cartridge can be in one of 6 states:
|
||||
// At this point the cartridge can be in one of 8 states:
|
||||
// - locked, identified
|
||||
// => unlock required, auto unlock available
|
||||
// - locked, unidentified
|
||||
// - locked, parsed but unidentified
|
||||
// => unlock required
|
||||
// - locked, parsing failed
|
||||
// => unlock required
|
||||
// - locked, blank or no public data
|
||||
// => unlock required
|
||||
// - unlocked, identified
|
||||
// => all actions available
|
||||
// - unlocked, no private data, unidentified
|
||||
// - unlocked, no private data, parsed but unidentified
|
||||
// => all actions available (not implemented yet)
|
||||
// - unlocked, no private data, parsing failed
|
||||
// => only dumping/flashing available
|
||||
// - unlocked, no private data, blank
|
||||
// => only dumping/flashing available
|
||||
@ -170,7 +181,9 @@ void CartInfoScreen::show(ui::Context &ctx, bool goBack) {
|
||||
STR("CartInfoScreen.pairing.otherSystem"), id1, id2
|
||||
);
|
||||
}
|
||||
} else if (dump.flags & cart::DUMP_PUBLIC_DATA_OK) {
|
||||
} else if (
|
||||
dump.flags & (cart::DUMP_PUBLIC_DATA_OK | cart::DUMP_PRIVATE_DATA_OK)
|
||||
) {
|
||||
state = APP->_dump.isReadableDataEmpty() ? BLANK_CART : UNIDENTIFIED;
|
||||
} else {
|
||||
state = UNKNOWN;
|
||||
@ -258,10 +271,14 @@ void UnlockKeyScreen::autoUnlock(ui::Context &ctx) {
|
||||
APP->_dump.dataKey, APP->_identified->dataKey,
|
||||
sizeof(APP->_dump.dataKey)
|
||||
);
|
||||
|
||||
//APP->_selectedEntry = APP->_identified;
|
||||
APP->_selectedEntry = nullptr;
|
||||
ctx.show(APP->_confirmScreen, false, true);
|
||||
}
|
||||
|
||||
void UnlockKeyScreen::useCustomKey(ui::Context &ctx) {
|
||||
APP->_selectedEntry = nullptr;
|
||||
ctx.show(APP->_keyEntryScreen, false, true);
|
||||
}
|
||||
|
||||
@ -269,6 +286,8 @@ void UnlockKeyScreen::use00Key(ui::Context &ctx) {
|
||||
__builtin_memset(
|
||||
APP->_dump.dataKey, 0x00, sizeof(APP->_dump.dataKey)
|
||||
);
|
||||
|
||||
APP->_selectedEntry = nullptr;
|
||||
ctx.show(APP->_confirmScreen, false, true);
|
||||
}
|
||||
|
||||
@ -276,6 +295,8 @@ void UnlockKeyScreen::useFFKey(ui::Context &ctx) {
|
||||
__builtin_memset(
|
||||
APP->_dump.dataKey, 0xff, sizeof(APP->_dump.dataKey)
|
||||
);
|
||||
|
||||
APP->_selectedEntry = nullptr;
|
||||
ctx.show(APP->_confirmScreen, false, true);
|
||||
}
|
||||
|
||||
@ -316,6 +337,8 @@ void UnlockKeyScreen::update(ui::Context &ctx) {
|
||||
APP->_dump.dataKey, APP->_db.get(index)->dataKey,
|
||||
sizeof(APP->_dump.dataKey)
|
||||
);
|
||||
|
||||
APP->_selectedEntry = APP->_db.get(index);
|
||||
ctx.show(APP->_confirmScreen, false, true);
|
||||
}
|
||||
} else if (
|
||||
|
21
src/cart.cpp
21
src/cart.cpp
@ -176,6 +176,27 @@ const ChipSize CHIP_SIZES[NUM_CHIP_TYPES]{
|
||||
{ .dataLength = 112, .publicDataOffset = 0, .publicDataLength = 32 }
|
||||
};
|
||||
|
||||
void Dump::initConfig(uint8_t maxAttempts, bool hasPublicSection) {
|
||||
clearConfig();
|
||||
|
||||
switch (chipType) {
|
||||
case X76F041:
|
||||
config[0] = 0xff;
|
||||
config[1] = hasPublicSection ? 0xaf : 0xff;
|
||||
config[2] = 0x20; // Disable retry counter
|
||||
config[3] = maxAttempts;
|
||||
break;
|
||||
|
||||
case ZS01:
|
||||
//assert(hasPublicSection);
|
||||
config[4] = maxAttempts;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool Dump::isPublicDataEmpty(void) const {
|
||||
if (!(flags & DUMP_PUBLIC_DATA_OK))
|
||||
return false;
|
||||
|
14
src/cart.hpp
14
src/cart.hpp
@ -171,7 +171,9 @@ public:
|
||||
return (sizeof(Dump) - sizeof(data)) + getChipSize().dataLength;
|
||||
}
|
||||
inline void clearIdentifiers(void) {
|
||||
__builtin_memset(&systemID, 0, sizeof(Identifier) * 3);
|
||||
systemID.clear();
|
||||
cartID.clear();
|
||||
zsID.clear();
|
||||
}
|
||||
inline void copyDataFrom(const uint8_t *source) {
|
||||
__builtin_memcpy(data, source, getChipSize().dataLength);
|
||||
@ -191,7 +193,17 @@ public:
|
||||
inline void clearKey(void) {
|
||||
__builtin_memset(dataKey, 0, sizeof(dataKey));
|
||||
}
|
||||
inline void copyConfigFrom(const uint8_t *source) {
|
||||
__builtin_memcpy(config, source, sizeof(config));
|
||||
}
|
||||
inline void copyConfigTo(uint8_t *dest) const {
|
||||
__builtin_memcpy(dest, config, sizeof(config));
|
||||
}
|
||||
inline void clearConfig(void) {
|
||||
__builtin_memset(config, 0, sizeof(config));
|
||||
}
|
||||
|
||||
void initConfig(uint8_t maxAttempts, bool hasPublicSection = false);
|
||||
bool isPublicDataEmpty(void) const;
|
||||
bool isDataEmpty(void) const;
|
||||
bool isReadableDataEmpty(void) const;
|
||||
|
@ -117,7 +117,7 @@ void ExtendedParser::setCode(const char *input) {
|
||||
__builtin_strncpy(header->code, input, sizeof(header->code));
|
||||
|
||||
if (flags & DATA_GX706_WORKAROUND)
|
||||
header->region[1] = 'E';
|
||||
header->code[1] = 'E';
|
||||
}
|
||||
|
||||
size_t ExtendedParser::getRegion(char *output) const {
|
||||
@ -172,7 +172,16 @@ void ExtendedParser::flush(void) {
|
||||
//pub->installID.copyFrom(pri->installID.data);
|
||||
pub->systemID.copyFrom(pri->systemID.data);
|
||||
|
||||
_getHeader()->updateChecksum(flags & DATA_CHECKSUM_INVERTED);
|
||||
auto header = _getHeader();
|
||||
char code = header->code[1];
|
||||
|
||||
if (flags & DATA_GX706_WORKAROUND)
|
||||
header->code[1] = 'X';
|
||||
|
||||
header->updateChecksum(flags & DATA_CHECKSUM_INVERTED);
|
||||
|
||||
if (flags & DATA_GX706_WORKAROUND)
|
||||
header->code[1] = code;
|
||||
}
|
||||
|
||||
bool ExtendedParser::validate(void) {
|
||||
@ -180,15 +189,15 @@ bool ExtendedParser::validate(void) {
|
||||
return false;
|
||||
|
||||
auto header = _getHeader();
|
||||
char region = header->region[1];
|
||||
char code = header->code[1];
|
||||
|
||||
if (flags & DATA_GX706_WORKAROUND)
|
||||
header->region[1] = 'X';
|
||||
header->code[1] = 'X';
|
||||
|
||||
bool valid = header->validateChecksum(flags & DATA_CHECKSUM_INVERTED);
|
||||
|
||||
if (flags & DATA_GX706_WORKAROUND)
|
||||
header->region[1] = region;
|
||||
header->code[1] = code;
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
@ -45,6 +45,9 @@ DriverError DummyDriver::readCartID(void) {
|
||||
}
|
||||
|
||||
DriverError DummyDriver::readPublicData(void) {
|
||||
if (_dummyDump.chipType != ZS01)
|
||||
return UNSUPPORTED_OP;
|
||||
|
||||
if (_dummyDump.flags & DUMP_PUBLIC_DATA_OK) {
|
||||
_dump.copyDataFrom(_dummyDump.data);
|
||||
_dump.flags |= DUMP_PUBLIC_DATA_OK;
|
||||
@ -59,7 +62,8 @@ DriverError DummyDriver::readPrivateData(void) {
|
||||
_dump.dataKey, _dummyDump.dataKey, sizeof(_dump.dataKey)
|
||||
)) {
|
||||
_dump.copyDataFrom(_dummyDump.data);
|
||||
_dump.flags |= DUMP_PRIVATE_DATA_OK;
|
||||
_dump.copyConfigFrom(_dummyDump.config);
|
||||
_dump.flags |= DUMP_PRIVATE_DATA_OK | DUMP_CONFIG_OK;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@ -411,8 +415,8 @@ DriverError ZS01Driver::_transact(
|
||||
LOG("D: %s", buffer);
|
||||
#endif
|
||||
|
||||
//if (!ok)
|
||||
//return ZS01_CRC_MISMATCH;
|
||||
if (!ok)
|
||||
return ZS01_CRC_MISMATCH;
|
||||
|
||||
_encoderState = response.address;
|
||||
|
||||
|
@ -52,6 +52,7 @@ static constexpr int SCREEN_MARGIN_X = 16;
|
||||
static constexpr int SCREEN_MARGIN_Y = 20;
|
||||
static constexpr int SCREEN_BLOCK_MARGIN = 6;
|
||||
static constexpr int SCREEN_PROMPT_HEIGHT = 30;
|
||||
static constexpr int SCREEN_PROMPT_HEIGHT_MIN = 10;
|
||||
|
||||
static constexpr int LIST_BOX_PADDING = 4;
|
||||
static constexpr int LIST_ITEM_PADDING = 2;
|
||||
|
@ -288,10 +288,10 @@ void TextScreen::draw(Context &ctx, bool active) const {
|
||||
ctx.font.draw(ctx.gpuCtx, _title, rect, COLOR_TITLE);
|
||||
|
||||
rect.y1 = gpu::FONT_LINE_HEIGHT + SCREEN_BLOCK_MARGIN;
|
||||
rect.y2 = screenHeight - SCREEN_PROMPT_HEIGHT;
|
||||
rect.y2 = screenHeight - SCREEN_PROMPT_HEIGHT_MIN;
|
||||
ctx.font.draw(ctx.gpuCtx, _body, rect, COLOR_TEXT1, true);
|
||||
|
||||
rect.y1 = screenHeight - SCREEN_PROMPT_HEIGHT;
|
||||
rect.y1 = screenHeight - SCREEN_PROMPT_HEIGHT_MIN;
|
||||
rect.y2 = screenHeight;
|
||||
ctx.font.draw(ctx.gpuCtx, _prompt, rect, COLOR_TEXT1, true);
|
||||
}
|
||||
|
12
src/zs01.cpp
12
src/zs01.cpp
@ -18,10 +18,8 @@ static const Key _COMMAND_KEY{
|
||||
// Konami's driver generates a pseudorandom key for each transaction, but it can
|
||||
// be a fixed key as well.
|
||||
static const Key _RESPONSE_KEY{
|
||||
.add = { 237, 8, 16, 11, 6, 4, 8, 30 },
|
||||
.shift = { 0, 3, 2, 2, 6, 2, 2, 1 }
|
||||
//.add = { 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
//.shift = { 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
.add = { 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
.shift = { 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
/* Packet encoding/decoding */
|
||||
@ -118,6 +116,8 @@ bool Packet::validateCRC(void) const {
|
||||
}
|
||||
|
||||
void Packet::encodeReadRequest(void) {
|
||||
LOG("addr=0x%02x, public", address);
|
||||
|
||||
command = REQ_READ;
|
||||
_RESPONSE_KEY.packInto(data);
|
||||
updateCRC();
|
||||
@ -126,6 +126,8 @@ void Packet::encodeReadRequest(void) {
|
||||
}
|
||||
|
||||
void Packet::encodeReadRequest(Key &dataKey, uint8_t state) {
|
||||
LOG("addr=0x%02x, private", address);
|
||||
|
||||
command = REQ_READ | REQ_USE_DATA_KEY;
|
||||
_RESPONSE_KEY.packInto(data);
|
||||
updateCRC();
|
||||
@ -135,6 +137,8 @@ void Packet::encodeReadRequest(Key &dataKey, uint8_t state) {
|
||||
}
|
||||
|
||||
void Packet::encodeWriteRequest(Key &dataKey, uint8_t state) {
|
||||
LOG("addr=0x%02x", address);
|
||||
|
||||
command = REQ_WRITE | REQ_USE_DATA_KEY;
|
||||
updateCRC();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user