summaryrefslogtreecommitdiff
path: root/src/Dolphin/dvd
diff options
context:
space:
mode:
Diffstat (limited to 'src/Dolphin/dvd')
-rw-r--r--src/Dolphin/dvd/dvd.c1389
-rw-r--r--src/Dolphin/dvd/dvderror.c56
-rw-r--r--src/Dolphin/dvd/dvdfatal.c136
-rw-r--r--src/Dolphin/dvd/dvdfs.c656
-rw-r--r--src/Dolphin/dvd/dvdidutils.c27
-rw-r--r--src/Dolphin/dvd/dvdlow.c109
-rw-r--r--src/Dolphin/dvd/dvdqueue.c142
-rw-r--r--src/Dolphin/dvd/fstload.c67
8 files changed, 2582 insertions, 0 deletions
diff --git a/src/Dolphin/dvd/dvd.c b/src/Dolphin/dvd/dvd.c
new file mode 100644
index 0000000..65c67f6
--- /dev/null
+++ b/src/Dolphin/dvd/dvd.c
@@ -0,0 +1,1389 @@
+#include <dolphin/DVDPriv.h>
+#include <dolphin/dvd.h>
+#include <dolphin/dvd_regs.h>
+#include <dolphin/os.h>
+#include <dolphin/os/OSBootInfo.h>
+
+const char* __DVDVersion = "<< Dolphin SDK - DVD\trelease build: Sep 5 2002 05:34:06 (0x2301) >>";
+
+typedef void (*stateFunc)(DVDCommandBlock* block);
+stateFunc LastState;
+
+extern OSThreadQueue __DVDThreadQueue;
+
+static DVDBB2 BB2 ATTRIBUTE_ALIGN(32);
+static DVDDiskID CurrDiskID ATTRIBUTE_ALIGN(32);
+static DVDCommandBlock* executing;
+static DVDDiskID* IDShouldBe;
+static OSBootInfo* bootInfo;
+static BOOL autoInvalidation = TRUE;
+static volatile BOOL PauseFlag = FALSE;
+static volatile BOOL PausingFlag = FALSE;
+static volatile BOOL AutoFinishing = FALSE;
+static volatile BOOL FatalErrorFlag = FALSE;
+static vu32 CurrCommand;
+static vu32 Canceling = FALSE;
+static DVDCBCallback CancelCallback;
+static vu32 ResumeFromHere = 0;
+static vu32 CancelLastError;
+static vu32 LastError;
+static vs32 NumInternalRetry = 0;
+static volatile BOOL ResetRequired;
+static volatile BOOL CancelAllSyncComplete = FALSE;
+static volatile BOOL FirstTimeInBootrom = FALSE;
+
+static DVDCommandBlock DummyCommandBlock;
+static OSAlarm ResetAlarm;
+
+static BOOL DVDInitialized = FALSE;
+
+/* States */
+static void stateReadingFST();
+static void stateTimeout();
+static void stateGettingError();
+static void stateGoToRetry();
+static void stateCheckID();
+static void stateCheckID3();
+static void stateCheckID2a();
+static void stateCheckID2();
+static void stateCoverClosed();
+static void stateCoverClosed_CMD();
+static void stateCoverOpen();
+static void stateMotorStopped();
+static void stateReady();
+static void stateBusy();
+
+/* Callbacks */
+static void cbForStateReadingFST(u32 intType);
+static void cbForStateError(u32 intType);
+static void cbForStateGettingError(u32 intType);
+static void cbForUnrecoveredError(u32 intType);
+static void cbForUnrecoveredErrorRetry(u32 intType);
+static void cbForStateGoToRetry(u32 intType);
+static void cbForStateCheckID2a(u32 intType);
+static void cbForStateCheckID1(u32 intType);
+static void cbForStateCheckID2(u32 intType);
+static void cbForStateCheckID3(u32 intType);
+static void cbForStateCoverClosed(u32 intType);
+static void cbForStateMotorStopped(u32 intType);
+static void cbForStateBusy(u32 intType);
+static void cbForCancelStreamSync(s32 result, DVDCommandBlock* block);
+static void cbForCancelSync(s32 result, DVDCommandBlock* block);
+static void cbForCancelAllSync(s32 result, DVDCommandBlock* block);
+
+static void defaultOptionalCommandChecker(DVDCommandBlock* block, DVDLowCallback cb);
+
+static DVDOptionalCommandChecker checkOptionalCommand = defaultOptionalCommandChecker;
+
+extern void __DVDInterruptHandler(__OSInterrupt interrupt, OSContext* context);
+
+static void defaultOptionalCommandChecker(DVDCommandBlock* block, DVDLowCallback cb) {}
+
+void DVDInit() {
+ if (DVDInitialized) {
+ return;
+ }
+
+ OSRegisterVersion(__DVDVersion);
+ DVDInitialized = TRUE;
+ __DVDFSInit();
+ __DVDClearWaitingQueue();
+ __DVDInitWA();
+ bootInfo = (OSBootInfo*)OSPhysicalToCached(0x0000);
+ IDShouldBe = &(bootInfo->DVDDiskID);
+ __OSSetInterruptHandler(21, __DVDInterruptHandler);
+ __OSUnmaskInterrupts(0x400);
+ OSInitThreadQueue(&__DVDThreadQueue);
+ __DIRegs[0] = 0x2a;
+ __DIRegs[1] = 0;
+ if (bootInfo->magic == 0xE5207C22) {
+ OSReport("load fst\n");
+ __fstLoad();
+ } else if (bootInfo->magic != 0xD15EA5E) {
+ FirstTimeInBootrom = TRUE;
+ }
+}
+
+static void stateReadingFST() {
+ LastState = (stateFunc)stateReadingFST;
+
+ if (bootInfo->FSTMaxLength < BB2.FSTLength) {
+ OSPanic("dvd.c", 630, "DVDChangeDisk(): FST in the new disc is too big. ");
+ }
+
+ DVDLowRead(bootInfo->FSTLocation, OSRoundUp32B(BB2.FSTLength), BB2.FSTPosition,
+ cbForStateReadingFST);
+}
+
+static void cbForStateReadingFST(u32 intType) {
+ DVDCommandBlock* finished;
+
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if (intType & 1) {
+ NumInternalRetry = 0;
+ __DVDFSInit();
+ finished = executing;
+ executing = &DummyCommandBlock;
+ finished->state = 0;
+ if (finished->callback) {
+ (finished->callback)(0, finished);
+ }
+
+ stateReady();
+
+ } else {
+
+ stateGettingError();
+ }
+}
+
+inline static void stateError(u32 error) {
+ __DVDStoreErrorCode(error);
+ DVDLowStopMotor(cbForStateError);
+}
+
+static void cbForStateError(u32 intType) {
+ DVDCommandBlock* finished;
+
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ __DVDPrintFatalMessage();
+
+ FatalErrorFlag = TRUE;
+ finished = executing;
+ executing = &DummyCommandBlock;
+ if (finished->callback) {
+ (finished->callback)(-1, finished);
+ }
+
+ if (Canceling) {
+ Canceling = FALSE;
+ if (CancelCallback)
+ (CancelCallback)(0, finished);
+ }
+
+ stateReady();
+
+ return;
+}
+
+static void stateTimeout() {
+ __DVDStoreErrorCode(0x1234568);
+ DVDReset();
+ cbForStateError(0);
+}
+
+static void stateGettingError() { DVDLowRequestError(cbForStateGettingError); }
+
+static u32 CategorizeError(u32 error) {
+ if (error == 0x20400) {
+ LastError = error;
+ return 1;
+ }
+
+ error &= 0xffffff;
+
+ if ((error == 0x62800) || (error == 0x23a00) || (error == 0xb5a01)) {
+ return 0;
+ }
+
+ ++NumInternalRetry;
+ if (NumInternalRetry == 2) {
+ if (error == LastError) {
+ LastError = error;
+ return 1;
+ } else {
+ LastError = error;
+ return 2;
+ }
+ } else {
+ LastError = error;
+
+ if ((error == 0x31100) || (executing->command == 5)) {
+ return 2;
+ } else {
+ return 3;
+ }
+ }
+}
+
+inline static BOOL CheckCancel(u32 resume) {
+ DVDCommandBlock* finished;
+
+ if (Canceling) {
+ ResumeFromHere = resume;
+ Canceling = FALSE;
+
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 10;
+ if (finished->callback)
+ (*finished->callback)(-3, finished);
+ if (CancelCallback)
+ (CancelCallback)(0, finished);
+ stateReady();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void cbForStateGettingError(u32 intType) {
+ u32 error;
+ u32 status;
+ u32 errorCategory;
+ u32 resume;
+
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if (intType & 2) {
+ executing->state = -1;
+ stateError(0x1234567);
+ return;
+ }
+
+ error = __DIRegs[8];
+ status = error & 0xff000000;
+
+ errorCategory = CategorizeError(error);
+
+ if (errorCategory == 1) {
+ executing->state = -1;
+ stateError(error);
+ return;
+ }
+
+ if ((errorCategory == 2) || (errorCategory == 3)) {
+ resume = 0;
+ } else {
+ if (status == 0x01000000)
+ resume = 4;
+ else if (status == 0x02000000)
+ resume = 6;
+ else if (status == 0x03000000)
+ resume = 3;
+ else
+ resume = 5;
+ }
+
+ if (CheckCancel(resume))
+ return;
+
+ if (errorCategory == 2) {
+ __DVDStoreErrorCode(error);
+ stateGoToRetry();
+ return;
+ }
+
+ if (errorCategory == 3) {
+ if ((error & 0x00ffffff) == 0x00031100) {
+ DVDLowSeek(executing->offset, cbForUnrecoveredError);
+ } else {
+ LastState(executing);
+ }
+ return;
+ }
+
+ if (status == 0x01000000) {
+ executing->state = 5;
+ stateMotorStopped();
+ return;
+ } else if (status == 0x02000000) {
+ executing->state = 3;
+ stateCoverClosed();
+ return;
+ } else if (status == 0x03000000) {
+ executing->state = 4;
+ stateMotorStopped();
+ return;
+ } else {
+ executing->state = -1;
+ stateError(0x1234567);
+ return;
+ }
+}
+
+static void cbForUnrecoveredError(u32 intType) {
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if (intType & 1) {
+ stateGoToRetry();
+ return;
+ }
+
+ DVDLowRequestError(cbForUnrecoveredErrorRetry);
+}
+
+static void cbForUnrecoveredErrorRetry(u32 intType) {
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+ executing->state = -1;
+
+ if (intType & 2) {
+ __DVDStoreErrorCode(0x1234567);
+ DVDLowStopMotor(cbForStateError);
+ return;
+ }
+
+ __DVDStoreErrorCode(__DIRegs[8]);
+ DVDLowStopMotor(cbForStateError);
+}
+
+static void stateGoToRetry() { DVDLowStopMotor(cbForStateGoToRetry); }
+
+static void cbForStateGoToRetry(u32 intType) {
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if (intType & 2) {
+ executing->state = -1;
+ stateError(0x1234567);
+ return;
+ }
+
+ NumInternalRetry = 0;
+
+ if ((CurrCommand == 4) || (CurrCommand == 5) || (CurrCommand == 13) || (CurrCommand == 15)) {
+ ResetRequired = TRUE;
+ }
+
+ if (!CheckCancel(2)) {
+ executing->state = 11;
+ stateMotorStopped();
+ }
+}
+
+static void stateCheckID() {
+ switch (CurrCommand) {
+ case 3:
+ if (DVDCompareDiskID(&CurrDiskID, executing->id)) {
+ memcpy(IDShouldBe, &CurrDiskID, sizeof(DVDDiskID));
+
+ executing->state = 1;
+ DCInvalidateRange(&BB2, sizeof(DVDBB2));
+ LastState = stateCheckID2a;
+ stateCheckID2a(executing);
+ return;
+ } else {
+ DVDLowStopMotor(cbForStateCheckID1);
+ }
+ break;
+
+ default:
+ if (memcmp(&CurrDiskID, IDShouldBe, sizeof(DVDDiskID))) {
+ DVDLowStopMotor(cbForStateCheckID1);
+ } else {
+ LastState = stateCheckID3;
+ stateCheckID3(executing);
+ }
+ break;
+ }
+}
+
+static void stateCheckID3() {
+ DVDLowAudioBufferConfig(IDShouldBe->streaming, 10, cbForStateCheckID3);
+}
+
+static void stateCheckID2a() {
+ DVDLowAudioBufferConfig(IDShouldBe->streaming, 10, cbForStateCheckID2a);
+}
+
+static void cbForStateCheckID2a(u32 intType) {
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if (intType & 1) {
+ NumInternalRetry = 0;
+ stateCheckID2(executing);
+ return;
+ }
+
+ DVDLowRequestError(cbForStateGettingError);
+}
+
+static void stateCheckID2() {
+ DVDLowRead(&BB2, OSRoundUp32B(sizeof(BB2)), 0x420, cbForStateCheckID2);
+}
+
+static void cbForStateCheckID1(u32 intType) {
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if (intType & 2) {
+ executing->state = -1;
+ stateError(0x1234567);
+ return;
+ }
+
+ NumInternalRetry = 0;
+
+ if (!CheckCancel(1)) {
+ executing->state = 6;
+ stateMotorStopped();
+ }
+}
+
+static void cbForStateCheckID2(u32 intType) {
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if (intType & 1) {
+
+ NumInternalRetry = 0;
+
+ stateReadingFST();
+
+ } else {
+
+ stateGettingError();
+ }
+}
+
+static void cbForStateCheckID3(u32 intType) {
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if (intType & 1) {
+
+ NumInternalRetry = 0;
+
+ if (!CheckCancel(0)) {
+ executing->state = 1;
+ stateBusy(executing);
+ }
+ } else {
+ stateGettingError();
+ }
+}
+
+static void AlarmHandler(OSAlarm* alarm, OSContext* context) {
+ DVDReset();
+ DCInvalidateRange(&CurrDiskID, sizeof(DVDDiskID));
+ LastState = stateCoverClosed_CMD;
+ stateCoverClosed_CMD(executing);
+}
+
+static void stateCoverClosed() {
+ DVDCommandBlock* finished;
+
+ switch (CurrCommand) {
+ case 5:
+ case 4:
+ case 13:
+ case 15:
+ __DVDClearWaitingQueue();
+ finished = executing;
+ executing = &DummyCommandBlock;
+ if (finished->callback) {
+ (finished->callback)(-4, finished);
+ }
+ stateReady();
+ break;
+
+ default:
+ DVDReset();
+ OSCreateAlarm(&ResetAlarm);
+ OSSetAlarm(&ResetAlarm, OSMillisecondsToTicks(1150), AlarmHandler);
+ break;
+ }
+}
+
+static void stateCoverClosed_CMD(DVDCommandBlock* block) {
+ DVDLowReadDiskID(&CurrDiskID, cbForStateCoverClosed);
+}
+
+static void cbForStateCoverClosed(u32 intType) {
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if (intType & 1) {
+ NumInternalRetry = 0;
+ stateCheckID();
+ } else {
+ stateGettingError();
+ }
+}
+
+static void stateMotorStopped(void) { DVDLowWaitCoverClose(cbForStateMotorStopped); }
+
+static void cbForStateMotorStopped(u32 intType) {
+ __DIRegs[1] = 0;
+ executing->state = 3;
+ stateCoverClosed();
+}
+
+static void stateReady() {
+ DVDCommandBlock* finished;
+
+ if (!__DVDCheckWaitingQueue()) {
+ executing = (DVDCommandBlock*)NULL;
+ return;
+ }
+
+ if (PauseFlag) {
+ PausingFlag = TRUE;
+ executing = (DVDCommandBlock*)NULL;
+ return;
+ }
+
+ executing = __DVDPopWaitingQueue();
+
+ if (FatalErrorFlag) {
+ executing->state = -1;
+ finished = executing;
+ executing = &DummyCommandBlock;
+ if (finished->callback) {
+ (finished->callback)(-1, finished);
+ }
+ stateReady();
+ return;
+ }
+
+ CurrCommand = executing->command;
+
+ if (ResumeFromHere) {
+ switch (ResumeFromHere) {
+ case 1:
+ executing->state = 1;
+ stateCoverClosed();
+ break;
+ case 2:
+ executing->state = 11;
+ stateMotorStopped();
+ break;
+
+ case 3:
+ executing->state = 4;
+ stateMotorStopped();
+ break;
+
+ case 4:
+ executing->state = 5;
+ stateMotorStopped();
+ break;
+ case 7:
+ case 6:
+ executing->state = 3;
+ stateCoverClosed();
+ break;
+
+ case 5:
+ executing->state = -1;
+ stateError(CancelLastError);
+ break;
+ }
+
+ ResumeFromHere = 0;
+ } else {
+ executing->state = 1;
+ stateBusy(executing);
+ }
+}
+
+#define MIN(a, b) (((a) > (b)) ? (b) : (a))
+static void stateBusy(DVDCommandBlock* block) {
+ DVDCommandBlock* finished;
+ LastState = stateBusy;
+ switch (block->command) {
+ case 5:
+ __DIRegs[1] = __DIRegs[1];
+ block->currTransferSize = sizeof(DVDDiskID);
+ DVDLowReadDiskID(block->addr, cbForStateBusy);
+ break;
+ case 1:
+ case 4:
+ if (!block->length) {
+ finished = executing;
+ executing = &DummyCommandBlock;
+ finished->state = 0;
+ if (finished->callback) {
+ finished->callback(0, finished);
+ }
+ stateReady();
+ } else {
+ __DIRegs[1] = __DIRegs[1];
+ block->currTransferSize = MIN(block->length - block->transferredSize, 0x80000);
+ DVDLowRead((void*)((u8*)block->addr + block->transferredSize), block->currTransferSize,
+ block->offset + block->transferredSize, cbForStateBusy);
+ }
+ break;
+ case 2:
+ __DIRegs[1] = __DIRegs[1];
+ DVDLowSeek(block->offset, cbForStateBusy);
+ break;
+ case 3:
+ DVDLowStopMotor(cbForStateBusy);
+ break;
+ case 15:
+ DVDLowStopMotor(cbForStateBusy);
+ break;
+ case 6:
+ __DIRegs[1] = __DIRegs[1];
+ if (AutoFinishing) {
+ executing->currTransferSize = 0;
+ DVDLowRequestAudioStatus(0, cbForStateBusy);
+ } else {
+ executing->currTransferSize = 1;
+ DVDLowAudioStream(0, block->length, block->offset, cbForStateBusy);
+ }
+ break;
+ case 7:
+ __DIRegs[1] = __DIRegs[1];
+ DVDLowAudioStream(0x10000, 0, 0, cbForStateBusy);
+ break;
+ case 8:
+ __DIRegs[1] = __DIRegs[1];
+ AutoFinishing = TRUE;
+ DVDLowAudioStream(0, 0, 0, cbForStateBusy);
+ break;
+ case 9:
+ __DIRegs[1] = __DIRegs[1];
+ DVDLowRequestAudioStatus(0, cbForStateBusy);
+ break;
+ case 10:
+ __DIRegs[1] = __DIRegs[1];
+ DVDLowRequestAudioStatus(0x10000, cbForStateBusy);
+ break;
+ case 11:
+ __DIRegs[1] = __DIRegs[1];
+ DVDLowRequestAudioStatus(0x20000, cbForStateBusy);
+ break;
+ case 12:
+ __DIRegs[1] = __DIRegs[1];
+ DVDLowRequestAudioStatus(0x30000, cbForStateBusy);
+ break;
+ case 13:
+ __DIRegs[1] = __DIRegs[1];
+ DVDLowAudioBufferConfig(block->offset, block->length, cbForStateBusy);
+ break;
+ case 14:
+ __DIRegs[1] = __DIRegs[1];
+ block->currTransferSize = sizeof(DVDDriveInfo);
+ DVDLowInquiry(block->addr, cbForStateBusy);
+ break;
+ default:
+ checkOptionalCommand(block, cbForStateBusy);
+ break;
+ }
+}
+
+static u32 ImmCommand[] = {0xffffffff, 0xffffffff, 0xffffffff};
+/* Somehow this got included even though the function is stripped? O.o */
+static char string_DVDChangeDiskAsyncMsg[] =
+ "DVDChangeDiskAsync(): You can't specify NULL to company name. \n";
+static u32 DmaCommand[] = {0xffffffff};
+
+inline static BOOL IsImmCommandWithResult(u32 command) {
+ u32 i;
+
+ if (command == 9 || command == 10 || command == 11 || command == 12) {
+ return TRUE;
+ }
+
+ for (i = 0; i < sizeof(ImmCommand) / sizeof(ImmCommand[0]); i++) {
+ if (command == ImmCommand[i])
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+inline static BOOL IsDmaCommand(u32 command) {
+ u32 i;
+
+ if (command == 1 || command == 4 || command == 5 || command == 14)
+ return TRUE;
+
+ for (i = 0; i < sizeof(DmaCommand) / sizeof(DmaCommand[0]); i++) {
+ if (command == DmaCommand[i])
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void cbForStateBusy(u32 intType) {
+ DVDCommandBlock* finished;
+
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if ((CurrCommand == 3) || (CurrCommand == 15)) {
+ if (intType & 2) {
+ executing->state = -1;
+ stateError(0x1234567);
+ return;
+ }
+
+ NumInternalRetry = 0;
+
+ if (CurrCommand == 15) {
+ ResetRequired = TRUE;
+ }
+
+ if (CheckCancel(7)) {
+ return;
+ }
+
+ executing->state = 7;
+ stateMotorStopped();
+ return;
+ }
+
+ if (IsDmaCommand(CurrCommand)) {
+ executing->transferredSize += executing->currTransferSize - __DIRegs[6];
+ }
+
+ if (intType & 8) {
+ Canceling = FALSE;
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 10;
+ if (finished->callback)
+ (*finished->callback)(-3, finished);
+ if (CancelCallback)
+ (CancelCallback)(0, finished);
+ stateReady();
+
+ return;
+ }
+
+ if (intType & 1) {
+ NumInternalRetry = 0;
+
+ if (CheckCancel(0))
+ return;
+
+ if (IsDmaCommand(CurrCommand)) {
+ if (executing->transferredSize != executing->length) {
+ stateBusy(executing);
+ return;
+ }
+
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 0;
+ if (finished->callback) {
+ (finished->callback)((s32)finished->transferredSize, finished);
+ }
+ stateReady();
+ } else if (IsImmCommandWithResult(CurrCommand)) {
+ s32 result;
+
+ if ((CurrCommand == 11) || (CurrCommand == 10)) {
+ result = (s32)(__DIRegs[8] << 2);
+ } else {
+ result = (s32)__DIRegs[8];
+ }
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 0;
+ if (finished->callback) {
+ (finished->callback)(result, finished);
+ }
+ stateReady();
+ } else if (CurrCommand == 6) {
+ if (executing->currTransferSize == 0) {
+ if (__DIRegs[8] & 1) {
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 9;
+ if (finished->callback) {
+ (finished->callback)(-2, finished);
+ }
+ stateReady();
+ } else {
+ AutoFinishing = FALSE;
+ executing->currTransferSize = 1;
+ DVDLowAudioStream(0, executing->length, executing->offset, cbForStateBusy);
+ }
+ } else {
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 0;
+ if (finished->callback) {
+ (finished->callback)(0, finished);
+ }
+ stateReady();
+ }
+ } else {
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 0;
+ if (finished->callback) {
+ (finished->callback)(0, finished);
+ }
+ stateReady();
+ }
+ } else {
+ if (CurrCommand == 14) {
+ executing->state = -1;
+ stateError(0x01234567);
+ return;
+ }
+
+ if ((CurrCommand == 1 || CurrCommand == 4 || CurrCommand == 5 || CurrCommand == 14) &&
+ (executing->transferredSize == executing->length)) {
+ if (CheckCancel(0)) {
+ return;
+ }
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 0;
+ if (finished->callback) {
+ (finished->callback)((s32)finished->transferredSize, finished);
+ }
+ stateReady();
+ return;
+ }
+
+ stateGettingError();
+ }
+}
+
+static BOOL issueCommand(s32 prio, DVDCommandBlock* block) {
+ BOOL level;
+ BOOL result;
+
+ if (autoInvalidation &&
+ (block->command == 1 || block->command == 4 || block->command == 5 || block->command == 14)) {
+ DCInvalidateRange(block->addr, block->length);
+ }
+
+ level = OSDisableInterrupts();
+
+ block->state = 2;
+ result = __DVDPushWaitingQueue(prio, block);
+
+ if ((executing == (DVDCommandBlock*)NULL) && (PauseFlag == FALSE)) {
+ stateReady();
+ }
+
+ OSRestoreInterrupts(level);
+
+ return result;
+}
+
+BOOL DVDReadAbsAsyncPrio(DVDCommandBlock* block, void* addr, s32 length, s32 offset,
+ DVDCBCallback callback, s32 prio) {
+ BOOL idle;
+ block->command = 1;
+ block->addr = addr;
+ block->length = length;
+ block->offset = offset;
+ block->transferredSize = 0;
+ block->callback = callback;
+
+ idle = issueCommand(prio, block);
+ return idle;
+}
+BOOL DVDReadAbsAsyncForBS(DVDCommandBlock* block, void* addr, s32 length, s32 offset,
+ DVDCBCallback callback) {
+ BOOL idle;
+ block->command = 4;
+ block->addr = addr;
+ block->length = length;
+ block->offset = offset;
+ block->transferredSize = 0;
+ block->callback = callback;
+
+ idle = issueCommand(2, block);
+ return idle;
+}
+BOOL DVDReadDiskID(DVDCommandBlock* block, DVDDiskID* diskID, DVDCBCallback callback) {
+ BOOL idle;
+ block->command = 5;
+ block->addr = diskID;
+ block->length = sizeof(DVDDiskID);
+ ;
+ block->offset = 0;
+ block->transferredSize = 0;
+ block->callback = callback;
+
+ idle = issueCommand(2, block);
+ return idle;
+}
+BOOL DVDPrepareStreamAbsAsync(DVDCommandBlock* block, u32 length, u32 offset,
+ DVDCBCallback callback) {
+ BOOL idle;
+ block->command = 6;
+ block->length = length;
+ block->offset = offset;
+ block->callback = callback;
+
+ idle = issueCommand(1, block);
+ return idle;
+}
+BOOL DVDCancelStreamAsync(DVDCommandBlock* block, DVDCBCallback callback) {
+ BOOL idle;
+ block->command = 7;
+ block->callback = callback;
+ idle = issueCommand(1, block);
+ return idle;
+}
+s32 DVDCancelStream(DVDCommandBlock* block) {
+ BOOL result;
+ s32 state;
+ BOOL enabled;
+ s32 retVal;
+
+ result = DVDCancelStreamAsync(block, cbForCancelStreamSync);
+
+ if (result == FALSE) {
+ return -1;
+ }
+
+ enabled = OSDisableInterrupts();
+
+ while (TRUE) {
+ state = ((volatile DVDCommandBlock*)block)->state;
+
+ if (state == 0 || state == -1 || state == 10) {
+ retVal = (s32)block->transferredSize;
+ break;
+ }
+
+ OSSleepThread(&__DVDThreadQueue);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return retVal;
+}
+static void cbForCancelStreamSync(s32 result, DVDCommandBlock* block) {
+ block->transferredSize = (u32)result;
+ OSWakeupThread(&__DVDThreadQueue);
+}
+BOOL DVDStopStreamAtEndAsync(DVDCommandBlock* block, DVDCBCallback callback) {
+ BOOL idle;
+
+ block->command = 8;
+ block->callback = callback;
+
+ idle = issueCommand(1, block);
+
+ return idle;
+}
+BOOL DVDGetStreamErrorStatusAsync(DVDCommandBlock* block, DVDCBCallback callback) {
+ BOOL idle;
+
+ block->command = 9;
+ block->callback = callback;
+
+ idle = issueCommand(1, block);
+
+ return idle;
+}
+BOOL DVDGetStreamPlayAddrAsync(DVDCommandBlock* block, DVDCBCallback callback) {
+ BOOL idle;
+
+ block->command = 10;
+ block->callback = callback;
+
+ idle = issueCommand(1, block);
+
+ return idle;
+}
+BOOL DVDInquiryAsync(DVDCommandBlock* block, DVDDriveInfo* info, DVDCBCallback callback) {
+ BOOL idle;
+
+ block->command = 14;
+ block->addr = (void*)info;
+ block->length = sizeof(DVDDriveInfo);
+ block->transferredSize = 0;
+ block->callback = callback;
+
+ idle = issueCommand(2, block);
+
+ return idle;
+}
+
+void DVDReset(void) {
+ DVDLowReset();
+ __DIRegs[0] = 0x2a;
+ __DIRegs[1] = __DIRegs[1];
+ ResetRequired = FALSE;
+ ResumeFromHere = 0;
+}
+
+s32 DVDGetCommandBlockStatus(const DVDCommandBlock* block) {
+ BOOL enabled;
+ s32 retVal;
+
+ enabled = OSDisableInterrupts();
+
+ if (block->state == 3) {
+ retVal = 1;
+ } else {
+ retVal = block->state;
+ }
+
+ OSRestoreInterrupts(enabled);
+
+ return retVal;
+}
+
+s32 DVDGetDriveStatus() {
+ BOOL enabled;
+ s32 retVal;
+
+ enabled = OSDisableInterrupts();
+
+ if (FatalErrorFlag) {
+ retVal = -1;
+ } else if (PausingFlag) {
+ retVal = 8;
+ } else {
+ if (executing == (DVDCommandBlock*)NULL) {
+ retVal = 0;
+ } else if (executing == &DummyCommandBlock) {
+ retVal = 0;
+ } else {
+ retVal = DVDGetCommandBlockStatus(executing);
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+
+ return retVal;
+}
+
+BOOL DVDSetAutoInvalidation(BOOL autoInval) {
+ BOOL prev;
+ prev = autoInvalidation;
+ autoInvalidation = autoInval;
+ return prev;
+}
+
+inline void DVDPause(void) {
+ BOOL level;
+ level = OSDisableInterrupts();
+ PauseFlag = TRUE;
+ if (executing == (DVDCommandBlock*)NULL) {
+ PausingFlag = TRUE;
+ }
+ OSRestoreInterrupts(level);
+}
+
+inline void DVDResume(void) {
+ BOOL level;
+ level = OSDisableInterrupts();
+ PauseFlag = FALSE;
+ if (PausingFlag) {
+ PausingFlag = FALSE;
+ stateReady();
+ }
+ OSRestoreInterrupts(level);
+}
+
+BOOL DVDCancelAsync(DVDCommandBlock* block, DVDCBCallback callback) {
+ BOOL enabled;
+ DVDLowCallback old;
+
+ enabled = OSDisableInterrupts();
+
+ switch (block->state) {
+ case -1:
+ case 0:
+ case 10:
+ if (callback)
+ (*callback)(0, block);
+ break;
+
+ case 1:
+ if (Canceling) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ Canceling = TRUE;
+ CancelCallback = callback;
+ if (block->command == 4 || block->command == 1) {
+ DVDLowBreak();
+ }
+ break;
+
+ case 2:
+ __DVDDequeueWaitingQueue(block);
+ block->state = 10;
+ if (block->callback)
+ (block->callback)(-3, block);
+ if (callback)
+ (*callback)(0, block);
+ break;
+
+ case 3:
+ switch (block->command) {
+ case 5:
+ case 4:
+ case 13:
+ case 15:
+ if (callback)
+ (*callback)(0, block);
+ break;
+
+ default:
+ if (Canceling) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+ Canceling = TRUE;
+ CancelCallback = callback;
+ break;
+ }
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 11:
+ old = DVDLowClearCallback();
+ if (old != cbForStateMotorStopped) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ if (block->state == 4)
+ ResumeFromHere = 3;
+ if (block->state == 5)
+ ResumeFromHere = 4;
+ if (block->state == 6)
+ ResumeFromHere = 1;
+ if (block->state == 11)
+ ResumeFromHere = 2;
+ if (block->state == 7)
+ ResumeFromHere = 7;
+
+ block->state = 10;
+ if (block->callback) {
+ (block->callback)(-3, block);
+ }
+ if (callback) {
+ (callback)(0, block);
+ }
+ stateReady();
+ break;
+ }
+
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+}
+
+s32 DVDCancel(DVDCommandBlock* block) {
+ BOOL result;
+ s32 state;
+ u32 command;
+ BOOL enabled;
+
+ result = DVDCancelAsync(block, cbForCancelSync);
+
+ if (result == FALSE) {
+ return -1;
+ }
+
+ enabled = OSDisableInterrupts();
+
+ for (;;) {
+ state = ((volatile DVDCommandBlock*)block)->state;
+
+ if ((state == 0) || (state == -1) || (state == 10)) {
+ break;
+ }
+
+ if (state == 3) {
+ command = ((volatile DVDCommandBlock*)block)->command;
+
+ if ((command == 4) || (command == 5) || (command == 13) || (command == 15)) {
+ break;
+ }
+ }
+
+ OSSleepThread(&__DVDThreadQueue);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return 0;
+}
+
+static void cbForCancelSync(s32 result, DVDCommandBlock* block) {
+ OSWakeupThread(&__DVDThreadQueue);
+}
+
+inline BOOL DVDCancelAllAsync(DVDCBCallback callback) {
+ BOOL enabled;
+ DVDCommandBlock* p;
+ BOOL retVal;
+
+ enabled = OSDisableInterrupts();
+ DVDPause();
+
+ while ((p = __DVDPopWaitingQueue()) != 0) {
+ DVDCancelAsync(p, NULL);
+ }
+
+ if (executing)
+ retVal = DVDCancelAsync(executing, callback);
+ else {
+ retVal = TRUE;
+ if (callback)
+ (*callback)(0, NULL);
+ }
+
+ DVDResume();
+ OSRestoreInterrupts(enabled);
+ return retVal;
+}
+
+s32 DVDCancelAll(void) {
+ BOOL result;
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ CancelAllSyncComplete = FALSE;
+
+ result = DVDCancelAllAsync(cbForCancelAllSync);
+
+ if (result == FALSE) {
+ OSRestoreInterrupts(enabled);
+ return -1;
+ }
+
+ for (;;) {
+ if (CancelAllSyncComplete)
+ break;
+
+ OSSleepThread(&__DVDThreadQueue);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return 0;
+}
+
+static void cbForCancelAllSync(s32 result, DVDCommandBlock* block) {
+ CancelAllSyncComplete = TRUE;
+ OSWakeupThread(&__DVDThreadQueue);
+}
+
+DVDDiskID* DVDGetCurrentDiskID(void) { return (DVDDiskID*)OSPhysicalToCached(0); }
+BOOL DVDCheckDisk(void) {
+ BOOL enabled;
+ s32 retVal;
+ s32 state;
+ u32 coverReg;
+
+ enabled = OSDisableInterrupts();
+
+ if (FatalErrorFlag) {
+ state = -1;
+ } else if (PausingFlag) {
+ state = 8;
+ } else {
+ if (executing == (DVDCommandBlock*)NULL) {
+ state = 0;
+ } else if (executing == &DummyCommandBlock) {
+ state = 0;
+ } else {
+ state = executing->state;
+ }
+ }
+
+ switch (state) {
+ case 1:
+ case 9:
+ case 10:
+ case 2:
+ retVal = TRUE;
+ break;
+
+ case -1:
+ case 11:
+ case 7:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ retVal = FALSE;
+ break;
+
+ case 0:
+ case 8:
+ coverReg = __DIRegs[1];
+ if (((coverReg >> 2) & 1) || (coverReg & 1)) {
+ retVal = FALSE;
+ } else {
+ retVal = TRUE;
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+
+ return retVal;
+}
+
+void __DVDPrepareResetAsync(DVDCBCallback callback) {
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+
+ __DVDClearWaitingQueue();
+
+ if (Canceling) {
+ CancelCallback = callback;
+ } else {
+ if (executing) {
+ executing->callback = NULL;
+ }
+
+ DVDCancelAllAsync(callback);
+ }
+
+ OSRestoreInterrupts(enabled);
+}
diff --git a/src/Dolphin/dvd/dvderror.c b/src/Dolphin/dvd/dvderror.c
new file mode 100644
index 0000000..c984a8b
--- /dev/null
+++ b/src/Dolphin/dvd/dvderror.c
@@ -0,0 +1,56 @@
+#include "dolphin/DVDPriv.h"
+#include "dolphin/OSRtcPriv.h"
+
+static u32 ErrorTable[] = {
+ 0, 0x00023A00, 0x00062800, 0x00030200, 0x00031100, 0x00052000,
+ 0x00052001, 0x00052100, 0x00052400, 0x00052401, 0x00052402, 0x000B5A01,
+ 0x00056300, 0x00020401, 0x00020400, 0x00040800, 0x00100007, 0,
+};
+
+static u8 ErrorCode2Num(u32 errorCode) {
+ u32 i;
+
+ for (i = 0; i < sizeof(ErrorTable) / sizeof(ErrorTable[0]); i++) {
+ if (ErrorTable[i] == errorCode) {
+ return (u8)i;
+ }
+ }
+
+ if ((errorCode >= 0x00100000) && (errorCode <= 0x00100008)) {
+ return 17;
+ }
+
+ return 29;
+}
+
+static u8 Convert(u32 error) {
+ u32 statusCode;
+ u32 errorCode;
+ u8 errorNum;
+
+ if (error == 0x01234567)
+ return 255;
+
+ if (error == 0x01234568)
+ return 254;
+
+ statusCode = (error & 0xff000000) >> 24;
+ errorCode = error & 0x00ffffff;
+
+ errorNum = ErrorCode2Num(errorCode);
+ if (statusCode >= 6)
+ statusCode = 6;
+
+ return (u8)(statusCode * 30 + errorNum);
+}
+
+void __DVDStoreErrorCode(u32 error) {
+ OSSramEx* sram;
+ u8 num;
+
+ num = Convert(error);
+
+ sram = __OSLockSramEx();
+ sram->dvdErrorCode = num;
+ __OSUnlockSramEx(TRUE);
+}
diff --git a/src/Dolphin/dvd/dvdfatal.c b/src/Dolphin/dvd/dvdfatal.c
new file mode 100644
index 0000000..ea8093c
--- /dev/null
+++ b/src/Dolphin/dvd/dvdfatal.c
@@ -0,0 +1,136 @@
+#include "dolphin/DVDPriv.h"
+#include "dolphin/gx/GXStruct.h"
+#include "dolphin/os.h"
+#include "dolphin/vi.h"
+
+void __DVDPrintFatalMessage(void);
+
+static void (*FatalFunc)(void) = NULL;
+
+const char* Japanese = "\n\n\nエラーが発生しました。"
+ "\n\n本体のパワーボタンを押して電源をOFFにし、"
+ "\n本体の取扱説明書の指示に従ってください。";
+
+const char* English = "\n\n\nAn error has occurred."
+ "\nTurn the power off and refer to the"
+ "\nNintendo GameCube Instruction Booklet"
+ "\nfor further instructions.";
+
+const char* const Europe[] = {
+ // English
+ "\n\n\nAn error has occurred."
+ "\nTurn the power off and refer to the"
+ "\nNintendo GameCube""\x99"" Instruction Booklet"
+ "\nfor further instructions.",
+
+ // German
+ "\n\n\nEin Fehler ist aufgetreten."
+ "\nBitte schalten Sie den NINTENDO GAMECUBE"
+ "\naus und lesen Sie die Bedienungsanleitung,"
+ "\num weitere Informationen zu erhalten.",
+
+ // French
+ "\n\n\nUne erreur est survenue."
+ "\nEteignez la console et r" "\xe9" "f" "\xe9" "rez-vous au"
+ "\nmanuel d'instructions NINTENDO GAMECUBE"
+ "\npour de plus amples informations.",
+
+ // Spanish
+ "\n\n\nSe ha producido un error."
+ "\nApaga la consola y consulta el manual"
+ "\nde instrucciones de NINTENDO GAMECUBE"
+ "\npara obtener m""\xe1""s informaci""\xf3""n.",
+
+ // Italian
+ "\n\n\nSi \xe8 verificato un errore."
+ "\nSpegni (OFF) e controlla il manuale"
+ "\nd'istruzioni del NINTENDO GAMECUBE"
+ "\nper ulteriori indicazioni.",
+
+ // Dutch
+ "\n\n\nEr is een fout opgetreden."
+ "\nZet de NINTENDO GAMECUBE uit en"
+ "\nraadpleeg de handleiding van de"
+ "\nNintendo GameCube voor nadere"
+ "\ninstructies.",
+};
+
+static void ShowMessage(void) {
+ const char* message;
+ GXColor bg = {0, 0, 0, 0};
+ GXColor fg = {255, 255, 255, 0};
+
+ if (VIGetTvFormat() == VI_NTSC) {
+ if (OSGetFontEncode() == OS_FONT_ENCODE_SJIS) {
+ message = Japanese;
+ } else {
+ message = English;
+ }
+ } else {
+ message = Europe[OSGetLanguage()];
+ }
+
+ OSFatal(fg, bg, message);
+}
+
+#ifdef FULL_FRANK
+BOOL DVDSetAutoFatalMessaging(BOOL enable) {
+ BOOL enabled;
+ BOOL prev;
+
+ enabled = OSDisableInterrupts();
+ prev = FatalFunc ? TRUE : FALSE;
+ FatalFunc = enable ? ShowMessage : NULL;
+ OSRestoreInterrupts(enabled);
+ return prev;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm BOOL DVDSetAutoFatalMessaging(BOOL enable) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x18(r1)
+ stw r31, 0x14(r1)
+ stw r30, 0x10(r1)
+ mr r30, r3
+ bl OSDisableInterrupts
+ lwz r0, FatalFunc
+ cmplwi r0, 0
+ beq lbl_80374DFC
+ li r31, 1
+ b lbl_80374E00
+lbl_80374DFC:
+ li r31, 0
+lbl_80374E00:
+ cmpwi r30, 0
+ beq lbl_80374E14
+ lis r4, ShowMessage@ha
+ addi r0, r4, ShowMessage@l
+ b lbl_80374E18
+lbl_80374E14:
+ li r0, 0
+lbl_80374E18:
+ stw r0, FatalFunc
+ bl OSRestoreInterrupts
+ mr r3, r31
+ lwz r0, 0x1c(r1)
+ lwz r31, 0x14(r1)
+ lwz r30, 0x10(r1)
+ addi r1, r1, 0x18
+ mtlr r0
+ blr
+}
+#pragma pop
+/* clang-format off */
+#endif
+
+void __DVDPrintFatalMessage(void) {
+ if (!FatalFunc) {
+ return;
+ }
+ FatalFunc();
+}
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;
+}
diff --git a/src/Dolphin/dvd/dvdidutils.c b/src/Dolphin/dvd/dvdidutils.c
new file mode 100644
index 0000000..3c58e98
--- /dev/null
+++ b/src/Dolphin/dvd/dvdidutils.c
@@ -0,0 +1,27 @@
+#include <dolphin/DVDPriv.h>
+#include <dolphin/dvd.h>
+#include <dolphin/dvd_regs.h>
+
+#include <string.h>
+
+BOOL DVDCompareDiskID(DVDDiskID* id1, DVDDiskID* id2) {
+
+ if (id1->gameName[0] && id2->gameName[0] && strncmp(&id1->gameName[0], &id2->gameName[0], 4)) {
+ return FALSE;
+ }
+
+ if (!id1->company[0] || !id2->company[0] || strncmp(&id1->company[0], &id2->company[0], 2)) {
+ return FALSE;
+ }
+
+ if (id1->diskNumber != 0xff && id2->diskNumber != 0xff && id1->diskNumber != id2->diskNumber) {
+ return FALSE;
+ }
+
+ if (id1->gameVersion != 0xff && id2->gameVersion != 0xff &&
+ id1->gameVersion != id2->gameVersion) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/src/Dolphin/dvd/dvdlow.c b/src/Dolphin/dvd/dvdlow.c
new file mode 100644
index 0000000..1a939ba
--- /dev/null
+++ b/src/Dolphin/dvd/dvdlow.c
@@ -0,0 +1,109 @@
+#include "dolphin/DVDPriv.h"
+#include "dolphin/os.h"
+
+static BOOL FirstRead = TRUE;
+static volatile BOOL StopAtNextInt = FALSE;
+static u32 LastLength = 0;
+static DVDLowCallback Callback = NULL;
+static DVDLowCallback ResetCoverCallback = NULL;
+static OSTime LastResetEnd = 0;
+static BOOL ResetOccurred = FALSE;
+static volatile BOOL WaitingCoverClose = FALSE;
+static BOOL Breaking = FALSE;
+static u32 WorkAroundType = 0;
+static u32 WorkAroundSeekLocation = 0;
+static OSTime LastReadFinished = 0;
+static OSTime LastReadIssued = 0;
+static BOOL LastCommandWasRead = FALSE;
+static u32 NextCommandNumber = 0;
+
+typedef struct DVDUnk {
+ u32 _0;
+ u32 _4;
+ u32 _8;
+} DVDUnk;
+
+typedef struct DVDCommand {
+ s32 _0;
+ u32 _4;
+ u32 _8;
+ u32 _c;
+ DVDLowCallback callback;
+} DVDCommand;
+
+static DVDCommand CommandList[4];
+static OSAlarm AlarmForWA;
+static OSAlarm AlarmForTimeout;
+static OSAlarm AlarmForBreak;
+static DVDUnk Prev;
+static DVDUnk Cur;
+
+void __DVDInitWA() {
+ NextCommandNumber = 0;
+ CommandList[0]._0 = -1;
+ __DVDLowSetWAType(0, 0);
+ OSInitAlarm();
+}
+static void Read(u32 tmp1, u32 tmp2, u32 tmp3, DVDLowCallback tmp4);
+void __DVDInterruptHandler(__OSInterrupt interrupt, OSContext* context) {}
+
+static void AlarmHandler(OSAlarm* alarm, OSContext* context) {
+ DVDCommand* cmd;
+ cmd = &CommandList[NextCommandNumber];
+
+ if (cmd->_0 == 1) {
+ ++NextCommandNumber;
+ Read(cmd->_4, cmd->_8, cmd->_c, cmd->callback);
+ } else if (cmd->_0 == 2) {
+ ++NextCommandNumber;
+ DVDLowSeek(cmd->_c, cmd->callback);
+ }
+}
+
+static void AlarmHandlerForTimeout(OSAlarm* alarm, OSContext* context) {
+ OSContext tmpContext;
+ DVDLowCallback callback;
+ __OSMaskInterrupts(0x400);
+ OSClearContext(&tmpContext);
+ OSSetCurrentContext(&tmpContext);
+ callback = Callback;
+ Callback = NULL;
+ if (callback != NULL) {
+ callback(0x10);
+ }
+ OSClearContext(&tmpContext);
+ OSSetCurrentContext(context);
+}
+
+static void Read(u32 tmp1, u32 tmp2, u32 tmp3, DVDLowCallback tmp4) {
+
+}
+
+static void SeekTwiceBeforeRead() {
+
+}
+
+void DVDLowRead(u32 unk, DVDLowCallback callback) {
+
+}
+
+void DVDLowSeek(u32 offset, DVDLowCallback callback) {
+
+}
+
+BOOL DVDLowWaitCoverClose(DVDLowCallback callback) {
+ Callback = callback;
+ WaitingCoverClose = TRUE;
+ StopAtNextInt = FALSE;
+ __DIRegs[1] = 2;
+ return TRUE;
+}
+
+
+void __DVDLowSetWAType(u32 type, u32 location) {
+ BOOL enabled;
+ enabled = OSDisableInterrupts();
+ WorkAroundType = type;
+ WorkAroundSeekLocation = location;
+ OSRestoreInterrupts(enabled);
+}
diff --git a/src/Dolphin/dvd/dvdqueue.c b/src/Dolphin/dvd/dvdqueue.c
new file mode 100644
index 0000000..a381ccf
--- /dev/null
+++ b/src/Dolphin/dvd/dvdqueue.c
@@ -0,0 +1,142 @@
+#include "dolphin/DVDPriv.h"
+
+#define MAX_QUEUES 4
+typedef struct {
+ DVDCommandBlock* next;
+ DVDCommandBlock* prev;
+} DVDQueue;
+
+static DVDQueue WaitingQueue[MAX_QUEUES];
+
+void __DVDClearWaitingQueue(void) {
+ u32 i;
+
+ for (i = 0; i < MAX_QUEUES; i++) {
+ DVDCommandBlock* q;
+
+ q = (DVDCommandBlock*)&(WaitingQueue[i]);
+ q->next = q;
+ q->prev = q;
+ }
+}
+
+BOOL __DVDPushWaitingQueue(s32 prio, DVDCommandBlock* block) {
+ BOOL enabled;
+ DVDCommandBlock* q;
+
+ enabled = OSDisableInterrupts();
+
+ q = (DVDCommandBlock*)&(WaitingQueue[prio]);
+
+ q->prev->next = block;
+ block->prev = q->prev;
+ block->next = q;
+ q->prev = block;
+
+ OSRestoreInterrupts(enabled);
+
+ return TRUE;
+}
+
+static DVDCommandBlock* PopWaitingQueuePrio(s32 prio) {
+ DVDCommandBlock* tmp;
+ BOOL enabled;
+ DVDCommandBlock* q;
+
+ enabled = OSDisableInterrupts();
+
+ q = (DVDCommandBlock*)&(WaitingQueue[prio]);
+
+ tmp = q->next;
+ q->next = tmp->next;
+ tmp->next->prev = q;
+
+ OSRestoreInterrupts(enabled);
+
+ tmp->next = (DVDCommandBlock*)NULL;
+ tmp->prev = (DVDCommandBlock*)NULL;
+
+ return tmp;
+}
+
+DVDCommandBlock* __DVDPopWaitingQueue(void) {
+ u32 i;
+ BOOL enabled;
+ DVDCommandBlock* q;
+
+ enabled = OSDisableInterrupts();
+
+ for (i = 0; i < MAX_QUEUES; i++) {
+ q = (DVDCommandBlock*)&(WaitingQueue[i]);
+ if (q->next != q) {
+ OSRestoreInterrupts(enabled);
+ return PopWaitingQueuePrio((s32)i);
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+
+ return (DVDCommandBlock*)NULL;
+}
+
+BOOL __DVDCheckWaitingQueue(void) {
+ u32 i;
+ BOOL enabled;
+ DVDCommandBlock* q;
+
+ enabled = OSDisableInterrupts();
+
+ for (i = 0; i < MAX_QUEUES; i++) {
+ q = (DVDCommandBlock*)&(WaitingQueue[i]);
+ if (q->next != q) {
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+
+ return FALSE;
+}
+
+BOOL __DVDDequeueWaitingQueue(DVDCommandBlock* block) {
+ BOOL enabled;
+ DVDCommandBlock* prev;
+ DVDCommandBlock* next;
+
+ enabled = OSDisableInterrupts();
+
+ prev = block->prev;
+ next = block->next;
+
+ if ((prev == (DVDCommandBlock*)NULL) || (next == (DVDCommandBlock*)NULL)) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ prev->next = next;
+ next->prev = prev;
+
+ OSRestoreInterrupts(enabled);
+
+ return TRUE;
+}
+
+BOOL __DVDIsBlockInWaitingQueue(DVDCommandBlock* block) {
+ u32 i;
+ DVDCommandBlock* start;
+ DVDCommandBlock* q;
+
+ for (i = 0; i < MAX_QUEUES; i++) {
+ start = (DVDCommandBlock*)&(WaitingQueue[i]);
+
+ if (start->next != start) {
+ for (q = start->next; q != start; q = q->next) {
+ if (q == block)
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
diff --git a/src/Dolphin/dvd/fstload.c b/src/Dolphin/dvd/fstload.c
new file mode 100644
index 0000000..1f7a81b
--- /dev/null
+++ b/src/Dolphin/dvd/fstload.c
@@ -0,0 +1,67 @@
+#include <dolphin/DVDPriv.h>
+#include <dolphin/dvd.h>
+#include <dolphin/dvd_regs.h>
+#include <dolphin/os.h>
+#include <dolphin/os/OSBootInfo.h>
+#include <string.h>
+
+static s32 status = 0;
+
+static u8 bb2Buf[OSRoundUp32B(sizeof(DVDBB2)) + 31];
+static DVDBB2* bb2 = 0;
+static DVDDiskID* idTmp = NULL;
+
+static void cb(s32 result, DVDCommandBlock* block) {
+ if (result > 0) {
+ switch (status) {
+ case 0:
+ status = 1;
+ DVDReadAbsAsyncForBS(block, bb2, OSRoundUp32B(sizeof(bb2)), 0x420, cb);
+ break;
+ case 1:
+ status = 2;
+ DVDReadAbsAsyncForBS(block, bb2->FSTAddress, OSRoundUp32B(bb2->FSTLength), bb2->FSTPosition,
+ cb);
+ }
+ } else if (result == -1) {
+
+ } else if (result == -4) {
+ status = 0;
+ DVDReset();
+ DVDReadDiskID(block, idTmp, cb);
+ }
+}
+
+void __fstLoad() {
+ OSBootInfo* bootInfo;
+ DVDDiskID* id;
+ u8 idTmpBuf[sizeof(DVDDiskID) + 31];
+ static DVDCommandBlock block;
+ void* arenaHi;
+
+ arenaHi = OSGetArenaHi();
+ bootInfo = (OSBootInfo*)OSPhysicalToCached(0);
+
+ idTmp = (DVDDiskID*)(OSRoundUp32B(idTmpBuf));
+ bb2 = (DVDBB2*)(OSRoundUp32B(bb2Buf));
+
+ DVDReset();
+ DVDReadDiskID(&block, idTmp, cb);
+ while (DVDGetDriveStatus() != 0);
+
+ bootInfo->FSTLocation = bb2->FSTAddress;
+ bootInfo->FSTMaxLength = bb2->FSTMaxLength;
+
+ id = &bootInfo->DVDDiskID;
+
+ memcpy(id, idTmp, sizeof(DVDDiskID));
+ OSReport("\n");
+ OSReport(" Game Name ... %c%c%c%c\n", id->gameName[0], id->gameName[1], id->gameName[2],
+ id->gameName[3]);
+ OSReport(" Company ..... %c%c\n", id->company[0], id->company[1]);
+ OSReport(" Disk # ...... %d\n", id->diskNumber);
+ OSReport(" Game ver .... %d\n", id->gameVersion);
+ OSReport(" Streaming ... %s\n", (id->streaming == 0) ? "OFF" : "ON");
+ OSReport("\n");
+ OSSetArenaHi(bb2->FSTAddress);
+}