Add PCDRV support, fix bugs and warnings

This commit is contained in:
spicyjpeg 2024-05-27 16:21:17 +02:00
parent 77a009b98a
commit 3cf7ae7aed
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
15 changed files with 385 additions and 83 deletions

View File

@ -1,6 +1,6 @@
{ {
"AboutScreen": { "AboutScreen": {
"title": "{CART_ICON} About this tool", "title": "{RIGHT_ARROW} About this tool",
"prompt": "{RIGHT_ARROW} Use {LEFT_BUTTON}{RIGHT_BUTTON} to scroll. Press {START_BUTTON} to go back." "prompt": "{RIGHT_ARROW} Use {LEFT_BUTTON}{RIGHT_BUTTON} to scroll. Press {START_BUTTON} to go back."
}, },
@ -111,7 +111,7 @@
}, },
"ButtonMappingScreen": { "ButtonMappingScreen": {
"title": "{CART_ICON} Select button mapping", "title": "{RIGHT_ARROW} Select button mapping",
"prompt": "Use {START_BUTTON} or the Test button to select a mapping preset suitable for your cabinet or JAMMA setup. Other buttons will be enabled once a mapping is selected.", "prompt": "Use {START_BUTTON} or the Test button to select a mapping preset suitable for your cabinet or JAMMA setup. Other buttons will be enabled once a mapping is selected.",
"itemPrompt": "{RIGHT_ARROW} Press and hold {START_BUTTON} or Test to confirm", "itemPrompt": "{RIGHT_ARROW} Press and hold {START_BUTTON} or Test to confirm",
@ -132,7 +132,7 @@
}, },
"CartActionsScreen": { "CartActionsScreen": {
"title": "{CART_ICON} Cartridge options", "title": "{RIGHT_ARROW} Cartridge options",
"itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back", "itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back",
"qrDump": { "qrDump": {
@ -184,7 +184,7 @@
}, },
"CartInfoScreen": { "CartInfoScreen": {
"title": "{CART_ICON} Cartridge information", "title": "{RIGHT_ARROW} Cartridge information",
"digitalIO": { "digitalIO": {
"header": "Digital I/O board:\n", "header": "Digital I/O board:\n",
@ -244,7 +244,7 @@
}, },
"ChecksumScreen": { "ChecksumScreen": {
"title": "{CART_ICON} Storage device checksums", "title": "{RIGHT_ARROW} Storage device checksums",
"prompt": "{RIGHT_ARROW} Press {START_BUTTON} to go back.", "prompt": "{RIGHT_ARROW} Press {START_BUTTON} to go back.",
"bios": "BIOS ROM (512 KB):\t\t\t\t%08X\n", "bios": "BIOS ROM (512 KB):\t\t\t\t%08X\n",
@ -261,7 +261,7 @@
}, },
"FileBrowserScreen": { "FileBrowserScreen": {
"title": "{CART_ICON} Select file", "title": "{RIGHT_ARROW} Select file",
"itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back", "itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back",
"parentDir": "[Parent directory]", "parentDir": "[Parent directory]",
@ -269,9 +269,11 @@
}, },
"FilePickerScreen": { "FilePickerScreen": {
"title": "{CART_ICON} Select IDE drive", "title": "{RIGHT_ARROW} Select IDE drive",
"itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back", "itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back",
"host": "{HOST_ICON} Host filesystem (PCDRV)",
"noDeviceError": "No drives have been found and successfully initialized on the IDE bus. Make sure the drives are appropriately configured as primary or secondary and are receiving power.\n\nPress the Test button to view debug logs.", "noDeviceError": "No drives have been found and successfully initialized on the IDE bus. Make sure the drives are appropriately configured as primary or secondary and are receiving power.\n\nPress the Test button to view debug logs.",
"atapiError": "Failed to initialize the CD-ROM drive or access the filesystem on it. Your drive might be incompatible with the ATAPI driver used by this tool.\n\nPress the Test button to view debug logs.", "atapiError": "Failed to initialize the CD-ROM drive or access the filesystem on it. Your drive might be incompatible with the ATAPI driver used by this tool.\n\nPress the Test button to view debug logs.",
"ideError": "Failed to initialize the drive or access the filesystem on it. 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\nPress the Test button to view debug logs.", "ideError": "Failed to initialize the drive or access the filesystem on it. 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\nPress the Test button to view debug logs.",
@ -279,12 +281,12 @@
}, },
"HexdumpScreen": { "HexdumpScreen": {
"title": "{CART_ICON} Cartridge dump", "title": "{RIGHT_ARROW} Cartridge dump",
"prompt": "{RIGHT_ARROW} Use {LEFT_BUTTON}{RIGHT_BUTTON} to scroll. Press {START_BUTTON} to go back." "prompt": "{RIGHT_ARROW} Use {LEFT_BUTTON}{RIGHT_BUTTON} to scroll. Press {START_BUTTON} to go back."
}, },
"IDEInfoScreen": { "IDEInfoScreen": {
"title": "{CART_ICON} IDE device information", "title": "{RIGHT_ARROW} IDE device information",
"prompt": "{RIGHT_ARROW} Use {LEFT_BUTTON}{RIGHT_BUTTON} to scroll. Press {START_BUTTON} to go back.", "prompt": "{RIGHT_ARROW} Use {LEFT_BUTTON}{RIGHT_BUTTON} to scroll. Press {START_BUTTON} to go back.",
"device": { "device": {
@ -326,7 +328,7 @@
}, },
"MainMenuScreen": { "MainMenuScreen": {
"title": "{CART_ICON} Main menu", "title": "{RIGHT_ARROW} Main menu",
"itemPrompt": "{RIGHT_ARROW} Use {LEFT_BUTTON}{RIGHT_BUTTON} to move, select by pressing {START_BUTTON}", "itemPrompt": "{RIGHT_ARROW} Use {LEFT_BUTTON}{RIGHT_BUTTON} to move, select by pressing {START_BUTTON}",
"cartInfo": { "cartInfo": {
@ -379,18 +381,18 @@
}, },
"QRCodeScreen": { "QRCodeScreen": {
"title": "{CART_ICON} Cartridge dump", "title": "{RIGHT_ARROW} Cartridge dump",
"prompt": "Scan this code and paste the resulting string into the decodeDump.py script provided alongside this tool to obtain a dump of the cartridge. Press {START_BUTTON} to go back." "prompt": "Scan this code and paste the resulting string into the decodeDump.py script provided alongside this tool to obtain a dump of the cartridge. Press {START_BUTTON} to go back."
}, },
"ReflashGameScreen": { "ReflashGameScreen": {
"title": "{CART_ICON} Select game to convert cartridge to", "title": "{RIGHT_ARROW} Select game to convert cartridge to",
"prompt": "Make sure you select the correct region. Note that cartridges can only be converted for use with games that accept the same cartridge type.", "prompt": "Make sure you select the correct region. Note that cartridges can only be converted for use with games that accept the same cartridge type.",
"itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back" "itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back"
}, },
"ResolutionScreen": { "ResolutionScreen": {
"title": "{CART_ICON} Select screen resolution", "title": "{RIGHT_ARROW} Select screen resolution",
"prompt": "Select a resolution appropriate for your monitor or upscaler setup. Note that interlaced modes may be subject to flickering.", "prompt": "Select a resolution appropriate for your monitor or upscaler setup. Note that interlaced modes may be subject to flickering.",
"itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back", "itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back",
@ -413,7 +415,7 @@
}, },
"StorageActionsScreen": { "StorageActionsScreen": {
"title": "{CART_ICON} Storage device options", "title": "{RIGHT_ARROW} Storage device options",
"itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back", "itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back",
"cardError": "The selected PCMCIA slot is empty.\n\nTurn off the system and insert a supported PCMCIA linear flash card in order to continue. DO NOT HOTPLUG CARDS; hotplugging may damage both the 573 and the card.", "cardError": "The selected PCMCIA slot is empty.\n\nTurn off the system and insert a supported PCMCIA linear flash card in order to continue. DO NOT HOTPLUG CARDS; hotplugging may damage both the 573 and the card.",
@ -486,7 +488,7 @@
}, },
"StorageInfoScreen": { "StorageInfoScreen": {
"title": "{CART_ICON} Storage device information", "title": "{RIGHT_ARROW} Storage device information",
"prompt": "{RIGHT_ARROW} Press {START_BUTTON} for more options, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back.", "prompt": "{RIGHT_ARROW} Press {START_BUTTON} for more options, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back.",
"bios": { "bios": {
@ -540,7 +542,7 @@
}, },
"UnlockKeyScreen": { "UnlockKeyScreen": {
"title": "{CART_ICON} Select unlocking key", "title": "{RIGHT_ARROW} Select unlocking key",
"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.", "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 select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back", "itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back",

View File

@ -105,7 +105,7 @@ bool ExecutableLauncherArgs::parseArgument(const char *arg) {
return true; return true;
case "arg"_h: case "arg"_h:
if (numArgs >= int(util::countOf(executableArgs))) if (numArgs >= util::countOf(executableArgs))
return false; return false;
executableArgs[numArgs++] = &arg[4]; executableArgs[numArgs++] = &arg[4];

View File

@ -11,7 +11,7 @@
#define VERSION_STRING VERSION "-debug" #define VERSION_STRING VERSION "-debug"
#endif #endif
#define EXTERNAL_DATA_DIR "hdd:cartdata" #define EXTERNAL_DATA_DIR "hdd:/cartdata"
enum Character : char { enum Character : char {
CH_UP_ARROW = '\x80', CH_UP_ARROW = '\x80',

View File

@ -8,7 +8,15 @@
namespace file { namespace file {
/* PCDRV file class */ /* PCDRV utilities */
static void _dirEntryToFileInfo(FileInfo &output, const PCDRVDirEntry &entry) {
__builtin_strncpy(output.name, entry.name, sizeof(output.name));
output.size = entry.size;
output.attributes = entry.attributes;
}
/* PCDRV file and directory classes */
size_t HostFile::read(void *output, size_t length) { size_t HostFile::read(void *output, size_t length) {
int actualLength = pcdrvRead(_fd, output, length); int actualLength = pcdrvRead(_fd, output, length);
@ -58,6 +66,18 @@ void HostFile::close(void) {
pcdrvClose(_fd); pcdrvClose(_fd);
} }
bool HostDirectory::getEntry(FileInfo &output) {
if (_fd < 0)
return false;
// Return the last entry fetched while also fetching the next one (if any).
_dirEntryToFileInfo(output, _entry);
if (pcdrvFindNext(_fd, &_entry) < 0)
_fd = -1;
return true;
}
/* PCDRV filesystem provider */ /* PCDRV filesystem provider */
bool HostProvider::init(void) { bool HostProvider::init(void) {
@ -72,15 +92,51 @@ bool HostProvider::init(void) {
return true; return true;
} }
bool HostProvider::createDirectory(const char *path) { bool HostProvider::getFileInfo(FileInfo &output, const char *path) {
int fd = pcdrvCreate(path, DIRECTORY); PCDRVDirEntry entry;
int fd = pcdrvFindFirst(path, &entry);
if (fd < 0) { if (fd < 0) {
LOG("PCDRV error, code=%d", fd); LOG("PCDRV error, code=%d", fd);
return false; return false;
} }
pcdrvClose(fd); _dirEntryToFileInfo(output, entry);
return true;
}
Directory *HostProvider::openDirectory(const char *path) {
char pattern[MAX_PATH_LENGTH];
char *ptr = pattern;
while (*path)
*(ptr++) = *(path++);
*(ptr++) = '/';
*(ptr++) = '*';
*(ptr++) = 0;
auto dir = new HostDirectory();
int fd = pcdrvFindFirst(pattern, &(dir->_entry));
if (fd < 0) {
LOG("PCDRV error, code=%d", fd);
delete dir;
return nullptr;
}
return dir;
}
bool HostProvider::createDirectory(const char *path) {
int error = pcdrvCreateDir(path);
if (error < 0) {
LOG("PCDRV error, code=%d", error);
return false;
}
return true; return true;
} }
@ -99,12 +155,11 @@ File *HostProvider::openFile(const char *path, uint32_t flags) {
return nullptr; return nullptr;
} }
auto file = new HostFile(); auto file = new HostFile();
file->_fd = fd; file->_fd = fd;
file->size = pcdrvSeek(fd, 0, PCDRV_SEEK_END); file->size = pcdrvSeek(fd, 0, PCDRV_SEEK_END);
pcdrvSeek(fd, 0, PCDRV_SEEK_SET);
pcdrvSeek(fd, 0, PCDRV_SEEK_SET);
return file; return file;
} }

View File

@ -5,6 +5,7 @@
#include <stdint.h> #include <stdint.h>
#include "common/file.hpp" #include "common/file.hpp"
#include "common/util.hpp" #include "common/util.hpp"
#include "ps1/pcdrv.h"
namespace file { namespace file {
@ -24,10 +25,23 @@ public:
void close(void); void close(void);
}; };
class HostDirectory : public Directory {
friend class HostProvider;
private:
int _fd;
PCDRVDirEntry _entry;
public:
bool getEntry(FileInfo &output);
};
class HostProvider : public Provider { class HostProvider : public Provider {
public: public:
bool init(void); bool init(void);
bool getFileInfo(FileInfo &output, const char *path);
Directory *openDirectory(const char *path);
bool createDirectory(const char *path); bool createDirectory(const char *path);
File *openFile(const char *path, uint32_t flags); File *openFile(const char *path, uint32_t flags);

View File

@ -43,6 +43,53 @@ static const char *const _MINIZ_ZIP_ERROR_NAMES[]{
"WRITE_CALLBACK_FAILED" "WRITE_CALLBACK_FAILED"
}; };
/* Utilities */
static bool _zipStatToFileInfo(
FileInfo &output, const mz_zip_archive_file_stat &stat
) {
// Ignore all unsupported files.
if (!stat.m_is_supported)
return false;
#if 0
auto ptr = __builtin_strrchr(stat.m_filename, '/');
if (ptr)
ptr++;
else
ptr = (char *) stat.m_filename;
#else
auto ptr = stat.m_filename;
#endif
__builtin_strncpy(output.name, ptr, sizeof(output.name));
output.size = stat.m_uncomp_size;
output.attributes = READ_ONLY | ARCHIVE;
if (stat.m_is_directory)
output.attributes |= DIRECTORY;
return true;
}
/* ZIP directory class */
bool ZIPDirectory::getEntry(FileInfo &output) {
mz_zip_archive_file_stat stat;
while (_index < _zip->m_total_files) {
if (!mz_zip_reader_file_stat(_zip, _index++, &stat))
continue;
if (!_zipStatToFileInfo(output, stat))
continue;
return true;
}
return false;
}
/* ZIP filesystem provider */ /* ZIP filesystem provider */
static constexpr uint32_t _ZIP_FLAGS = 0 static constexpr uint32_t _ZIP_FLAGS = 0
@ -113,35 +160,38 @@ void ZIPProvider::close(void) {
} }
bool ZIPProvider::getFileInfo(FileInfo &output, const char *path) { bool ZIPProvider::getFileInfo(FileInfo &output, const char *path) {
mz_zip_archive_file_stat info; // Any leading path separators must be stripped manually.
while ((*path == '/') || (*path == '\\'))
path++;
mz_zip_archive_file_stat stat;
int index = mz_zip_reader_locate_file(&_zip, path, nullptr, 0); int index = mz_zip_reader_locate_file(&_zip, path, nullptr, 0);
if (index < 0) if (index < 0)
return false; return false;
if (!mz_zip_reader_file_stat(&_zip, index, &info)) if (!mz_zip_reader_file_stat(&_zip, index, &stat))
return false;
if (!info.m_is_supported)
return false; return false;
auto ptr = __builtin_strrchr(info.m_filename, '/'); return _zipStatToFileInfo(output, stat);
}
if (ptr) Directory *ZIPProvider::openDirectory(const char *path) {
ptr++; while ((*path == '/') || (*path == '\\'))
else path++;
ptr = info.m_filename;
__builtin_strncpy(output.name, ptr, sizeof(output.name)); // ZIP subdirectories are not currently handled; all files are instead
output.size = info.m_uncomp_size; // returned as if they were part of the root directory.
output.attributes = READ_ONLY | ARCHIVE; if (*path)
return nullptr;
if (info.m_is_directory) return new ZIPDirectory(_zip);
output.attributes |= DIRECTORY;
return true;
} }
size_t ZIPProvider::loadData(util::Data &output, const char *path) { size_t ZIPProvider::loadData(util::Data &output, const char *path) {
while ((*path == '/') || (*path == '\\'))
path++;
output.destroy(); output.destroy();
output.ptr = mz_zip_reader_extract_file_to_heap( output.ptr = mz_zip_reader_extract_file_to_heap(
&_zip, path, &(output.length), 0 &_zip, path, &(output.length), 0
@ -158,6 +208,9 @@ size_t ZIPProvider::loadData(util::Data &output, const char *path) {
} }
size_t ZIPProvider::loadData(void *output, size_t length, const char *path) { size_t ZIPProvider::loadData(void *output, size_t length, const char *path) {
while ((*path == '/') || (*path == '\\'))
path++;
if (!mz_zip_reader_extract_file_to_mem(&_zip, path, output, length, 0)) { if (!mz_zip_reader_extract_file_to_mem(&_zip, path, output, length, 0)) {
auto error = mz_zip_get_last_error(&_zip); auto error = mz_zip_get_last_error(&_zip);

View File

@ -8,6 +8,20 @@
namespace file { namespace file {
/* ZIP directory class */
class ZIPDirectory : public Directory {
private:
mz_zip_archive *_zip;
size_t _index;
public:
inline ZIPDirectory(mz_zip_archive &zip)
: _zip(&zip), _index(0) {}
bool getEntry(FileInfo &output);
};
/* ZIP filesystem provider */ /* ZIP filesystem provider */
// This implementation only supports loading an entire file at once. // This implementation only supports loading an entire file at once.
@ -22,6 +36,7 @@ public:
void close(void); void close(void);
bool getFileInfo(FileInfo &output, const char *path); bool getFileInfo(FileInfo &output, const char *path);
Directory *openDirectory(const char *path);
size_t loadData(util::Data &output, const char *path); size_t loadData(util::Data &output, const char *path);
size_t loadData(void *output, size_t length, const char *path); size_t loadData(void *output, size_t length, const char *path);

View File

@ -71,7 +71,9 @@ FileIOManager::FileIOManager(void)
__builtin_memset(ide, 0, sizeof(ide)); __builtin_memset(ide, 0, sizeof(ide));
vfs.mount("resource:", &resource); vfs.mount("resource:", &resource);
#ifndef NDEBUG
vfs.mount("host:", &host); vfs.mount("host:", &host);
#endif
} }
void FileIOManager::_closeResourceFile(void) { void FileIOManager::_closeResourceFile(void) {
@ -86,7 +88,7 @@ void FileIOManager::_closeResourceFile(void) {
void FileIOManager::initIDE(void) { void FileIOManager::initIDE(void) {
char name[6]{ "ide#:" }; char name[6]{ "ide#:" };
for (int i = 0; i < util::countOf(ide::devices); i++) { for (size_t i = 0; i < util::countOf(ide::devices); i++) {
if (ide[i]) if (ide[i])
continue; continue;
@ -143,10 +145,12 @@ bool FileIOManager::loadResourceFile(const char *path) {
void FileIOManager::close(void) { void FileIOManager::close(void) {
vfs.close(); vfs.close();
resource.close(); resource.close();
#ifndef NDEBUG
host.close(); host.close();
#endif
_closeResourceFile(); _closeResourceFile();
for (int i = 0; i < util::countOf(ide::devices); i++) { for (size_t i = 0; i < util::countOf(ide::devices); i++) {
if (!ide[i]) if (!ide[i])
continue; continue;

View File

@ -62,7 +62,9 @@ private:
public: public:
file::Provider *ide[util::countOf(ide::devices)]; file::Provider *ide[util::countOf(ide::devices)];
file::ZIPProvider resource; file::ZIPProvider resource;
#ifndef NDEBUG
file::HostProvider host; file::HostProvider host;
#endif
file::VFSProvider vfs; file::VFSProvider vfs;
inline ~FileIOManager(void) { inline ~FileIOManager(void) {

View File

@ -216,13 +216,6 @@ void CartInfoScreen::update(ui::Context &ctx) {
} }
} }
enum SpecialEntryIndex {
ENTRY_AUTO_UNLOCK = -4,
ENTRY_CUSTOM_KEY = -3,
ENTRY_NULL_KEY1 = -2,
ENTRY_NULL_KEY2 = -1
};
struct SpecialEntry { struct SpecialEntry {
public: public:
util::Hash name; util::Hash name;
@ -231,9 +224,6 @@ public:
static const SpecialEntry _SPECIAL_ENTRIES[]{ static const SpecialEntry _SPECIAL_ENTRIES[]{
{ {
.name = 0,
.target = nullptr
}, {
.name = "UnlockKeyScreen.useFFKey"_h, .name = "UnlockKeyScreen.useFFKey"_h,
.target = &UnlockKeyScreen::useFFKey .target = &UnlockKeyScreen::useFFKey
}, { }, {
@ -248,19 +238,26 @@ static const SpecialEntry _SPECIAL_ENTRIES[]{
} }
}; };
int UnlockKeyScreen::_getSpecialEntryOffset(ui::Context &ctx) const { int UnlockKeyScreen::_getNumSpecialEntries(ui::Context &ctx) const {
return APP->_identified ? ENTRY_AUTO_UNLOCK : ENTRY_CUSTOM_KEY; int count = util::countOf(_SPECIAL_ENTRIES);
if (!(APP->_identified))
count--;
return count;
} }
const char *UnlockKeyScreen::_getItemName(ui::Context &ctx, int index) const { const char *UnlockKeyScreen::_getItemName(ui::Context &ctx, int index) const {
index += _getSpecialEntryOffset(ctx); int offset = _getNumSpecialEntries(ctx);
if (index < 0) if (index < offset) {
return STRH(_SPECIAL_ENTRIES[-index].name); offset -= index + 1;
return STRH(_SPECIAL_ENTRIES[offset].name);
}
static char name[96]; // TODO: get rid of this ugly crap static char name[96]; // TODO: get rid of this ugly crap
APP->_cartDB.get(index)->getDisplayName(name, sizeof(name)); APP->_cartDB.get(index - offset)->getDisplayName(name, sizeof(name));
return name; return name;
} }
@ -296,7 +293,7 @@ void UnlockKeyScreen::show(ui::Context &ctx, bool goBack) {
_prompt = STR("UnlockKeyScreen.prompt"); _prompt = STR("UnlockKeyScreen.prompt");
_itemPrompt = STR("UnlockKeyScreen.itemPrompt"); _itemPrompt = STR("UnlockKeyScreen.itemPrompt");
_listLength = APP->_cartDB.getNumEntries() - _getSpecialEntryOffset(ctx); _listLength = APP->_cartDB.getNumEntries() + _getNumSpecialEntries(ctx);
ListScreen::show(ctx, goBack); ListScreen::show(ctx, goBack);
} }
@ -308,8 +305,8 @@ void UnlockKeyScreen::update(ui::Context &ctx) {
if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT)) { if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT)) {
ctx.show(APP->_cartInfoScreen, true, true); ctx.show(APP->_cartInfoScreen, true, true);
} else { } else {
auto &dump = APP->_cartDump; auto &dump = APP->_cartDump;
int index = _activeItem + _getSpecialEntryOffset(ctx); int offset = _getNumSpecialEntries(ctx);
APP->_confirmScreen.setMessage( APP->_confirmScreen.setMessage(
APP->_unlockKeyScreen, APP->_unlockKeyScreen,
@ -320,12 +317,13 @@ void UnlockKeyScreen::update(ui::Context &ctx) {
STRH(_UNLOCK_WARNINGS[dump.chipType]) STRH(_UNLOCK_WARNINGS[dump.chipType])
); );
if (index < 0) { if (_activeItem < offset) {
(this->*_SPECIAL_ENTRIES[-index].target)(ctx); offset -= _activeItem + 1;
(this->*_SPECIAL_ENTRIES[offset].target)(ctx);
} else { } else {
dump.copyKeyFrom(APP->_cartDB.get(index)->dataKey); APP->_selectedEntry = APP->_cartDB.get(_activeItem - offset);
APP->_selectedEntry = APP->_cartDB.get(index); dump.copyKeyFrom(APP->_selectedEntry->dataKey);
ctx.show(APP->_confirmScreen, false, true); ctx.show(APP->_confirmScreen, false, true);
} }
} }

View File

@ -18,7 +18,7 @@ public:
class UnlockKeyScreen : public ui::ListScreen { class UnlockKeyScreen : public ui::ListScreen {
private: private:
int _getSpecialEntryOffset(ui::Context &ctx) const; int _getNumSpecialEntries(ui::Context &ctx) const;
protected: protected:
const char *_getItemName(ui::Context &ctx, int index) const; const char *_getItemName(ui::Context &ctx, int index) const;

View File

@ -112,7 +112,31 @@ void ConfirmScreen::update(ui::Context &ctx) {
/* File picker screen */ /* File picker screen */
#ifndef NDEBUG
struct SpecialEntry {
public:
util::Hash name;
const char *prefix;
};
static const SpecialEntry _SPECIAL_ENTRIES[]{
{
.name = "FilePickerScreen.host"_h,
.prefix = "host:"
}
};
#endif
const char *FilePickerScreen::_getItemName(ui::Context &ctx, int index) const { const char *FilePickerScreen::_getItemName(ui::Context &ctx, int index) const {
#ifndef NDEBUG
int offset = util::countOf(_SPECIAL_ENTRIES);
if (index < offset)
return STRH(_SPECIAL_ENTRIES[index].name);
else
index -= offset;
#endif
static char name[file::MAX_NAME_LENGTH]; // TODO: get rid of this ugly crap static char name[file::MAX_NAME_LENGTH]; // TODO: get rid of this ugly crap
int drive = _drives[index]; int drive = _drives[index];
@ -151,13 +175,19 @@ void FilePickerScreen::setMessage(
} }
int FilePickerScreen::loadRootAndShow(ui::Context &ctx) { int FilePickerScreen::loadRootAndShow(ui::Context &ctx) {
_listLength = 0; int numDrives = 0;
for (size_t i = 0; i < util::countOf(ide::devices); i++) { for (size_t i = 0; i < util::countOf(ide::devices); i++) {
if (ide::devices[i].flags & ide::DEVICE_READY) if (ide::devices[i].flags & ide::DEVICE_READY)
_drives[_listLength++] = i; _drives[numDrives++] = i;
} }
#ifdef NDEBUG
_listLength = numDrives;
#else
_listLength = numDrives + util::countOf(_SPECIAL_ENTRIES);
#endif
if (_listLength) { if (_listLength) {
ctx.show(APP->_filePickerScreen, false, true); ctx.show(APP->_filePickerScreen, false, true);
} else { } else {
@ -185,9 +215,24 @@ void FilePickerScreen::update(ui::Context &ctx) {
if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT)) { if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT)) {
ctx.show(*_prevScreen, true, true); ctx.show(*_prevScreen, true, true);
} else { } else {
int index = _activeItem;
#ifndef NDEBUG
int offset = util::countOf(_SPECIAL_ENTRIES);
if (index < offset) {
APP->_fileBrowserScreen.loadDirectory(
ctx, _SPECIAL_ENTRIES[index].prefix
);
ctx.show(APP->_fileBrowserScreen, false, true);
return;
} else {
index -= offset;
}
#endif
char name[6]{ "ide#:" }; char name[6]{ "ide#:" };
int drive = _drives[_activeItem]; int drive = _drives[index];
auto &dev = ide::devices[drive]; auto &dev = ide::devices[drive];
name[3] = drive + '0'; name[3] = drive + '0';
@ -242,6 +287,7 @@ void FileBrowserScreen::_setPathToChild(const char *entry) {
} }
void FileBrowserScreen::_unloadDirectory(void) { void FileBrowserScreen::_unloadDirectory(void) {
_listLength = 0;
_numFiles = 0; _numFiles = 0;
_numDirectories = 0; _numDirectories = 0;
@ -278,7 +324,9 @@ const char *FileBrowserScreen::_getItemName(ui::Context &ctx, int index) const {
return name; return name;
} }
int FileBrowserScreen::loadDirectory(ui::Context &ctx, const char *path) { int FileBrowserScreen::loadDirectory(
ui::Context &ctx, const char *path, bool updateCurrent
) {
_unloadDirectory(); _unloadDirectory();
// Count the number of files and subfolders in the current directory, so // Count the number of files and subfolders in the current directory, so
@ -309,7 +357,8 @@ int FileBrowserScreen::loadDirectory(ui::Context &ctx, const char *path) {
LOG("files=%d, dirs=%d", _numFiles, _numDirectories); LOG("files=%d, dirs=%d", _numFiles, _numDirectories);
file::FileInfo *files, *directories; file::FileInfo *files = nullptr;
file::FileInfo *directories = nullptr;
if (_numFiles) if (_numFiles)
files = _files.allocate<file::FileInfo>(_numFiles); files = _files.allocate<file::FileInfo>(_numFiles);
@ -332,7 +381,9 @@ int FileBrowserScreen::loadDirectory(ui::Context &ctx, const char *path) {
directory->close(); directory->close();
delete directory; delete directory;
__builtin_strncpy(_currentPath, path, sizeof(_currentPath)); if (updateCurrent)
__builtin_strncpy(_currentPath, path, sizeof(_currentPath));
return _numFiles + _numDirectories; return _numFiles + _numDirectories;
} }
@ -349,7 +400,6 @@ void FileBrowserScreen::update(ui::Context &ctx) {
if (ctx.buttons.pressed(ui::BTN_START)) { if (ctx.buttons.pressed(ui::BTN_START)) {
if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT)) { if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT)) {
_unloadDirectory();
ctx.show(APP->_filePickerScreen, true, true); ctx.show(APP->_filePickerScreen, true, true);
} else { } else {
int index = _activeItem; int index = _activeItem;
@ -366,11 +416,12 @@ void FileBrowserScreen::update(ui::Context &ctx) {
_setPathToChild(entries[index].name); _setPathToChild(entries[index].name);
if (loadDirectory(ctx, selectedPath) < 0) { if (loadDirectory(ctx, selectedPath) < 0) {
loadDirectory(ctx, _currentPath, false);
APP->_messageScreen.setMessage( APP->_messageScreen.setMessage(
MESSAGE_ERROR, *this, MESSAGE_ERROR, *this,
STR("FileBrowserScreen.subdirError"), selectedPath STR("FileBrowserScreen.subdirError"), selectedPath
); );
ctx.show(APP->_messageScreen, false, true); ctx.show(APP->_messageScreen, false, true);
} }
} else { } else {

View File

@ -97,7 +97,9 @@ protected:
public: public:
char selectedPath[file::MAX_PATH_LENGTH]; char selectedPath[file::MAX_PATH_LENGTH];
int loadDirectory(ui::Context &ctx, const char *path); int loadDirectory(
ui::Context &ctx, const char *path, bool updateCurrent = true
);
void show(ui::Context &ctx, bool goBack = false); void show(ui::Context &ctx, bool goBack = false);
void update(ui::Context &ctx); void update(ui::Context &ctx);

View File

@ -39,10 +39,17 @@ typedef enum {
PCDRV_ATTR_ARCHIVE = 1 << 5 PCDRV_ATTR_ARCHIVE = 1 << 5
} PCDRVAttribute; } PCDRVAttribute;
typedef struct {
uint32_t attributes, size;
char name[32];
} PCDRVDirEntry;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
/* Standard PCDRV API */
int pcdrvInit(void); int pcdrvInit(void);
int pcdrvCreate(const char *path, uint32_t attributes); int pcdrvCreate(const char *path, uint32_t attributes);
int pcdrvOpen(const char *path, PCDRVOpenMode mode); int pcdrvOpen(const char *path, PCDRVOpenMode mode);
@ -51,6 +58,16 @@ int pcdrvRead(int fd, void *data, size_t length);
int pcdrvWrite(int fd, const void *data, size_t length); int pcdrvWrite(int fd, const void *data, size_t length);
int pcdrvSeek(int fd, int offset, PCDRVSeekMode mode); int pcdrvSeek(int fd, int offset, PCDRVSeekMode mode);
/* Extended PCDRV API */
int pcdrvCreateDir(const char *path);
int pcdrvRemoveDir(const char *path);
int pcdrvUnlink(const char *path);
int pcdrvChmod(const char *path, uint32_t attributes);
int pcdrvFindFirst(const char *path, PCDRVDirEntry *entry);
int pcdrvFindNext(int fd, PCDRVDirEntry *entry);
int pcdrvRename(const char *path, const char *newPath);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -14,12 +14,14 @@
.set noreorder .set noreorder
## Standard PCDRV API
.section .text.pcdrvInit, "ax", @progbits .section .text.pcdrvInit, "ax", @progbits
.global pcdrvInit .global pcdrvInit
.type pcdrvInit, @function .type pcdrvInit, @function
pcdrvInit: pcdrvInit:
break 0, 0x101 # () -> error break 0, 0x101 # (_) -> error
jr $ra jr $ra
nop nop
@ -31,7 +33,7 @@ pcdrvInit:
pcdrvCreate: pcdrvCreate:
move $a2, $a1 move $a2, $a1
move $a1, $a0 move $a1, $a0
break 0, 0x102 # (path, path, 0) -> error, fd break 0, 0x102 # (_, path, attributes) -> error, fd
bgez $v0, .LcreateOK # if (error < 0) fd = error bgez $v0, .LcreateOK # if (error < 0) fd = error
nop nop
@ -47,7 +49,7 @@ pcdrvCreate:
pcdrvOpen: pcdrvOpen:
move $a2, $a1 move $a2, $a1
move $a1, $a0 move $a1, $a0
break 0, 0x103 # (path, path, mode) -> error, fd break 0, 0x103 # (_, path, mode) -> error, fd
bgez $v0, .LopenOK # if (error < 0) fd = error bgez $v0, .LopenOK # if (error < 0) fd = error
nop nop
@ -62,7 +64,7 @@ pcdrvOpen:
pcdrvClose: pcdrvClose:
move $a1, $a0 move $a1, $a0
break 0, 0x104 # (fd, fd) -> error break 0, 0x104 # (_, fd) -> error
jr $ra jr $ra
nop nop
@ -74,7 +76,7 @@ pcdrvClose:
pcdrvRead: pcdrvRead:
move $a3, $a1 move $a3, $a1
move $a1, $a0 move $a1, $a0
break 0, 0x105 # (fd, fd, length, data) -> error, length break 0, 0x105 # (_, fd, length, data) -> error, length
bgez $v0, .LreadOK # if (error < 0) length = error bgez $v0, .LreadOK # if (error < 0) length = error
nop nop
@ -90,7 +92,7 @@ pcdrvRead:
pcdrvWrite: pcdrvWrite:
move $a3, $a1 move $a3, $a1
move $a1, $a0 move $a1, $a0
break 0, 0x106 # (fd, fd, length, data) -> error, length break 0, 0x106 # (_, fd, length, data) -> error, length
bgez $v0, .LwriteOK # if (error < 0) length = error bgez $v0, .LwriteOK # if (error < 0) length = error
nop nop
@ -107,7 +109,7 @@ pcdrvSeek:
move $a3, $a2 move $a3, $a2
move $a2, $a1 move $a2, $a1
move $a1, $a0 move $a1, $a0
break 0, 0x107 # (fd, fd, offset, mode) -> error, offset break 0, 0x107 # (_, fd, offset, mode) -> error, offset
bgez $v0, .LseekOK # if (error < 0) offset = error bgez $v0, .LseekOK # if (error < 0) offset = error
nop nop
@ -115,3 +117,90 @@ pcdrvSeek:
.LseekOK: .LseekOK:
jr $ra # return offset jr $ra # return offset
move $v0, $v1 move $v0, $v1
## Extended PCDRV API
.section .text.pcdrvCreateDir, "ax", @progbits
.global pcdrvCreateDir
.type pcdrvCreateDir, @function
pcdrvCreateDir:
move $a1, $a0
break 0, 0x108 # (_, path) -> error
jr $ra
nop
.section .text.pcdrvRemoveDir, "ax", @progbits
.global pcdrvRemoveDir
.type pcdrvRemoveDir, @function
pcdrvRemoveDir:
move $a1, $a0
break 0, 0x109 # (_, path) -> error
jr $ra
nop
.section .text.pcdrvUnlink, "ax", @progbits
.global pcdrvUnlink
.type pcdrvUnlink, @function
pcdrvUnlink:
move $a1, $a0
break 0, 0x10a # (_, path) -> error
jr $ra
nop
.section .text.pcdrvChmod, "ax", @progbits
.global pcdrvChmod
.type pcdrvChmod, @function
pcdrvChmod:
move $a2, $a1
move $a1, $a0
break 0, 0x10b # (_, path, attributes) -> error
jr $ra
nop
.section .text.pcdrvFindFirst, "ax", @progbits
.global pcdrvFindFirst
.type pcdrvFindFirst, @function
pcdrvFindFirst:
move $a2, $a1
move $a1, $a0
break 0, 0x10c # (_, path, entry) -> error, fd
bgez $v0, .LfindFirstOK # if (error < 0) fd = error
nop
move $v1, $v0
.LfindFirstOK:
jr $ra # return fd
move $v0, $v1
.section .text.pcdrvFindNext, "ax", @progbits
.global pcdrvFindNext
.type pcdrvFindNext, @function
pcdrvFindNext:
move $a2, $a1
move $a1, $a0
break 0, 0x10d # (_, fd, entry) -> error
jr $ra
nop
.section .text.pcdrvRename, "ax", @progbits
.global pcdrvRename
.type pcdrvRename, @function
pcdrvRename:
move $a2, $a1
move $a1, $a0
break 0, 0x10e # (_, path, newPath) -> error
jr $ra
nop