Fix crashes, add custom shell detection, update schemas

This commit is contained in:
spicyjpeg 2024-06-06 00:48:03 +02:00
parent f4a8d16b20
commit 11d07e1e51
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
18 changed files with 244 additions and 131 deletions

View File

@ -6,13 +6,13 @@
"App": {
"ideInitWorker": {
"init": "Initializing IDE devices...\nDo not turn off the 573 or unplug drives."
"init": "Initializing IDE devices...\nDo not turn off the 573 or unplug drives.",
"autoboot": "Searching for boot executables...\nDo not turn off the 573 or unplug drives."
},
"fileInitWorker": {
"unmount": "Unmounting all mounted filesystems...\nDo not turn off the 573 or unplug drives.",
"mount": "Detecting and mounting filesystems...\nDo not turn off the 573 or unplug drives.",
"loadResources": "Loading resource pack...\nDo not turn off the 573 or unplug drives.",
"autoboot": "Searching for boot executables...\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...",
@ -56,10 +56,11 @@
"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 (if a CD-ROM drive is also present) 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 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"
"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",
"fragmentError": "The selected file cannot be loaded as it is fragmented across too many non-contiguous areas on the filesystem. Try defragging or reformatting the drive.\n\nFile:\t\t\t%s\nTotal fragments:\t%d\nOverflow:\t\t%d"
},
"flashHeaderWriteWorker": {
"erase": "Erasing existing header...\nDo not turn off the 573.",
@ -583,11 +584,12 @@
"kernelInfo": {
"sony": " Kernel type:\tSony PlayStation kernel\n Kernel version:\t%s\n Kernel date:\t%04X-%02X-%02X\n",
"openbios": " Kernel type:\tOpenBIOS\n Kernel build ID:\n %s\n",
"unknown": " Kernel type:\tUnknown\n"
"unknown": " Kernel type:\tunknown\n"
},
"shellInfo": {
"konami": " Shell type:\t\t%s\n Boot file name:\t%s\n",
"unknown": " Shell type:\t\tUnknown\n"
"custom": " Shell type:\t\tcustom executable\n Shell name:\n %s\n",
"unknown": " Shell type:\t\tunknown\n"
}
},
"rtc": {

View File

@ -5,7 +5,7 @@
"title": "Security cartridge description",
"type": "object",
"required": [ "pcb", "dataFormat" ],
"required": [ "pcb", "dataKey", "dataFormat" ],
"additionalProperties": false,
"properties": {
@ -27,6 +27,14 @@
"PWB0000088954"
]
},
"dataKey": {
"title": "Data key",
"description": "The 8-byte key this cartridge's EEPROM is normally locked with. Must be specified as 8 hexadecimal values separated by dashes.",
"type": "string",
"pattern": "^([0-9a-f]{2}-){7}[0-9a-f]{2}$",
"default": "00-00-00-00-00-00-00-00"
},
"dataFormat": {
"title": "Data format type",
"description": "Format of the data stored in the cartridge's EEPROM. The 'simple' format is just the region string, the 'basic' format includes some additional information and the 'extended' format is the full 16-byte game header plus any IDs.",

View File

@ -86,12 +86,17 @@
"maximum": 2004,
"default": 2000
},
"identifier": {
"title": "MAME game identifier",
"description": "The name used by MAME to identify this game. Omit if the game is not currently emulated by MAME.",
"type": "string",
"identifiers": {
"title": "MAME game identifier list",
"description": "The names used by MAME to identify this game, one for each region/version listed in the regions field. Omit if the game is not currently emulated by MAME.",
"type": "array",
"pattern": "^[0-9a-z_]*$"
"items": {
"title": "MAME game identifier",
"type": "string",
"pattern": "^[0-9a-z_]*$"
}
},
"ioBoard": {

View File

@ -69,8 +69,10 @@ int main(int argc, const char **argv) {
loader.addArgument(_lengthArg);
#ifdef ENABLE_ARGV
for (; argc > 0; argc--)
loader.addArgument(*(argv++));
for (; argc > 0; argc--) {
if (!loader.copyArgument(*(argv++)))
break;
}
#endif
io::clearWatchdog();

View File

@ -88,7 +88,7 @@ bool ExecutableLauncherArgs::parseArgument(const char *arg) {
return true;
case "device"_h:
device = int(strtol(&arg[6], nullptr, 0));
device = int(strtol(&arg[7], nullptr, 0));
return true;
case "frag"_h:

View File

@ -202,7 +202,8 @@ bool VFSProvider::mount(const char *prefix, Provider *provider, bool force) {
}
freeMP->prefix = hash;
freeMP->pathOffset = __builtin_strlen(prefix);
freeMP->pathOffset =
(__builtin_strchr(prefix, VFS_PREFIX_SEPARATOR) - prefix) + 1;
freeMP->provider = provider;
LOG_FS("mapped %s", prefix);

View File

@ -232,8 +232,10 @@ bool Device::_writeDMA(const void *data, size_t length) const {
static constexpr int _COMMAND_TIMEOUT = 30000000;
static constexpr int _DRQ_TIMEOUT = 30000000;
static constexpr int _DETECT_TIMEOUT = 500000;
static constexpr int _SRST_DELAY = 5000;
static constexpr int _DETECT_TIMEOUT = 2500000;
static constexpr int _SRST_SET_DELAY = 5000;
static constexpr int _SRST_CLEAR_DELAY = 50000;
// Note that ATA drives will always assert DRDY when ready, but ATAPI drives
// will not. This is an intentional feature meant to prevent ATA-only drivers
@ -309,8 +311,10 @@ void Device::_handleError(void) {
// Issuing a device reset command to an ATAPI drive would result in the
// error's sense data being lost.
if (!(flags & DEVICE_ATAPI))
#if 0
if (flags & DEVICE_ATAPI)
_write(CS0_COMMAND, ATA_DEVICE_RESET);
#endif
}
void Device::_handleTimeout(void) {
@ -323,15 +327,16 @@ void Device::_handleTimeout(void) {
lastErrorReg, lastCountReg
);
_write(CS0_COMMAND, ATA_DEVICE_RESET);
if (flags & DEVICE_ATAPI)
_write(CS0_COMMAND, ATA_DEVICE_RESET);
}
DeviceError Device::_resetDrive(void) {
// Issue a software reset, which affects both devices on the bus.
_write(CS1_DEVICE_CTRL, CS1_DEVICE_CTRL_IEN | CS1_DEVICE_CTRL_SRST);
delayMicroseconds(_SRST_DELAY);
delayMicroseconds(_SRST_SET_DELAY);
_write(CS1_DEVICE_CTRL, CS1_DEVICE_CTRL_IEN);
delayMicroseconds(_SRST_DELAY);
delayMicroseconds(_SRST_CLEAR_DELAY);
_select(0);
@ -344,6 +349,7 @@ DeviceError Device::_resetDrive(void) {
io::clearWatchdog();
#endif
#if 0
// Issue dummy writes to the sector count register and attempt to read back
// the written value. This should not fail even if the drive is busy.
uint8_t pattern = 0x55;
@ -366,6 +372,9 @@ DeviceError Device::_resetDrive(void) {
LOG_IDE("drive %d not responding", getDriveIndex());
return NO_DRIVE;
#else
return NO_ERROR;
#endif
}
/* ATA-specific function */

View File

@ -62,37 +62,37 @@ enum CS0CountFlag : uint8_t {
/* ATA command definitions */
enum ATACommand : uint8_t {
ATA_NOP = 0x00,
ATA_DEVICE_RESET = 0x08,
ATA_READ_SECTORS = 0x20,
ATA_READ_SECTORS_EXT = 0x24,
ATA_READ_DMA_EXT = 0x25,
ATA_READ_DMA_QUEUED_EXT = 0x26,
ATA_WRITE_SECTORS = 0x30,
ATA_WRITE_SECTORS_EXT = 0x34,
ATA_WRITE_DMA_EXT = 0x35,
ATA_WRITE_DMA_QUEUED_EXT = 0x36,
ATA_SEEK = 0x70,
ATA_EXECUTE_DIAGNOSTIC = 0x90,
ATA_PACKET = 0xa0,
ATA_IDENTIFY_PACKET = 0xa1,
ATA_SERVICE = 0xa2,
ATA_DEVICE_CONFIG = 0xb1,
ATA_ERASE_SECTORS = 0xc0,
ATA_READ_DMA_QUEUED = 0xc7,
ATA_READ_DMA = 0xc8,
ATA_WRITE_DMA = 0xca,
ATA_WRITE_DMA_QUEUED = 0xcc,
ATA_STANDBY_IMMEDIATE = 0xe0,
ATA_IDLE_IMMEDIATE = 0xe1,
ATA_STANDBY = 0xe2,
ATA_IDLE = 0xe3,
ATA_CHECK_POWER_MODE = 0xe5,
ATA_SLEEP = 0xe6,
ATA_FLUSH_CACHE = 0xe7,
ATA_FLUSH_CACHE_EXT = 0xea,
ATA_IDENTIFY = 0xec,
ATA_SET_FEATURES = 0xef
ATA_NOP = 0x00, // ATAPI
ATA_DEVICE_RESET = 0x08, // ATAPI
ATA_READ_SECTORS = 0x20, // ATA
ATA_READ_SECTORS_EXT = 0x24, // ATA
ATA_READ_DMA_EXT = 0x25, // ATA
ATA_READ_DMA_QUEUED_EXT = 0x26, // ATA
ATA_WRITE_SECTORS = 0x30, // ATA
ATA_WRITE_SECTORS_EXT = 0x34, // ATA
ATA_WRITE_DMA_EXT = 0x35, // ATA
ATA_WRITE_DMA_QUEUED_EXT = 0x36, // ATA
ATA_SEEK = 0x70, // ATA
ATA_EXECUTE_DIAGNOSTIC = 0x90, // ATA/ATAPI
ATA_PACKET = 0xa0, // ATAPI
ATA_IDENTIFY_PACKET = 0xa1, // ATAPI
ATA_SERVICE = 0xa2, // ATA/ATAPI
ATA_DEVICE_CONFIG = 0xb1, // ATA
ATA_ERASE_SECTORS = 0xc0, // ATA
ATA_READ_DMA_QUEUED = 0xc7, // ATA
ATA_READ_DMA = 0xc8, // ATA
ATA_WRITE_DMA = 0xca, // ATA
ATA_WRITE_DMA_QUEUED = 0xcc, // ATA
ATA_STANDBY_IMMEDIATE = 0xe0, // ATA/ATAPI
ATA_IDLE_IMMEDIATE = 0xe1, // ATA/ATAPI
ATA_STANDBY = 0xe2, // ATA
ATA_IDLE = 0xe3, // ATA
ATA_CHECK_POWER_MODE = 0xe5, // ATA/ATAPI
ATA_SLEEP = 0xe6, // ATA/ATAPI
ATA_FLUSH_CACHE = 0xe7, // ATA
ATA_FLUSH_CACHE_EXT = 0xea, // ATA
ATA_IDENTIFY = 0xec, // ATA
ATA_SET_FEATURES = 0xef // ATA/ATAPI
};
enum ATAFeature : uint8_t {

View File

@ -301,22 +301,28 @@ const FlashRegion pcmcia[2]{
/* BIOS ROM headers */
static const ShellInfo _SHELL_VERSIONS[]{
static const ShellInfo _KONAMI_SHELLS[]{
{
.name = "700A01",
.bootFileName = reinterpret_cast<const char *>(DEV2_BASE | 0x40890),
.headerPtr = reinterpret_cast<const uint8_t *>(DEV2_BASE | 0x40000),
.headerHash = 0x9c615f57
.headerHash = 0x9c615f57,
.header = reinterpret_cast<const util::ExecutableHeader *>(
DEV2_BASE | 0x40000
)
}, {
.name = "700A01 (Gachagachamp)",
.bootFileName = reinterpret_cast<const char *>(DEV2_BASE | 0x40890),
.headerPtr = reinterpret_cast<const uint8_t *>(DEV2_BASE | 0x40000),
.headerHash = 0x7e31a844
.headerHash = 0x7e31a844,
.header = reinterpret_cast<const util::ExecutableHeader *>(
DEV2_BASE | 0x40000
)
}, {
.name = "700B01",
.bootFileName = reinterpret_cast<const char *>(DEV2_BASE | 0x61334),
.headerPtr = reinterpret_cast<const uint8_t *>(DEV2_BASE | 0x28000),
.headerHash = 0xb257d3b5
.headerHash = 0xb257d3b5,
.header = reinterpret_cast<const util::ExecutableHeader *>(
DEV2_BASE | 0x28000
)
}
};
@ -331,16 +337,43 @@ bool OpenBIOSHeader::validateMagic(void) const {
}
bool ShellInfo::validateHash(void) const {
return (util::hash(headerPtr, sizeof(util::ExecutableHeader)) == headerHash);
return (util::hash(
reinterpret_cast<const uint8_t *>(header), sizeof(util::ExecutableHeader)
) == headerHash);
}
const ShellInfo *getShellInfo(void) {
for (auto &shell : _SHELL_VERSIONS) {
if (shell.validateHash())
return &shell;
bool getShellInfo(ShellInfo &output) {
for (auto &shell : _KONAMI_SHELLS) {
if (!shell.validateHash())
continue;
__builtin_memcpy(&output, &shell, sizeof(ShellInfo));
return true;
}
return nullptr;
// If no official shell was found, fall back to searching the entire ROM for
// a valid PS1 executable. Note that the executable has to be 32-byte
// aligned for this to work.
// TODO: use a binary search instead of a linear one
for (uintptr_t ptr = DEV2_BASE; ptr < (DEV2_BASE + 0x80000); ptr += 32) {
auto header = reinterpret_cast<const util::ExecutableHeader *>(ptr);
if (!header->validateMagic())
continue;
output.name = header->getRegionString();
output.bootFileName = nullptr;
#if 0
output.headerHash = util::hash(
reinterpret_cast<const uint8_t *>(header),
sizeof(util::ExecutableHeader)
);
#endif
output.header = header;
return true;
}
return false;
}
}

View File

@ -80,7 +80,7 @@ extern const FlashRegion flash, pcmcia[2];
/* BIOS ROM headers */
struct SonyKernelHeader {
class SonyKernelHeader {
public:
uint8_t day, month;
uint16_t year;
@ -90,7 +90,7 @@ public:
bool validateMagic(void) const;
};
struct OpenBIOSHeader {
class OpenBIOSHeader {
public:
uint8_t magic[8];
uint32_t idNameLength, idDescLength, idType;
@ -103,11 +103,12 @@ public:
bool validateMagic(void) const;
};
struct ShellInfo {
class ShellInfo {
public:
const char *name, *bootFileName;
const uint8_t *headerPtr;
util::Hash headerHash;
const char *name, *bootFileName;
util::Hash headerHash;
const util::ExecutableHeader *header;
bool validateHash(void) const;
};
@ -117,6 +118,6 @@ static const auto &sonyKernelHeader =
static const auto &openBIOSHeader =
*reinterpret_cast<const OpenBIOSHeader *>(DEV2_BASE | 0x78);
const ShellInfo *getShellInfo(void);
bool getShellInfo(ShellInfo &output);
}

View File

@ -599,7 +599,20 @@ size_t encodeBase41(char *output, const uint8_t *input, size_t length) {
/* PS1 executable loader */
bool ExecutableHeader::validateMagic(void) const {
return (hash(magic, sizeof(magic)) == "PS-X EXE"_h);
#if 0
return (
hash(magic, sizeof(magic)) ==
hash("PS-X EXE\0\0\0\0\0\0\0\0", sizeof(magic), 0)
);
#else
return true
&& (magic[0] == 0x582d5350)
&& (magic[1] == 0x45584520)
&& !magic[2]
&& !magic[3]
&& (entryPoint >= 0x80000000)
&& (textOffset >= 0x80000000);
#endif
}
ExecutableLoader::ExecutableLoader(
@ -610,19 +623,30 @@ ExecutableLoader::ExecutableLoader(
_currentStackPtr = reinterpret_cast<char *>(_argListPtr);
}
void ExecutableLoader::copyArgument(const char *arg, size_t length) {
bool ExecutableLoader::addArgument(const char *arg) {
if (_numArgs >= MAX_EXECUTABLE_ARGS)
return false;
_argListPtr[_numArgs++] = arg;
return true;
}
bool ExecutableLoader::copyArgument(const char *arg, size_t length) {
if (_numArgs >= MAX_EXECUTABLE_ARGS)
return false;
// Command-line arguments must be copied to the top of the new stack in
// order to ensure the executable is going to be able to access them at any
// time.
length++;
_currentStackPtr -= (length + 7) & ~7;
_currentStackPtr -= (length + 7) & ~7;
_argListPtr[_numArgs++] = _currentStackPtr;
addArgument(_currentStackPtr);
__builtin_memcpy(_currentStackPtr, arg, length);
//assert(_argCount <= MAX_EXECUTABLE_ARGS);
return true;
}
void ExecutableLoader::formatArgument(const char *format, ...) {
bool ExecutableLoader::formatArgument(const char *format, ...) {
char buffer[64];
va_list ap;
@ -630,7 +654,7 @@ void ExecutableLoader::formatArgument(const char *format, ...) {
int length = vsnprintf(buffer, sizeof(buffer), format, ap);
va_end(ap);
copyArgument(buffer, length + 1);
return copyArgument(buffer, length + 1);
}
[[noreturn]] void ExecutableLoader::run(

View File

@ -290,7 +290,7 @@ static constexpr size_t MAX_EXECUTABLE_ARGS = 32;
class ExecutableHeader {
public:
uint8_t magic[8], _pad[8];
uint32_t magic[4];
uint32_t entryPoint, initialGP;
uint32_t textOffset, textLength;
@ -311,6 +311,9 @@ public:
inline void *getStackPtr(void) const {
return reinterpret_cast<void *>(stackOffset + stackLength);
}
inline const char *getRegionString(void) const {
return reinterpret_cast<const char *>(&this[1]);
}
inline void relocateText(const void *source) const {
__builtin_memcpy(getTextPtr(), source, textLength);
}
@ -322,24 +325,22 @@ class ExecutableLoader {
private:
void *_entryPoint, *_initialGP;
int _numArgs;
size_t _numArgs;
const char **_argListPtr;
char *_currentStackPtr;
public:
inline void addArgument(const char *arg) {
_argListPtr[_numArgs++] = arg;
}
inline void copyArgument(const char *arg) {
copyArgument(arg, __builtin_strlen(arg));
inline bool copyArgument(const char *arg) {
return copyArgument(arg, __builtin_strlen(arg));
}
[[noreturn]] inline void run(void) {
run(_numArgs, _argListPtr);
}
ExecutableLoader(void *entryPoint, void *initialGP, void *stackTop);
void copyArgument(const char *arg, size_t length);
void formatArgument(const char *format, ...);
bool addArgument(const char *arg);
bool copyArgument(const char *arg, size_t length);
bool formatArgument(const char *format, ...);
[[noreturn]] void run(int rawArgc, const char *const *rawArgv);
};

View File

@ -117,8 +117,10 @@ int main(int argc, const char **argv) {
auto executableArg = args.executableArgs;
for (size_t i = args.numArgs; i; i--)
loader.copyArgument(*(executableArg++));
for (size_t i = args.numArgs; i; i--) {
if (!loader.copyArgument(*(executableArg++)))
break;
}
loader.run();
return 0;

View File

@ -82,7 +82,6 @@ void FileIOManager::initIDE(void) {
for (size_t i = 0; i < util::countOf(ide::devices); i++) {
auto &dev = ide::devices[i];
name[3] = i + '0';
if (!(dev.flags & ide::DEVICE_READY))
continue;
@ -112,6 +111,7 @@ void FileIOManager::initIDE(void) {
vfs.mount("hdd:", fat);
}
name[3] = i + '0';
vfs.mount(name, ide[i], true);
}
}
@ -120,15 +120,19 @@ void FileIOManager::closeIDE(void) {
char name[8]{ "ide#:\0" };
for (size_t i = 0; i < util::countOf(ide::devices); i++) {
if (ide[i]) {
ide[i]->close();
delete ide[i];
ide[i] = nullptr;
}
if (!ide[i])
continue;
ide[i]->close();
delete ide[i];
ide[i] = nullptr;
name[3] = i + '0';
vfs.unmount(name);
}
vfs.unmount("cdrom:");
vfs.unmount("hdd:");
}
bool FileIOManager::loadResourceFile(const char *path) {
@ -161,17 +165,6 @@ void FileIOManager::closeResourceFile(void) {
}
}
void FileIOManager::close(void) {
vfs.close();
resource.close();
#ifdef ENABLE_PCDRV
host.close();
#endif
closeResourceFile();
closeIDE();
}
/* App class */
static constexpr size_t _WORKER_STACK_SIZE = 0x20000;
@ -186,7 +179,7 @@ _ctx(ctx), _cartDriver(nullptr), _cartParser(nullptr), _identified(nullptr) {}
App::~App(void) {
_unloadCartData();
_fileIO.close();
_workerStack.destroy();
}
void App::_unloadCartData(void) {
@ -308,10 +301,10 @@ void App::_runWorker(
}
void App::_worker(void) {
if (_workerFunction) {
if (_workerFunction)
(this->*_workerFunction)();
_workerStatus.setStatus(WORKER_DONE);
}
_workerStatus.setStatus(WORKER_DONE);
// Do nothing while waiting for vblank once the task is done.
for (;;)
@ -359,7 +352,13 @@ void App::_interruptHandler(void) {
#endif
_ctx.overlays[1] = &_screenshotOverlay;
#ifdef ENABLE_AUTOBOOT
_runWorker(&App::_ideInitWorker, _warningScreen);
#else
// If autoboot is disabled, show the warning screen before initializing the
// drives in order to give them enough time to spin up.
_runWorker(nullptr, _warningScreen);
#endif
_setupInterrupts();
_ctx.sounds[ui::SOUND_STARTUP].play();

View File

@ -65,7 +65,8 @@ public:
file::VFSProvider vfs;
inline ~FileIOManager(void) {
close();
closeResourceFile();
closeIDE();
}
FileIOManager(void);
@ -74,7 +75,6 @@ public:
void closeIDE(void);
bool loadResourceFile(const char *path);
void closeResourceFile(void);
void close(void);
};
/* App class */

View File

@ -47,7 +47,13 @@ void WarningScreen::update(ui::Context &ctx) {
_buttons[0] = STR("WarningScreen.ok");
if (ctx.buttons.pressed(ui::BTN_START))
#ifdef ENABLE_AUTOBOOT
ctx.show(APP->_buttonMappingScreen, false, true);
#else
APP->_runWorker(
&App::_ideInitWorker, APP->_buttonMappingScreen, false, true
);
#endif
}
void AutobootScreen::show(ui::Context &ctx, bool goBack) {

View File

@ -64,7 +64,7 @@ bool App::_ideInitWorker(void) {
bool App::_fileInitWorker(void) {
_workerStatus.update(0, 3, WSTR("App.fileInitWorker.unmount"));
_fileIO.closeResourceFile();
_fileIO.close();
_fileIO.closeIDE();
_workerStatus.update(1, 3, WSTR("App.fileInitWorker.mount"));
_fileIO.initIDE();
@ -99,7 +99,9 @@ bool App::_executableWorker(void) {
_workerStatus.update(0, 1, WSTR("App.executableWorker.init"));
const char *path = _fileBrowserScreen.selectedPath;
auto _file = _fileIO.vfs.openFile(path, file::READ);
int device = -(path[3] - '0' + 1); // ide#: -> -1 or -2
auto _file = _fileIO.vfs.openFile(path, file::READ);
util::ExecutableHeader header;
@ -168,25 +170,38 @@ bool App::_executableWorker(void) {
loader.formatArgument("entry.gp=%08x", header.getInitialGP());
loader.formatArgument("entry.sp=%08x", header.getStackPtr());
loader.formatArgument("load=%08x", header.getTextPtr());
loader.formatArgument("drive=%c", path[3]); // ide#:...
loader.formatArgument("device=%d", device);
file::FileFragmentTable fragments;
_fileIO.vfs.getFileFragments(fragments, path);
auto fragment = fragments.as<const file::FileFragment>();
auto count = fragments.getNumFragments();
for (size_t i = fragments.getNumFragments(); i; i--, fragment++)
loader.formatArgument(
for (size_t i = count; i; i--, fragment++) {
if (loader.formatArgument(
"frag=%llx,%llx", fragment->lba, fragment->length
))
continue;
fragments.destroy();
_messageScreen.setMessage(
MESSAGE_ERROR, WSTR("App.executableWorker.fragmentError"), path,
count, i
);
_workerStatus.setNextScreen(_messageScreen);
return false;
}
fragments.destroy();
// All destructors must be invoked manually as we are not returning to
// main() before starting the new executable.
_unloadCartData();
_fileIO.close();
_fileIO.closeResourceFile();
_fileIO.closeIDE();
uninstallExceptionHandler();
loader.run();
@ -233,7 +248,8 @@ bool App::_rebootWorker(void) {
_workerStatus.update(0, 1, WSTR("App.rebootWorker.reboot"));
_unloadCartData();
_fileIO.close();
_fileIO.closeResourceFile();
_fileIO.closeIDE();
_workerStatus.setStatus(WORKER_REBOOT);
// Fall back to a soft reboot if the watchdog fails to reset the system.

View File

@ -38,15 +38,19 @@ void StorageInfoScreen::show(ui::Context &ctx, bool goBack) {
_PRINT(STR("StorageInfoScreen.bios.kernelInfo.unknown"));
}
auto shell = rom::getShellInfo();
rom::ShellInfo shell;
if (shell)
_PRINT(
STR("StorageInfoScreen.bios.shellInfo.konami"), shell->name,
shell->bootFileName
);
else
if (rom::getShellInfo(shell)) {
if (shell.bootFileName)
_PRINT(
STR("StorageInfoScreen.bios.shellInfo.konami"), shell.name,
shell.bootFileName
);
else
_PRINT(STR("StorageInfoScreen.bios.shellInfo.custom"), shell.name);
} else {
_PRINT(STR("StorageInfoScreen.bios.shellInfo.unknown"));
}
_PRINTLN();