summaryrefslogtreecommitdiff
path: root/src/Dolphin/dvd/dvdfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/Dolphin/dvd/dvdfs.c')
-rw-r--r--src/Dolphin/dvd/dvdfs.c656
1 files changed, 656 insertions, 0 deletions
diff --git a/src/Dolphin/dvd/dvdfs.c b/src/Dolphin/dvd/dvdfs.c
new file mode 100644
index 0000000..d0dc27c
--- /dev/null
+++ b/src/Dolphin/dvd/dvdfs.c
@@ -0,0 +1,656 @@
+#include "dolphin/DVDPriv.h"
+#include "dolphin/os.h"
+#include "dolphin/os/OSBootInfo.h"
+
+typedef struct FSTEntry FSTEntry;
+
+struct FSTEntry {
+ unsigned int isDirAndStringOff;
+ unsigned int parentOrPosition;
+ unsigned int nextEntryOrLength;
+};
+
+static OSBootInfo* BootInfo;
+static FSTEntry* FstStart;
+static char* FstStringStart;
+static u32 MaxEntryNum;
+static u32 currentDirectory = 0;
+OSThreadQueue __DVDThreadQueue;
+u32 __DVDLongFileNameFlag = 0;
+
+static void cbForReadAsync(s32 result, DVDCommandBlock* block);
+static void cbForReadSync(s32 result, DVDCommandBlock* block);
+static void cbForSeekAsync(s32 result, DVDCommandBlock* block);
+static void cbForSeekSync(s32 result, DVDCommandBlock* block);
+static void cbForPrepareStreamAsync(s32 result, DVDCommandBlock* block);
+static void cbForPrepareStreamSync(s32 result, DVDCommandBlock* block);
+
+void __DVDFSInit() {
+ BootInfo = (OSBootInfo*)OSPhysicalToCached(0);
+ FstStart = (FSTEntry*)BootInfo->FSTLocation;
+
+ if (FstStart) {
+ MaxEntryNum = FstStart[0].nextEntryOrLength;
+ FstStringStart = (char*)&(FstStart[MaxEntryNum]);
+ }
+}
+
+/* For convenience */
+#define entryIsDir(i) (((FstStart[i].isDirAndStringOff & 0xff000000) == 0) ? FALSE : TRUE)
+#define stringOff(i) (FstStart[i].isDirAndStringOff & ~0xff000000)
+#define parentDir(i) (FstStart[i].parentOrPosition)
+#define nextDir(i) (FstStart[i].nextEntryOrLength)
+#define filePosition(i) (FstStart[i].parentOrPosition)
+#define fileLength(i) (FstStart[i].nextEntryOrLength)
+
+static BOOL isSame(const char* path, const char* string) {
+ while (*string != '\0') {
+ if (tolower(*path++) != tolower(*string++)) {
+ return FALSE;
+ }
+ }
+
+ if ((*path == '/') || (*path == '\0')) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+s32 DVDConvertPathToEntrynum(char* pathPtr) {
+ const char* ptr;
+ char* stringPtr;
+ BOOL isDir;
+ u32 length;
+ u32 dirLookAt;
+ u32 i;
+ const char* origPathPtr = pathPtr;
+ const char* extentionStart;
+ BOOL illegal;
+ BOOL extention;
+
+ dirLookAt = currentDirectory;
+
+ while (1) {
+
+ if (*pathPtr == '\0') {
+ return (s32)dirLookAt;
+ } else if (*pathPtr == '/') {
+ dirLookAt = 0;
+ pathPtr++;
+ continue;
+ } else if (*pathPtr == '.') {
+ if (*(pathPtr + 1) == '.') {
+ if (*(pathPtr + 2) == '/') {
+ dirLookAt = parentDir(dirLookAt);
+ pathPtr += 3;
+ continue;
+ } else if (*(pathPtr + 2) == '\0') {
+ return (s32)parentDir(dirLookAt);
+ }
+ } else if (*(pathPtr + 1) == '/') {
+ pathPtr += 2;
+ continue;
+ } else if (*(pathPtr + 1) == '\0') {
+ return (s32)dirLookAt;
+ }
+ }
+
+ if (__DVDLongFileNameFlag == 0) {
+ extention = FALSE;
+ illegal = FALSE;
+
+ for (ptr = pathPtr; (*ptr != '\0') && (*ptr != '/'); ptr++) {
+ if (*ptr == '.') {
+ if ((ptr - pathPtr > 8) || (extention == TRUE)) {
+ illegal = TRUE;
+ break;
+ }
+ extention = TRUE;
+ extentionStart = ptr + 1;
+
+ } else if (*ptr == ' ')
+ illegal = TRUE;
+ }
+
+ if ((extention == TRUE) && (ptr - extentionStart > 3))
+ illegal = TRUE;
+
+ if (illegal)
+ OSPanic(__FILE__, 379,
+ "DVDConvertEntrynumToPath(possibly DVDOpen or DVDChangeDir or DVDOpenDir): "
+ "specified directory or file (%s) doesn't match standard 8.3 format. This is a "
+ "temporary restriction and will be removed soon\n",
+ origPathPtr);
+ } else {
+ for (ptr = pathPtr; (*ptr != '\0') && (*ptr != '/'); ptr++)
+ ;
+ }
+
+ isDir = (*ptr == '\0') ? FALSE : TRUE;
+ length = (u32)(ptr - pathPtr);
+
+ ptr = pathPtr;
+
+ for (i = dirLookAt + 1; i < nextDir(dirLookAt); i = entryIsDir(i) ? nextDir(i) : (i + 1)) {
+ if ((entryIsDir(i) == FALSE) && (isDir == TRUE)) {
+ continue;
+ }
+
+ stringPtr = FstStringStart + stringOff(i);
+
+ if (isSame(ptr, stringPtr) == TRUE) {
+ goto next_hier;
+ }
+ }
+
+ return -1;
+
+ next_hier:
+ if (!isDir) {
+ return (s32)i;
+ }
+
+ dirLookAt = i;
+ pathPtr += length + 1;
+ }
+}
+
+BOOL DVDFastOpen(s32 entrynum, DVDFileInfo* fileInfo) {
+ if ((entrynum < 0) || (entrynum >= MaxEntryNum) || entryIsDir(entrynum)) {
+ return FALSE;
+ }
+
+ fileInfo->startAddr = filePosition(entrynum);
+ fileInfo->length = fileLength(entrynum);
+ fileInfo->callback = (DVDCallback)NULL;
+ fileInfo->cb.state = DVD_STATE_END;
+
+ return TRUE;
+}
+
+BOOL DVDOpen(char* fileName, DVDFileInfo* fileInfo) {
+ s32 entry;
+ char currentDir[128];
+
+ entry = DVDConvertPathToEntrynum(fileName);
+
+ if (0 > entry) {
+ DVDGetCurrentDir(currentDir, 128);
+ OSReport("Warning: DVDOpen(): file '%s' was not found under %s.\n", fileName, currentDir);
+ return FALSE;
+ }
+
+ if (entryIsDir(entry)) {
+ return FALSE;
+ }
+
+ fileInfo->startAddr = filePosition(entry);
+ fileInfo->length = fileLength(entry);
+ fileInfo->callback = (DVDCallback)NULL;
+ fileInfo->cb.state = DVD_STATE_END;
+
+ return TRUE;
+}
+
+#ifdef FULL_FRANK
+BOOL DVDClose(DVDFileInfo* fileInfo) {
+ DVDCancel(&(fileInfo->cb));
+ return TRUE;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm BOOL DVDClose(DVDFileInfo* fileInfo) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -8(r1)
+ bl DVDCancel
+ li r3, 1
+ lwz r0, 0xc(r1)
+ addi r1, r1, 8
+ mtlr r0
+ blr
+}
+#pragma pop
+#endif
+
+static u32 myStrncpy(char* dest, char* src, u32 maxlen) {
+ u32 i = maxlen;
+
+ while ((i > 0) && (*src != 0)) {
+ *dest++ = *src++;
+ i--;
+ }
+
+ return (maxlen - i);
+}
+
+static u32 entryToPath(u32 entry, char* path, u32 maxlen) {
+ char* name;
+ u32 loc;
+
+ if (entry == 0) {
+ return 0;
+ }
+
+ name = FstStringStart + stringOff(entry);
+
+ loc = entryToPath(parentDir(entry), path, maxlen);
+
+ if (loc == maxlen) {
+ return loc;
+ }
+
+ *(path + loc++) = '/';
+
+ loc += myStrncpy(path + loc, name, maxlen - loc);
+
+ return loc;
+}
+
+static BOOL DVDConvertEntrynumToPath(s32 entrynum, char* path, u32 maxlen) {
+ u32 loc;
+
+ loc = entryToPath((u32)entrynum, path, maxlen);
+
+ if (loc == maxlen) {
+ path[maxlen - 1] = '\0';
+ return FALSE;
+ }
+
+ if (entryIsDir(entrynum)) {
+ if (loc == maxlen - 1) {
+ path[loc] = '\0';
+ return FALSE;
+ }
+
+ path[loc++] = '/';
+ }
+
+ path[loc] = '\0';
+ return TRUE;
+}
+
+BOOL DVDGetCurrentDir(char* path, u32 maxlen) {
+ return DVDConvertEntrynumToPath((s32)currentDirectory, path, maxlen);
+}
+
+BOOL DVDChangeDir(char* dirName) {
+ s32 entry;
+ entry = DVDConvertPathToEntrynum(dirName);
+ if ((entry < 0) || (entryIsDir(entry) == FALSE)) {
+ return FALSE;
+ }
+
+ currentDirectory = (u32)entry;
+
+ return TRUE;
+}
+
+BOOL DVDReadAsyncPrio(DVDFileInfo* fileInfo, void* addr, s32 length, s32 offset,
+ DVDCallback callback, s32 prio) {
+
+ if (!((0 <= offset) && (offset < fileInfo->length))) {
+ OSPanic(__FILE__, 742, "DVDReadAsync(): specified area is out of the file ");
+ }
+
+ if (!((0 <= offset + length) && (offset + length < fileInfo->length + DVD_MIN_TRANSFER_SIZE))) {
+ OSPanic(__FILE__, 748, "DVDReadAsync(): specified area is out of the file ");
+ }
+
+ fileInfo->callback = callback;
+ DVDReadAbsAsyncPrio(&(fileInfo->cb), addr, length, (s32)(fileInfo->startAddr + offset),
+ cbForReadAsync, prio);
+
+ return TRUE;
+}
+#ifndef offsetof
+#define offsetof(type, memb) ((u32) & ((type*)0)->memb)
+#endif
+
+static void cbForReadAsync(s32 result, DVDCommandBlock* block) {
+ DVDFileInfo* fileInfo;
+
+ fileInfo = (DVDFileInfo*)((char*)block - offsetof(DVDFileInfo, cb));
+ if (fileInfo->callback) {
+ (fileInfo->callback)(result, fileInfo);
+ }
+}
+
+/* This is based on the revolution SDK, these may not match in all cases I have also left the line numbers at 0 */
+s32 DVDReadPrio(DVDFileInfo* fileInfo, void* addr, s32 length, s32 offset, s32 prio) {
+ BOOL result;
+ DVDCommandBlock* block;
+ s32 state;
+ BOOL enabled;
+ s32 retVal;
+
+ if (!((0 <= offset) && (offset <= fileInfo->length))) {
+ OSPanic(__FILE__, 0, "DVDRead(): specified area is out of the file ");
+ }
+
+ if (!((0 <= offset + length) && (offset + length < fileInfo->length + DVD_MIN_TRANSFER_SIZE))) {
+ OSPanic(__FILE__, 0, "DVDRead(): specified area is out of the file ");
+ }
+
+ block = &(fileInfo->cb);
+
+ result = DVDReadAbsAsyncPrio(block, addr, length, (s32)(fileInfo->startAddr + offset),
+ cbForReadSync, prio);
+
+ if (result == FALSE) {
+ return -1;
+ }
+
+ enabled = OSDisableInterrupts();
+
+ while(1) {
+ state = ((volatile DVDCommandBlock*)block)->state;
+
+ if (state == DVD_STATE_END) {
+ retVal = (s32)block->transferredSize;
+ break;
+ }
+ if (state == DVD_STATE_FATAL_ERROR) {
+ retVal = DVD_RESULT_FATAL_ERROR;
+ break;
+ }
+ if (state == DVD_STATE_CANCELED) {
+ retVal = DVD_RESULT_CANCELED;
+ break;
+ }
+
+ OSSleepThread(&__DVDThreadQueue);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return retVal;
+}
+
+/* This is based on the revolution SDK, these may not match in all cases */
+static void cbForReadSync(s32 result, DVDCommandBlock* block) { OSWakeupThread(&__DVDThreadQueue); }
+/* This is based on the revolution SDK, these may not match in all cases */
+BOOL DVDSeekAsyncPrio(DVDFileInfo* fileInfo, s32 offset, DVDCallback callback, s32 prio) {
+ if (!((0 <= offset) && (offset <= fileInfo->length))) {
+ OSPanic(__FILE__, 0, "DVDSeek(): offset is out of the file ");
+ }
+
+ fileInfo->callback = callback;
+ DVDSeekAbsAsyncPrio(&(fileInfo->cb), (s32)(fileInfo->startAddr + offset), cbForSeekAsync,
+ prio);
+
+ return TRUE;
+}
+/* This is based on the revolution SDK, these may not match in all cases */
+static void cbForSeekAsync(s32 result, DVDCommandBlock* block) {
+ DVDFileInfo* fileInfo;
+
+ fileInfo = (DVDFileInfo*)((char*)block - offsetof(DVDFileInfo, cb));
+
+ if (fileInfo->callback) {
+ (fileInfo->callback)(result, fileInfo);
+ }
+}
+/* This is based on the revolution SDK, these may not match in all cases */
+s32 DVDSeekPrio(DVDFileInfo* fileInfo, s32 offset, s32 prio) {
+ BOOL result;
+ DVDCommandBlock* block;
+ s32 state;
+ BOOL enabled;
+ s32 retVal;
+
+ block = &(fileInfo->cb);
+
+ result =
+ DVDSeekAbsAsyncPrio(block, (s32)(fileInfo->startAddr + offset), cbForSeekSync, prio);
+
+ if (result == FALSE) {
+ return -1;
+ }
+
+ enabled = OSDisableInterrupts();
+
+ while (1) {
+ state = ((volatile DVDCommandBlock*)block)->state;
+
+ if (state == DVD_STATE_END) {
+ retVal = 0;
+ break;
+ }
+ if (state == DVD_STATE_FATAL_ERROR) {
+ retVal = DVD_RESULT_FATAL_ERROR;
+ break;
+ }
+ if (state == DVD_STATE_CANCELED) {
+ retVal = DVD_RESULT_CANCELED;
+ break;
+ }
+
+ OSSleepThread(&__DVDThreadQueue);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return retVal;
+}
+/* This is based on the revolution SDK, these may not match in all cases */
+static void cbForSeekSync(s32 result, DVDCommandBlock* block) { OSWakeupThread(&__DVDThreadQueue); }
+
+/* This is based on the revolution SDK, these may not match in all cases */
+s32 DVDGetFileInfoStatus(DVDFileInfo* fileInfo) {
+ return DVDGetCommandBlockStatus(&fileInfo->cb);
+}
+
+/* This is based on the revolution SDK, these may not match in all cases */
+BOOL DVDFastOpenDir(s32 entrynum, DVDDir* dir) {
+
+ if ((entrynum < 0) || (entrynum >= MaxEntryNum) || !entryIsDir(entrynum)) {
+ return FALSE;
+ }
+
+ dir->entryNum = (u32)entrynum;
+ dir->location = (u32)entrynum + 1;
+ dir->next = nextDir(entrynum);
+
+ return TRUE;
+}
+
+/* This is based on the revolution SDK, these may not match in all cases */
+BOOL DVDOpenDir(char* dirName, DVDDir* dir) {
+ s32 entry;
+ char currentDir[128];
+ entry = DVDConvertPathToEntrynum(dirName);
+
+ if (entry < 0) {
+ DVDGetCurrentDir(currentDir, 128);
+ OSReport("Warning: DVDOpenDir(): file '%s' was not found under %s.\n", dirName, currentDir);
+ return FALSE;
+ }
+
+ if (!entryIsDir(entry)) {
+ return FALSE;
+ }
+
+ dir->entryNum = (u32)entry;
+ dir->location = (u32)entry + 1;
+ dir->next = nextDir(entry);
+
+ return TRUE;
+}
+
+BOOL DVDReadDir(DVDDir* dir, DVDDirEntry* dirent) {
+ u32 loc = dir->location;
+ if ((loc <= dir->entryNum) || (dir->next <= loc))
+ return FALSE;
+
+ dirent->entryNum = loc;
+ dirent->isDir = entryIsDir(loc);
+ dirent->name = FstStringStart + stringOff(loc);
+
+ dir->location = entryIsDir(loc) ? nextDir(loc) : (loc + 1);
+
+ return TRUE;
+}
+
+/* This is based on the revolution SDK, these may not match in all cases */
+BOOL DVDCloseDir(DVDDir* dir) { return TRUE; }
+
+/* This is based on the revolution SDK, these may not match in all cases */
+void DVDRewindDir(DVDDir* dir) { dir->location = dir->entryNum + 1; }
+
+/* This is based on the revolution SDK, these may not match in all cases */
+void* DVDGetFSTLocation(void) { return BootInfo->FSTLocation; }
+
+#define RoundUp32KB(x) (((u32)(x) + 32 * 1024 - 1) & ~(32 * 1024 - 1))
+#define Is32KBAligned(x) (((u32)(x) & (32 * 1024 - 1)) == 0)
+
+BOOL DVDPrepareStreamAsync(DVDFileInfo* fileInfo, u32 length, u32 offset, DVDCallback callback) {
+ u32 start;
+
+ start = fileInfo->startAddr + offset;
+
+ if (!Is32KBAligned(start)) {
+ OSPanic(__FILE__, 1189,
+ "DVDPrepareStreamAsync(): Specified start address (filestart(0x%x) + offset(0x%x)) is "
+ "not 32KB aligned",
+ fileInfo->startAddr, offset);
+ }
+
+ if (length == 0)
+ length = fileInfo->length - offset;
+
+ if (!Is32KBAligned(length)) {
+ OSPanic(__FILE__, 1199,
+ "DVDPrepareStreamAsync(): Specified length (0x%x) is not a multiple of 32768(32*1024)",
+ length);
+ }
+
+ if (!((offset < fileInfo->length) && (offset + length <= fileInfo->length))) {
+ OSPanic(__FILE__, 1207,
+ "DVDPrepareStreamAsync(): The area specified (offset(0x%x), length(0x%x)) is out of "
+ "the file",
+ offset, length);
+ }
+
+ fileInfo->callback = callback;
+ return DVDPrepareStreamAbsAsync(&(fileInfo->cb), length, fileInfo->startAddr + offset,
+ cbForPrepareStreamAsync);
+}
+
+static void cbForPrepareStreamAsync(s32 result, DVDCommandBlock* block) {
+ DVDFileInfo* fileInfo;
+
+ fileInfo = (DVDFileInfo*)((char*)block - offsetof(DVDFileInfo, cb));
+
+ if (fileInfo->callback) {
+ (fileInfo->callback)(result, fileInfo);
+ }
+}
+
+/* This is based on the revolution SDK, these may not match in all cases */
+s32 DVDPrepareStream(DVDFileInfo* fileInfo, u32 length, u32 offset) {
+ BOOL result;
+ DVDCommandBlock* block;
+ s32 state;
+ BOOL enabled;
+ s32 retVal;
+ u32 start;
+ start = fileInfo->startAddr + offset;
+
+ if (!Is32KBAligned(start)) {
+ OSPanic(__FILE__, 0,
+ "DVDPrepareStream(): Specified start address (filestart(0x%x) + offset(0x%x)) is not "
+ "32KB aligned",
+ fileInfo->startAddr, offset);
+ }
+
+ if (length == 0)
+ length = fileInfo->length - offset;
+
+ if (!Is32KBAligned(length)) {
+ OSPanic(__FILE__, 0,
+ "DVDPrepareStream(): Specified length (0x%x) is not a multiple of 32768(32*1024)",
+ length);
+ }
+
+ if (!((offset <= fileInfo->length) && (offset + length <= fileInfo->length))) {
+ OSPanic(
+ __FILE__, 0,
+ "DVDPrepareStream(): The area specified (offset(0x%x), length(0x%x)) is out of the file",
+ offset, length);
+ }
+
+ block = &(fileInfo->cb);
+ result = DVDPrepareStreamAbsAsync(block, length, start, cbForPrepareStreamSync);
+
+ if (result == FALSE) {
+ return -1;
+ }
+
+ enabled = OSDisableInterrupts();
+
+ while(1) {
+ state = ((volatile DVDCommandBlock*)block)->state;
+
+ if (state == DVD_STATE_END) {
+ retVal = 0;
+ break;
+ }
+ if (state == DVD_STATE_FATAL_ERROR) {
+ retVal = DVD_RESULT_FATAL_ERROR;
+ break;
+ }
+ if (state == DVD_STATE_CANCELED) {
+ retVal = DVD_RESULT_CANCELED;
+ break;
+ }
+
+ OSSleepThread(&__DVDThreadQueue);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return retVal;
+}
+
+/* This is based on the revolution SDK, these may not match in all cases */
+static void cbForPrepareStreamSync(s32 result, DVDCommandBlock* block) {
+ OSWakeupThread(&__DVDThreadQueue);
+}
+
+/* This is based on the revolution SDK, these may not match in all cases */
+s32 DVDGetTransferredSize(DVDFileInfo* fileinfo) {
+ s32 bytes;
+ DVDCommandBlock* cb;
+
+ cb = &(fileinfo->cb);
+
+ switch (cb->state) {
+ case DVD_STATE_END:
+ case DVD_STATE_COVER_CLOSED:
+ case DVD_STATE_NO_DISK:
+ case DVD_STATE_COVER_OPEN:
+ case DVD_STATE_WRONG_DISK:
+ case DVD_STATE_FATAL_ERROR:
+ case DVD_STATE_MOTOR_STOPPED:
+ case DVD_STATE_CANCELED:
+ case DVD_STATE_RETRY:
+ bytes = (s32)cb->transferredSize;
+ break;
+
+ case DVD_STATE_WAITING:
+ bytes = 0;
+ break;
+
+ case DVD_STATE_BUSY:
+ bytes = (s32)(cb->transferredSize + (cb->currTransferSize - DVDLowGetLength()));
+ break;
+
+ default:
+ break;
+ }
+
+ return bytes;
+}