Add flash chip drivers, cart restoring from dump

This commit is contained in:
spicyjpeg 2024-03-24 13:24:21 +01:00
parent ed4cc53631
commit 5e2a1e07fd
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
9 changed files with 548 additions and 71 deletions

View File

@ -6,9 +6,9 @@
"App": {
"startupWorker": {
"initIDE": "Initializing IDE devices...\nDo not turn off the 573.",
"initFAT": "Attempting to mount FAT filesystem...\nDo not turn off the 573.",
"loadResources": "Loading resource pack..."
"initIDE": "Initializing IDE devices...\nDo not turn off the 573 or unplug drives.",
"initFAT": "Attempting to mount FAT filesystem...\nDo not turn off the 573 or unplug drives.",
"loadResources": "Loading resource pack...\nDo not turn off the 573 or unplug drives."
},
"cartDetectWorker": {
"readDigitalIO": "Retrieving digital I/O board ID...",
@ -20,10 +20,20 @@
"error": "An error occurred while erasing the cartridge's EEPROM. The unlocking key may or may not have been successfully changed.\n\nError code: %s\nPress the Test button to view debug logs."
},
"cartReflashWorker": {
"init": "Generating cartridge data...\nDo not turn off the 573.",
"setDataKey": "Setting new unlocking key...\nDo not turn off the 573.",
"write": "Writing new data to cartridge...\nDo not turn off the 573.",
"idError": "The selected game is not compatible with the currently inserted cartridge, as it requires one with a valid DS2401 identifier.",
"writeError": "An error occurred while reflashing the cartridge. The unlocking key may or may not have been successfully changed.\n\nError code: %s\nPress the Test button to view debug logs."
"writeError": "An error occurred while writing to the cartridge. The unlocking key may or may not have been successfully changed.\n\nError code: %s\nPress the Test button to view debug logs."
},
"cartRestoreWorker": {
"init": "Validating dump file...\nDo not turn off the 573 or unplug drives.",
"setDataKey": "Setting new unlocking key...\nDo not turn off the 573 or unplug drives.",
"write": "Writing new data to cartridge...\nDo not turn off the 573 or unplug drives.",
"fileError": "The selected file could not be accessed or is not a valid cartridge dump. Make sure the file has been copied properly.\n\nFile: %s",
"typeError": "The selected dump is not compatible with the currently inserted cartridge, as it requires a different chip type.\n\nFile: %s",
"writeError": "An error occurred while writing to the cartridge's EEPROM. The unlocking key may or may not have been successfully changed.\n\nError code: %s\nPress the Test button to view debug logs.",
"idWarning": "The dump has been successfully restored, but games may reject the cartridge as the dump was obtained from a cartridge with a different ID.\n\nDump ID:\t%s\nCart ID:\t%s"
},
"cartUnlockWorker": {
"read": "Dumping cartridge...\nDo not turn off the 573.",
@ -37,14 +47,14 @@
"error": "An error occurred while writing to the cartridge's EEPROM.\n\nError code: %s\nPress the Test button to view debug logs."
},
"cartDumpWorker": {
"save": "Saving cartridge dump...\nDo not turn off the 573.",
"save": "Saving cartridge dump...\nDo not turn off the 573 or unplug drives.",
"success": "A dump of the cartridge and all its identifiers has been saved as %s in the root of the drive. The dump can be decoded and viewed using the decodeDump.py script provided with this tool.",
"error": "An error occurred while saving the dump. 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\nFile: %s\nPress the Test button to view debug logs."
},
"executableWorker": {
"init": "Validating executable file...\nDo not turn off the 573.",
"load": "Loading executable file...\nDo not turn off the 573.",
"fileError": "The selected file is not a valid System 573 executable. Make sure the file has been built, extracted or copied properly.\n\nFile: %s",
"init": "Validating executable file...\nDo not turn off the 573 or unplug drives.",
"load": "Loading executable file...\nDo not turn off the 573 or unplug drives.",
"fileError": "The selected file could not be accessed or is not a valid System 573 executable. Make sure the file has been copied properly.\n\nFile: %s",
"addressError": "The selected file cannot be loaded as it overlaps the memory region reserved for use by the executable launcher.\n\nFile:\t\t%s\nRegion:\t%08X-%08X\nStack top:\t%08X"
},
"qrCodeWorker": {
@ -52,12 +62,12 @@
"generate": "Generating QR code..."
},
"romDumpWorker": {
"init": "Creating dump directory...\nDo not turn off the 573.",
"dumpBIOS": "Dumping BIOS ROM...\nDo not turn off the 573.",
"dumpRTC": "Dumping RTC RAM...\nDo not turn off the 573.",
"dumpFlash": "Dumping internal flash...\nDo not turn off the 573.",
"dumpPCMCIA1": "Dumping PCMCIA card in slot 1...\nDo not turn off the 573.",
"dumpPCMCIA2": "Dumping PCMCIA card in slot 2...\nDo not turn off the 573.",
"init": "Creating dump directory...\nDo not turn off the 573 or unplug drives.",
"dumpBIOS": "Dumping BIOS ROM...\nDo not turn off the 573 or unplug drives.",
"dumpRTC": "Dumping RTC RAM...\nDo not turn off the 573 or unplug drives.",
"dumpFlash": "Dumping internal flash...\nDo not turn off the 573 or unplug drives.",
"dumpPCMCIA1": "Dumping PCMCIA card in slot 1...\nDo not turn off the 573 or unplug drives.",
"dumpPCMCIA2": "Dumping PCMCIA card in slot 2...\nDo not turn off the 573 or unplug drives.",
"success": "All dumps have been saved to the %s directory in the root of the drive.",
"initError": "An error occurred while creating the dump directory. 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\nPath: %s\nPress the Test button to view debug logs.",
"dumpError": "An error occurred while saving one of the dumps. Ensure the drive has at least 32 MB of free space (256 MB if both PCMCIA cards are inserted) and the filesystem is not damaged.\n\nFile: %s\nPress the Test button to view debug logs."
@ -109,8 +119,10 @@
"prompt": "Display the raw contents of the cartridge's EEPROM in hexadecimal format."
},
"hddRestore": {
"name": "Restore cartridge from dump",
"prompt": "Wipe all data and restore the cartridge's contents from a dump that had been previously been saved to the IDE hard drive or CF card connected as secondary drive (if any)."
"name": "Restore cartridge from dump",
"prompt": "Wipe all data and restore the cartridge's contents from a previously saved dump on the IDE hard drive or CF card connected as secondary drive (if any).",
"filePrompt": "Note that the dump must have the same chip type, and should have the same DS2401 ID (if any), as the cartridge.",
"confirm": "The contents of the cartridge's EEPROM will be altered and the unlocking key updated to match the selected dump.\n\nDo you wish to proceed?"
},
"reflash": {
"name": "Erase and convert cartridge to another game",
@ -225,7 +237,7 @@
"KeyEntryScreen": {
"title": "Enter unlocking key",
"body": "Enter the 8-byte key this cartridge was last locked with.\n\nUse {LEFT_BUTTON}{RIGHT_BUTTON} to move between digits, hold {START_BUTTON} and use {LEFT_BUTTON}{RIGHT_BUTTON} to edit the highlighted digit.",
"body": "Enter the 8-byte key this cartridge was last locked with.\n\nUse {LEFT_BUTTON}{RIGHT_BUTTON} to move the cursor, hold {START_BUTTON} and use {LEFT_BUTTON}{RIGHT_BUTTON} to edit the highlighted digit.",
"cancel": "Cancel",
"ok": "Confirm"
},
@ -312,7 +324,7 @@
"SystemIDEntryScreen": {
"title": "Edit system identifier",
"body": "Enter the new digital I/O board's identifier. To obtain the ID of another board, run this tool on its respective system.\n\nUse {LEFT_BUTTON}{RIGHT_BUTTON} to move between digits, hold {START_BUTTON} and use {LEFT_BUTTON}{RIGHT_BUTTON} to edit the highlighted digit.",
"body": "Enter the new digital I/O board's identifier. To obtain the ID of another board, run this tool on its respective system.\n\nUse {LEFT_BUTTON}{RIGHT_BUTTON} to move the cursor, hold {START_BUTTON} and use {LEFT_BUTTON}{RIGHT_BUTTON} to edit the highlighted digit.",
"cancel": "Cancel",
"ok": "Confirm"
},

View File

@ -6,6 +6,7 @@
#include "common/util.hpp"
#include "ps1/registers.h"
#include "ps1/registers573.h"
#include "ps1/system.h"
namespace rom {
@ -133,6 +134,31 @@ uint32_t FlashRegion::zipCRC32(
return ~crc;
}
/* Flash-specific functions */
enum JEDECCommand : uint16_t {
_JEDEC_RESET = 0xf0f0,
_JEDEC_HANDSHAKE1 = 0xaaaa,
_JEDEC_HANDSHAKE2 = 0x5555,
_JEDEC_GET_ID = 0x9090,
_JEDEC_WRITE_BYTE = 0xa0a0,
_JEDEC_ERASE_HANDSHAKE = 0x8080,
_JEDEC_ERASE_CHIP = 0x1010,
_JEDEC_ERASE_SECTOR = 0x3030
};
enum SharpCommand : uint16_t {
_SHARP_RESET = 0xffff,
_SHARP_GET_ID = 0x9090,
_SHARP_WRITE_BYTE = 0x4040,
_SHARP_ERASE_SECTOR1 = 0x2020,
_SHARP_ERASE_SECTOR2 = 0xd0d0,
_SHARP_GET_STATUS = 0x7070,
_SHARP_CLEAR_STATUS = 0x5050,
_SHARP_SUSPEND = 0xb0b0,
_SHARP_RESUME = 0xd0d0
};
bool FlashRegion::hasBootExecutable(void) const {
// FIXME: this implementation will not detect executables that cross bank
// boundaries (but it shouldn't matter as executables must be <4 MB anyway)
@ -164,25 +190,16 @@ bool FlashRegion::hasBootExecutable(void) const {
return (~crc == *crcPtr);
}
uint16_t FlashRegion::getJEDECID(void) const {
auto flash = reinterpret_cast<volatile uint16_t *>(ptr);
uint32_t FlashRegion::getJEDECID(void) const {
auto ptr = getFlashPtr();
io::setFlashBank(bank);
ptr[0x000] = _SHARP_RESET;
ptr[0x000] = _SHARP_RESET;
ptr[0x555] = _JEDEC_HANDSHAKE1;
ptr[0x2aa] = _JEDEC_HANDSHAKE2;
ptr[0x555] = _JEDEC_GET_ID; // Same as _SHARP_GET_ID
flash[0x000] = SHARP_RESET;
flash[0x000] = SHARP_RESET;
flash[0x555] = FUJITSU_HANDSHAKE1;
flash[0x2aa] = FUJITSU_HANDSHAKE2;
flash[0x555] = FUJITSU_GET_JEDEC_ID;
uint16_t id = (flash[0] & 0xff) | ((flash[1] & 0xff) << 8);
if (id == ID_SHARP_LH28F016S)
flash[0x000] = SHARP_RESET;
else
flash[0x000] = FUJITSU_RESET;
return id;
return ptr[0] | (ptr[1] << 16);
}
const BIOSRegion bios;
@ -193,6 +210,286 @@ const FlashRegion pcmcia[2]{
{ SYS573_BANK_PCMCIA2, 0x4000000, io::JAMMA_PCMCIA_CD2 }
};
/* Data common to all flash chip drivers */
static constexpr int _FLASH_WRITE_TIMEOUT = 10000;
static constexpr int _FLASH_ERASE_TIMEOUT = 10000000;
const char *const FLASH_DRIVER_ERROR_NAMES[]{
"NO_ERROR",
"UNSUPPORTED_OP",
"CHIP_TIMEOUT",
"CHIP_ERROR",
"VERIFY_MISMATCH",
"WRITE_PROTECTED"
};
static const FlashChipSize _DUMMY_CHIP_SIZE{
.chipLength = 0,
.eraseSectorLength = 0
};
// The onboard flash and all Konami-supplied flash cards use 2 MB chips with 64
// KB sectors.
static const FlashChipSize _STANDARD_CHIP_SIZE{
.chipLength = 0x200000,
.eraseSectorLength = 0x10000
};
const FlashChipSize &FlashDriver::getChipSize(void) const {
return _DUMMY_CHIP_SIZE;
}
/* Fujitsu MBM29F016A driver */
enum FujitsuStatusFlag : uint16_t {
_FUJITSU_STATUS_ERASE_TOGGLE = 0x101 << 2,
_FUJITSU_STATUS_ERASE_START = 0x101 << 3,
_FUJITSU_STATUS_ERROR = 0x101 << 5,
_FUJITSU_STATUS_TOGGLE = 0x101 << 6,
_FUJITSU_STATUS_POLL_BIT = 0x101 << 7
};
FlashDriverError MBM29F016AFlashDriver::_flush(
volatile uint16_t *ptr, uint16_t value, int timeout
) {
for (; timeout > 0; timeout--) {
auto status = *ptr;
if (status == value)
return NO_ERROR;
if (!((status ^ value) & _FUJITSU_STATUS_POLL_BIT)) {
LOG("mismatch, exp=0x%04x, got=0x%04x", value, status);
return VERIFY_MISMATCH;
}
if (status & _FUJITSU_STATUS_ERROR) {
LOG("MBM29F016A error, stat=0x%04x", status);
return CHIP_ERROR;
}
delayMicroseconds(1);
}
LOG("MBM29F016A timeout, stat=0x%04x", *ptr);
return CHIP_TIMEOUT;
}
void MBM29F016AFlashDriver::write(uint32_t offset, uint16_t value) {
auto ptr = _region.getFlashPtr();
ptr[0x000] = _JEDEC_RESET;
ptr[0x555] = _JEDEC_HANDSHAKE1;
ptr[0x2aa] = _JEDEC_HANDSHAKE2;
ptr[0x555] = _JEDEC_WRITE_BYTE;
ptr[offset] = value;
}
void MBM29F016AFlashDriver::eraseSector(uint32_t offset) {
auto ptr = _region.getFlashPtr();
ptr[0x000] = _JEDEC_RESET;
ptr[0x555] = _JEDEC_HANDSHAKE1;
ptr[0x2aa] = _JEDEC_HANDSHAKE2;
ptr[0x555] = _JEDEC_ERASE_HANDSHAKE;
ptr[0x555] = _JEDEC_HANDSHAKE1;
ptr[0x2aa] = _JEDEC_HANDSHAKE2;
ptr[offset] = _JEDEC_ERASE_SECTOR;
}
void MBM29F016AFlashDriver::eraseChip(uint32_t offset) {
auto ptr = _region.getFlashPtr();
ptr[0x000] = _JEDEC_RESET;
ptr[0x555] = _JEDEC_HANDSHAKE1;
ptr[0x2aa] = _JEDEC_HANDSHAKE2;
ptr[0x555] = _JEDEC_ERASE_HANDSHAKE;
ptr[0x555] = _JEDEC_HANDSHAKE1;
ptr[0x2aa] = _JEDEC_HANDSHAKE2;
ptr[0x555] = _JEDEC_ERASE_CHIP;
}
FlashDriverError MBM29F016AFlashDriver::flushWrite(
uint32_t offset, uint16_t value
) {
auto ptr = &(_region.getFlashPtr())[offset];
return _flush(ptr, value, _FLASH_WRITE_TIMEOUT);
}
FlashDriverError MBM29F016AFlashDriver::flushErase(uint32_t offset) {
auto ptr = &(_region.getFlashPtr())[offset];
return _flush(ptr, 0xffff, _FLASH_ERASE_TIMEOUT);
}
const FlashChipSize &MBM29F016AFlashDriver::getChipSize(void) const {
return _STANDARD_CHIP_SIZE;
}
/* Unknown Fujitsu chip driver */
// Konami's drivers handle this chip pretty much identically to the MBM29F016A,
// but using 0x5555/0x2aaa as command addresses instead of 0x555/0x2aa. This
// could actually be a >2 MB chip.
void FujitsuUnknownFlashDriver::write(uint32_t offset, uint16_t value) {
auto ptr = _region.getFlashPtr();
ptr[0x0000] = _JEDEC_RESET;
ptr[0x5555] = _JEDEC_HANDSHAKE1;
ptr[0x2aaa] = _JEDEC_HANDSHAKE2;
ptr[0x5555] = _JEDEC_WRITE_BYTE;
ptr[offset] = value;
}
void FujitsuUnknownFlashDriver::eraseSector(uint32_t offset) {
auto ptr = _region.getFlashPtr();
ptr[0x0000] = _JEDEC_RESET;
ptr[0x5555] = _JEDEC_HANDSHAKE1;
ptr[0x2aaa] = _JEDEC_HANDSHAKE2;
ptr[0x5555] = _JEDEC_ERASE_HANDSHAKE;
ptr[0x5555] = _JEDEC_HANDSHAKE1;
ptr[0x2aaa] = _JEDEC_HANDSHAKE2;
ptr[offset] = _JEDEC_ERASE_SECTOR;
}
void FujitsuUnknownFlashDriver::eraseChip(uint32_t offset) {
auto ptr = _region.getFlashPtr();
ptr[0x0005] = _JEDEC_RESET;
ptr[0x5555] = _JEDEC_HANDSHAKE1;
ptr[0x2aaa] = _JEDEC_HANDSHAKE2;
ptr[0x5555] = _JEDEC_ERASE_HANDSHAKE;
ptr[0x5555] = _JEDEC_HANDSHAKE1;
ptr[0x2aaa] = _JEDEC_HANDSHAKE2;
ptr[0x5555] = _JEDEC_ERASE_CHIP;
}
/* Sharp LH28F016S driver */
enum SharpStatusFlag : uint16_t {
_SHARP_STATUS_DPS = 0x101 << 1,
_SHARP_STATUS_BWSS = 0x101 << 2,
_SHARP_STATUS_VPPS = 0x101 << 3,
_SHARP_STATUS_BWSLBS = 0x101 << 4,
_SHARP_STATUS_ECLBS = 0x101 << 5,
_SHARP_STATUS_ESS = 0x101 << 6,
_SHARP_STATUS_WSMS = 0x101 << 7
};
FlashDriverError LH28F016SFlashDriver::_flush(
volatile uint16_t *ptr, int timeout
) {
// Not required as all write/erase commands already put the chip into status
// reading mode.
//*ptr = _SHARP_GET_STATUS;
for (; timeout > 0; timeout--) {
auto status = *ptr;
if (status & (_SHARP_STATUS_DPS | _SHARP_STATUS_VPPS)) {
*ptr = _SHARP_CLEAR_STATUS;
LOG("LH28F016S locked, stat=0x%04x", status);
return WRITE_PROTECTED;
}
if (status & (_SHARP_STATUS_BWSLBS | _SHARP_STATUS_ECLBS)) {
*ptr = _SHARP_CLEAR_STATUS;
LOG("LH28F016S error, stat=0x%04x", status);
return CHIP_ERROR;
}
if (status & _SHARP_STATUS_WSMS)
return NO_ERROR;
delayMicroseconds(1);
}
LOG("LH28F016S timeout, stat=0x%04x", *ptr);
return CHIP_TIMEOUT;
}
void LH28F016SFlashDriver::write(uint32_t offset, uint16_t value) {
auto ptr = &(_region.getFlashPtr())[offset];
*ptr = _SHARP_RESET;
*ptr = _SHARP_CLEAR_STATUS;
*ptr = _SHARP_WRITE_BYTE;
*ptr = value;
}
void LH28F016SFlashDriver::eraseSector(uint32_t offset) {
auto ptr = &(_region.getFlashPtr())[offset];
*ptr = _SHARP_RESET;
*ptr = _SHARP_ERASE_SECTOR1;
*ptr = _SHARP_ERASE_SECTOR2;
}
FlashDriverError LH28F016SFlashDriver::flushWrite(
uint32_t offset, uint16_t value
) {
auto ptr = &(_region.getFlashPtr())[offset];
return _flush(ptr, _FLASH_WRITE_TIMEOUT);
}
FlashDriverError LH28F016SFlashDriver::flushErase(uint32_t offset) {
auto ptr = &(_region.getFlashPtr())[offset];
return _flush(ptr, _FLASH_ERASE_TIMEOUT);
}
const FlashChipSize &LH28F016SFlashDriver::getChipSize(void) const {
return _STANDARD_CHIP_SIZE;
}
/* Flash chip identification */
enum FlashIdentifier : uint16_t {
// NOTE: the MBM29F017A datasheet incorrectly lists the device ID as 0xad in
// some places. The chip behaves pretty much identically to the MBM29F016A.
_ID_MBM29F016A = 0x04 | (0xad << 8),
_ID_MBM29F017A = 0x04 | (0x3d << 8),
_ID_FUJITSU_UNKNOWN = 0x04 | (0xa4 << 8),
_ID_LH28F016S = 0x89 | (0xaa << 8)
};
FlashDriver *newFlashDriver(FlashRegion &region) {
if (!region.isPresent()) {
LOG("card not present");
return new FlashDriver(region);
}
uint32_t id = region.getJEDECID();
uint16_t low = ((id >> 0) & 0xff) | ((id >> 8) & 0xff00);
uint16_t high = ((id >> 8) & 0xff) | ((id >> 16) & 0xff00);
LOG("low=0x%04x, high=0x%04x", low, high);
if (low != high) {
// TODO: implement single-chip (16-bit) flash support
return new FlashDriver(region);
} else {
switch (low) {
case _ID_MBM29F016A:
case _ID_MBM29F017A:
return new MBM29F016AFlashDriver(region);
case _ID_FUJITSU_UNKNOWN:
return new FujitsuUnknownFlashDriver(region);
case _ID_LH28F016S:
return new LH28F016SFlashDriver(region);
default:
return new FlashDriver(region);
}
}
}
/* BIOS ROM headers */
static const ShellInfo _SHELL_VERSIONS[]{

View File

@ -3,6 +3,7 @@
#include <stddef.h>
#include <stdint.h>
#include "common/io.hpp"
#include "common/util.hpp"
#include "ps1/registers.h"
@ -52,52 +53,113 @@ public:
inline FlashRegion(int bank, size_t regionLength, uint32_t inputs = 0)
: Region(DEV0_BASE, regionLength), bank(bank), inputs(inputs) {}
inline volatile uint16_t *getFlashPtr(void) const {
io::setFlashBank(bank);
return reinterpret_cast<volatile uint16_t *>(ptr);
}
bool isPresent(void) const;
void read(void *data, uint32_t offset, size_t length) const;
uint32_t zipCRC32(uint32_t offset, size_t length, uint32_t crc = 0) const;
bool hasBootExecutable(void) const;
uint16_t getJEDECID(void) const;
uint32_t getJEDECID(void) const;
};
extern const BIOSRegion bios;
extern const RTCRegion rtc;
extern const FlashRegion flash, pcmcia[2];
/* Flash memory driver */
/* Flash chip drivers */
enum FlashManufacturer : uint8_t {
MANUF_FUJITSU = 0x04,
MANUF_SHARP = 0x89
enum FlashDriverError {
NO_ERROR = 0,
UNSUPPORTED_OP = 1,
CHIP_TIMEOUT = 2,
CHIP_ERROR = 3,
VERIFY_MISMATCH = 4,
WRITE_PROTECTED = 5
};
enum FlashJEDECID : uint16_t {
ID_FUJITSU_MBM29F016A = MANUF_FUJITSU | (0xad << 8),
ID_FUJITSU_MBM29F016A_ALT = MANUF_FUJITSU | (0x3d << 8),
ID_FUJITSU_UNKNOWN = MANUF_FUJITSU | (0xa4 << 8),
ID_SHARP_LH28F016S = MANUF_SHARP | (0xaa << 8)
struct FlashChipSize {
public:
size_t chipLength, eraseSectorLength;
};
enum FlashCommand : uint16_t {
SHARP_ERASE_SECTOR = 0x2020,
SHARP_WRITE = 0x4040,
SHARP_CLEAR_STATUS = 0x5050,
SHARP_GET_STATUS = 0x7070,
SHARP_GET_JEDEC_ID = 0x9090,
SHARP_SUSPEND = 0xb0b0,
SHARP_RESUME = 0xd0d0,
SHARP_RESET = 0xffff,
class FlashDriver {
protected:
FlashRegion &_region;
FUJITSU_ERASE_CHIP = 0x1010,
FUJITSU_ERASE_SECTOR = 0x3030,
FUJITSU_HANDSHAKE2 = 0x5555,
FUJITSU_ERASE = 0x8080,
FUJITSU_GET_JEDEC_ID = 0x9090,
FUJITSU_WRITE = 0xa0a0,
FUJITSU_HANDSHAKE1 = 0xaaaa,
FUJITSU_RESET = 0xf0f0
public:
inline FlashDriver(FlashRegion &region)
: _region(region) {}
virtual ~FlashDriver(void) {}
virtual void write(uint32_t offset, uint16_t value) {}
virtual void eraseSector(uint32_t offset) {}
virtual void eraseChip(uint32_t offset) {}
virtual FlashDriverError flushWrite(uint32_t offset, uint16_t value) {
return UNSUPPORTED_OP;
}
virtual FlashDriverError flushErase(uint32_t offset) {
return UNSUPPORTED_OP;
}
virtual const FlashChipSize &getChipSize(void) const;
};
class MBM29F016AFlashDriver : public FlashDriver {
private:
FlashDriverError _flush(
volatile uint16_t *ptr, uint16_t value, int timeout
);
public:
inline MBM29F016AFlashDriver(FlashRegion &region)
: FlashDriver(region) {}
void write(uint32_t offset, uint16_t value);
void eraseSector(uint32_t offset);
void eraseChip(uint32_t offset);
FlashDriverError flushWrite(uint32_t offset, uint16_t value);
FlashDriverError flushErase(uint32_t offset);
const FlashChipSize &getChipSize(void) const;
};
class FujitsuUnknownFlashDriver : public MBM29F016AFlashDriver {
public:
inline FujitsuUnknownFlashDriver(FlashRegion &region)
: MBM29F016AFlashDriver(region) {}
void write(uint32_t offset, uint16_t value);
void eraseSector(uint32_t offset);
void eraseChip(uint32_t offset);
};
class LH28F016SFlashDriver : public FlashDriver {
private:
FlashDriverError _flush(volatile uint16_t *ptr, int timeout);
public:
inline LH28F016SFlashDriver(FlashRegion &region)
: FlashDriver(region) {}
void write(uint32_t offset, uint16_t value);
void eraseSector(uint32_t offset);
void eraseChip(uint32_t offset);
FlashDriverError flushWrite(uint32_t offset, uint16_t value);
FlashDriverError flushErase(uint32_t offset);
const FlashChipSize &getChipSize(void) const;
};
extern const char *const FLASH_DRIVER_ERROR_NAMES[];
static inline const char *getErrorString(FlashDriverError error) {
return FLASH_DRIVER_ERROR_NAMES[error];
}
FlashDriver *newFlashDriver(FlashRegion &region);
/* BIOS ROM headers */
struct [[gnu::packed]] SonyKernelHeader {

View File

@ -57,8 +57,8 @@ enum FlashRegionInfoFlag : uint16_t {
class FlashRegionInfo {
public:
uint16_t flags, jedecID;
uint32_t crc[4];
uint32_t flags;
uint32_t jedecID, crc[4];
inline void clearFlags(void) {
flags = 0;
@ -168,6 +168,7 @@ private:
bool _qrCodeWorker(void);
bool _cartDumpWorker(void);
bool _cartWriteWorker(void);
bool _cartRestoreWorker(void);
bool _cartReflashWorker(void);
bool _cartEraseWorker(void);

View File

@ -28,6 +28,10 @@ static const Action _ACTIONS[]{
.name = "CartActionsScreen.hexdump.name"_h,
.prompt = "CartActionsScreen.hexdump.prompt"_h,
.target = &CartActionsScreen::hexdump
}, {
.name = "CartActionsScreen.hddRestore.name"_h,
.prompt = "CartActionsScreen.hddRestore.prompt"_h,
.target = &CartActionsScreen::hddRestore
}, {
.name = "CartActionsScreen.reflash.name"_h,
.prompt = "CartActionsScreen.reflash.prompt"_h,
@ -69,6 +73,26 @@ void CartActionsScreen::hexdump(ui::Context &ctx) {
ctx.show(APP->_hexdumpScreen, false, true);
}
void CartActionsScreen::hddRestore(ui::Context &ctx) {
APP->_filePickerScreen.setMessage(
*this,
[](ui::Context &ctx) {
ctx.show(APP->_confirmScreen, false, true);
},
STR("CartActionsScreen.hddRestore.filePrompt")
);
APP->_confirmScreen.setMessage(
*this,
[](ui::Context &ctx) {
APP->_setupWorker(&App::_cartRestoreWorker);
ctx.show(APP->_workerStatusScreen, false, true);
},
STR("CartActionsScreen.hddRestore.confirm")
);
APP->_filePickerScreen.loadRootAndShow(ctx);
}
void CartActionsScreen::reflash(ui::Context &ctx) {
ctx.show(APP->_reflashGameScreen, false, true);
}

View File

@ -18,6 +18,7 @@ public:
void qrDump(ui::Context &ctx);
void hddDump(ui::Context &ctx);
void hexdump(ui::Context &ctx);
void hddRestore(ui::Context &ctx);
void reflash(ui::Context &ctx);
void erase(ui::Context &ctx);
void resetSystemID(ui::Context &ctx);

View File

@ -265,6 +265,83 @@ bool App::_cartWriteWorker(void) {
return _cartUnlockWorker();
}
bool App::_cartRestoreWorker(void) {
_workerStatus.update(0, 3, WSTR("App.cartRestoreWorker.init"));
const char *path = _filePickerScreen.selectedPath;
cart::Dump newDump;
cart::DriverError error;
auto _file = _fileProvider.openFile(path, file::READ);
size_t length;
if (!_file)
goto _fileOpenError;
length = _file->read(&newDump, sizeof(newDump));
if (length < (sizeof(newDump) - sizeof(newDump.data)))
goto _fileError;
if (false) // TODO: validate dump
goto _fileError;
if (length != newDump.getDumpLength())
goto _fileError;
delete _file;
if (_dump.chipType != newDump.chipType) {
_messageScreen.setMessage(
MESSAGE_ERROR, _filePickerScreen,
WSTR("App.cartRestoreWorker.typeError"), path
);
_workerStatus.setNextScreen(_messageScreen);
return false;
}
_workerStatus.update(1, 3, WSTR("App.cartRestoreWorker.setDataKey"));
error = _driver->setDataKey(newDump.dataKey);
if (error) {
LOG("key error [%s]", cart::getErrorString(error));
} else {
if (newDump.flags & (
cart::DUMP_PUBLIC_DATA_OK | cart::DUMP_PRIVATE_DATA_OK
))
_dump.copyDataFrom(newDump.data);
if (newDump.flags & cart::DUMP_CONFIG_OK)
_dump.copyConfigFrom(newDump.config);
_workerStatus.update(2, 3, WSTR("App.cartRestoreWorker.write"));
error = _driver->writeData();
}
_cartDetectWorker();
if (error) {
_messageScreen.setMessage(
MESSAGE_ERROR, _filePickerScreen,
WSTR("App.cartRestoreWorker.writeError"),
cart::getErrorString(error)
);
_workerStatus.setNextScreen(_messageScreen);
return false;
}
return _cartUnlockWorker();
_fileError:
delete _file;
_fileOpenError:
_messageScreen.setMessage(
MESSAGE_ERROR, _filePickerScreen,
WSTR("App.cartRestoreWorker.fileError"), path
);
_workerStatus.setNextScreen(_messageScreen);
return false;
}
bool App::_cartReflashWorker(void) {
// Make sure a valid cart ID is present if required by the new data.
if (
@ -279,6 +356,8 @@ bool App::_cartReflashWorker(void) {
return false;
}
_workerStatus.update(0, 3, WSTR("App.cartReflashWorker.init"));
// TODO: preserve 0x81 traceid if possible
//uint8_t traceID[8];
//_parser->getIdentifiers()->traceID.copyTo(traceID);

View File

@ -156,7 +156,7 @@ void SystemInfoScreen::show(ui::Context &ctx, bool goBack) {
_PRINT(STR("SystemInfoScreen.flash.header"));
_PRINT(
STR("SystemInfoScreen.flash.info"), info.flash.jedecID & 0xff,
info.flash.jedecID >> 8, info.flash.crc[0]
(info.flash.jedecID >> 16) & 0xff, info.flash.crc[0]
);
if (info.flash.flags & FLASH_REGION_INFO_BOOTABLE)
@ -173,7 +173,8 @@ void SystemInfoScreen::show(ui::Context &ctx, bool goBack) {
if (card.flags & FLASH_REGION_INFO_PRESENT) {
_PRINT(
STR("SystemInfoScreen.pcmcia.info"), card.jedecID & 0xff,
card.jedecID >> 8, card.crc[0], card.crc[1], card.crc[3]
(card.jedecID >> 16) & 0xff, card.crc[0], card.crc[1],
card.crc[3]
);
if (card.flags & FLASH_REGION_INFO_BOOTABLE)

View File

@ -87,7 +87,7 @@ public:
DriverError readSystemID(void);
};
class [[gnu::packed]] X76Driver : public CartDriver {
class X76Driver : public CartDriver {
protected:
DriverError _x76Command(
uint8_t pollByte, uint8_t cmd, int param = -1
@ -100,7 +100,7 @@ public:
DriverError readCartID(void);
};
class [[gnu::packed]] X76F041Driver : public X76Driver {
class X76F041Driver : public X76Driver {
public:
inline X76F041Driver(Dump &dump)
: X76Driver(dump, X76F041) {}
@ -111,7 +111,7 @@ public:
DriverError setDataKey(const uint8_t *key);
};
class [[gnu::packed]] X76F100Driver : public X76Driver {
class X76F100Driver : public X76Driver {
public:
inline X76F100Driver(Dump &dump)
: X76Driver(dump, X76F100) {}
@ -122,7 +122,7 @@ public:
DriverError setDataKey(const uint8_t *key);
};
class [[gnu::packed]] ZS01Driver : public CartDriver {
class ZS01Driver : public CartDriver {
private:
uint8_t _encoderState;