mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-08-09 02:15:44 -04:00
9649319ec4
* Reorganize files into libs/{dolphin,JSystem,PowerPC_EABI_Support,revolution,TRK_MINNOW_DOLPHIN}
* Update configure.py and project.py for new libs structure
* Refactor `#include <dolphin/x.h>` -> `<x.h>`
* Remove `__REVOLUTION_SDK__` forwards from dolphin
* Fix dolphin/ references in revolution
* Wrap `#include <dolphin.h>` in `!__REVOLUTION_SDK__`
* Always build TRK against dolphin headers
* Resolve revolution SDK header resolution issues
515 lines
15 KiB
C
515 lines
15 KiB
C
#include <revolution/nand.h>
|
|
#include <revolution/esp.h>
|
|
#include <revolution/fs.h>
|
|
#include <revolution/os.h>
|
|
#include <cstring>
|
|
|
|
#ifdef SDK_AUG2010
|
|
#define BUILD_DATE "Aug 23 2010"
|
|
#if DEBUG
|
|
#define BUILD_TIME "17:28:24"
|
|
#else
|
|
#define BUILD_TIME "17:33:06"
|
|
#endif
|
|
#elif SDK_SEP2006
|
|
#define BUILD_DATE "Sep 21 2006"
|
|
#define BUILD_TIME "14:32:13"
|
|
#endif
|
|
|
|
#ifdef SDK_AUG2010
|
|
#if DEBUG
|
|
const char* __NANDVersion = "<< RVL_SDK - NAND \tdebug build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
|
|
#else
|
|
const char* __NANDVersion = "<< RVL_SDK - NAND \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
|
|
#endif
|
|
#elif SDK_SEP2006
|
|
const char* __NANDVersion = "<< RVL_SDK - NAND \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4200_60422) >>";
|
|
#endif
|
|
|
|
enum LibState {
|
|
STATE_NOT_INITIALIZED,
|
|
STATE_WORKING,
|
|
STATE_INITIALIZED
|
|
};
|
|
|
|
static enum LibState s_libState = STATE_NOT_INITIALIZED;
|
|
static char s_currentDir[64] ATTRIBUTE_ALIGN(32) = "/";
|
|
static char s_homeDir[64] ATTRIBUTE_ALIGN(32) = "";
|
|
|
|
static BOOL nandOnShutdown(BOOL final, u32 event);
|
|
void nandConvertPath(char* abspath, const char* wd, const char* relpath);
|
|
static void nandShutdownCallback(ISFSError result, void* ctxt);
|
|
|
|
static OSShutdownFunctionInfo s_shutdownFuncInfo = {
|
|
nandOnShutdown,
|
|
255
|
|
};
|
|
|
|
void nandRemoveTailToken(char* newpath, const char* oldpath) {
|
|
ASSERTMSGLINE(189, newpath, "null pointer is detected in argument of newpath\n");
|
|
ASSERTMSGLINE(190, oldpath, "null pointer is detected in argument of oldpath\n");
|
|
ASSERTMSGLINE(191, oldpath[0] == '/', "Head of path must be \'/\' .\n");
|
|
|
|
if (oldpath[0] == '/' && oldpath[1] == '\0') {
|
|
newpath[0] = '/';
|
|
newpath[1] = '\0';
|
|
} else {
|
|
int i = 0;
|
|
|
|
for (i = (int)(strlen(oldpath)) - 1; i >= 0; --i) {
|
|
if (oldpath[i] == '/') {
|
|
if (i != 0) {
|
|
strncpy(newpath, oldpath, (u32)i);
|
|
newpath[i] = '\0';
|
|
break;
|
|
} else {
|
|
newpath[0] = '/';
|
|
newpath[1] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void nandGetHeadToken(char* token, char* newpath, const char* oldpath) {
|
|
unsigned int i = 0;
|
|
|
|
ASSERTMSGLINE(224, token && newpath && oldpath, "Null pointer detected!\n");
|
|
ASSERTMSGLINE(225, strlen(oldpath) != 0, "Null string was detected!\n");
|
|
ASSERTMSGLINE(226, oldpath[0] != '/', "Head of relative path must not be \'/\' .\n");
|
|
|
|
for (i = 0; i <= strlen(oldpath); ++i) {
|
|
if (oldpath[i] == '/') {
|
|
strncpy(token, oldpath, i);
|
|
token[i] = '\0';
|
|
|
|
if (oldpath[i + 1] == '\0') {
|
|
newpath[0] = '\0';
|
|
} else {
|
|
strcpy(newpath, oldpath + i + 1);
|
|
}
|
|
|
|
break;
|
|
} else if (oldpath[i] == '\0') {
|
|
strncpy(token, oldpath, i);
|
|
token[i] = '\0';
|
|
newpath[0] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void nandGetRelativeName(char* name, const char* path) {
|
|
if (strcmp("/", path) == 0) {
|
|
strcpy(name, "");
|
|
} else {
|
|
int i = 0;
|
|
|
|
ASSERTMSGLINE(260, nandIsAbsolutePath(path), "path must be absolute path.");
|
|
ASSERTMSGLINE(261, path[strlen(path) - 1] != '/', "path must not be terminated with \'/\'.");
|
|
|
|
for (i = (int)(strlen(path) - 1); i >= 0; --i) {
|
|
if (path[i] == '/') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERTMSGLINE(270, strlen(path + i + 1) <= ISFS_INODE_NAMELEN, "path must be smaller than or equal to ISFS_INODE_NAMELEN.");
|
|
|
|
strcpy(name, path + i + 1);
|
|
}
|
|
}
|
|
|
|
void nandConvertPath(char* abspath, const char* wd, const char* relpath) {
|
|
char token[128];
|
|
char new_relpath[128];
|
|
|
|
ASSERTMSGLINE(283, abspath && wd && relpath, "Null pointer detected!\n");
|
|
ASSERTMSGLINE(284, wd[0] == '/', "Head of \'wd\' must be \'/\'.\n");
|
|
|
|
if (strlen(relpath) == 0) {
|
|
strcpy(abspath, wd);
|
|
return;
|
|
}
|
|
|
|
nandGetHeadToken(token, new_relpath, relpath);
|
|
|
|
if (strcmp(token, ".") == 0) {
|
|
nandConvertPath(abspath, wd, new_relpath);
|
|
} else if (strcmp(token, "..") == 0) {
|
|
char new_wd[128];
|
|
nandRemoveTailToken(new_wd, wd);
|
|
nandConvertPath(abspath, new_wd, new_relpath);
|
|
} else if (token[0] != '\0') {
|
|
char new_wd[128];
|
|
if (strcmp(wd, "/") == 0) {
|
|
sprintf(new_wd, "/%s", token);
|
|
} else {
|
|
sprintf(new_wd, "%s/%s", wd, token);
|
|
}
|
|
|
|
nandConvertPath(abspath, new_wd, new_relpath);
|
|
} else {
|
|
strcpy(abspath, wd);
|
|
}
|
|
}
|
|
|
|
BOOL nandIsRelativePath(const char* path) {
|
|
if (path[0] == '/') {
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
BOOL nandIsAbsolutePath(const char* path) {
|
|
if (nandIsRelativePath(path)) {
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
BOOL nandIsPrivatePath(const char* path) {
|
|
if (strncmp(path, "/shared2", 8) == 0) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOL nandIsUnderPrivatePath(const char* path) {
|
|
if (strncmp(path, "/shared2/", 9) == 0 && path[9] != '\0') {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOL nandIsInitialized(void) {
|
|
if (s_libState == STATE_INITIALIZED) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
void nandReportErrorCode(const ISFSError err) {
|
|
// NONMATCHING
|
|
return;
|
|
}
|
|
|
|
void nandLoggingCallback(BOOL, ISFSError err) {
|
|
if (err == ISFS_ERROR_UNKNOWN || err == IOS_ERROR_UNKNOWN) {
|
|
__NANDPrintErrorMessage(err);
|
|
}
|
|
}
|
|
|
|
s32 nandConvertErrorCode(const ISFSError err) {
|
|
const int ERRMAP[] = {
|
|
ISFS_ERROR_OK, NAND_RESULT_OK,
|
|
ISFS_ERROR_ACCESS, NAND_RESULT_ACCESS,
|
|
ISFS_ERROR_CORRUPT, NAND_RESULT_CORRUPT,
|
|
ISFS_ERROR_ECC_CRIT, NAND_RESULT_ECC_CRIT,
|
|
ISFS_ERROR_EXISTS, NAND_RESULT_EXISTS,
|
|
ISFS_ERROR_HMAC, NAND_RESULT_AUTHENTICATION,
|
|
ISFS_ERROR_INVALID, NAND_RESULT_INVALID,
|
|
ISFS_ERROR_MAXBLOCKS, NAND_RESULT_MAXBLOCKS,
|
|
ISFS_ERROR_MAXFD, NAND_RESULT_MAXFD,
|
|
ISFS_ERROR_MAXFILES, NAND_RESULT_MAXFILES,
|
|
ISFS_ERROR_MAXDEPTH, NAND_RESULT_MAXDEPTH,
|
|
ISFS_ERROR_NOEXISTS, NAND_RESULT_NOEXISTS,
|
|
ISFS_ERROR_NOTEMPTY, NAND_RESULT_NOTEMPTY,
|
|
ISFS_ERROR_NOTREADY, NAND_RESULT_UNKNOWN,
|
|
ISFS_ERROR_OPENFD, NAND_RESULT_OPENFD,
|
|
ISFS_ERROR_UNKNOWN, NAND_RESULT_UNKNOWN,
|
|
ISFS_ERROR_BUSY, NAND_RESULT_BUSY,
|
|
ISFS_ERROR_SHUTDOWN, NAND_RESULT_FATAL_ERROR,
|
|
|
|
IOS_ERROR_ACCESS, NAND_RESULT_ACCESS,
|
|
IOS_ERROR_EXISTS, NAND_RESULT_EXISTS,
|
|
IOS_ERROR_INTR, NAND_RESULT_UNKNOWN,
|
|
IOS_ERROR_INVALID, NAND_RESULT_INVALID,
|
|
IOS_ERROR_MAX, NAND_RESULT_UNKNOWN,
|
|
IOS_ERROR_NOEXISTS, NAND_RESULT_NOEXISTS,
|
|
IOS_ERROR_QEMPTY, NAND_RESULT_UNKNOWN,
|
|
IOS_ERROR_QFULL, NAND_RESULT_BUSY,
|
|
IOS_ERROR_UNKNOWN, NAND_RESULT_UNKNOWN,
|
|
IOS_ERROR_NOTREADY, NAND_RESULT_UNKNOWN,
|
|
IOS_ERROR_ECC, NAND_RESULT_UNKNOWN,
|
|
IOS_ERROR_ECC_CRIT, NAND_RESULT_ECC_CRIT,
|
|
IOS_ERROR_BADBLOCK, NAND_RESULT_UNKNOWN,
|
|
|
|
IOS_ERROR_INVALID_OBJTYPE, NAND_RESULT_UNKNOWN,
|
|
IOS_ERROR_INVALID_RNG, NAND_RESULT_UNKNOWN,
|
|
IOS_ERROR_INVALID_FLAG, NAND_RESULT_UNKNOWN,
|
|
IOS_ERROR_INVALID_FORMAT, NAND_RESULT_UNKNOWN,
|
|
IOS_ERROR_INVALID_VERSION, NAND_RESULT_UNKNOWN,
|
|
IOS_ERROR_INVALID_SIGNER, NAND_RESULT_UNKNOWN,
|
|
IOS_ERROR_FAIL_CHECKVALUE, NAND_RESULT_UNKNOWN,
|
|
IOS_ERROR_FAIL_INTERNAL, NAND_RESULT_UNKNOWN,
|
|
IOS_ERROR_FAIL_ALLOC, NAND_RESULT_ALLOC_FAILED,
|
|
IOS_ERROR_INVALID_SIZE, NAND_RESULT_UNKNOWN,
|
|
};
|
|
|
|
int i = 0;
|
|
|
|
if (err >= 0) {
|
|
return err;
|
|
}
|
|
|
|
for (; i < sizeof(ERRMAP) / 4; i = i + 2) {
|
|
if (ERRMAP[i] == err) {
|
|
if (err == ISFS_ERROR_ECC_CRIT || err == ISFS_ERROR_HMAC || err == ISFS_ERROR_UNKNOWN || err == IOS_ERROR_UNKNOWN || err == IOS_ERROR_ECC_CRIT) {
|
|
char buf[128] ATTRIBUTE_ALIGN(64);
|
|
sprintf(buf, "ISFS error code: %d", err);
|
|
NANDLoggingAddMessageAsync(nandLoggingCallback, err, buf);
|
|
}
|
|
|
|
nandReportErrorCode(err);
|
|
|
|
if (err == ISFS_ERROR_MAXBLOCKS || err == ISFS_ERROR_MAXFILES || err == ISFS_ERROR_CORRUPT || err == ISFS_ERROR_BUSY || err == IOS_ERROR_QFULL || err == IOS_ERROR_FAIL_ALLOC) {
|
|
__NANDPrintErrorMessage(err);
|
|
}
|
|
|
|
return ERRMAP[i + 1];
|
|
}
|
|
}
|
|
|
|
OSReport("CAUTION! Unexpected error code [%d] was found.\n", err);
|
|
{
|
|
char buf[128] ATTRIBUTE_ALIGN(64);
|
|
sprintf(buf, "ISFS unexpected error code: %d", err);
|
|
NANDLoggingAddMessageAsync(nandLoggingCallback, err, buf);
|
|
}
|
|
|
|
nandReportErrorCode(err);
|
|
return -64;
|
|
}
|
|
|
|
void nandGenerateAbsPath(char* absPath, const char* path) {
|
|
if (strlen(path) == 0) {
|
|
strcpy(absPath, "");
|
|
} else if (nandIsRelativePath(path)) {
|
|
nandConvertPath(absPath, s_currentDir, path);
|
|
} else {
|
|
u32 len = 0xFFFFFFFF;
|
|
strcpy(absPath, path);
|
|
|
|
len = strlen(absPath);
|
|
if (len > 0) {
|
|
if ((absPath[len - 1] == '/') && (len - 1 != 0)) {
|
|
absPath[len - 1] = '\0';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void nandGetParentDirectory(char* parentDir, const char* absPath) {
|
|
int i = 0;
|
|
for (i = (int)strlen(absPath); i >= 0; --i) {
|
|
if (absPath[i] == '/') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == 0) {
|
|
strcpy(parentDir, "/");
|
|
} else {
|
|
strncpy(parentDir, absPath, (u32)i);
|
|
parentDir[i] = '\0';
|
|
}
|
|
}
|
|
|
|
s32 NANDInit(void) {
|
|
BOOL enabled = OSDisableInterrupts();
|
|
|
|
if (s_libState == STATE_WORKING) {
|
|
OSRestoreInterrupts(enabled);
|
|
return -3;
|
|
} else if (s_libState == 2) {
|
|
OSRestoreInterrupts(enabled);
|
|
return 0;
|
|
} else {
|
|
ISFSError result = ISFS_ERROR_UNKNOWN;
|
|
s_libState = STATE_WORKING;
|
|
OSRestoreInterrupts(enabled);
|
|
|
|
result = ISFS_OpenLib();
|
|
if (result == 0) {
|
|
s32 rv;
|
|
ESTitleId id;
|
|
|
|
rv = ESP_InitLib();
|
|
|
|
if (rv == 0) {
|
|
rv = ESP_GetTitleId(&id);
|
|
}
|
|
|
|
if (rv == 0) {
|
|
rv = ESP_GetDataDir(id, s_homeDir);
|
|
}
|
|
|
|
if (rv == 0) {
|
|
strcpy(s_currentDir, s_homeDir);
|
|
}
|
|
|
|
ESP_CloseLib();
|
|
|
|
if (rv != 0) {
|
|
OSReport("Failed to set home directory.\n");
|
|
}
|
|
|
|
OSRegisterShutdownFunction(&s_shutdownFuncInfo);
|
|
enabled = OSDisableInterrupts();
|
|
s_libState = STATE_INITIALIZED;
|
|
OSRestoreInterrupts(enabled);
|
|
NANDSetAutoErrorMessaging(1);
|
|
OSRegisterVersion(__NANDVersion);
|
|
return 0;
|
|
} else {
|
|
enabled = OSDisableInterrupts();
|
|
s_libState = STATE_NOT_INITIALIZED;
|
|
OSRestoreInterrupts(enabled);
|
|
return nandConvertErrorCode(result);
|
|
}
|
|
}
|
|
}
|
|
|
|
static BOOL nandOnShutdown(BOOL final, u32 event) {
|
|
if (!final) {
|
|
if (event == 2) {
|
|
volatile BOOL flag = FALSE;
|
|
OSTime t = OSGetTime();
|
|
ISFS_ShutdownAsync(nandShutdownCallback, (void*)&flag);
|
|
|
|
while (OSTicksToMilliseconds(OSGetTime() - t) < 500) {
|
|
if (flag) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static void nandShutdownCallback(ISFSError result, void* ctxt) {
|
|
(void)result;
|
|
*(BOOL*)ctxt = TRUE;
|
|
}
|
|
|
|
// NOTE: function doesn't exist in TP debug, needed for string data
|
|
s32 NANDGetCurrentDir() {
|
|
OSReport("NANDGetCurrentDir");
|
|
}
|
|
|
|
s32 NANDGetHomeDir(char* path) {
|
|
ASSERTMSG1LINE(0, path, "NULL pointer is detected: %s()", "NANDGetHomeDir");
|
|
|
|
if (!nandIsInitialized()) {
|
|
return -128;
|
|
}
|
|
|
|
strcpy(path, s_homeDir);
|
|
return 0;
|
|
}
|
|
|
|
void nandCallback(ISFSError result, void* ctxt) {
|
|
NANDCommandBlock* b = (NANDCommandBlock*)ctxt;
|
|
((NANDCallback)(b->callback))(nandConvertErrorCode(result), b);
|
|
}
|
|
|
|
void nandGetTypeCallback(ISFSError result, void* ctxt);
|
|
|
|
static ISFSError nandGetType(const char* path, u8* type, NANDCommandBlock* block, const BOOL async_flag, const BOOL privilege_flag) {
|
|
ASSERTMSG1LINE(1133, path, "NULL pointer is detected: %s()", __FUNCTION__);
|
|
ASSERTMSG1LINE(1134, type, "NULL pointer is detected: %s()", __FUNCTION__);
|
|
|
|
if (strlen(path) == 0) {
|
|
return ISFS_ERROR_INVALID;
|
|
}
|
|
|
|
if (async_flag) {
|
|
nandGenerateAbsPath(block->absPath, path);
|
|
if (!privilege_flag && nandIsUnderPrivatePath(block->absPath)) {
|
|
return ISFS_ERROR_ACCESS;
|
|
} else {
|
|
block->type = type;
|
|
return ISFS_ReadDirAsync((u8*)(block->absPath), NULL, &(block->num), nandGetTypeCallback, block);
|
|
}
|
|
} else {
|
|
char absPath[64] = "";
|
|
nandGenerateAbsPath(absPath, path);
|
|
|
|
if (!privilege_flag && nandIsUnderPrivatePath(absPath)) {
|
|
return ISFS_ERROR_ACCESS;
|
|
} else {
|
|
u32 dmy = 0;
|
|
ISFSError err = ISFS_ReadDir((u8*)absPath, NULL, &dmy);
|
|
|
|
if (err == ISFS_ERROR_OK || err == ISFS_ERROR_ACCESS) {
|
|
*type = 2;
|
|
err = ISFS_ERROR_OK;
|
|
} else if (err == ISFS_ERROR_INVALID) {
|
|
*type = 1;
|
|
err = ISFS_ERROR_OK;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
}
|
|
}
|
|
|
|
s32 NANDPrivateGetTypeAsync(const char* path, u8* type, NANDCallback cb, NANDCommandBlock* block) {
|
|
if (!nandIsInitialized()) {
|
|
return NAND_RESULT_FATAL_ERROR;
|
|
}
|
|
|
|
block->callback = cb;
|
|
return nandConvertErrorCode(nandGetType(path, type, block, TRUE, TRUE));
|
|
}
|
|
|
|
void nandGetTypeCallback(ISFSError result, void* ctxt) {
|
|
NANDCommandBlock* b = (NANDCommandBlock*)ctxt;
|
|
|
|
if (result == ISFS_ERROR_OK || result == ISFS_ERROR_ACCESS) {
|
|
*(b->type) = 2;
|
|
result = ISFS_ERROR_OK;
|
|
} else if (result == ISFS_ERROR_INVALID) {
|
|
*(b->type) = 1;
|
|
result = ISFS_ERROR_OK;
|
|
}
|
|
|
|
((NANDCallback)(b->callback))(nandConvertErrorCode(result), b);
|
|
}
|
|
|
|
const char* nandGetHomeDir(void) {
|
|
return s_homeDir;
|
|
}
|
|
|
|
void NANDInitBanner(NANDBanner* bnr, u32 const flag, const u16* title, const u16* comment) {
|
|
ASSERTMSG1LINE(1253, bnr, "Null pointer is detected at %s()", __FUNCTION__);
|
|
ASSERTMSG1LINE(1254, title, "Null pointer is detected at %s()", __FUNCTION__);
|
|
ASSERTMSG1LINE(1255, comment, "Null pointer is detected at %s()", __FUNCTION__);
|
|
|
|
memset(bnr, 0, sizeof(NANDBanner));
|
|
bnr->signature = 0x5749424E;
|
|
bnr->flag = flag;
|
|
|
|
if (wcscmp((wchar_t*)title, L"") == 0) {
|
|
wcsncpy(bnr->comment[0], L" ", 32);
|
|
} else {
|
|
wcsncpy(bnr->comment[0], (wchar_t*)title, 32);
|
|
}
|
|
|
|
if (wcscmp((wchar_t*)comment, L"") == 0) {
|
|
wcsncpy(bnr->comment[1], L" ", 32);
|
|
} else {
|
|
wcsncpy(bnr->comment[1], (wchar_t*)comment, 32);
|
|
}
|
|
}
|