diff options
| author | LagoLunatic <LagoLunatic@users.noreply.github.com> | 2025-03-16 21:52:11 -0400 |
|---|---|---|
| committer | LagoLunatic <LagoLunatic@users.noreply.github.com> | 2025-03-16 21:52:11 -0400 |
| commit | 6acb5f801bae8bec14b7c0d854b0363daa792d1e (patch) | |
| tree | c84a7e8ea6e596d014de7af359454b7e79344f11 /src/dolphin | |
| parent | c91ade372a9560eb511fc94369cbc229819f7ef6 (diff) | |
dolphin/dvd OK
Diffstat (limited to 'src/dolphin')
| -rw-r--r-- | src/dolphin/dvd/dvd.c | 1731 | ||||
| -rw-r--r-- | src/dolphin/dvd/dvdFatal.c | 94 | ||||
| -rw-r--r-- | src/dolphin/dvd/dvderror.c | 74 | ||||
| -rw-r--r-- | src/dolphin/dvd/dvdfs.c | 664 | ||||
| -rw-r--r-- | src/dolphin/dvd/dvdidutils.c | 95 | ||||
| -rw-r--r-- | src/dolphin/dvd/dvdlow.c | 544 | ||||
| -rw-r--r-- | src/dolphin/dvd/dvdqueue.c | 164 | ||||
| -rw-r--r-- | src/dolphin/dvd/fstload.c | 82 |
8 files changed, 3448 insertions, 0 deletions
diff --git a/src/dolphin/dvd/dvd.c b/src/dolphin/dvd/dvd.c new file mode 100644 index 00000000..61a5f255 --- /dev/null +++ b/src/dolphin/dvd/dvd.c @@ -0,0 +1,1731 @@ +#include "dolphin/dvd/dvd.h" +#include "dolphin/dvd/dvderror.h" +#include "dolphin/dvd/dvdfs.h" +#include "dolphin/dvd/dvdlow.h" +#include "dolphin/os/OS.h" + +const char* __DVDVersion = "<< Dolphin SDK - DVD\trelease build: Sep 5 2002 05:34:06 (0x2301) >>"; + +static DVDBB2 BB2; +static DVDDiskID CurrDiskID; +static DVDCommandBlock DummyCommandBlock; +static OSAlarm ResetAlarm; + +static DVDCommandBlock* executing; +static DVDDiskID* IDShouldBe; +static OSBootInfo* bootInfo; +static volatile int PauseFlag; +static volatile int PausingFlag; +static int AutoFinishing; +static BOOL FatalErrorFlag; +static volatile u32 CurrCommand; +static volatile u32 Canceling; +static void (*CancelCallback)(s32, DVDCommandBlock*); +static volatile u32 ResumeFromHere; +static volatile u32 CancelLastError; +static u32 LastError; +static volatile s32 NumInternalRetry; +static int ResetRequired; +static int CancelAllSyncComplete; +static volatile u32 ResetCount; +static BOOL FirstTimeInBootrom; +static u32 MotorState; +static int DVDInitialized; +void (*LastState)(DVDCommandBlock*); + +static int autoInvalidation = 1; + +static void defaultOptionalCommandChecker(DVDCommandBlock*, DVDCommandCheckerCallback); +static DVDCommandChecker checkOptionalCommand = defaultOptionalCommandChecker; + +static void stateReadingFST(); +static void cbForStateReadingFST(unsigned long intType); +static void stateError(); +static void stateGettingError(); +static unsigned long CategorizeError(unsigned long error); +static void cbForStateGettingError(unsigned long intType); +static void stateGoToRetry(); +static void cbForStateGoToRetry(unsigned long intType); +static void stateCheckID(); +static void stateCheckID3(); +static void stateCheckID2a(); +static void cbForStateCheckID1(unsigned long intType); +static void cbForStateCheckID2(unsigned long intType); +static void cbForStateCheckID3(unsigned long intType); +static void cbForStateCheckID2a(u32 intType); +static void stateCheckID2(); +static void stateCoverClosed(); +static void stateCoverClosed_CMD(DVDCommandBlock* command); +static void cbForStateCoverClosed(unsigned long intType); +static void stateMotorStopped(); +static void cbForStateMotorStopped(unsigned long intType); +static void stateReady(); +static void stateBusy(struct DVDCommandBlock * block); +static void cbForStateBusy(unsigned long intType); +static int issueCommand(long prio, struct DVDCommandBlock * block); +static void cbForCancelStreamSync(long result, struct DVDCommandBlock * block); +static void cbForStopStreamAtEndSync(long result, struct DVDCommandBlock *block); +static void cbForGetStreamErrorStatusSync(long result, struct DVDCommandBlock *block); +static void cbForGetStreamPlayAddrSync(long result, struct DVDCommandBlock *block); +static void cbForGetStreamStartAddrSync(long result, struct DVDCommandBlock *block); +static void cbForGetStreamLengthSync(long result, struct DVDCommandBlock *block); +static void cbForChangeDiskSync(); +static void cbForInquirySync(long result, struct DVDCommandBlock *block); +static void cbForCancelSync(); +static void cbForCancelAllSync(); +static void stateTimeout(void); +static void cbForUnrecoveredError(u32 intType); +static void cbForUnrecoveredErrorRetry(u32 intType); + +static void defaultOptionalCommandChecker(DVDCommandBlock*, DVDCommandCheckerCallback) {} + +void DVDInit() { + if (DVDInitialized == FALSE) { + OSRegisterVersion(__DVDVersion); + DVDInitialized = TRUE; + __DVDFSInit(); + __DVDClearWaitingQueue(); + __DVDInitWA(); + bootInfo = (void*)OSPhysicalToCached(0); + IDShouldBe = &bootInfo->disk_info; + __OSSetInterruptHandler(0x15, __DVDInterruptHandler); + __OSUnmaskInterrupts(0x400U); + OSInitThreadQueue(&__DVDThreadQueue); + __DIRegs[0] = 0x2A; + __DIRegs[1] = 0; + if (bootInfo->boot_code == 0xE5207C22) { + // OSReport("app booted via JTAG\n"); + OSReport("load fst\n"); + __fstLoad(); + } else if (bootInfo->boot_code == 0x0D15EA5E) { + // OSReport("app booted from bootrom\n"); + } else { + FirstTimeInBootrom = 1; + // OSReport("bootrom\n"); + } + } +} + +static void stateReadingFST() { + LastState = stateReadingFST; + ASSERTLINE(652, ((u32)(bootInfo->FSTLocation) & (32 - 1)) == 0); + DVD_ASSERTMSGLINE(630, bootInfo->fst_max_length >= BB2.FSTLength, "DVDChangeDisk(): FST in the new disc is too big. "); + DVDLowRead(bootInfo->fst_location, (u32)(BB2.FSTLength + 0x1F) & 0xFFFFFFE0, BB2.FSTPosition, cbForStateReadingFST); +} + +static u32 DmaCommand[1] = {0xFFFFFFFF}; + +static void cbForStateReadingFST(u32 intType) { + DVDCommandBlock* finished; + + if (intType == 16) { + executing->state = DVD_STATE_FATAL_ERROR; + stateTimeout(); + return; + } + + ASSERTLINE(680, (intType & DVD_INTTYPE_CVR) == 0); + + if (intType & DVD_INTTYPE_TC) { + ASSERTLINE(685, (intType & DVD_INTTYPE_DE) == 0); + NumInternalRetry = 0; + + __DVDFSInit(); + finished = executing; + executing = &DummyCommandBlock; + finished->state = DVD_STATE_END; + + if (finished->callback) { + finished->callback(0, finished); + } + + stateReady(); + return; + } + + ASSERTLINE(712, intType == DVD_INTTYPE_DE); + stateGettingError(); +} + +static void cbForStateError(u32 intType) { + DVDCommandBlock* finished; + + if (intType == 16) { + executing->state = DVD_STATE_FATAL_ERROR; + 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(); +} + +static void stateError(u32 intType) { + __DVDStoreErrorCode(intType); + DVDLowStopMotor(&cbForStateError); +} + +static void stateTimeout(void) { + __DVDStoreErrorCode(0x01234568); + DVDReset(); + cbForStateError(0); +} + +static void stateGettingError() { + DVDLowRequestError(cbForStateGettingError); +} + +static unsigned long CategorizeError(unsigned long error) { + if (error == 0x20400) { + LastError = error; + return 1; + } + + error &= 0x00FFFFFF; + if ((error == 0x62800) || (error == 0x23A00) || (error == 0xB5A01)) { + return 0; + } + NumInternalRetry += 1; + if (NumInternalRetry == 2) { + if (error == LastError) { + LastError = error; + return 1; + } + LastError = error; + return 2; + } + + LastError = error; + + if (error == 0x31100 || executing->command == DVD_COMMAND_READID) { + return 2; + } + + return 3; +} + +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 = DVD_STATE_FATAL_ERROR; + stateTimeout(); + return; + } + + if (intType & 2) { + executing->state = DVD_STATE_FATAL_ERROR; + stateError(0x1234567); + return; + } + + ASSERTLINE(0x35D, intType == DVD_INTTYPE_TC); + + error = __DIRegs[8]; + status = error & 0xff000000; + + errorCategory = CategorizeError(error); + + if (errorCategory == 1) { + executing->state = DVD_STATE_FATAL_ERROR; + 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 = DVD_STATE_FATAL_ERROR; + stateError(0x1234567); + return; + } +} + +static void cbForUnrecoveredError(u32 intType) +{ + if (intType == 16) { + executing->state = DVD_STATE_FATAL_ERROR; + stateTimeout(); + return; + } + + if (intType & 1) { + stateGoToRetry(); + return; + } + + ASSERTLINE(0x3C3, intType == DVD_INTTYPE_DE); + DVDLowRequestError(cbForUnrecoveredErrorRetry); +} + +static void cbForUnrecoveredErrorRetry(u32 intType) +{ + if (intType == 0x10) { + executing->state = DVD_STATE_FATAL_ERROR; +#ifdef DEBUG + DVDReset(); +#else + stateTimeout(); +#endif + } else { + executing->state = DVD_STATE_FATAL_ERROR; + + if ((intType & 2) != 0) { +#ifndef DEBUG + __DVDStoreErrorCode(0x01234567); +#endif + DVDLowStopMotor(cbForStateError); + return; + } + + stateError(__DIRegs[8]); + } +} + +static void stateGoToRetry() { + DVDLowStopMotor(cbForStateGoToRetry); +} + +static void cbForStateGoToRetry(u32 intType) +{ + if (intType == 16) { + executing->state = DVD_STATE_FATAL_ERROR; + stateTimeout(); + return; + } + + if (intType & 2) { + executing->state = DVD_STATE_FATAL_ERROR; + stateError(0x1234567); + return; + } + + ASSERTLINE(0x3F9, intType == DVD_INTTYPE_TC); + 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 DVD_COMMAND_CHANGE_DISK: + if (DVDCompareDiskID(&CurrDiskID, executing->disk_id)) { + memcpy(IDShouldBe, &CurrDiskID, sizeof(DVDDiskID)); + executing->state = DVD_STATE_BUSY; + DCInvalidateRange(&BB2.bootFilePosition, 0x20); + LastState = stateCheckID2a; + stateCheckID2a(executing); + } else { + DVDLowStopMotor(cbForStateCheckID1); + } + break; + default: + if (memcmp(&CurrDiskID, IDShouldBe, sizeof(DVDDiskID)) != 0) { + DVDLowStopMotor(cbForStateCheckID1); + } else { + LastState = stateCheckID3; + stateCheckID3(executing); + } + break; + } +} + +static void stateCheckID3() { + DVDLowAudioBufferConfig(IDShouldBe->is_streaming, 0xA, cbForStateCheckID3); +} + +static void stateCheckID2a() { + DVDLowAudioBufferConfig(IDShouldBe->is_streaming, 0xA, cbForStateCheckID2a); +} + +static void cbForStateCheckID2a(u32 intType) { + if (intType == 16) { + executing->state = DVD_STATE_FATAL_ERROR; + stateTimeout(); + return; + } + + ASSERTLINE(1227, (intType & DVD_INTTYPE_CVR) == 0); + + if (intType & DVD_INTTYPE_TC) { + ASSERTLINE(1232, (intType & DVD_INTTYPE_DE) == 0); + NumInternalRetry = 0; + stateCheckID2(executing); + return; + } + + ASSERTLINE(1243, intType == DVD_INTTYPE_DE); + stateGettingError(); +} + +static void stateCheckID2() { + DVDLowRead(&BB2, 0x20, 0x420, cbForStateCheckID2); +} + +static void cbForStateCheckID1(unsigned long intType) { + if (intType == 16) { + executing->state = DVD_STATE_FATAL_ERROR; + stateTimeout(); + return; + } + + if (intType & DVD_INTTYPE_DE) { + executing->state = DVD_STATE_FATAL_ERROR; + stateError(0x01234567); + return; + } + + ASSERTLINE(0x483, intType == DVD_INTTYPE_TC); + NumInternalRetry = 0; + + if (CheckCancel(1) == FALSE) { + executing->state = DVD_STATE_WRONG_DISK; + stateMotorStopped(); + } +} + +static void cbForStateCheckID2(unsigned long intType) { + if (intType == 16) { + executing->state = DVD_STATE_FATAL_ERROR; + stateTimeout(); + return; + } + + ASSERTLINE(0x499, (intType & DVD_INTTYPE_CVR) == 0); + if (intType & DVD_INTTYPE_TC) { + ASSERTLINE(0x49E, (intType & DVD_INTTYPE_DE) == 0); + NumInternalRetry = 0; + stateReadingFST(); + return; + } + ASSERTLINE(0x4AF, intType == DVD_INTTYPE_DE); + stateGettingError(); +} + +static void cbForStateCheckID3(unsigned long intType) { + if (intType == 16) { + executing->state = DVD_STATE_FATAL_ERROR; + stateTimeout(); + return; + } + + ASSERTLINE(0x4BF, (intType & DVD_INTTYPE_CVR) == 0); + if (intType & DVD_INTTYPE_TC) { + ASSERTLINE(0x4C4, (intType & DVD_INTTYPE_DE) == 0); + NumInternalRetry = 0; + if (CheckCancel(0) == FALSE) { + executing->state = DVD_STATE_BUSY; + stateBusy(executing); + } + return; + } + ASSERTLINE(0x4D2, intType == DVD_INTTYPE_DE); + stateGettingError(); +} + +static void AlarmHandler(OSAlarm* alarm, OSContext* context) { + DVDReset(); + DCInvalidateRange(&CurrDiskID, sizeof(DVDDiskID)); + LastState = &stateCoverClosed_CMD; + stateCoverClosed_CMD(executing); +} + +static void stateCoverClosed() { + struct DVDCommandBlock * finished; + + switch(CurrCommand) { + case DVD_COMMAND_BSREAD: + case DVD_COMMAND_READID: + case DVD_COMMAND_AUDIO_BUFFER_CONFIG: + case DVD_COMMAND_BS_CHANGE_DISK: + __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* command) { + DVDLowReadDiskID(&CurrDiskID, cbForStateCoverClosed); +} + +static void cbForStateCoverClosed(unsigned long intType) { + if (intType == 16) { + executing->state = DVD_STATE_FATAL_ERROR; + stateTimeout(); + return; + } + + ASSERTLINE(0x524, (intType & DVD_INTTYPE_CVR) == 0); + if (intType & DVD_INTTYPE_TC) { + ASSERTLINE(0x529, (intType & DVD_INTTYPE_DE) == 0); + NumInternalRetry = 0; + stateCheckID(); + return; + } + ASSERTLINE(0x535, intType == DVD_INTTYPE_DE); + stateGettingError(); +} + +static void stateMotorStopped() { + DVDLowWaitCoverClose(cbForStateMotorStopped); +} + +static void cbForStateMotorStopped(unsigned long intType) { + ASSERTLINE(0x552, intType == DVD_INTTYPE_CVR); + __DIRegs[1] = 0; + executing->state = DVD_STATE_COVER_CLOSED; + stateCoverClosed(); +} + +static void stateReady() { + if (__DVDCheckWaitingQueue() == 0) { + executing = NULL; + return; + } + if (PauseFlag != 0) { + PausingFlag = 1; + executing = NULL; + return; + } + executing = __DVDPopWaitingQueue(); + if (FatalErrorFlag) { + DVDCommandBlock* finished; + + executing->state = DVD_STATE_FATAL_ERROR; + finished = executing; + executing = &DummyCommandBlock; + if (finished->callback) { + (*finished->callback)(-1, finished); + } + + stateReady(); + return; + } + + CurrCommand = executing->command; + if (ResumeFromHere != 0) { + DVDCommandBlock* finished; + switch (ResumeFromHere) { + case 1: + executing->state = DVD_STATE_BUSY; + switch (CurrCommand) { + case 4: + case 5: + case 0xD: // DVD_COMMAND_AUDIO_BUFFER_CONFIG + case 0xF: // DVD_COMMAND_AUDIO_BUFFER_CONFIG + __DVDClearWaitingQueue(); + finished = executing; + executing = &DummyCommandBlock; + if (finished->callback) { + finished->callback(-4, finished); + } + stateReady(); + break; + default: + DVDReset(); + OSCreateAlarm(&ResetAlarm); + OSSetAlarm(&ResetAlarm, OSMillisecondsToTicks(1150), &AlarmHandler); + break; + } + break; + case 2: + executing->state = DVD_STATE_RETRY; + stateMotorStopped(); + break; + case 3: + executing->state = DVD_STATE_NO_DISK; + stateMotorStopped(); + break; + // case 7: + // executing->state = DVD_STATE_MOTOR_STOPPED; + // stateMotorStopped(); + // break; + case 4: + executing->state = DVD_STATE_COVER_OPEN; + stateMotorStopped(); + break; + case 6: + case 7: + executing->state = DVD_STATE_COVER_CLOSED; + stateCoverClosed(); + break; + case 5: + executing->state = DVD_STATE_FATAL_ERROR; + stateError(CancelLastError); + break; + } + ResumeFromHere = 0; + return; + } + executing->state = DVD_STATE_BUSY; + stateBusy(executing); +} + +static void stateBusy(struct DVDCommandBlock * block) { + DVDCommandBlock* finished; + LastState = stateBusy; + switch(block->command) { + case DVD_COMMAND_READID: + __DIRegs[1] = __DIRegs[1]; + block->current_transfer_size = 0x20; + DVDLowReadDiskID(block->buffer, cbForStateBusy); + return; + case DVD_COMMAND_READ: + case DVD_COMMAND_BSREAD: + if (block->length == 0) { + finished = executing; + executing = &DummyCommandBlock; + finished->state = 0; + + if (finished->callback != 0) { + (*finished->callback)(0, finished); + } + + stateReady(); + } else { + __DIRegs[1] = __DIRegs[1]; + block->current_transfer_size = (block->length - block->transferred_size > 0x80000) ? 0x80000 : (block->length - block->transferred_size); + DVDLowRead((char*)block->buffer + block->transferred_size, block->current_transfer_size, block->offset + block->transferred_size, cbForStateBusy); + } + return; + case DVD_COMMAND_SEEK: + __DIRegs[1] = __DIRegs[1]; + DVDLowSeek(block->offset, cbForStateBusy); + return; + case DVD_COMMAND_CHANGE_DISK: + DVDLowStopMotor(cbForStateBusy); + return; + case DVD_COMMAND_BS_CHANGE_DISK: + DVDLowStopMotor(cbForStateBusy); + return; + case DVD_COMMAND_INITSTREAM: + __DIRegs[1] = __DIRegs[1]; + if (AutoFinishing != 0) { + executing->current_transfer_size = 0; + DVDLowRequestAudioStatus(0, cbForStateBusy); + return; + } + executing->current_transfer_size = 1; + DVDLowAudioStream(0, block->length, block->offset, cbForStateBusy); + return; + case DVD_COMMAND_CANCELSTREAM: + __DIRegs[1] = __DIRegs[1]; + DVDLowAudioStream(0x10000, 0U, 0U, cbForStateBusy); + return; + case DVD_COMMAND_STOP_STREAM_AT_END: + __DIRegs[1] = __DIRegs[1]; + AutoFinishing = 1; + DVDLowAudioStream(0, 0U, 0U, cbForStateBusy); + return; + case DVD_COMMAND_REQUEST_AUDIO_ERROR: + __DIRegs[1] = __DIRegs[1]; + DVDLowRequestAudioStatus(0, cbForStateBusy); + return; + case DVD_COMMAND_REQUEST_PLAY_ADDR: + __DIRegs[1] = __DIRegs[1]; + DVDLowRequestAudioStatus(0x10000, cbForStateBusy); + return; + case DVD_COMMAND_REQUEST_START_ADDR: + __DIRegs[1] = __DIRegs[1]; + DVDLowRequestAudioStatus(0x20000, cbForStateBusy); + return; + case DVD_COMMAND_REQUEST_LENGTH: + __DIRegs[1] = __DIRegs[1]; + DVDLowRequestAudioStatus(0x30000, cbForStateBusy); + return; + case DVD_COMMAND_AUDIO_BUFFER_CONFIG: + __DIRegs[1] = __DIRegs[1]; + DVDLowAudioBufferConfig(block->offset, block->length, cbForStateBusy); + return; + case DVD_COMMAND_INQUIRY: + __DIRegs[1] = __DIRegs[1]; + block->current_transfer_size = 0x20; + DVDLowInquiry(block->buffer, cbForStateBusy); + return; + default: + checkOptionalCommand(block, cbForStateBusy); + return; + } +} + +static u32 ImmCommand[3] = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}; + +static BOOL IsImmCommandWithResult(u32 command) { + u32 i; + + if ((command == DVD_COMMAND_REQUEST_AUDIO_ERROR) || (command == DVD_COMMAND_REQUEST_PLAY_ADDR) + || (command == DVD_COMMAND_REQUEST_START_ADDR) || (command == DVD_COMMAND_REQUEST_LENGTH)) { + return 1; + } + + for (i = 0; i < 3; i++) { + if (command == ImmCommand[i]) { + return TRUE; + } + } + + return FALSE; +} + +static int IsDmaCommand(u32 command) { + if ((command == DVD_COMMAND_READ) || (command == DVD_COMMAND_BSREAD) + || (command == DVD_COMMAND_READID) || (command == DVD_COMMAND_INQUIRY)) { + return 1; + } + + if (command == DmaCommand[0]) { + return TRUE; + } + + return FALSE; +} + +static void cbForStateBusy(unsigned long intType) { + struct DVDCommandBlock * finished; + long result; + + if (intType == 16) { + executing->state = DVD_STATE_FATAL_ERROR; + stateTimeout(); + return; + } + + if ((CurrCommand == DVD_COMMAND_CHANGE_DISK) || (CurrCommand == DVD_COMMAND_BS_CHANGE_DISK)) { + if (intType & DVD_INTTYPE_DE) { + executing->state = DVD_STATE_FATAL_ERROR; + stateError(0x01234567); + return; + } + ASSERTLINE(0x675, intType == DVD_INTTYPE_TC); + NumInternalRetry = 0; + if (CurrCommand == DVD_COMMAND_BS_CHANGE_DISK) { + ResetRequired = 1; + } + + if (CheckCancel(7) == FALSE) { + executing->state = DVD_STATE_MOTOR_STOPPED; + stateMotorStopped(); + } + return; + } + ASSERTLINE(0x689, (intType & DVD_INTTYPE_CVR) == 0); + if (IsDmaCommand(CurrCommand)) { + executing->transferred_size += executing->current_transfer_size - __DIRegs[6]; + } + if (intType & 8) { + Canceling = 0; + finished = executing; + executing = &DummyCommandBlock; + finished->state = DVD_STATE_CANCELED; + if (finished->callback) { + finished->callback(-3, finished); + } + if (CancelCallback) { + CancelCallback(0, finished); + } + stateReady(); + return; + } + if (intType & 1) { + ASSERTLINE(0x6AF, (intType & DVD_INTTYPE_DE) == 0); + NumInternalRetry = 0; + if (CheckCancel(0) != FALSE) { + return; + } + + if (IsDmaCommand(CurrCommand)) { + if (executing->transferred_size != executing->length) { + stateBusy(executing); + return; + } + finished = executing; + executing = &DummyCommandBlock; + finished->state = DVD_STATE_END; + if (finished->callback) { + finished->callback(finished->transferred_size, finished); + } + stateReady(); + return; + } else if (IsImmCommandWithResult(CurrCommand)) { + + if (CurrCommand == DVD_COMMAND_REQUEST_START_ADDR || CurrCommand == DVD_COMMAND_REQUEST_PLAY_ADDR) { + result = __DIRegs[8] * 4; + } else { + result = __DIRegs[8]; + } + + finished = executing; + executing = &DummyCommandBlock; + finished->state = DVD_STATE_END; + if (finished->callback) { + finished->callback(result, finished); + } + stateReady(); + return; + } else if (CurrCommand == DVD_COMMAND_INITSTREAM) { + if (executing->current_transfer_size == 0) { + if (__DIRegs[8] & 1) { + finished = executing; + executing = &DummyCommandBlock; + finished->state = DVD_STATE_IGNORED; + if (finished->callback) { + finished->callback(-2, finished); + } + stateReady(); + return; + } + AutoFinishing = 0; + executing->current_transfer_size = 1; + DVDLowAudioStream(0, executing->length, executing->offset, cbForStateBusy); + return; + } + finished = executing; + executing = &DummyCommandBlock; + finished->state = DVD_STATE_END; + if (finished->callback) { + finished->callback(0, finished); + } + stateReady(); + return; + } else { + finished = executing; + executing = &DummyCommandBlock; + finished->state = DVD_STATE_END; + if (finished->callback) { + finished->callback(0, finished); + } + stateReady(); + return; + } + } else { + ASSERTLINE(0x72E, intType == DVD_INTTYPE_DE); + + if (CurrCommand == 14) { + executing->state = DVD_STATE_FATAL_ERROR; + stateError(0x01234567); + return; + } + + if ((CurrCommand == 1 || CurrCommand == 4 || CurrCommand == 5 || CurrCommand == 14) + && (executing->transferred_size == executing->length)) { + + if (CheckCancel(0)) { + return; + } + finished = executing; + executing = &DummyCommandBlock; + + finished->state = DVD_STATE_END; + if (finished->callback) { + (finished->callback)((s32)finished->transferred_size, finished); + } + stateReady(); + return; + } + + stateGettingError(); + } +} + +static int issueCommand(long prio, struct DVDCommandBlock * block) { + int level; + int result; + + if (autoInvalidation != 0 && (block->command == DVD_COMMAND_READ || block->command == DVD_COMMAND_BSREAD + || block->command == DVD_COMMAND_READID || block->command == DVD_COMMAND_INQUIRY)) { + DCInvalidateRange(block->buffer, block->length); + } + level = OSDisableInterrupts(); +#if DEBUG + if (executing == block || block->state == DVD_STATE_WAITING && __DVDIsBlockInWaitingQueue(block)) { + ASSERTMSGLINE(0x781, FALSE, "DVD library: Specified command block (or file info) is already in use\n"); + } +#endif + block->state = DVD_STATE_WAITING; + result = __DVDPushWaitingQueue(prio, block); + if (executing == NULL && PauseFlag == 0) { + stateReady(); + } + OSRestoreInterrupts(level); + return result; +} + +int DVDReadAbsAsyncPrio(struct DVDCommandBlock * block, void * addr, long length, long offset, void (* callback)(long, struct DVDCommandBlock *), long prio) { + int idle; + + ASSERTMSGLINE(0x7A9, block, "DVDReadAbsAsync(): null pointer is specified to command block address."); + ASSERTMSGLINE(0x7AA, addr, "DVDReadAbsAsync(): null pointer is specified to addr."); + ASSERTMSGLINE(0x7AC, !OFFSET(addr, 32), "DVDReadAbsAsync(): address must be aligned with 32 byte boundary."); + ASSERTMSGLINE(0x7AE, !(length & (32-1)), "DVDReadAbsAsync(): length must be a multiple of 32."); + ASSERTMSGLINE(0x7B0, !(offset & (4-1)), "DVDReadAbsAsync(): offset must be a multiple of 4."); + ASSERTMSGLINE(0x7B2, length > 0, "DVD read: 0 or negative value was specified to length of the read\n"); + block->command = DVD_COMMAND_READ; + block->buffer = addr; + block->length = length; + block->offset = offset; + block->transferred_size = 0; + block->callback = callback; + idle = issueCommand(prio, block); + ASSERTMSGLINE(0x7BC, idle, "DVDReadAbsAsync(): command block is used for processing previous request."); + return idle; +} + +int DVDSeekAbsAsyncPrio(struct DVDCommandBlock * block, long offset, void (* callback)(long, struct DVDCommandBlock *), long prio) { + int idle; + + ASSERTMSGLINE(0x7D3, block, "DVDSeekAbs(): null pointer is specified to command block address."); + ASSERTMSGLINE(0x7D5, !(offset & (4-1)), "DVDSeekAbs(): offset must be a multiple of 4."); + block->command = DVD_COMMAND_SEEK; + block->offset = offset; + block->callback = callback; + idle = issueCommand(prio, block); + ASSERTMSGLINE(0x7DC, idle, "DVDSeekAbs(): command block is used for processing previous request."); + return idle; +} + +int DVDReadAbsAsyncForBS(struct DVDCommandBlock * block, void * addr, long length, long offset, void (* callback)(long, struct DVDCommandBlock *)) { + int idle; + + ASSERTMSGLINE(0x7FA, block, "DVDReadAbsAsyncForBS(): null pointer is specified to command block address."); + ASSERTMSGLINE(0x7FB, addr, "DVDReadAbsAsyncForBS(): null pointer is specified to addr."); + ASSERTMSGLINE(0x7FD, !OFFSET(addr, 32), "DVDReadAbsAsyncForBS(): address must be aligned with 32 byte boundary."); + ASSERTMSGLINE(0x7FF, !(length & (32-1)), "DVDReadAbsAsyncForBS(): length must be a multiple of 32."); + ASSERTMSGLINE(0x801, !(offset & (4-1)), "DVDReadAbsAsyncForBS(): offset must be a multiple of 4."); + block->command = DVD_COMMAND_BSREAD; + block->buffer = addr; + block->length = length; + block->offset = offset; + block->transferred_size = 0; + block->callback = callback; + idle = issueCommand(2, block); + ASSERTMSGLINE(0x80B, idle, "DVDReadAbsAsyncForBS(): command block is used for processing previous request."); + return idle; +} + +int DVDReadDiskID(struct DVDCommandBlock * block, struct DVDDiskID * diskID, void (* callback)(long, struct DVDCommandBlock *)) { + int idle; + + ASSERTMSGLINE(0x822, block, "DVDReadDiskID(): null pointer is specified to command block address."); + ASSERTMSGLINE(0x823, diskID, "DVDReadDiskID(): null pointer is specified to id address."); + ASSERTMSGLINE(0x825, !OFFSET(diskID, 32), "DVDReadDiskID(): id must be aligned with 32 byte boundary."); + + block->command = DVD_COMMAND_READID; + block->buffer = diskID; + block->length = 0x20; + block->offset = 0; + block->transferred_size = 0; + block->callback = callback; + idle = issueCommand(2, block); + ASSERTMSGLINE(0x82F, idle, "DVDReadDiskID(): command block is used for processing previous request."); + return idle; +} + +int DVDPrepareStreamAbsAsync(struct DVDCommandBlock * block, unsigned long length, unsigned long offset, void (* callback)(long, struct DVDCommandBlock *)) { + int idle; + + block->command = DVD_COMMAND_INITSTREAM; + block->length = length; + block->offset = offset; + block->callback = callback; + idle = issueCommand(1, block); + return idle; +} + +int DVDCancelStreamAsync(struct DVDCommandBlock * block, void (* callback)(long, struct DVDCommandBlock *)) { + int idle; + + block->command = DVD_COMMAND_CANCELSTREAM; + block->callback = callback; + idle = issueCommand(1, block); + return idle; +} + +long DVDCancelStream(struct DVDCommandBlock * block) { + int result; + long state; + int enabled; + long retVal; + + result = DVDCancelStreamAsync(block, cbForCancelStreamSync); + if (result == 0) { + return -1; + } + enabled = OSDisableInterrupts(); + + while(1) { + state = block->state; + if (state == DVD_STATE_END || state == DVD_STATE_FATAL_ERROR || state == DVD_STATE_CANCELED) { + retVal = block->transferred_size; + break; + } + + OSSleepThread(&__DVDThreadQueue); + } + + OSRestoreInterrupts(enabled); + return retVal; +} + +static void cbForCancelStreamSync(long result, struct DVDCommandBlock * block) { + block->transferred_size = (u32)result; + OSWakeupThread(&__DVDThreadQueue); +} + +int DVDStopStreamAtEndAsync(struct DVDCommandBlock * block, void (* callback)(long, struct DVDCommandBlock *)) { + int idle; + + block->command = DVD_COMMAND_STOP_STREAM_AT_END; + block->callback = callback; + idle = issueCommand(1, block); + return idle; +} + +long DVDStopStreamAtEnd(struct DVDCommandBlock * block) { + int result; + long state; + int enabled; + long retVal; + + result = DVDStopStreamAtEndAsync(block, cbForStopStreamAtEndSync); + if (result == 0) { + return -1; + } + enabled = OSDisableInterrupts(); + + while(1) { + state = block->state; + if (state == DVD_STATE_END || state == DVD_STATE_FATAL_ERROR || state == DVD_STATE_CANCELED) { + retVal = block->transferred_size; + break; + } + + OSSleepThread(&__DVDThreadQueue); + } + OSRestoreInterrupts(enabled); + return retVal; +} + +static void cbForStopStreamAtEndSync(long result, struct DVDCommandBlock *block) { + block->transferred_size = (u32)result; + OSWakeupThread(&__DVDThreadQueue); +} + +int DVDGetStreamErrorStatusAsync(struct DVDCommandBlock * block, void (* callback)(long, struct DVDCommandBlock *)) { + int idle; + + block->command = DVD_COMMAND_REQUEST_AUDIO_ERROR; + block->callback = callback; + idle = issueCommand(1, block); + return idle; +} + +long DVDGetStreamErrorStatus(struct DVDCommandBlock * block) { + int result; + long state; + int enabled; + long retVal; + + result = DVDGetStreamErrorStatusAsync(block, cbForGetStreamErrorStatusSync); + if (result == 0) { + return -1; + } + enabled = OSDisableInterrupts(); + + while(1) { + state = block->state; + if (state == DVD_STATE_END || state == DVD_STATE_FATAL_ERROR || state == DVD_STATE_CANCELED) { + retVal = block->transferred_size; + break; + } + + OSSleepThread(&__DVDThreadQueue); + } + OSRestoreInterrupts(enabled); + return retVal; +} + +static void cbForGetStreamErrorStatusSync(long result, struct DVDCommandBlock *block) { + block->transferred_size = (u32)result; + OSWakeupThread(&__DVDThreadQueue); +} + +int DVDGetStreamPlayAddrAsync(struct DVDCommandBlock * block, void (* callback)(long, struct DVDCommandBlock *)) { + int idle; + + block->command = DVD_COMMAND_REQUEST_PLAY_ADDR; + block->callback = callback; + idle = issueCommand(1, block); + return idle; +} + +long DVDGetStreamPlayAddr(struct DVDCommandBlock * block) { + int result; + long state; + int enabled; + long retVal; + + result = DVDGetStreamPlayAddrAsync(block, cbForGetStreamPlayAddrSync); + if (result == 0) { + return -1; + } + enabled = OSDisableInterrupts(); + + while(1) { + state = block->state; + if (state == DVD_STATE_END || state == DVD_STATE_FATAL_ERROR || state == DVD_STATE_CANCELED) { + retVal = block->transferred_size; + break; + } + + OSSleepThread(&__DVDThreadQueue); + } + OSRestoreInterrupts(enabled); + return retVal; +} + +static void cbForGetStreamPlayAddrSync(long result, struct DVDCommandBlock *block) { + block->transferred_size = (u32)result; + OSWakeupThread(&__DVDThreadQueue); +} + +int DVDGetStreamStartAddrAsync(struct DVDCommandBlock * block, void (* callback)(long, struct DVDCommandBlock *)) { + int idle; + + block->command = DVD_COMMAND_REQUEST_START_ADDR; + block->callback = callback; + idle = issueCommand(1, block); + return idle; +} + +long DVDGetStreamStartAddr(struct DVDCommandBlock * block) { + int result; + long state; + int enabled; + long retVal; + + result = DVDGetStreamStartAddrAsync(block, cbForGetStreamStartAddrSync); + if (result == 0) { + return -1; + } + enabled = OSDisableInterrupts(); + + while(1) { + state = block->state; + if (state == DVD_STATE_END || state == DVD_STATE_FATAL_ERROR || state == DVD_STATE_CANCELED) { + retVal = block->transferred_size; + break; + } + + OSSleepThread(&__DVDThreadQueue); + } + OSRestoreInterrupts(enabled); + return retVal; +} + +static void cbForGetStreamStartAddrSync(long result, struct DVDCommandBlock *block) { + block->transferred_size = (u32)result; + OSWakeupThread(&__DVDThreadQueue); +} + +int DVDGetStreamLengthAsync(struct DVDCommandBlock * block, void (* callback)(long, struct DVDCommandBlock *)) { + int idle; + + block->command = DVD_COMMAND_REQUEST_LENGTH; + block->callback = callback; + idle = issueCommand(1, block); + return idle; +} + +long DVDGetStreamLength(struct DVDCommandBlock * block) { + int result; + long state; + int enabled; + long retVal; + + result = DVDGetStreamLengthAsync(block, cbForGetStreamLengthSync); + if (result == 0) { + return -1; + } + enabled = OSDisableInterrupts(); + + while(1) { + state = block->state; + if (state == DVD_STATE_END || state == DVD_STATE_FATAL_ERROR || state == DVD_STATE_CANCELED) { + retVal = block->transferred_size; + break; + } + + OSSleepThread(&__DVDThreadQueue); + } + OSRestoreInterrupts(enabled); + return retVal; +} + +static void cbForGetStreamLengthSync(long result, struct DVDCommandBlock *block) { + block->transferred_size = (u32)result; + OSWakeupThread(&__DVDThreadQueue); +} + +void __DVDAudioBufferConfig(struct DVDCommandBlock * block, unsigned long enable, unsigned long size, void (* callback)(long, struct DVDCommandBlock *)) { + int idle; + + block->command = DVD_COMMAND_AUDIO_BUFFER_CONFIG; + block->offset = enable; + block->length = size; + block->callback = callback; + idle = issueCommand(2, block); +} + +int DVDChangeDiskAsyncForBS(struct DVDCommandBlock * block, void (* callback)(long, struct DVDCommandBlock *)) { + int idle; + + ASSERTMSGLINE(0xA4F, block, "DVDChangeDiskAsyncForBS(): null pointer is specified to command block address."); + block->command = DVD_COMMAND_BS_CHANGE_DISK; + block->callback = callback; + idle = issueCommand(2, block); + ASSERTMSGLINE(0xA55, idle, "DVDChangeDiskAsyncForBS(): command block is used for processing previous request."); + return idle; +} + +int DVDChangeDiskAsync(DVDCommandBlock* block, DVDDiskID* id, DVDCBCallback callback) { + int idle; + + ASSERTMSGLINE(2896, block, "DVDChangeDisk(): null pointer is specified to command block address."); + ASSERTMSGLINE(2897, id, "DVDChangeDisk(): null pointer is specified to id address."); + + if (id->company[0] == 0) { + OSReport("DVDChangeDiskAsync(): You can't specify NULL to company name. \n"); + DVD_ASSERTMSGLINE(2902, 0, ""); + } + + block->command = DVD_COMMAND_CHANGE_DISK; + block->disk_id = id; + block->callback = callback; + DCInvalidateRange(bootInfo->fst_location, bootInfo->fst_max_length); + + idle = issueCommand(2, block); + ASSERTMSGLINE(2913, idle, "DVDChangeDisk(): command block is used for processing previous request."); + return idle; +} + +long DVDChangeDisk(struct DVDCommandBlock * block, struct DVDDiskID * id) { + int result; + long state; + int enabled; + long retVal; + + result = DVDChangeDiskAsync(block, id, cbForChangeDiskSync); + if (result == 0) { + return -1; + } + enabled = OSDisableInterrupts(); + while(1) { + state = block->state; + if (state == DVD_STATE_END) { + retVal = 0; + break; + } else if (state == DVD_STATE_FATAL_ERROR) { + retVal = -1; + break; + } else if (state == DVD_STATE_CANCELED) { + retVal = -3; + break; + } + OSSleepThread(&__DVDThreadQueue); + } + OSRestoreInterrupts(enabled); + return retVal; +} + +static void cbForChangeDiskSync() { + OSWakeupThread(&__DVDThreadQueue); +} + +int DVDInquiryAsync(struct DVDCommandBlock * block, struct DVDDriveInfo * info, void (* callback)(long, struct DVDCommandBlock *)) { + int idle; + + ASSERTMSGLINE(0xAC9, block, "DVDInquiry(): Null address was specified for block"); + ASSERTMSGLINE(0xACA, info, "DVDInquiry(): Null address was specified for info"); + ASSERTMSGLINE(0xACC, !OFFSET(info, 32), "DVDInquiry(): Address for info is not 32 bytes aligned"); + + block->command = 0xE; + block->buffer = info; + block->length = 0x20; + block->transferred_size = 0; + block->callback = callback; + idle = issueCommand(2, block); + return idle; +} + +long DVDInquiry(struct DVDCommandBlock * block, struct DVDDriveInfo * info) { + int result; + long state; + int enabled; + long retVal; + + result = DVDInquiryAsync(block, info, cbForInquirySync); + if (result == 0) { + return -1; + } + enabled = OSDisableInterrupts(); + while(1) { + state = block->state; + if (state == DVD_STATE_END) { + retVal = (u32)block->transferred_size; + break; + } else if (state == DVD_STATE_FATAL_ERROR) { + retVal = -1; + break; + } else if (state == DVD_STATE_CANCELED) { + retVal = -3; + break; + } + OSSleepThread(&__DVDThreadQueue); + } + OSRestoreInterrupts(enabled); + return retVal; +} + +static void cbForInquirySync(long result, struct DVDCommandBlock *block) { + OSWakeupThread(&__DVDThreadQueue); +} + +void DVDReset() { + DVDLowReset(); + __DIRegs[0] = 0x2A; + __DIRegs[1] = __DIRegs[1]; + ResetRequired = 0; + ResumeFromHere = 0; +} + +int DVDResetRequired() { + return ResetRequired; +} + +long DVDGetCommandBlockStatus(const struct DVDCommandBlock * block) { + BOOL enabled; + s32 retVal; + + ASSERTMSGLINE(0xB39, block, "DVDGetCommandBlockStatus(): null pointer is specified to command block address."); + enabled = OSDisableInterrupts(); + + if (block->state == 3) { + retVal = 1; + } else { + retVal = block->state; + } + + OSRestoreInterrupts(enabled); + return retVal; +} + +long DVDGetDriveStatus() { + BOOL enabled = OSDisableInterrupts(); + s32 retVal; + + if (FatalErrorFlag != FALSE) { + retVal = DVD_STATE_FATAL_ERROR; + } else { + if (PausingFlag != FALSE) { + retVal = DVD_STATE_PAUSING; + } else { + if (executing == NULL) { + retVal = DVD_STATE_END; + } else if (executing == &DummyCommandBlock) { + retVal = DVD_STATE_END; + } else { + retVal = DVDGetCommandBlockStatus((struct DVDCommandBlock*)executing); + } + } + } + OSRestoreInterrupts(enabled); + return retVal; +} + +int DVDSetAutoInvalidation(int autoInval) { + int prev; + + prev = autoInvalidation; + autoInvalidation = autoInval; + return prev; +} + +void DVDPause() { + int level; + + level = OSDisableInterrupts(); + PauseFlag = 1; + if (executing == NULL) { + PausingFlag = 1; + } + OSRestoreInterrupts(level); +} + +void DVDResume() { + int level; + + level = OSDisableInterrupts(); + PauseFlag = 0; + if (PausingFlag != 0) { + PausingFlag = 0; + stateReady(); + } + OSRestoreInterrupts(level); +} + +int DVDCancelAsync(struct DVDCommandBlock * block, void (* callback)(long, struct DVDCommandBlock *)) { + BOOL enabled; + DVDLowCallback old; + + enabled = OSDisableInterrupts(); + + switch (block->state) { + case DVD_STATE_FATAL_ERROR: + case DVD_STATE_END: + case DVD_STATE_CANCELED: + if (callback) + (*callback)(0, block); + break; + + case DVD_STATE_BUSY: + if (Canceling) { + OSRestoreInterrupts(enabled); + return FALSE; + } + + Canceling = TRUE; + CancelCallback = callback; + if (block->command == DVD_COMMAND_BSREAD || block->command == DVD_COMMAND_READ) { + DVDLowBreak(); + } + break; + + case DVD_STATE_WAITING: + __DVDDequeueWaitingQueue(block); + block->state = DVD_STATE_CANCELED; + if (block->callback) + (block->callback)(-3, block); + if (callback) + (*callback)(0, block); + break; + + case DVD_STATE_COVER_CLOSED: + switch (block->command) { + case DVD_COMMAND_READID: + case DVD_COMMAND_BSREAD: + case DVD_COMMAND_AUDIO_BUFFER_CONFIG: + case DVD_COMMAND_BS_CHANGE_DISK: + if (callback) + (*callback)(0, block); + break; + + default: + if (Canceling) { + OSRestoreInterrupts(enabled); + return FALSE; + } + Canceling = TRUE; + CancelCallback = callback; + break; + } + break; + + case DVD_STATE_NO_DISK: + case DVD_STATE_COVER_OPEN: + case DVD_STATE_WRONG_DISK: + case DVD_STATE_MOTOR_STOPPED: + case DVD_STATE_RETRY: + old = DVDLowClearCallback(); + ASSERTLINE(0xC13, old == cbForStateMotorStopped); + if (old != cbForStateMotorStopped) { + OSRestoreInterrupts(enabled); + return FALSE; + } + + if (block->state == DVD_STATE_NO_DISK) + ResumeFromHere = 3; + if (block->state == DVD_STATE_COVER_OPEN) + ResumeFromHere = 4; + if (block->state == DVD_STATE_WRONG_DISK) + ResumeFromHere = 1; + if (block->state == DVD_STATE_RETRY) + ResumeFromHere = 2; + if (block->state == DVD_STATE_MOTOR_STOPPED) + ResumeFromHere = 7; + + block->state = DVD_STATE_CANCELED; + if (block->callback) { + (block->callback)(-3, block); + } + if (callback) { + (callback)(0, block); + } + stateReady(); + break; + } + + OSRestoreInterrupts(enabled); + return TRUE; +} + +long DVDCancel(struct DVDCommandBlock * block) { + int result; + long state; + unsigned long command; + int enabled; + + result = DVDCancelAsync((void*)block, cbForCancelSync); + if (result == 0) { + return -1; + } + enabled = OSDisableInterrupts(); + while(1) { + state = block->state; + if (state == DVD_STATE_END || state == DVD_STATE_FATAL_ERROR || state == DVD_STATE_CANCELED) { + break; + } + if (state == DVD_STATE_COVER_CLOSED) { + command = block->command; + // ((command - DVD_COMMAND_BSREAD) <= 1) is a fakematch, correct line should be: + // if ((command == DVD_COMMAND_BSREAD) || (command == DVD_COMMAND_READID) || (command == DVD_COMMAND_AUDIO_BUFFER_CONFIG) || (command == DVD_COMMAND_BS_CHANGE_DISK)) { + if (((command - DVD_COMMAND_BSREAD) <= 1) || (command == DVD_COMMAND_AUDIO_BUFFER_CONFIG) || (command == DVD_COMMAND_BS_CHANGE_DISK)) { + break; + } + } + OSSleepThread(&__DVDThreadQueue); + } + OSRestoreInterrupts(enabled); + return 0; +} + +static void cbForCancelSync() { + OSWakeupThread(&__DVDThreadQueue); +} + +int DVDCancelAllAsync(void (* callback)(long, struct DVDCommandBlock *)) { + int enabled; + struct DVDCommandBlock * p; + int retVal; + + enabled = OSDisableInterrupts(); + DVDPause(); + while ((p = __DVDPopWaitingQueue())) { + DVDCancelAsync(p, NULL); + } + if (executing) { + retVal = DVDCancelAsync(executing, callback); + } else { + retVal = 1; + if (callback) { + callback(0, NULL); + } + } + DVDResume(); + OSRestoreInterrupts(enabled); + return retVal; +} + +long DVDCancelAll() { + int result; + int enabled; + + enabled = OSDisableInterrupts(); + CancelAllSyncComplete = 0; + result = DVDCancelAllAsync(cbForCancelAllSync); + if (result == 0) { + OSRestoreInterrupts(enabled); + return -1; + } + while(1) { + if (CancelAllSyncComplete == 0) { + OSSleepThread(&__DVDThreadQueue); + } else { + break; + } + } + OSRestoreInterrupts(enabled); + return 0; +} + +static void cbForCancelAllSync() { + CancelAllSyncComplete = 1; + OSWakeupThread(&__DVDThreadQueue); +} + +struct DVDDiskID * DVDGetCurrentDiskID() { + return (void*)OSPhysicalToCached(0); +} + +BOOL DVDCheckDisk() +{ + BOOL enabled; + s32 retVal; + s32 state; + u32 coverReg; + + enabled = OSDisableInterrupts(); + + if (FatalErrorFlag) { + state = -1; + } else if (PausingFlag) { + state = 8; + } else { + if (executing == NULL) { + state = 0; + } else if (executing == &DummyCommandBlock) { + state = 0; + } else { + state = executing->state; + } + } + + switch (state) { + case DVD_STATE_BUSY: + case DVD_STATE_IGNORED: + case DVD_STATE_CANCELED: + case DVD_STATE_WAITING: + retVal = TRUE; + break; + + case DVD_STATE_FATAL_ERROR: + case DVD_STATE_RETRY: + case DVD_STATE_MOTOR_STOPPED: + case DVD_STATE_COVER_CLOSED: + case DVD_STATE_NO_DISK: + case DVD_STATE_COVER_OPEN: + case DVD_STATE_WRONG_DISK: + retVal = FALSE; + break; + + case DVD_STATE_END: + case DVD_STATE_PAUSING: + 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/dvdFatal.c b/src/dolphin/dvd/dvdFatal.c new file mode 100644 index 00000000..35c6527d --- /dev/null +++ b/src/dolphin/dvd/dvdFatal.c @@ -0,0 +1,94 @@ +#include "dolphin/dvd/dvdFatal.h" +#include "dolphin/gx/GXStruct.h" +#include "dolphin/os/OS.h" + +static void (*FatalFunc)(); + +const char* Japanese = + "\n\n\nエラーが発生しました。\n\n" + "本体のパワーボタンを押して電源をOFFにし、\n" + "本体の取扱説明書の指示に従ってください。"; + +const char* English = + "\n\n\nAn error has occurred.\n" + "Turn the power off and refer to the\n" + "Nintendo GameCube Instruction Booklet\n" + "for further instructions."; + +// TODO: need solution to compile special characters in a cleaner way +const char* const Europe[6] = { + { + "\n\n\nAn error has occurred.\n" + "Turn the power off and refer to the\n" + "Nintendo GameCube Instruction Booklet\n" + "for further instructions." + }, + { + "\n\n\nEin Fehler ist aufgetreten.\n" + "Bitte schalten Sie den Nintendo GameCube\n" + "aus und lesen Sie die Bedienungsanleitung,\n" + "um weitere Informationen zu erhalten." + }, + { + "\n\n\nUne erreur est survenue.\n" + "Eteignez la console et r\xE9" "f" "\xE9rez-vous au\n" + "manuel d'instructions Nintendo GameCube\n" + "pour de plus amples informations." + }, + { + "\n\n\nSe ha producido un error.\n" + "Apaga la consola y consulta el manual\n" + "de instrucciones de Nintendo GameCube\n" + "para obtener m\xE1" "s informaci\xF3" "n." + }, + { + "\n\n\nSi \xE8" " verificato un errore.\n" + "Spegni (OFF) e controlla il manuale\n" + "d'istruzioni del Nintendo GameCube\n" + "per ulteriori indicazioni." + }, + { + "\n\n\nEr is een fout opgetreden.\n" + "Zet de Nintendo GameCube uit en\n" + "raadpleeg de handleiding van de\n" + "Nintendo GameCube voor nadere\n" + "instructies." + }, +}; + +static void ShowMessage(void) { + const char* message; + GXColor bg = {0x00, 0x00, 0x00, 0x00}; + GXColor fg = {0xFF, 0xFF, 0xFF, 0x00}; + + if (VIGetTvFormat() == VI_NTSC) { + if (OSGetFontEncode() == OS_FONT_ENCODE_SJIS) { + message = Japanese; + } else { + message = English; + } + } else { + message = Europe[OSGetLanguage()]; + } + + OSFatal(fg, bg, message); +} + +int DVDSetAutoFatalMessaging(BOOL enable) { + BOOL enabled; + int prev; + + enabled = OSDisableInterrupts(); + + prev = FatalFunc ? 1 : 0; + FatalFunc = enable ? ShowMessage : NULL; + + OSRestoreInterrupts(enabled); + return prev; +} + +void __DVDPrintFatalMessage(void) { + if (FatalFunc) { + FatalFunc(); + } +} diff --git a/src/dolphin/dvd/dvderror.c b/src/dolphin/dvd/dvderror.c new file mode 100644 index 00000000..a44c372d --- /dev/null +++ b/src/dolphin/dvd/dvderror.c @@ -0,0 +1,74 @@ +#include "dolphin/dvd/dvderror.h" +#include "dolphin/os/OS.h" + +static u32 ErrorTable[18] = { + 0x00000000, + 0x00023A00, + 0x00062800, + 0x00030200, + 0x00031100, + 0x00052000, + 0x00052001, + 0x00052100, + 0x00052400, + 0x00052401, + 0x00052402, + 0x000B5A01, + 0x00056300, + 0x00020401, + 0x00020400, + 0x00040800, + 0x00100007, + 0x00000000, +}; + +#define DIDNT_MATCH 29 + +static u8 ErrorCode2Num(u32 errorCode) { + u32 i; + + for (i = 0; i < 18; i++) { + if (errorCode == ErrorTable[i]) { + ASSERTLINE(0x49, i < DIDNT_MATCH); + return i; + } + } + + if (errorCode >= 0x100000 && errorCode <= 0x100008) { + return 17; + } + + return DIDNT_MATCH; +} + +static u8 Convert(u32 error) { + u32 statusCode; + u32 errorCode; + u8 errorNum; + + if (error == 0x01234567) { + return -1; + } else if (error == 0x01234568) { + return -2; + } + + statusCode = (error >> 24) & 0xFF; + errorCode = error & 0x00FFFFFF; + errorNum = ErrorCode2Num(errorCode); + if (statusCode >= 6) { + statusCode = 6; + } + + return 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/dvdfs.c b/src/dolphin/dvd/dvdfs.c new file mode 100644 index 00000000..8ccb9f22 --- /dev/null +++ b/src/dolphin/dvd/dvdfs.c @@ -0,0 +1,664 @@ +#include "dolphin/dvd/dvdfs.h" +#include "dolphin/dvd/dvdlow.h" +#include "dolphin/os/OS.h" + +struct FSTEntry { + /* 0x00 */ u32 isDirAndStringOff; + /* 0x04 */ u32 parentOrPosition; + /* 0x08 */ u32 nextEntryOrLength; +}; + +// .sbss +static struct OSBootInfo * BootInfo; // size: 0x4, address: 0x0 +static struct FSTEntry * FstStart; // size: 0x4, address: 0x4 +static char * FstStringStart; // size: 0x4, address: 0x8 +static unsigned long MaxEntryNum; // size: 0x4, address: 0xC +static unsigned long currentDirectory; // size: 0x4, address: 0x10 +struct OSThreadQueue __DVDThreadQueue; // size: 0x8, address: 0x18 +unsigned long __DVDLongFileNameFlag; // size: 0x4, address: 0x14 + +// functions +static BOOL isSame(const char* path, const char* string); +static u32 myStrncpy(char* dest, char* src, u32 maxlen); +static u32 entryToPath(u32 entry, char* path, u32 maxlen); +static BOOL DVDConvertEntrynumToPath(s32 entrynum, char* path, u32 maxlen); +static void cbForReadAsync(s32 result, DVDCommandBlock* block); +static void cbForReadSync(); +static void cbForSeekAsync(long result, struct DVDCommandBlock * block); +static void cbForSeekSync(); +static void cbForPrepareStreamAsync(s32 result, DVDCommandBlock* block); +static void cbForPrepareStreamSync(); + +void __DVDFSInit() { + BootInfo = (void*)OSPhysicalToCached(0); + FstStart = BootInfo->fst_location; + if (FstStart) { + MaxEntryNum = FstStart->nextEntryOrLength; + FstStringStart = (char*)FstStart + (MaxEntryNum * 0xC); + } +} + +/* 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; +} + +int DVDConvertPathToEntrynum(const 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; + + ASSERTMSGLINE(0x133, pathPtr, "DVDConvertPathToEntrynum(): null pointer is specified "); + + 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__, 0x17B, + "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) { + ASSERTMSGLINE(0x1BC, fileInfo, "DVDFastOpen(): null pointer is specified to file info address "); + ASSERTMSG1LINE(0x1BF, (entrynum >= 0) && ((u32) entrynum < (u32) MaxEntryNum), "DVDFastOpen(): specified entry number '%d' is out of range ", entrynum); + ASSERTMSG1LINE(0x1C2, !entryIsDir(entrynum), "DVDFastOpen(): entry number '%d' is assigned to a directory ", entrynum); + + if ((entrynum < 0) || (entrynum >= MaxEntryNum) || entryIsDir(entrynum)) { + return FALSE; + } + + fileInfo->start_address = filePosition(entrynum); + fileInfo->length = fileLength(entrynum); + fileInfo->callback = (DVDCallback)NULL; + fileInfo->block.state = DVD_STATE_END; + + return TRUE; +} + +BOOL DVDOpen(const char* fileName, DVDFileInfo* fileInfo) { + s32 entry; + char currentDir[128]; + + ASSERTMSGLINE(0x1E0, fileName, "DVDOpen(): null pointer is specified to file name "); + ASSERTMSGLINE(0x1E1, fileInfo, "DVDOpen(): null pointer is specified to file info address "); + + 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)) { + ASSERTMSG1LINE(0x1EF, !entryIsDir(entry), "DVDOpen(): directory '%s' is specified as a filename ", fileName); + return FALSE; + } + + fileInfo->start_address = filePosition(entry); + fileInfo->length = fileLength(entry); + fileInfo->callback = (DVDCallback)NULL; + fileInfo->block.state = DVD_STATE_END; + + return TRUE; +} + +BOOL DVDClose(DVDFileInfo* fileInfo) { + ASSERTMSGLINE(0x207, fileInfo, "DVDClose(): null pointer is specified to file info address "); + DVDCancel(&(fileInfo->block)); + return TRUE; +} + +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; + + ASSERTMSG1LINE(0x263, (entrynum >= 0) && (entrynum < MaxEntryNum), "DVDConvertEntrynumToPath: specified entrynum(%d) is out of range ", entrynum); + ASSERTMSG1LINE(0x265, maxlen > 1, "DVDConvertEntrynumToPath: maxlen should be more than 1 (%d is specified)", maxlen); + ASSERTMSGLINE(0x26A, entryIsDir(entrynum), "DVDConvertEntrynumToPath: cannot convert an entry num for a file to path "); + + 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) { + ASSERTMSG1LINE(0x294, (maxlen > 1), "DVDGetCurrentDir: maxlen should be more than 1 (%d is specified)", maxlen); + return DVDConvertEntrynumToPath((s32)currentDirectory, path, maxlen); +} + +BOOL DVDChangeDir(const char* dirName) { + s32 entry; + char currentDir[128]; + + ASSERTMSGLINE(0x2AA, dirName, "DVDChangeDir(): null pointer is specified to directory name "); + entry = DVDConvertPathToEntrynum(dirName); + ASSERTMSG2LINE(0x2B2, entry >= 0, "DVDChangeDir(): directory '%s' is not found under %s ", dirName, (DVDGetCurrentDir(currentDir, 128), currentDir)); + ASSERTMSG1LINE(0x2B6, entryIsDir(entry), "DVDChangeDir(): file '%s' is specified as a directory name ", 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) { + + ASSERTMSGLINE(0x2D5, fileInfo, "DVDReadAsync(): null pointer is specified to file info address "); + ASSERTMSGLINE(0x2D6, addr, "DVDReadAsync(): null pointer is specified to addr "); + ASSERTMSGLINE(0x2DA, !OFFSET(addr, 32), "DVDReadAsync(): address must be aligned with 32 byte boundaries "); + ASSERTMSGLINE(0x2DC, !(length & 0x1F), "DVDReadAsync(): length must be multiple of 32 byte "); + ASSERTMSGLINE(0x2DE, !(offset & 3), "DVDReadAsync(): offset must be multiple of 4 byte "); + + if (!((0 <= offset) && (offset < fileInfo->length))) { + OSPanic(__FILE__, 0x2E6, "DVDReadAsync(): specified area is out of the file "); + } + + if (!((0 <= offset + length) && (offset + length < fileInfo->length + DVD_MIN_TRANSFER_SIZE))) { + OSPanic(__FILE__, 0x2EC, "DVDReadAsync(): specified area is out of the file "); + } + + fileInfo->callback = callback; + DVDReadAbsAsyncPrio(&(fileInfo->block), addr, length, (s32)(fileInfo->start_address + 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, block)); + ASSERTLINE(0x2FB, (void*) &fileInfo->block == (void*) block); + if (fileInfo->callback) { + (fileInfo->callback)(result, fileInfo); + } +} + +int DVDReadPrio(struct DVDFileInfo * fileInfo, void * addr, long length, long offset, long prio) { + int result; + struct DVDCommandBlock * block; + long state; + int enabled; + long retVal; + + ASSERTMSGLINE(0x31B, fileInfo, "DVDRead(): null pointer is specified to file info address "); + ASSERTMSGLINE(0x31C, addr, "DVDRead(): null pointer is specified to addr "); + ASSERTMSGLINE(0x320, !OFFSET(addr, 32), "DVDRead(): address must be aligned with 32 byte boundaries "); + ASSERTMSGLINE(0x322, !(length & 0x1F), "DVDRead(): length must be multiple of 32 byte "); + ASSERTMSGLINE(0x324, !(offset & 3), "DVDRead(): offset must be multiple of 4 byte "); + + if (!((0 <= offset) && (offset < fileInfo->length))) { + OSPanic(__FILE__, 0x32C, "DVDRead(): specified area is out of the file "); + } + + if (!((0 <= offset + length) && (offset + length < fileInfo->length + DVD_MIN_TRANSFER_SIZE))) { + OSPanic(__FILE__, 0x332, "DVDRead(): specified area is out of the file "); + } + + block = &fileInfo->block; + result = DVDReadAbsAsyncPrio(block, addr, length, fileInfo->start_address + offset, cbForReadSync, prio); + if (result == 0) { + return -1; + } + enabled = OSDisableInterrupts(); + + while(1) { + state = ((volatile DVDCommandBlock*)block)->state; + if (state == 0) { + retVal = (s32)block->transferred_size; + break; + } else if (state == -1) { + retVal = -1; + break; + } else if (state == 10) { + retVal = -3; + break; + } + OSSleepThread(&__DVDThreadQueue); + } + OSRestoreInterrupts(enabled); + return retVal; +} + +static void cbForReadSync() { + OSWakeupThread(&__DVDThreadQueue); +} + +int DVDSeekAsyncPrio(struct DVDFileInfo * fileInfo, long offset, void (* callback)(long, struct DVDFileInfo *), long prio) { + ASSERTMSGLINE(0x377, fileInfo, "DVDSeek(): null pointer is specified to file info address "); + ASSERTMSGLINE(0x37B, !(offset & 3), "DVDSeek(): offset must be multiple of 4 byte "); + + if (!((0 <= offset) && (offset < fileInfo->length))) { + OSPanic(__FILE__, 0x380, "DVDSeek(): offset is out of the file "); + } + + fileInfo->callback = callback; + DVDSeekAbsAsyncPrio(&fileInfo->block, (u32)(char*)fileInfo->start_address + offset, cbForSeekAsync, prio); + return 1; +} + +static void cbForSeekAsync(long result, struct DVDCommandBlock * block) { + struct DVDFileInfo * fileInfo; + + fileInfo = (struct DVDFileInfo *)&block->next; + ASSERTLINE(0x392, (void*) &fileInfo->block == (void*) block); + if (fileInfo->callback) { + (fileInfo->callback)(result, fileInfo); + } +} + +long DVDSeekPrio(struct DVDFileInfo * fileInfo, long offset, long prio) { + int result; + struct DVDCommandBlock * block; + long state; + int enabled; + long retVal; + + ASSERTMSGLINE(0x3B0, fileInfo, "DVDSeek(): null pointer is specified to file info address "); + ASSERTMSGLINE(0x3B4, !(offset & 3), "DVDSeek(): offset must be multiple of 4 byte "); + ASSERTMSGLINE(0x3B8, (offset >= 0) && ((u32) offset < (u32) fileInfo->length), "DVDSeek(): offset is out of the file "); + + block = &fileInfo->block; + result = DVDSeekAbsAsyncPrio(block, (u32)(char*)fileInfo->start_address + offset, cbForSeekSync, prio); + if (!result) { + return -1; + } + enabled = OSDisableInterrupts(); + + while(1) { + state = ((volatile DVDCommandBlock*)block)->state; + if (state == 0) { + retVal = 0; + break; + } else if (state == -1) { + retVal = -1; + break; + } else if (state == 10) { + retVal = -3; + break; + } + OSSleepThread(&__DVDThreadQueue); + } + OSRestoreInterrupts(enabled); + return retVal; +} + +static void cbForSeekSync() { + OSWakeupThread(&__DVDThreadQueue); +} + +// long DVDGetFileInfoStatus(struct DVDFileInfo * fileInfo) { +// return DVDGetCommandBlockStatus(&fileInfo->block); +// } + +BOOL DVDFastOpenDir(s32 entrynum, DVDDirectory * dir) { + ASSERTMSGLINE(0x40D, dir, "DVDFastOpenDir(): null pointer is specified to dir structure address "); + ASSERTMSG1LINE(0x410, entrynum >= 0 && entrynum < MaxEntryNum, "DVDFastOpenDir(): specified entry number \'%d\' is out of range ", entrynum); + ASSERTMSG1LINE(0x413, entryIsDir(entrynum), "DVDFastOpenDir(): entry number \'%d\' is assigned to a file ", entrynum); + + if (entrynum < 0 || entrynum >= MaxEntryNum || !entryIsDir(entrynum)) { + return FALSE; + } + + dir->entry_number = entrynum; + dir->location = entrynum + 1; + dir->next = FstStart[entrynum].nextEntryOrLength; + return TRUE; +} + +BOOL DVDOpenDir(const char* dirName, DVDDirectory* dir) { + long entry; + char currentDir[128]; + + ASSERTMSGLINE(0x430, dirName, "DVDOpendir(): null pointer is specified to directory name "); + ASSERTMSGLINE(0x431, dir, "DVDOpenDir(): null pointer is specified to dir structure address "); + entry = DVDConvertPathToEntrynum(dirName); + if (entry < 0) { + DVDGetCurrentDir(currentDir, sizeof(currentDir)); + OSReport("Warning: DVDOpenDir(): file \'%s\' was not found under %s.\n", dirName, currentDir); + return FALSE; + } + + if (!entryIsDir(entry)) { + ASSERTMSG1LINE(0x43F, entryIsDir(entry), "DVDOpendir(): file \'%s\' is specified as a directory name ", dirName); + return FALSE; + } + + dir->entry_number = entry; + dir->location = entry + 1; + dir->next = nextDir(entry); + return TRUE; +} + +int DVDReadDir(DVDDirectory * dir, DVDDirectoryEntry* dirent) { + unsigned long loc; + + loc = dir->location; + if ((loc <= (u32) dir->entry_number) || ((u32) dir->next <= loc)) { + return 0; + } + dirent->entry_number = loc; + dirent->is_directory = entryIsDir(loc); + dirent->name = FstStringStart + stringOff(loc); + dir->location = entryIsDir(loc) ? nextDir(loc) : loc + 1; + return 1; +} + +int DVDCloseDir(DVDDirectory* dir) { + return 1; +} + +void * DVDGetFSTLocation() { + return BootInfo->fst_location; +} + +#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; + + ASSERTMSGLINE(0x49C, fileInfo, "DVDPrepareStreamAsync(): NULL file info was specified"); + + start = fileInfo->start_address + offset; + + if (!Is32KBAligned(start)) { + OSPanic(__FILE__, 0x4A5, + "DVDPrepareStreamAsync(): Specified start address (filestart(0x%x) + offset(0x%x)) is " + "not 32KB aligned", + fileInfo->start_address, offset); + } + + if (length == 0) + length = fileInfo->length - offset; + + if (!Is32KBAligned(length)) { + OSPanic(__FILE__, 0x4AF, + "DVDPrepareStreamAsync(): Specified length (0x%x) is not a multiple of 32768(32*1024)", + length); + } + + if (!((offset < fileInfo->length) && (offset + length <= fileInfo->length))) { + OSPanic(__FILE__, 0x4B7, + "DVDPrepareStreamAsync(): The area specified (offset(0x%x), length(0x%x)) is out of " + "the file", + offset, length); + } + + fileInfo->callback = callback; + return DVDPrepareStreamAbsAsync(&(fileInfo->block), length, fileInfo->start_address + offset, + cbForPrepareStreamAsync); +} + +static void cbForPrepareStreamAsync(s32 result, DVDCommandBlock* block) { + DVDFileInfo* fileInfo; + + fileInfo = (DVDFileInfo*)((char*)block - offsetof(DVDFileInfo, block)); + + ASSERTLINE(0x4C7, (void*) &fileInfo->block == (void*) block); + + 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; + + ASSERTMSGLINE(0x4E9, fileInfo, "DVDPrepareStream(): NULL file info was specified"); + + start = fileInfo->start_address + offset; + + if (!Is32KBAligned(start)) { + OSPanic(__FILE__, 0x4EF, + "DVDPrepareStream(): Specified start address (filestart(0x%x) + offset(0x%x)) is not " + "32KB aligned", + fileInfo->start_address, offset); + } + + if (length == 0) + length = fileInfo->length - offset; + + if (!Is32KBAligned(length)) { + OSPanic(__FILE__, 0x4F9, + "DVDPrepareStream(): Specified length (0x%x) is not a multiple of 32768(32*1024)", + length); + } + + if (!((offset < fileInfo->length) && (offset + length <= fileInfo->length))) { + OSPanic( + __FILE__, 0x501, + "DVDPrepareStream(): The area specified (offset(0x%x), length(0x%x)) is out of the file", + offset, length); + } + + block = &(fileInfo->block); + 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; +} + +static void cbForPrepareStreamSync(s32 result, DVDCommandBlock* block) { + OSWakeupThread(&__DVDThreadQueue); +} + +s32 DVDGetTransferredSize(DVDFileInfo* fileinfo) { + s32 bytes; + DVDCommandBlock* cb; + + cb = &(fileinfo->block); + + switch (cb->state) { + 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: + case DVD_STATE_END: + bytes = (s32)cb->transferred_size; + break; + case DVD_STATE_WAITING: + bytes = 0; + break; + case DVD_STATE_BUSY: + bytes = (s32)(cb->transferred_size + (cb->current_transfer_size - __DIRegs[6])); + break; + default: + ASSERTMSG1LINE(0x556, FALSE, "DVDGetTransferredSize(): Illegal state (%d)", cb->state); + break; + } + return bytes; +} diff --git a/src/dolphin/dvd/dvdidutils.c b/src/dolphin/dvd/dvdidutils.c new file mode 100644 index 00000000..5493f537 --- /dev/null +++ b/src/dolphin/dvd/dvdidutils.c @@ -0,0 +1,95 @@ +#include "dolphin/dvd/dvdidutils.h" +#include "dolphin/os/OS.h" + +static u32 strnlen(const char* str, u32 maxlen) { + u32 i; + + for (i = 0; i < maxlen; i++) { + if (*str++ == 0) { + return i; + } + } + + return maxlen; +} + +int DVDCompareDiskID(DVDDiskID* id1, DVDDiskID* id2) { +#ifdef DEBUG + const char* game1; + const char* game2; + const char* company1; + const char* company2; + u8 diskNum1; + u8 diskNum2; + u8 version1; + u8 version2; + u32 length; + + ASSERTMSGLINE(64, id1, "DVDCompareDiskID(): Specified id1 is NULL\n"); + ASSERTMSGLINE(65, id2, "DVDCompareDiskID(): Specified id2 is NULL\n"); + + game1 = id1->game_name; + game2 = id2->game_name; + company1 = id1->company; + company2 = id2->company; + diskNum1 = id1->disk_number; + diskNum2 = id2->disk_number; + version1 = id1->game_version; + version2 = id2->game_version; + + length = strnlen(game1, sizeof(game1)); + ASSERTMSGLINE(78, length == 0 || length == 4, "DVDCompareDiskID(): Specified game name for id1 is neither NULL nor 4 character long\n"); + ASSERTMSGLINE(79, company1, "DVDCompareDiskID(): Specified company name for id1 is NULL\n"); + ASSERTMSGLINE(80, company1[1] != 0, "DVDCompareDiskID(): Specified company name for id1 is not 2 character long\n"); + ASSERTMSGLINE(81, diskNum1 == 0xFF || ((diskNum1 / 16) < 10 && diskNum1 % 16 < 10), "DVDCompareDiskID(): Specified disk number for id1 is neither 0xff nor a BCD number"); + ASSERTMSGLINE(82, version1 == 0xFF || ((version1 / 16) < 10 && version1 % 16 < 10), "DVDCompareDiskID(): Specified version number for id1 is neither 0xff nor a BCD number"); + + length = strnlen(game2, sizeof(game2)); + ASSERTMSGLINE(85, length == 0 || length == 4, "DVDCompareDiskID(): Specified game name for id2 is neither NULL nor 4 character long\n"); + ASSERTMSGLINE(86, company2, "DVDCompareDiskID(): Specified company name for id2 is NULL\n"); + ASSERTMSGLINE(87, company2[1] != 0, "DVDCompareDiskID(): Specified company name for id2 is not 2 character long\n"); + ASSERTMSGLINE(88, diskNum2 == 0xFF || ((diskNum2 / 16) < 10 && diskNum2 % 16 < 10), "DVDCompareDiskID(): Specified disk number for id2 is neither 0xff nor a BCD number"); + ASSERTMSGLINE(89, version2 == 0xFF || ((version2 / 16) < 10 && version2 % 16 < 10), "DVDCompareDiskID(): Specified version number for id2 is neither 0xff nor a BCD number"); +#endif + + if (id1->game_name[0] != 0 && id2->game_name[0] != 0 && strncmp(id1->game_name, id2->game_name, 4) != 0) { + return 0; + } + + if (id1->company[0] == 0 || id2->company[0] == 0 || strncmp(id1->company, id2->company, 2) != 0) { + return 0; + } + + if (id1->disk_number != 0xFF && id2->disk_number != 0xFF && id1->disk_number != id2->disk_number) { + return 0; + } + + if (id1->game_version != 0xFF && id2->game_version != 0xFF && id1->game_version != id2->game_version) { + return 0; + } + + return 1; +} + +DVDDiskID* DVDGenerateDiskID(DVDDiskID* id, const char* game, const char* company, u8 diskNum, u8 version) { + ASSERTMSGLINE(123, id, "DVDGenerateDiskID(): Specified id is NULL\n"); + ASSERTMSGLINE(124, game == NULL || strlen(game) == 4, "DVDGenerateDiskID(): Specified game name is neither NULL nor 4 character long\n"); + ASSERTMSGLINE(125, company, "DVDGenerateDiskID(): Specified company name is NULL\n"); + ASSERTMSGLINE(126, strlen(company) == 2, "DVDGenerateDiskID(): Specified company name is not 2 character long\n"); + ASSERTMSGLINE(127, diskNum == 0xFF || ((diskNum / 16) < 10 && diskNum % 16 < 10), "DVDGenerateDiskID(): Specified disk number is neither 0xff nor a BCD number"); + ASSERTMSGLINE(128, version == 0xFF || ((version / 16) < 10 && version % 16 < 10), "DVDGenerateDiskID(): Specified version number is neither 0xff nor a BCD number"); + + memset(id, 0, sizeof(DVDDiskID)); + + if (game != NULL) { + strncpy(id->game_name, game, 4); + } + + if (company != NULL) { + strncpy(id->company, company, 2); + } + + id->disk_number = diskNum; + id->game_version = version; + return id; +} diff --git a/src/dolphin/dvd/dvdlow.c b/src/dolphin/dvd/dvdlow.c new file mode 100644 index 00000000..8a8fde43 --- /dev/null +++ b/src/dolphin/dvd/dvdlow.c @@ -0,0 +1,544 @@ +#include "dolphin/dvd/dvdlow.h" +#include "dolphin/os/OS.h" + +static BOOL FirstRead = TRUE; +static volatile BOOL StopAtNextInt = FALSE; +static u32 LastLength = 0; +static DVDLowCallback Callback = NULL; +static DVDLowCallback ResetCoverCallback = NULL; +static volatile OSTime LastResetEnd = 0; +static volatile u32 ResetOccurred = FALSE; +static volatile BOOL WaitingCoverClose = FALSE; +static volatile BOOL Breaking = FALSE; +static volatile u32 WorkAroundType = 0; +static u32 WorkAroundSeekLocation = 0; +static volatile OSTime LastReadFinished = 0; +static OSTime LastReadIssued = 0; +static volatile BOOL LastCommandWasRead = FALSE; +static volatile u32 NextCommandNumber = 0; + +typedef struct { + void* addr; + u32 length; + u32 offset; +} DVDBuffer; + +typedef struct { + s32 cmd; + void* addr; + u32 length; + u32 offset; + DVDLowCallback callback; +} DVDCommand; + +static DVDCommand CommandList[3]; +static OSAlarm AlarmForWA; +static OSAlarm AlarmForTimeout; +static OSAlarm AlarmForBreak; +static DVDBuffer Prev; +static DVDBuffer Curr; + +void __DVDInitWA() +{ + NextCommandNumber = 0; + CommandList[0].cmd = -1; + __DVDLowSetWAType(0, 0); + OSInitAlarm(); +} + +static void Read(void* addr, u32 length, u32 offset, DVDLowCallback callback); + +static BOOL ProcessNextCommand() +{ + s32 n = NextCommandNumber; + + ASSERTLINE(0x127, n < 3); + if (CommandList[n].cmd == 1) { + ++NextCommandNumber; + Read(CommandList[n].addr, CommandList[n].length, CommandList[n].offset, CommandList[n].callback); + return TRUE; + } else if (CommandList[n].cmd == 2) { + ++NextCommandNumber; + DVDLowSeek(CommandList[n].offset, CommandList[n].callback); + return TRUE; + } + + return FALSE; +} + +void __DVDInterruptHandler(__OSInterrupt interrupt, OSContext* context) +{ + DVDLowCallback cb; + OSContext exceptionContext; + u32 cause = 0; + u32 reg; + u32 intr; + u32 mask; + + if (LastCommandWasRead) { + LastReadFinished = __OSGetSystemTime(); + FirstRead = FALSE; + Prev.addr = Curr.addr; + Prev.length = Curr.length; + Prev.offset = Curr.offset; + if (StopAtNextInt == TRUE) { + cause |= 8; + } + } + + LastCommandWasRead = FALSE; + StopAtNextInt = FALSE; + reg = __DIRegs[0]; + mask = reg & 0x2a; + intr = (reg & 0x54) & (mask << 1); + + if (intr & 0x40) { + cause |= 8; + } + + if (intr & 0x10) { + cause |= 1; + } + + if (intr & 4) { + cause |= 2; + } + + if (cause) { + ResetOccurred = FALSE; + OSCancelAlarm(&AlarmForTimeout); + } + + __DIRegs[0] = intr | mask; + + if (ResetOccurred && (__OSGetSystemTime() - LastResetEnd) < OSMillisecondsToTicks(200)) { + reg = __DIRegs[1]; + mask = reg & 0x2; + intr = (reg & 4) & (mask << 1); + if (intr & 4) { + if (ResetCoverCallback) { + ResetCoverCallback(4); + } + ResetCoverCallback = NULL; + } + + __DIRegs[1] = __DIRegs[1]; + } else if (WaitingCoverClose) { + reg = __DIRegs[1]; + mask = reg & 2; + intr = (reg & 4) & (mask << 1); + + if (intr & 4) { + cause |= 4; + } + + __DIRegs[1] = intr | mask; + WaitingCoverClose = FALSE; + } else { + __DIRegs[1] = 0; + } + + if ((cause & 8) && !Breaking) { + cause &= ~8; + } + + if ((cause & 1)) { + if (ProcessNextCommand()) { + return; + } + } else { + CommandList[0].cmd = -1; + NextCommandNumber = 0; + } + + OSClearContext(&exceptionContext); + OSSetCurrentContext(&exceptionContext); + + if (cause) { + cb = Callback; + Callback = NULL; + if (cb) { + cb(cause); + } + + Breaking = FALSE; + } + + OSClearContext(&exceptionContext); + OSSetCurrentContext(context); +} + +static void AlarmHandler(OSAlarm* alarm, OSContext* context) { + BOOL processed = ProcessNextCommand(); + + ASSERTLINE(0x28A, processed); +} + +static void AlarmHandlerForTimeout(OSAlarm* alarm, OSContext* context) +{ + OSContext tmpContext; + DVDLowCallback callback; + __OSMaskInterrupts(0x400); + OSClearContext(&tmpContext); + OSSetCurrentContext(&tmpContext); + callback = Callback; + Callback = NULL; + if (callback) { + callback(0x10); + } + OSClearContext(&tmpContext); + OSSetCurrentContext(context); +} + +static void SetTimeoutAlarm(OSTime timeout) +{ + OSCreateAlarm(&AlarmForTimeout); + OSSetAlarm(&AlarmForTimeout, timeout, AlarmHandlerForTimeout); +} + +static void Read(void* addr, u32 length, u32 offset, DVDLowCallback callback) +{ + Callback = callback; + StopAtNextInt = FALSE; + LastCommandWasRead = TRUE; + LastReadIssued = __OSGetSystemTime(); + + __DIRegs[2] = 0xa8000000; + __DIRegs[3] = offset / 4; + __DIRegs[4] = length; + __DIRegs[5] = (u32)addr; + __DIRegs[6] = length; + LastLength = length; + __DIRegs[7] = 3; + + if (length > 0xa00000) { + SetTimeoutAlarm(OSSecondsToTicks(20)); + } else { + SetTimeoutAlarm(OSSecondsToTicks(10)); + } +} + +static BOOL AudioBufferOn(void) +{ + DVDDiskID* id; + + id = DVDGetCurrentDiskID(); + if (id->is_streaming) { + return TRUE; + } + + return FALSE; +} + +static BOOL HitCache(DVDBuffer* cur, DVDBuffer* prev) +{ + u32 uVar1 = (prev->offset + prev->length - 1) >> 15; + u32 uVar2 = (cur->offset >> 15); + u32 iVar3 = AudioBufferOn() ? 5 : 15; + + if ((uVar2 > uVar1 - 2) || (uVar2 < uVar1 + iVar3 + 3)) { + return TRUE; + } + return FALSE; +} + +static void DoJustRead(void* addr, u32 length, u32 offset, DVDLowCallback callback) +{ + CommandList[0].cmd = -1; + NextCommandNumber = 0; + Read(addr, length, offset, callback); +} + +static void SeekTwiceBeforeRead(void* addr, u32 length, u32 offset, DVDLowCallback callback) +{ + u32 newOffset; + if ((offset & ~0x7FFF) == 0) { + newOffset = 0; + } else { + newOffset = (offset & ~0x7FFF) + WorkAroundSeekLocation; + } + CommandList[0].cmd = 2; + CommandList[0].offset = newOffset; + CommandList[0].callback = callback; + CommandList[1].cmd = 1; + CommandList[1].addr = addr; + CommandList[1].length = length; + CommandList[1].offset = offset; + CommandList[1].callback = callback; + CommandList[2].cmd = -1; + NextCommandNumber = 0; + DVDLowSeek(newOffset, callback); +} + +static void WaitBeforeRead(void* addr, u32 length, u32 offset, DVDLowCallback callback, OSTime timeout) +{ + CommandList[0].cmd = 1; + CommandList[0].addr = addr; + CommandList[0].length = length; + CommandList[0].offset = offset; + CommandList[0].callback = callback; + CommandList[1].cmd = -1; + NextCommandNumber = 0; + OSCreateAlarm(&AlarmForWA); + OSSetAlarm(&AlarmForWA, timeout, AlarmHandler); +} + +BOOL DVDLowRead(void* addr, u32 length, u32 offset, DVDLowCallback callback) +{ + u32 blockNumOfPrevEnd; + u32 blockNumOfCurrStart; + OSTime diff; + + ASSERTMSGLINE(0x341, (((u32)addr) & 31) == 0, "DVDLowRead(): address must be aligned with 32 byte boundary."); + ASSERTMSGLINE(0x342, (length & 31) == 0, "DVDLowRead(): length must be a multiple of 32."); + ASSERTMSGLINE(0x343, (offset & 3) == 0, "DVDLowRead(): offset must be a multiple of 4."); + ASSERTMSGLINE(0x345, length != 0, "DVD read: 0 was specified to length of the read\n"); + + __DIRegs[6] = length; + Curr.addr = addr; + Curr.length = length; + Curr.offset = offset; + + if (WorkAroundType == 0) { + DoJustRead(addr, length, offset, callback); + } else if (WorkAroundType == 1) { + if (FirstRead) { + SeekTwiceBeforeRead(addr, length, offset, callback); + } else { + if (!HitCache(&Curr, &Prev)) { + DoJustRead(addr, length, offset, callback); + } else { + blockNumOfPrevEnd = (u32)((Prev.offset + Prev.length - 1) >> 15) & 0x1FFFF; + blockNumOfCurrStart = (u32)((Curr.offset >> 15) & 0x1FFFF); + if (blockNumOfPrevEnd == blockNumOfCurrStart || blockNumOfPrevEnd + 1 == blockNumOfCurrStart) { + diff = __OSGetSystemTime() - LastReadFinished; + if (OSMillisecondsToTicks(5) < diff) { + DoJustRead(addr, length, offset, callback); + } else { + WaitBeforeRead(addr, length, offset, callback, OSMillisecondsToTicks(5) - diff + OSMicrosecondsToTicks(500)); + } + } else { + SeekTwiceBeforeRead(addr, length, offset, callback); + } + } + } + } else { + ASSERTLINE(0x380, FALSE); + } + + return TRUE; +} + +BOOL DVDLowSeek(u32 offset, DVDLowCallback callback) +{ + ASSERTMSGLINE(0x394, (offset & 3) == 0, "DVDLowSeek(): offset must be a multiple of 4."); + + Callback = callback; + StopAtNextInt = FALSE; + __DIRegs[2] = 0xab000000; + __DIRegs[3] = offset / 4; + __DIRegs[7] = 1; + SetTimeoutAlarm(OSSecondsToTicks(10)); + return TRUE; +} + +BOOL DVDLowWaitCoverClose(DVDLowCallback callback) +{ + Callback = callback; + WaitingCoverClose = TRUE; + StopAtNextInt = FALSE; + __DIRegs[1] = 2; + return TRUE; +} + +BOOL DVDLowReadDiskID(DVDDiskID* diskID, DVDLowCallback callback) +{ + ASSERTMSGLINE(0x3d2, (((u32)diskID) & 31) == 0, "DVDLowReadID(): id must be aligned with 32 byte boundary."); + + Callback = callback; + StopAtNextInt = FALSE; + __DIRegs[2] = 0xa8000040; + __DIRegs[3] = 0; + __DIRegs[4] = sizeof(DVDDiskID); + __DIRegs[5] = (u32)diskID; + __DIRegs[6] = sizeof(DVDDiskID); + __DIRegs[7] = 3; + SetTimeoutAlarm(OSSecondsToTicks(10)); + return TRUE; +} + +BOOL DVDLowStopMotor(DVDLowCallback callback) +{ + Callback = callback; + StopAtNextInt = FALSE; + __DIRegs[2] = 0xe3000000; + __DIRegs[7] = 1; + SetTimeoutAlarm(OSSecondsToTicks(10)); + return TRUE; +} + +BOOL DVDLowRequestError(DVDLowCallback callback) +{ + Callback = callback; + StopAtNextInt = FALSE; + __DIRegs[2] = 0xe0000000; + __DIRegs[7] = 1; + SetTimeoutAlarm(OSSecondsToTicks(10)); + return TRUE; +} + +BOOL DVDLowInquiry(DVDDriveInfo* info, DVDLowCallback callback) +{ + Callback = callback; + StopAtNextInt = FALSE; + __DIRegs[2] = 0x12000000; + __DIRegs[4] = sizeof(DVDDriveInfo); + __DIRegs[5] = (u32)info; + __DIRegs[6] = sizeof(DVDDriveInfo); + __DIRegs[7] = 3; + SetTimeoutAlarm(OSSecondsToTicks(10)); + return TRUE; +} + +BOOL DVDLowAudioStream(u32 subcmd, u32 length, u32 offset, DVDLowCallback callback) +{ + Callback = callback; + StopAtNextInt = FALSE; + __DIRegs[2] = subcmd | 0xe1000000; + __DIRegs[3] = offset >> 2; + __DIRegs[4] = length; + __DIRegs[7] = 1; + SetTimeoutAlarm(OSSecondsToTicks(10)); + return TRUE; +} + +BOOL DVDLowRequestAudioStatus(u32 subcmd, DVDLowCallback callback) +{ + Callback = callback; + StopAtNextInt = FALSE; + __DIRegs[2] = subcmd | 0xe2000000; + __DIRegs[7] = 1; + SetTimeoutAlarm(OSSecondsToTicks(10)); + return TRUE; +} + +BOOL DVDLowAudioBufferConfig(BOOL enable, u32 size, DVDLowCallback callback) +{ +#ifdef DEBUG + u32 bufSize; + u32 trigger; +#endif + + Callback = callback; + StopAtNextInt = FALSE; + +#ifdef DEBUG + bufSize = size & 0xF; + trigger = (size >> 4) & 0xF; + ASSERTLINE(0x4BD, bufSize < 16); + ASSERTLINE(0x4BE, trigger <= 2); +#endif + + __DIRegs[2] = 0xe4000000 | (enable != 0 ? 0x10000 : 0) | size; + __DIRegs[7] = 1; + SetTimeoutAlarm(OSSecondsToTicks(10)); + return TRUE; +} + +void DVDLowReset() +{ + u32 reg; + OSTime resetStart; + + __DIRegs[1] = 2; + reg = __PIRegs[9]; + __PIRegs[9] = (reg & ~4) | 1; + + resetStart = __OSGetSystemTime(); + while ((__OSGetSystemTime() - resetStart) < OSMicrosecondsToTicks(12)) + ; + + __PIRegs[9] = reg | 4 | 1; + ResetOccurred = TRUE; + LastResetEnd = __OSGetSystemTime(); +} + +DVDLowCallback DVDLowSetResetCoverCallback(DVDLowCallback callback) +{ + DVDLowCallback old; + BOOL enabled; + + enabled = OSDisableInterrupts(); + old = ResetCoverCallback; + ResetCoverCallback = callback; + OSRestoreInterrupts(enabled); + return old; +} + +static void DoBreak(void) +{ + u32 statusReg; + + statusReg = __DIRegs[0]; + statusReg |= 0x40 | 1; + __DIRegs[0] = statusReg; + Breaking = TRUE; +} + +static void SetBreakAlarm(OSTime timeout); + +static void AlarmHandlerForBreak(OSAlarm* alarm, OSContext* context) +{ + if (__DIRegs[6] < LastLength) { + DoBreak(); + } else { + SetBreakAlarm(OSMillisecondsToTicks(20)); + } +} + +static void SetBreakAlarm(OSTime timeout) +{ + OSCreateAlarm(&AlarmForBreak); + OSSetAlarm(&AlarmForBreak, timeout, AlarmHandlerForBreak); +} + +BOOL DVDLowBreak() +{ + StopAtNextInt = TRUE; + Breaking = TRUE; + return TRUE; +} + +DVDLowCallback DVDLowClearCallback() +{ + DVDLowCallback old; + __DIRegs[1] = 0; + old = Callback; + Callback = NULL; + return old; +} + +u32 DVDLowGetCoverStatus(void) +{ + if ((__OSGetSystemTime() - LastResetEnd) < OSMillisecondsToTicks(100)) { + return 0; + } + + if (__DIRegs[1] & 1) { + return 1; + } + + return 2; +} + + +void __DVDLowSetWAType(u32 type, u32 location) +{ + BOOL enabled; + + enabled = OSDisableInterrupts(); + ASSERTLINE(0x5B1, type < DVD_WATYPE_MAX); + 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 00000000..683226f0 --- /dev/null +++ b/src/dolphin/dvd/dvdqueue.c @@ -0,0 +1,164 @@ +#include "dolphin/dvd/dvdqueue.h" +#include "dolphin/os/OS.h" + +static struct { + /* 0x00 */ struct DVDCommandBlock * next; + /* 0x04 */ struct DVDCommandBlock * prev; +} WaitingQueue[4]; + +static struct DVDCommandBlock * PopWaitingQueuePrio(long prio); + +void __DVDClearWaitingQueue(void) { + unsigned long i; + struct DVDCommandBlock * q; + + for(i = 0; i < 4; i++) { + q = (struct DVDCommandBlock *)&WaitingQueue[i].next; + q->next = q; + q->prev = q; + } +} + +int __DVDPushWaitingQueue(long prio, struct DVDCommandBlock * block) { + int enabled = OSDisableInterrupts(); + struct DVDCommandBlock * q = (struct DVDCommandBlock *)&WaitingQueue[prio]; + + q->prev->next = block; + block->prev = q->prev; + block->next = q; + q->prev = block; + OSRestoreInterrupts(enabled); + return 1; +} + +static struct DVDCommandBlock * PopWaitingQueuePrio(long prio) { + struct DVDCommandBlock * tmp; + int enabled; + struct DVDCommandBlock * q; + + enabled = OSDisableInterrupts(); + q = (struct DVDCommandBlock *)&WaitingQueue[prio]; + ASSERTLINE(0x57, q->next != q); + tmp = q->next; + q->next = tmp->next; + tmp->next->prev = q; + OSRestoreInterrupts(enabled); + tmp->next = 0; + tmp->prev = 0; + return tmp; +} + +struct DVDCommandBlock * __DVDPopWaitingQueue(void) { + unsigned long i; + int enabled; + struct DVDCommandBlock * q; + + enabled = OSDisableInterrupts(); + for(i = 0; i < 4; i++) { + q = (struct DVDCommandBlock *)&WaitingQueue[i]; + if (q->next != q) { + OSRestoreInterrupts(enabled); + return PopWaitingQueuePrio(i); + } + } + OSRestoreInterrupts(enabled); + return NULL; +} + +int __DVDCheckWaitingQueue(void) { + unsigned long i; + int enabled; + struct DVDCommandBlock * q; + + enabled = OSDisableInterrupts(); + for(i = 0; i < 4; i++) { + q = (struct DVDCommandBlock *)&WaitingQueue[i]; + if (q->next != q) { + OSRestoreInterrupts(enabled); + return 1; + } + } + OSRestoreInterrupts(enabled); + return 0; +} + +int __DVDDequeueWaitingQueue(struct DVDCommandBlock * block) { + int enabled; + struct DVDCommandBlock * prev; + struct DVDCommandBlock * next; + + enabled = OSDisableInterrupts(); + prev = block->prev; + next = block->next; + if (prev == NULL || next == NULL) { + OSRestoreInterrupts(enabled); + return 0; + } + prev->next = next; + next->prev = prev; + OSRestoreInterrupts(enabled); + return 1; +} + +int __DVDIsBlockInWaitingQueue(struct DVDCommandBlock * block) { + unsigned long i; + struct DVDCommandBlock * start; + struct DVDCommandBlock * q; + + for(i = 0; i < 4; i++) { + start = (struct DVDCommandBlock *)&WaitingQueue[i]; + if (start->next == start) { + continue; + } + for(q = start->next; q != start; q = q->next) { + if (q == block) { + return 1; + } + } + } + return 0; +} + +static char * CommandNames[16] = { + "", + "READ", + "SEEK", + "CHANGE_DISK", + "BSREAD", + "READID", + "INITSTREAM", + "CANCELSTREAM", + "STOP_STREAM_AT_END", + "REQUEST_AUDIO_ERROR", + "REQUEST_PLAY_ADDR", + "REQUEST_START_ADDR", + "REQUEST_LENGTH", + "AUDIO_BUFFER_CONFIG", + "INQUIRY", + "BS_CHANGE_DISK", +}; + +void DVDDumpWaitingQueue(void) { + unsigned long i; + struct DVDCommandBlock * start; + struct DVDCommandBlock * q; + + OSReport("==== DVD Waiting Queue Status ====\n"); + for(i = 0; i < 4; i++) { + OSReport("< Queue #%d > ", i); + start = (struct DVDCommandBlock *)&WaitingQueue[i]; + if (start->next == start) { + OSReport("None\n"); + } else { + OSReport("\n"); + for(q = start->next; q != start; q = q->next) { + OSReport("0x%08x: Command: %s ", q, CommandNames[q->command]); + if (q->command == 1) { + OSReport("Disk offset: %d, Length: %d, Addr: 0x%08x\n", q->offset, q->length, q->buffer); + } else { + OSReport("\n"); + } + } + } + } +} diff --git a/src/dolphin/dvd/fstload.c b/src/dolphin/dvd/fstload.c new file mode 100644 index 00000000..6287b149 --- /dev/null +++ b/src/dolphin/dvd/fstload.c @@ -0,0 +1,82 @@ +#include "dolphin/dvd/fstload.h" +#include "dolphin/os/OS.h" + +// .bss +static unsigned char bb2Buf[63]; // size: 0x3F, address: 0x0 + +// .sbss +static unsigned long status; // size: 0x4, address: 0x0 +static struct DVDBB2 * bb2; // size: 0x4, address: 0x4 +static struct DVDDiskID * idTmp; // size: 0x4, address: 0x8 + +// functions +static void cb(long result, struct DVDCommandBlock * block); +void __fstLoad(); + +static void cb(long result, struct DVDCommandBlock * block) { + if (result > 0) { + switch(status) { + case 0: + status = 1; + DVDReadAbsAsyncForBS(block, bb2, 0x20, 0x420, cb); + return; + case 1: + status = 2; + DVDReadAbsAsyncForBS(block, bb2->FSTAddress, (bb2->FSTLength + 0x1F) & 0xFFFFFFE0, bb2->FSTPosition, cb); + default: + return; + } + } + if (result == -1) { + return; + } else if (result == -4) { + status = 0; + DVDReset(); + DVDReadDiskID(block, idTmp, cb); + } +} + +void __fstLoad() { + struct OSBootInfo * bootInfo; + struct DVDDiskID * id; + unsigned char idTmpBuf[63]; + long state; + void * arenaHi; + static struct DVDCommandBlock block; + + arenaHi = OSGetArenaHi(); + bootInfo = (void*)OSPhysicalToCached(0); + idTmp = (void*)OSRoundUp32B(idTmpBuf); + bb2 = (void*)OSRoundUp32B(bb2Buf); + DVDReset(); + DVDReadDiskID(&block, idTmp, cb); + while (1) { + state = DVDGetDriveStatus(); + if (state == 0) { + break; + } + + // weird switch that seemingly wont do anything but break out of its own switch. What was this for? Early DVD development pre-hardware? + switch(state) { + case DVD_STATE_FATAL_ERROR: break; + case DVD_STATE_BUSY: break; + case DVD_STATE_WAITING: break; + case DVD_STATE_COVER_CLOSED: break; + case DVD_STATE_NO_DISK: break; + case DVD_STATE_COVER_OPEN: break; + case DVD_STATE_MOTOR_STOPPED: break; + } + } + bootInfo->fst_location = (void*)bb2->FSTAddress; + bootInfo->fst_max_length = bb2->FSTMaxLength; + id = &bootInfo->disk_info; + memcpy(id, idTmp, 0x20); + OSReport("\n"); + OSReport(" Game Name ... %c%c%c%c\n", id->game_name[0], id->game_name[1], id->game_name[2], id->game_name[3]); + OSReport(" Company ..... %c%c\n", id->company[0], id->company[1]); + OSReport(" Disk # ...... %d\n", id->disk_number); + OSReport(" Game ver .... %d\n", id->game_version); + OSReport(" Streaming ... %s\n", !(id->is_streaming) ? "OFF" : "ON"); + OSReport("\n"); + OSSetArenaHi(bb2->FSTAddress); +} |
