From 11d07e1e5147b6fac2d74b1b537c3d0d9eb714a1 Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Thu, 6 Jun 2024 00:48:03 +0200 Subject: [PATCH] Fix crashes, add custom shell detection, update schemas --- assets/app.strings.json | 20 ++++++------ schema/cart.json | 10 +++++- schema/games.json | 15 ++++++--- src/boot/main.cpp | 6 ++-- src/common/args.cpp | 2 +- src/common/file/misc.cpp | 3 +- src/common/ide.cpp | 21 ++++++++---- src/common/idedefs.hpp | 62 ++++++++++++++++++------------------ src/common/rom.cpp | 59 ++++++++++++++++++++++++++-------- src/common/rom.hpp | 15 +++++---- src/common/util.cpp | 38 ++++++++++++++++++---- src/common/util.hpp | 19 +++++------ src/launcher/main.cpp | 6 ++-- src/main/app/app.cpp | 41 ++++++++++++------------ src/main/app/app.hpp | 4 +-- src/main/app/main.cpp | 6 ++++ src/main/app/miscworkers.cpp | 30 +++++++++++++---- src/main/app/romactions.cpp | 18 +++++++---- 18 files changed, 244 insertions(+), 131 deletions(-) diff --git a/assets/app.strings.json b/assets/app.strings.json index 5138723..25e3965 100644 --- a/assets/app.strings.json +++ b/assets/app.strings.json @@ -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": { diff --git a/schema/cart.json b/schema/cart.json index 869c272..950df01 100644 --- a/schema/cart.json +++ b/schema/cart.json @@ -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.", diff --git a/schema/games.json b/schema/games.json index a5bfdc7..d86e26d 100644 --- a/schema/games.json +++ b/schema/games.json @@ -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": { diff --git a/src/boot/main.cpp b/src/boot/main.cpp index 007931b..6a15b71 100644 --- a/src/boot/main.cpp +++ b/src/boot/main.cpp @@ -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(); diff --git a/src/common/args.cpp b/src/common/args.cpp index 3bb7fbf..22edd4b 100644 --- a/src/common/args.cpp +++ b/src/common/args.cpp @@ -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: diff --git a/src/common/file/misc.cpp b/src/common/file/misc.cpp index 434dfbf..a5f85c0 100644 --- a/src/common/file/misc.cpp +++ b/src/common/file/misc.cpp @@ -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); diff --git a/src/common/ide.cpp b/src/common/ide.cpp index 774f261..b91103c 100644 --- a/src/common/ide.cpp +++ b/src/common/ide.cpp @@ -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 */ diff --git a/src/common/idedefs.hpp b/src/common/idedefs.hpp index d6ff0a8..bb207e2 100644 --- a/src/common/idedefs.hpp +++ b/src/common/idedefs.hpp @@ -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 { diff --git a/src/common/rom.cpp b/src/common/rom.cpp index ec56c31..2cead95 100644 --- a/src/common/rom.cpp +++ b/src/common/rom.cpp @@ -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(DEV2_BASE | 0x40890), - .headerPtr = reinterpret_cast(DEV2_BASE | 0x40000), - .headerHash = 0x9c615f57 + .headerHash = 0x9c615f57, + .header = reinterpret_cast( + DEV2_BASE | 0x40000 + ) }, { .name = "700A01 (Gachagachamp)", .bootFileName = reinterpret_cast(DEV2_BASE | 0x40890), - .headerPtr = reinterpret_cast(DEV2_BASE | 0x40000), - .headerHash = 0x7e31a844 + .headerHash = 0x7e31a844, + .header = reinterpret_cast( + DEV2_BASE | 0x40000 + ) }, { .name = "700B01", .bootFileName = reinterpret_cast(DEV2_BASE | 0x61334), - .headerPtr = reinterpret_cast(DEV2_BASE | 0x28000), - .headerHash = 0xb257d3b5 + .headerHash = 0xb257d3b5, + .header = reinterpret_cast( + 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(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(ptr); + + if (!header->validateMagic()) + continue; + + output.name = header->getRegionString(); + output.bootFileName = nullptr; +#if 0 + output.headerHash = util::hash( + reinterpret_cast(header), + sizeof(util::ExecutableHeader) + ); +#endif + output.header = header; + return true; + } + + return false; } } diff --git a/src/common/rom.hpp b/src/common/rom.hpp index 831d74f..d7cc59c 100644 --- a/src/common/rom.hpp +++ b/src/common/rom.hpp @@ -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(DEV2_BASE | 0x78); -const ShellInfo *getShellInfo(void); +bool getShellInfo(ShellInfo &output); } diff --git a/src/common/util.cpp b/src/common/util.cpp index ff83670..4944696 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -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(_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( diff --git a/src/common/util.hpp b/src/common/util.hpp index 6076b66..c0f486e 100644 --- a/src/common/util.hpp +++ b/src/common/util.hpp @@ -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(stackOffset + stackLength); } + inline const char *getRegionString(void) const { + return reinterpret_cast(&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); }; diff --git a/src/launcher/main.cpp b/src/launcher/main.cpp index 2b0416a..aa56bee 100644 --- a/src/launcher/main.cpp +++ b/src/launcher/main.cpp @@ -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; diff --git a/src/main/app/app.cpp b/src/main/app/app.cpp index aa7a107..9373c23 100644 --- a/src/main/app/app.cpp +++ b/src/main/app/app.cpp @@ -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(); diff --git a/src/main/app/app.hpp b/src/main/app/app.hpp index ee8df74..4c07c53 100644 --- a/src/main/app/app.hpp +++ b/src/main/app/app.hpp @@ -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 */ diff --git a/src/main/app/main.cpp b/src/main/app/main.cpp index bbb8fb8..2470601 100644 --- a/src/main/app/main.cpp +++ b/src/main/app/main.cpp @@ -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) { diff --git a/src/main/app/miscworkers.cpp b/src/main/app/miscworkers.cpp index 10d2bf7..e889fb1 100644 --- a/src/main/app/miscworkers.cpp +++ b/src/main/app/miscworkers.cpp @@ -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(); + 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. diff --git a/src/main/app/romactions.cpp b/src/main/app/romactions.cpp index 915f503..b27838f 100644 --- a/src/main/app/romactions.cpp +++ b/src/main/app/romactions.cpp @@ -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();