diff options
author | mrb0nk500 <b0nk@b0nk.xyz> | 2023-02-01 18:45:02 -0400 |
---|---|---|
committer | mrb0nk500 <b0nk@b0nk.xyz> | 2023-02-01 18:50:25 -0400 |
commit | 9fa0a7f1da1b70bee995f53c6c96c43189018772 (patch) | |
tree | 114548896790eaff23cdca84a025281de86bbb51 /src/Dolphin/dvd | |
parent | 2ba3289286bbfcf9fcc13fd135d976058d8b6c2e (diff) |
global: Import Dolphin SDK
This version comes from the Metroid Prime decompilation project.
https://github.com/PrimeDecomp/prime
Diffstat (limited to 'src/Dolphin/dvd')
-rw-r--r-- | src/Dolphin/dvd/dvd.c | 1389 | ||||
-rw-r--r-- | src/Dolphin/dvd/dvderror.c | 56 | ||||
-rw-r--r-- | src/Dolphin/dvd/dvdfatal.c | 136 | ||||
-rw-r--r-- | src/Dolphin/dvd/dvdfs.c | 656 | ||||
-rw-r--r-- | src/Dolphin/dvd/dvdidutils.c | 27 | ||||
-rw-r--r-- | src/Dolphin/dvd/dvdlow.c | 109 | ||||
-rw-r--r-- | src/Dolphin/dvd/dvdqueue.c | 142 | ||||
-rw-r--r-- | src/Dolphin/dvd/fstload.c | 67 |
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); +} |