mirror of
https://github.com/spicyjpeg/573in1.git
synced 2025-01-22 19:52:05 +01:00
Add PCDRV support, fix bugs and warnings
This commit is contained in:
parent
77a009b98a
commit
3cf7ae7aed
@ -1,6 +1,6 @@
|
||||
{
|
||||
"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."
|
||||
},
|
||||
|
||||
@ -111,7 +111,7 @@
|
||||
},
|
||||
|
||||
"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.",
|
||||
"itemPrompt": "{RIGHT_ARROW} Press and hold {START_BUTTON} or Test to confirm",
|
||||
|
||||
@ -132,7 +132,7 @@
|
||||
},
|
||||
|
||||
"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",
|
||||
|
||||
"qrDump": {
|
||||
@ -184,7 +184,7 @@
|
||||
},
|
||||
|
||||
"CartInfoScreen": {
|
||||
"title": "{CART_ICON} Cartridge information",
|
||||
"title": "{RIGHT_ARROW} Cartridge information",
|
||||
|
||||
"digitalIO": {
|
||||
"header": "Digital I/O board:\n",
|
||||
@ -244,7 +244,7 @@
|
||||
},
|
||||
|
||||
"ChecksumScreen": {
|
||||
"title": "{CART_ICON} Storage device checksums",
|
||||
"title": "{RIGHT_ARROW} Storage device checksums",
|
||||
"prompt": "{RIGHT_ARROW} Press {START_BUTTON} to go back.",
|
||||
|
||||
"bios": "BIOS ROM (512 KB):\t\t\t\t%08X\n",
|
||||
@ -261,7 +261,7 @@
|
||||
},
|
||||
|
||||
"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",
|
||||
|
||||
"parentDir": "[Parent directory]",
|
||||
@ -269,9 +269,11 @@
|
||||
},
|
||||
|
||||
"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",
|
||||
|
||||
"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.",
|
||||
"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.",
|
||||
@ -279,12 +281,12 @@
|
||||
},
|
||||
|
||||
"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."
|
||||
},
|
||||
|
||||
"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.",
|
||||
|
||||
"device": {
|
||||
@ -326,7 +328,7 @@
|
||||
},
|
||||
|
||||
"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}",
|
||||
|
||||
"cartInfo": {
|
||||
@ -379,18 +381,18 @@
|
||||
},
|
||||
|
||||
"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."
|
||||
},
|
||||
|
||||
"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.",
|
||||
"itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back"
|
||||
},
|
||||
|
||||
"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.",
|
||||
"itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back",
|
||||
|
||||
@ -413,7 +415,7 @@
|
||||
},
|
||||
|
||||
"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",
|
||||
"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": {
|
||||
"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.",
|
||||
|
||||
"bios": {
|
||||
@ -540,7 +542,7 @@
|
||||
},
|
||||
|
||||
"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.",
|
||||
"itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back",
|
||||
|
||||
|
@ -105,7 +105,7 @@ bool ExecutableLauncherArgs::parseArgument(const char *arg) {
|
||||
return true;
|
||||
|
||||
case "arg"_h:
|
||||
if (numArgs >= int(util::countOf(executableArgs)))
|
||||
if (numArgs >= util::countOf(executableArgs))
|
||||
return false;
|
||||
|
||||
executableArgs[numArgs++] = &arg[4];
|
||||
|
@ -11,7 +11,7 @@
|
||||
#define VERSION_STRING VERSION "-debug"
|
||||
#endif
|
||||
|
||||
#define EXTERNAL_DATA_DIR "hdd:cartdata"
|
||||
#define EXTERNAL_DATA_DIR "hdd:/cartdata"
|
||||
|
||||
enum Character : char {
|
||||
CH_UP_ARROW = '\x80',
|
||||
|
@ -8,7 +8,15 @@
|
||||
|
||||
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) {
|
||||
int actualLength = pcdrvRead(_fd, output, length);
|
||||
@ -58,6 +66,18 @@ void HostFile::close(void) {
|
||||
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 */
|
||||
|
||||
bool HostProvider::init(void) {
|
||||
@ -72,15 +92,51 @@ bool HostProvider::init(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HostProvider::createDirectory(const char *path) {
|
||||
int fd = pcdrvCreate(path, DIRECTORY);
|
||||
bool HostProvider::getFileInfo(FileInfo &output, const char *path) {
|
||||
PCDRVDirEntry entry;
|
||||
|
||||
int fd = pcdrvFindFirst(path, &entry);
|
||||
|
||||
if (fd < 0) {
|
||||
LOG("PCDRV error, code=%d", fd);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -100,11 +156,10 @@ File *HostProvider::openFile(const char *path, uint32_t flags) {
|
||||
}
|
||||
|
||||
auto file = new HostFile();
|
||||
|
||||
file->_fd = fd;
|
||||
file->size = pcdrvSeek(fd, 0, PCDRV_SEEK_END);
|
||||
pcdrvSeek(fd, 0, PCDRV_SEEK_SET);
|
||||
|
||||
pcdrvSeek(fd, 0, PCDRV_SEEK_SET);
|
||||
return file;
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <stdint.h>
|
||||
#include "common/file.hpp"
|
||||
#include "common/util.hpp"
|
||||
#include "ps1/pcdrv.h"
|
||||
|
||||
namespace file {
|
||||
|
||||
@ -24,10 +25,23 @@ public:
|
||||
void close(void);
|
||||
};
|
||||
|
||||
class HostDirectory : public Directory {
|
||||
friend class HostProvider;
|
||||
|
||||
private:
|
||||
int _fd;
|
||||
PCDRVDirEntry _entry;
|
||||
|
||||
public:
|
||||
bool getEntry(FileInfo &output);
|
||||
};
|
||||
|
||||
class HostProvider : public Provider {
|
||||
public:
|
||||
bool init(void);
|
||||
|
||||
bool getFileInfo(FileInfo &output, const char *path);
|
||||
Directory *openDirectory(const char *path);
|
||||
bool createDirectory(const char *path);
|
||||
|
||||
File *openFile(const char *path, uint32_t flags);
|
||||
|
@ -43,6 +43,53 @@ static const char *const _MINIZ_ZIP_ERROR_NAMES[]{
|
||||
"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 */
|
||||
|
||||
static constexpr uint32_t _ZIP_FLAGS = 0
|
||||
@ -113,35 +160,38 @@ void ZIPProvider::close(void) {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (index < 0)
|
||||
return false;
|
||||
if (!mz_zip_reader_file_stat(&_zip, index, &info))
|
||||
return false;
|
||||
if (!info.m_is_supported)
|
||||
if (!mz_zip_reader_file_stat(&_zip, index, &stat))
|
||||
return false;
|
||||
|
||||
auto ptr = __builtin_strrchr(info.m_filename, '/');
|
||||
return _zipStatToFileInfo(output, stat);
|
||||
}
|
||||
|
||||
if (ptr)
|
||||
ptr++;
|
||||
else
|
||||
ptr = info.m_filename;
|
||||
Directory *ZIPProvider::openDirectory(const char *path) {
|
||||
while ((*path == '/') || (*path == '\\'))
|
||||
path++;
|
||||
|
||||
__builtin_strncpy(output.name, ptr, sizeof(output.name));
|
||||
output.size = info.m_uncomp_size;
|
||||
output.attributes = READ_ONLY | ARCHIVE;
|
||||
// ZIP subdirectories are not currently handled; all files are instead
|
||||
// returned as if they were part of the root directory.
|
||||
if (*path)
|
||||
return nullptr;
|
||||
|
||||
if (info.m_is_directory)
|
||||
output.attributes |= DIRECTORY;
|
||||
|
||||
return true;
|
||||
return new ZIPDirectory(_zip);
|
||||
}
|
||||
|
||||
size_t ZIPProvider::loadData(util::Data &output, const char *path) {
|
||||
while ((*path == '/') || (*path == '\\'))
|
||||
path++;
|
||||
|
||||
output.destroy();
|
||||
output.ptr = mz_zip_reader_extract_file_to_heap(
|
||||
&_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) {
|
||||
while ((*path == '/') || (*path == '\\'))
|
||||
path++;
|
||||
|
||||
if (!mz_zip_reader_extract_file_to_mem(&_zip, path, output, length, 0)) {
|
||||
auto error = mz_zip_get_last_error(&_zip);
|
||||
|
||||
|
@ -8,6 +8,20 @@
|
||||
|
||||
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 */
|
||||
|
||||
// This implementation only supports loading an entire file at once.
|
||||
@ -22,6 +36,7 @@ public:
|
||||
void close(void);
|
||||
|
||||
bool getFileInfo(FileInfo &output, const char *path);
|
||||
Directory *openDirectory(const char *path);
|
||||
|
||||
size_t loadData(util::Data &output, const char *path);
|
||||
size_t loadData(void *output, size_t length, const char *path);
|
||||
|
@ -71,7 +71,9 @@ FileIOManager::FileIOManager(void)
|
||||
__builtin_memset(ide, 0, sizeof(ide));
|
||||
|
||||
vfs.mount("resource:", &resource);
|
||||
#ifndef NDEBUG
|
||||
vfs.mount("host:", &host);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FileIOManager::_closeResourceFile(void) {
|
||||
@ -86,7 +88,7 @@ void FileIOManager::_closeResourceFile(void) {
|
||||
void FileIOManager::initIDE(void) {
|
||||
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])
|
||||
continue;
|
||||
|
||||
@ -143,10 +145,12 @@ bool FileIOManager::loadResourceFile(const char *path) {
|
||||
void FileIOManager::close(void) {
|
||||
vfs.close();
|
||||
resource.close();
|
||||
#ifndef NDEBUG
|
||||
host.close();
|
||||
#endif
|
||||
_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])
|
||||
continue;
|
||||
|
||||
|
@ -62,7 +62,9 @@ private:
|
||||
public:
|
||||
file::Provider *ide[util::countOf(ide::devices)];
|
||||
file::ZIPProvider resource;
|
||||
#ifndef NDEBUG
|
||||
file::HostProvider host;
|
||||
#endif
|
||||
file::VFSProvider vfs;
|
||||
|
||||
inline ~FileIOManager(void) {
|
||||
|
@ -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 {
|
||||
public:
|
||||
util::Hash name;
|
||||
@ -231,9 +224,6 @@ public:
|
||||
|
||||
static const SpecialEntry _SPECIAL_ENTRIES[]{
|
||||
{
|
||||
.name = 0,
|
||||
.target = nullptr
|
||||
}, {
|
||||
.name = "UnlockKeyScreen.useFFKey"_h,
|
||||
.target = &UnlockKeyScreen::useFFKey
|
||||
}, {
|
||||
@ -248,19 +238,26 @@ static const SpecialEntry _SPECIAL_ENTRIES[]{
|
||||
}
|
||||
};
|
||||
|
||||
int UnlockKeyScreen::_getSpecialEntryOffset(ui::Context &ctx) const {
|
||||
return APP->_identified ? ENTRY_AUTO_UNLOCK : ENTRY_CUSTOM_KEY;
|
||||
int UnlockKeyScreen::_getNumSpecialEntries(ui::Context &ctx) const {
|
||||
int count = util::countOf(_SPECIAL_ENTRIES);
|
||||
|
||||
if (!(APP->_identified))
|
||||
count--;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
const char *UnlockKeyScreen::_getItemName(ui::Context &ctx, int index) const {
|
||||
index += _getSpecialEntryOffset(ctx);
|
||||
int offset = _getNumSpecialEntries(ctx);
|
||||
|
||||
if (index < 0)
|
||||
return STRH(_SPECIAL_ENTRIES[-index].name);
|
||||
if (index < offset) {
|
||||
offset -= index + 1;
|
||||
return STRH(_SPECIAL_ENTRIES[offset].name);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -296,7 +293,7 @@ void UnlockKeyScreen::show(ui::Context &ctx, bool goBack) {
|
||||
_prompt = STR("UnlockKeyScreen.prompt");
|
||||
_itemPrompt = STR("UnlockKeyScreen.itemPrompt");
|
||||
|
||||
_listLength = APP->_cartDB.getNumEntries() - _getSpecialEntryOffset(ctx);
|
||||
_listLength = APP->_cartDB.getNumEntries() + _getNumSpecialEntries(ctx);
|
||||
|
||||
ListScreen::show(ctx, goBack);
|
||||
}
|
||||
@ -309,7 +306,7 @@ void UnlockKeyScreen::update(ui::Context &ctx) {
|
||||
ctx.show(APP->_cartInfoScreen, true, true);
|
||||
} else {
|
||||
auto &dump = APP->_cartDump;
|
||||
int index = _activeItem + _getSpecialEntryOffset(ctx);
|
||||
int offset = _getNumSpecialEntries(ctx);
|
||||
|
||||
APP->_confirmScreen.setMessage(
|
||||
APP->_unlockKeyScreen,
|
||||
@ -320,12 +317,13 @@ void UnlockKeyScreen::update(ui::Context &ctx) {
|
||||
STRH(_UNLOCK_WARNINGS[dump.chipType])
|
||||
);
|
||||
|
||||
if (index < 0) {
|
||||
(this->*_SPECIAL_ENTRIES[-index].target)(ctx);
|
||||
if (_activeItem < offset) {
|
||||
offset -= _activeItem + 1;
|
||||
(this->*_SPECIAL_ENTRIES[offset].target)(ctx);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ public:
|
||||
|
||||
class UnlockKeyScreen : public ui::ListScreen {
|
||||
private:
|
||||
int _getSpecialEntryOffset(ui::Context &ctx) const;
|
||||
int _getNumSpecialEntries(ui::Context &ctx) const;
|
||||
|
||||
protected:
|
||||
const char *_getItemName(ui::Context &ctx, int index) const;
|
||||
|
@ -112,7 +112,31 @@ void ConfirmScreen::update(ui::Context &ctx) {
|
||||
|
||||
/* 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 {
|
||||
#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
|
||||
|
||||
int drive = _drives[index];
|
||||
@ -151,13 +175,19 @@ void FilePickerScreen::setMessage(
|
||||
}
|
||||
|
||||
int FilePickerScreen::loadRootAndShow(ui::Context &ctx) {
|
||||
_listLength = 0;
|
||||
int numDrives = 0;
|
||||
|
||||
for (size_t i = 0; i < util::countOf(ide::devices); i++) {
|
||||
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) {
|
||||
ctx.show(APP->_filePickerScreen, false, true);
|
||||
} else {
|
||||
@ -185,9 +215,24 @@ void FilePickerScreen::update(ui::Context &ctx) {
|
||||
if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT)) {
|
||||
ctx.show(*_prevScreen, true, true);
|
||||
} 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#:" };
|
||||
|
||||
int drive = _drives[_activeItem];
|
||||
int drive = _drives[index];
|
||||
auto &dev = ide::devices[drive];
|
||||
|
||||
name[3] = drive + '0';
|
||||
@ -242,6 +287,7 @@ void FileBrowserScreen::_setPathToChild(const char *entry) {
|
||||
}
|
||||
|
||||
void FileBrowserScreen::_unloadDirectory(void) {
|
||||
_listLength = 0;
|
||||
_numFiles = 0;
|
||||
_numDirectories = 0;
|
||||
|
||||
@ -278,7 +324,9 @@ const char *FileBrowserScreen::_getItemName(ui::Context &ctx, int index) const {
|
||||
return name;
|
||||
}
|
||||
|
||||
int FileBrowserScreen::loadDirectory(ui::Context &ctx, const char *path) {
|
||||
int FileBrowserScreen::loadDirectory(
|
||||
ui::Context &ctx, const char *path, bool updateCurrent
|
||||
) {
|
||||
_unloadDirectory();
|
||||
|
||||
// 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);
|
||||
|
||||
file::FileInfo *files, *directories;
|
||||
file::FileInfo *files = nullptr;
|
||||
file::FileInfo *directories = nullptr;
|
||||
|
||||
if (_numFiles)
|
||||
files = _files.allocate<file::FileInfo>(_numFiles);
|
||||
@ -332,7 +381,9 @@ int FileBrowserScreen::loadDirectory(ui::Context &ctx, const char *path) {
|
||||
directory->close();
|
||||
delete directory;
|
||||
|
||||
if (updateCurrent)
|
||||
__builtin_strncpy(_currentPath, path, sizeof(_currentPath));
|
||||
|
||||
return _numFiles + _numDirectories;
|
||||
}
|
||||
|
||||
@ -349,7 +400,6 @@ void FileBrowserScreen::update(ui::Context &ctx) {
|
||||
|
||||
if (ctx.buttons.pressed(ui::BTN_START)) {
|
||||
if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT)) {
|
||||
_unloadDirectory();
|
||||
ctx.show(APP->_filePickerScreen, true, true);
|
||||
} else {
|
||||
int index = _activeItem;
|
||||
@ -366,11 +416,12 @@ void FileBrowserScreen::update(ui::Context &ctx) {
|
||||
_setPathToChild(entries[index].name);
|
||||
|
||||
if (loadDirectory(ctx, selectedPath) < 0) {
|
||||
loadDirectory(ctx, _currentPath, false);
|
||||
|
||||
APP->_messageScreen.setMessage(
|
||||
MESSAGE_ERROR, *this,
|
||||
STR("FileBrowserScreen.subdirError"), selectedPath
|
||||
);
|
||||
|
||||
ctx.show(APP->_messageScreen, false, true);
|
||||
}
|
||||
} else {
|
||||
|
@ -97,7 +97,9 @@ protected:
|
||||
public:
|
||||
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 update(ui::Context &ctx);
|
||||
|
@ -39,10 +39,17 @@ typedef enum {
|
||||
PCDRV_ATTR_ARCHIVE = 1 << 5
|
||||
} PCDRVAttribute;
|
||||
|
||||
typedef struct {
|
||||
uint32_t attributes, size;
|
||||
char name[32];
|
||||
} PCDRVDirEntry;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Standard PCDRV API */
|
||||
|
||||
int pcdrvInit(void);
|
||||
int pcdrvCreate(const char *path, uint32_t attributes);
|
||||
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 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
|
||||
}
|
||||
#endif
|
||||
|
103
src/ps1/pcdrv.s
103
src/ps1/pcdrv.s
@ -14,12 +14,14 @@
|
||||
|
||||
.set noreorder
|
||||
|
||||
## Standard PCDRV API
|
||||
|
||||
.section .text.pcdrvInit, "ax", @progbits
|
||||
.global pcdrvInit
|
||||
.type pcdrvInit, @function
|
||||
|
||||
pcdrvInit:
|
||||
break 0, 0x101 # () -> error
|
||||
break 0, 0x101 # (_) -> error
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
@ -31,7 +33,7 @@ pcdrvInit:
|
||||
pcdrvCreate:
|
||||
move $a2, $a1
|
||||
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
|
||||
nop
|
||||
@ -47,7 +49,7 @@ pcdrvCreate:
|
||||
pcdrvOpen:
|
||||
move $a2, $a1
|
||||
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
|
||||
nop
|
||||
@ -62,7 +64,7 @@ pcdrvOpen:
|
||||
|
||||
pcdrvClose:
|
||||
move $a1, $a0
|
||||
break 0, 0x104 # (fd, fd) -> error
|
||||
break 0, 0x104 # (_, fd) -> error
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
@ -74,7 +76,7 @@ pcdrvClose:
|
||||
pcdrvRead:
|
||||
move $a3, $a1
|
||||
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
|
||||
nop
|
||||
@ -90,7 +92,7 @@ pcdrvRead:
|
||||
pcdrvWrite:
|
||||
move $a3, $a1
|
||||
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
|
||||
nop
|
||||
@ -107,7 +109,7 @@ pcdrvSeek:
|
||||
move $a3, $a2
|
||||
move $a2, $a1
|
||||
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
|
||||
nop
|
||||
@ -115,3 +117,90 @@ pcdrvSeek:
|
||||
.LseekOK:
|
||||
jr $ra # return offset
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user