summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTakaRikka <38417346+TakaRikka@users.noreply.github.com>2025-12-08 02:44:29 -0800
committerGitHub <noreply@github.com>2025-12-08 12:44:29 +0200
commit353721578d0ec7b49a4d7b56b0641acce8d8a52f (patch)
tree6ad8854c362ef65d04d62ced28ec040e7a2135e1 /src
parent251d49c6f14151b842b7ceb9f2dfc5e0a0e69fc5 (diff)
shieldD revolution dvd/nand mostly done (#2922)
* shieldD revo dvd mostly done * shieldD revo nand mostly done * shieldD revo fs mostly done * shieldD revo ipc mostly done * shieldD revo sdk pad done
Diffstat (limited to 'src')
-rw-r--r--src/revolution/dvd/__dvd.h34
-rw-r--r--src/revolution/dvd/dvd.c2553
-rw-r--r--src/revolution/dvd/dvdDeviceError.c200
-rw-r--r--src/revolution/dvd/dvdFatal.c211
-rw-r--r--src/revolution/dvd/dvd_broadway.c978
-rw-r--r--src/revolution/dvd/dvderror.c202
-rw-r--r--src/revolution/dvd/dvdfs.c557
-rw-r--r--src/revolution/dvd/dvdidutils.c98
-rw-r--r--src/revolution/dvd/dvdqueue.c192
-rw-r--r--src/revolution/fs/fs.c973
-rw-r--r--src/revolution/ipc/ipcMain.c51
-rw-r--r--src/revolution/ipc/ipcProfile.c53
-rw-r--r--src/revolution/ipc/ipcclt.c835
-rw-r--r--src/revolution/ipc/memory.c240
-rw-r--r--src/revolution/nand/NANDCheck.c94
-rw-r--r--src/revolution/nand/NANDCore.c514
-rw-r--r--src/revolution/nand/NANDErrorMessage.c235
-rw-r--r--src/revolution/nand/NANDLogging.c181
-rw-r--r--src/revolution/nand/NANDOpenClose.c683
-rw-r--r--src/revolution/nand/nand.c565
-rw-r--r--src/revolution/os/OS.c7
-rw-r--r--src/revolution/os/OSAlarm.c4
-rw-r--r--src/revolution/os/OSAudioSystem.c2
-rw-r--r--src/revolution/os/OSExec.c3
-rw-r--r--src/revolution/os/OSInterrupt.c1
-rw-r--r--src/revolution/os/OSLaunch.c1
-rw-r--r--src/revolution/os/OSLink.c1
-rw-r--r--src/revolution/os/OSNet.c19
-rw-r--r--src/revolution/os/OSPlayTime.c4
-rw-r--r--src/revolution/os/OSReset.c2
-rw-r--r--src/revolution/os/OSStateFlags.c2
-rw-r--r--src/revolution/os/OSStateTM.c2
-rw-r--r--src/revolution/os/OSSync.c1
-rw-r--r--src/revolution/os/OSThread.c6
-rw-r--r--src/revolution/os/__os.h47
-rw-r--r--src/revolution/pad/Pad.c894
-rw-r--r--src/revolution/pad/Padclamp.c153
-rw-r--r--src/revolution/si/__si.h21
38 files changed, 10557 insertions, 62 deletions
diff --git a/src/revolution/dvd/__dvd.h b/src/revolution/dvd/__dvd.h
index 6443d85b68..c0c6cb8452 100644
--- a/src/revolution/dvd/__dvd.h
+++ b/src/revolution/dvd/__dvd.h
@@ -1,8 +1,8 @@
-#ifndef _DOLPHIN_DVD_INTERNAL_H_
-#define _DOLPHIN_DVD_INTERNAL_H_
+#ifndef _REVOLUTION_DVD_INTERNAL_H_
+#define _REVOLUTION_DVD_INTERNAL_H_
-#include <dolphin/os.h>
-#include <dolphin/dvd.h>
+#include <revolution/os.h>
+#include <revolution/dvd.h>
#ifdef __cplusplus
extern "C" {
@@ -11,17 +11,21 @@ extern "C" {
// DVD
DVDCommandChecker __DVDSetOptionalCommandChecker(DVDCommandChecker func);
void __DVDSetImmCommand(u32 command);
-void __DVDSetDmaCommand(u32 command);
-void* __DVDGetIssueCommandAddr(void);
void __DVDAudioBufferConfig(DVDCommandBlock* block, u32 enable, u32 size, DVDCBCallback callback);
-void __DVDPrepareResetAsync(DVDCBCallback callback);
int __DVDTestAlarm(const OSAlarm* alarm);
+BOOL __DVDLowBreak(void);
+u32 __DVDGetCoverStatus(void);
+void __DVDPrepareReset(void);
+
+extern vu32 __DVDLayoutFormat;
// DVD ERROR
-void __DVDStoreErrorCode(u32 error);
+void __DVDStoreErrorCode(u32 error, DVDCBCallback callback);
+BOOL __DVDCheckDevice(void);
// DVD FATAL
void __DVDPrintFatalMessage(void);
+void __DVDShowFatalMessage(void);
// DVD FS
extern OSThreadQueue __DVDThreadQueue;
@@ -29,10 +33,7 @@ extern u32 __DVDLongFileNameFlag;
void __DVDFSInit(void);
-// DVD LOW
-void __DVDInitWA(void);
-void __DVDInterruptHandler(__OSInterrupt interrupt, OSContext* context);
-void __DVDLowSetWAType(u32 type, s32 seekLoc);
+// DVD BROADWAY
int __DVDLowTestAlarm(const OSAlarm* alarm);
// DVD QUEUE
@@ -42,15 +43,10 @@ DVDCommandBlock* __DVDPopWaitingQueue(void);
int __DVDCheckWaitingQueue(void);
int __DVDDequeueWaitingQueue(DVDCommandBlock* block);
int __DVDIsBlockInWaitingQueue(DVDCommandBlock* block);
-
-// FST LOAD
-void __fstLoad(void);
-
-// unsorted
-u32 __DVDGetCoverStatus(void);
+DVDCommandBlock* __DVDGetNextWaitingQueue(void);
#ifdef __cplusplus
}
#endif
-#endif // _DOLPHIN_DVD_INTERNAL_H_
+#endif // _REVOLUTION_DVD_INTERNAL_H_
diff --git a/src/revolution/dvd/dvd.c b/src/revolution/dvd/dvd.c
new file mode 100644
index 0000000000..d000b71429
--- /dev/null
+++ b/src/revolution/dvd/dvd.c
@@ -0,0 +1,2553 @@
+#include <revolution.h>
+#include <revolution/os.h>
+#include <revolution/dvd.h>
+#include <string.h>
+
+#include "os/__os.h"
+#include "__dvd.h"
+
+// externs
+extern DVDErrorInfo __ErrorInfo;
+
+#ifdef SDK_AUG2010
+#define BUILD_DATE "Aug 23 2010"
+#if DEBUG
+#define BUILD_TIME "17:24:50"
+#else
+#define BUILD_TIME "17:33:06"
+#endif
+#elif SDK_SEP2006
+#define BUILD_DATE "Sep 21 2006"
+#define BUILD_TIME "14:32:13"
+#endif
+
+#ifdef SDK_AUG2010
+#if DEBUG
+const char* __DVDVersion = "<< RVL_SDK - DVD \tdebug build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#else
+const char* __DVDVersion = "<< RVL_SDK - DVD \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#endif
+#elif SDK_SEP2006
+const char* __DVDVersion = "<< RVL_SDK - DVD \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4200_60422) >>";
+#endif
+
+typedef void (*stateFunc)(DVDCommandBlock* block);
+stateFunc LastState;
+
+static DVDCommandBlock* executing;
+static DVDDiskID* IDShouldBe;
+static OSBootInfo* bootInfo;
+static vu32 CurrCommand;
+static void (*CancelCallback)(s32, DVDCommandBlock*);
+static vu32 CancelLastError;
+static u32 LastError;
+static BOOL ResetRequired;
+static u32 MotorState;
+static volatile OSTime LastResetEnd;
+static u32 __DVDNumTmdBytes ATTRIBUTE_ALIGN(32);
+static DVDGameTOC* GameToc;
+static DVDPartitionInfo* PartInfo;
+static DVDPartitionInfo* BootGameInfo;
+static volatile BOOL Prepared;
+static vu32 __BS2DVDLowIntType;
+static int DVDInitialized;
+vu32 __DVDLayoutFormat;
+static volatile BOOL PreparingCover;
+static vu32 ChangedDisc;
+static vu32 MotorStopped;
+static vu32 WaitingForCoverClose;
+static vu32 WaitingForCoverOpen;
+static vu32 Breaking;
+static BOOL FirstTimeInBootrom;
+static vs32 NumInternalRetry;
+static vu32 ResumeFromHere;
+static vu32 Canceling;
+static BOOL FatalErrorFlag;
+static volatile BOOL PausingFlag;
+static volatile BOOL PauseFlag;
+
+static BOOL autoInvalidation = TRUE;
+static int CancelAllSyncComplete;
+volatile u32 CommandInfoCounter = 0;
+
+static void defaultOptionalCommandChecker(DVDCommandBlock*, DVDCommandCheckerCallback);
+static DVDCommandChecker checkOptionalCommand = defaultOptionalCommandChecker;
+
+
+static DVDBB2 BB2;
+static DVDDiskID CurrDiskID;
+static DVDCommandBlock DummyCommandBlock;
+static OSAlarm ResetAlarm;
+static OSAlarm CoverAlarm;
+
+static u8 __DVDGameTocBuffer[OSRoundUp32B(sizeof(DVDGameTOC) * 4)] ATTRIBUTE_ALIGN(32);
+static u8 __DVDPartInfoBuffer[OSRoundUp32B(sizeof(DVDPartitionInfo) * 4)] ATTRIBUTE_ALIGN(32);
+static u8 __DVDTmdBuffer[OSRoundUp32B(sizeof(ESTitleMeta))] ATTRIBUTE_ALIGN(32);
+static u8 __DVDTicketViewBuffer[OSRoundUp32B(sizeof(ESTicketView))] ATTRIBUTE_ALIGN(32);
+
+static OSAlarm FatalAlarm;
+DVDCommandBlock __DVDStopMotorCommandBlock;
+
+// prototypes
+static void stateDownRotation(DVDCommandBlock*);
+static void stateReadingTOC(DVDCommandBlock*);
+static void stateReadingPartitionInfo(DVDCommandBlock* block);
+static void stateOpenPartition(DVDCommandBlock* block);
+static void stateOpenPartition2(DVDCommandBlock* block);
+static void stateReadingFST();
+static void cbForStateReadingFST(u32 intType);
+static void cbForStateError(u32 intType);
+static void stateError(u32 error);
+static void stateTimeout();
+static void stateSecurityError(void);
+static void stateGettingError();
+static u32 CategorizeError(u32 error);
+static BOOL CheckCancel(u32 resume);
+static void cbForStateGettingError(u32 intType);
+static void cbForUnrecoveredError(u32 intType);
+static void cbForUnrecoveredErrorRetry(u32 intType);
+static void stateGoToRetry();
+static void cbForStateGoToRetry(u32 intType);
+static void stateCheckID();
+static void stateCheckID3();
+static void stateCheckID2a();
+static void stateCheckID2(DVDCommandBlock* block);
+static void cbForStateCheckID1(u32 intType);
+static void cbForStateCheckID2(u32 intType);
+static void cbForStateCheckID3(u32 intType);
+static void cbForStateCheckID2a(u32 intType);
+static void AlarmHandler(OSAlarm* alarm, OSContext* context);
+static void stateCoverClosed();
+static void stateCoverClosed_CMD(DVDCommandBlock* command);
+static void cbForStateCoverClosed(u32 intType);
+static void stateMotorStopped();
+static void cbForStateMotorStopped(u32 intType);
+static void stateReady();
+static void stateBusy(DVDCommandBlock* block);
+static BOOL IsImmCommandWithResult(u32 command);
+static int IsDmaCommand(u32 command);
+static void cbForStateBusy(u32 intType);
+static int issueCommand(s32 prio, DVDCommandBlock* block);
+static void cbForChangeDiskSync(s32 result, DVDCommandBlock* block);
+static void cbForStopMotorSync(s32 result, DVDCommandBlock* block);
+static void cbForInquirySync(s32 result, DVDCommandBlock* block);
+static void cbForCancelSync(s32 result, DVDCommandBlock* block);
+static void cbForCancelAllSync(s32 result, DVDCommandBlock* block);
+static void cbForStateOpenPartition(u32 intType);
+static void cbForStateReset(u32 intType);
+static void cbForStateDownRotation(u32 intType);
+
+DECL_WEAK void StampCommand(u32 command, u32 offset, u32 length) {
+ BOOL enabled = OSDisableInterrupts();
+
+ if (CommandInfoCounter >= 5) {
+ CommandInfoCounter = 0;
+ }
+
+ __ErrorInfo.lastCommand[CommandInfoCounter].command = command;
+ __ErrorInfo.lastCommand[CommandInfoCounter].offset = offset;
+ __ErrorInfo.lastCommand[CommandInfoCounter].length = length;
+ __ErrorInfo.lastCommand[CommandInfoCounter].tick = OSGetTick();
+ CommandInfoCounter++;
+ OSRestoreInterrupts(enabled);
+}
+
+void StampIntType(u32 intType) {
+ BOOL enabled = OSDisableInterrupts();
+
+ if (CommandInfoCounter == 0) {
+ __ErrorInfo.lastCommand[4].intType = intType;
+ } else {
+ __ErrorInfo.lastCommand[CommandInfoCounter - 1].intType = intType;
+ }
+
+ OSRestoreInterrupts(enabled);
+}
+
+static void defaultOptionalCommandChecker(DVDCommandBlock*, DVDCommandCheckerCallback) {}
+
+DVDCommandChecker __DVDSetOptionalCommandChecker(DVDCommandChecker func) {
+ DVDCommandChecker old = checkOptionalCommand;
+ checkOptionalCommand = func;
+ return checkOptionalCommand;
+}
+
+void DVDInit(void) {
+ DVDDiskID* id;
+ s32 rv;
+
+ if (!DVDInitialized) {
+ OSRegisterVersion(__DVDVersion);
+ DVDInitialized = TRUE;
+ DVDLowInit();
+
+ if (!__OSInIPL && __OSLockedFlag == 0x80) {
+ rv = ESP_InitLib();
+
+ if (rv == 0) {
+ rv = ESP_DiGetTicketView(NULL, (ESTicketView*)__DVDTicketViewBuffer);
+ }
+
+ if (rv == 0) {
+ rv = ESP_DiGetTmd(NULL, &__DVDNumTmdBytes);
+ }
+
+ if (rv == 0) {
+ rv = ESP_DiGetTmd((ESTitleMeta*)__DVDTmdBuffer, &__DVDNumTmdBytes);
+ }
+
+ ESP_CloseLib();
+ }
+
+ __DVDFSInit();
+ __DVDClearWaitingQueue();
+
+ MotorState = 0;
+ bootInfo = (void*)OSPhysicalToCached(0);
+ IDShouldBe = &bootInfo->DVDDiskID;
+
+ OSInitThreadQueue(&__DVDThreadQueue);
+
+ DVDLowUnmaskStatusInterrupts();
+ DVDLowMaskCoverInterrupt();
+
+ if (bootInfo->magic == 0xE5207C22) {
+
+ } else if (bootInfo->magic == 0x0D15EA5E) {
+
+ } else {
+ FirstTimeInBootrom = TRUE;
+ }
+
+ memset(&__ErrorInfo, 0, sizeof(DVDErrorInfo));
+ id = (DVDDiskID*)OSPhysicalToCached(0);
+ memcpy(__ErrorInfo.gameName, id->gameName, 4);
+ __ErrorInfo.diskNumber = id->diskNumber;
+ __ErrorInfo.gameVersion = id->gameVersion;
+ __DVDLayoutFormat = 0;
+ DVDSetAutoFatalMessaging(TRUE);
+ }
+}
+
+static void stateReadingFST() {
+ LastState = (stateFunc)stateReadingFST;
+ ASSERTLINE(1084, ((u32)(bootInfo->FSTLocation) & (32 - 1)) == 0);
+ DVD_ASSERTMSGLINE(1093, bootInfo->FSTMaxLength >= BB2.FSTLength, "DVDChangeDisk(): FST in the new disc is too big. ");
+ DVDLowClearCoverInterrupt(NULL);
+ StampCommand(1, BB2.FSTPosition >> __DVDLayoutFormat, OSRoundUp32B(BB2.FSTLength << (~__DVDLayoutFormat & 2)));
+ DVDLowRead(bootInfo->FSTLocation, OSRoundUp32B(BB2.FSTLength << (~__DVDLayoutFormat & 2)), BB2.FSTPosition >> __DVDLayoutFormat, cbForStateReadingFST);
+}
+
+static u32 DmaCommand[1] = {0xFFFFFFFF};
+
+static void cbForStateReadingFST(u32 intType) {
+ DVDCommandBlock* finished;
+ StampIntType(intType);
+
+ if (intType == 16) {
+ stateTimeout();
+ return;
+ }
+
+ if (intType == 32) {
+ stateSecurityError();
+ return;
+ }
+
+ ASSERTLINE(1125, (intType & DVD_INTTYPE_CVR) == 0);
+
+ if (intType & DVD_INTTYPE_TC) {
+ ASSERTLINE(1130, (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(1157, intType == DVD_INTTYPE_DE);
+ stateGettingError();
+}
+
+static void FatalAlarmHandler(OSAlarm* alarm, OSContext* context) {
+ __DVDPrintFatalMessage();
+}
+
+static void cbForStateError(u32 intType) {
+ DVDCommandBlock* finished;
+
+ if (__DVDGetAutoFatalMessaging()) {
+ OSCreateAlarm(&FatalAlarm);
+ OSSetAlarm(&FatalAlarm, 1, FatalAlarmHandler);
+ } else {
+ executing->state = -1;
+
+ if (intType == 16) {
+ stateTimeout();
+ return;
+ }
+
+ if (intType == 32) {
+ stateSecurityError();
+ return;
+ }
+
+ 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 cbForStoreErrorCode1(s32 result, DVDCommandBlock* block) {
+ DVDLowStopMotor(FALSE, FALSE, cbForStateError);
+}
+
+static void stateError(u32 error) {
+ __DVDStoreErrorCode(error, cbForStoreErrorCode1);
+}
+
+static void cbForStoreErrorCode2(s32 result, DVDCommandBlock* block) {
+ DVDLowSetSpinupFlag(0);
+ DVDLowReset(cbForStateError);
+ ResetRequired = FALSE;
+ ResumeFromHere = 0;
+}
+
+static void stateTimeout() {
+ __DVDStoreErrorCode(0x01234568, cbForStoreErrorCode2);
+}
+
+static void stateSecurityError(void) {
+ __DVDStoreErrorCode(0x1234569, cbForStoreErrorCode2);
+}
+
+static void stateGettingError() {
+ StampCommand(39, 0, 0);
+ DVDLowRequestError(cbForStateGettingError);
+}
+
+static u32 CategorizeError(u32 error) {
+ if (error == 0x20400) {
+ LastError = error;
+ return 1;
+ }
+
+ error &= 0x00FFFFFF;
+ if (error == 0x62800 || error == 0x23A00 || error == 0x53000 || error == 0xB5A01) {
+ return 0;
+ }
+
+ if (error == 0x52000 && (executing->command == 37 || LastState == stateDownRotation)) {
+ return 0;
+ }
+
+ NumInternalRetry++;
+ 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 cbForStoreErrorCode3(s32 result, DVDCommandBlock* block) {
+ stateGoToRetry();
+}
+
+static void cbForStateGettingError(u32 intType) {
+ u32 error;
+ u32 status;
+ u32 errorCategory;
+ u32 resume;
+
+ StampIntType(intType);
+
+ if (intType == 16) {
+ stateTimeout();
+ return;
+ }
+
+ if (intType == 32) {
+ stateSecurityError();
+ return;
+ }
+
+ if (intType & 2) {
+ stateError(0x1234567);
+ return;
+ }
+
+ ASSERTLINE(1474, intType == DVD_INTTYPE_TC);
+
+ error = DVDLowGetImmBufferReg();
+ status = error & 0xff000000;
+
+ errorCategory = CategorizeError(error);
+
+ if (errorCategory == 1) {
+ stateError(error);
+ return;
+ }
+
+ if (errorCategory == 2 || errorCategory == 3) {
+ resume = 0;
+ } else {
+ if (status == 0x1000000) {
+ resume = 4;
+ } else if (status == 0x2000000) {
+ resume = 6;
+ } else if (status == 0x3000000) {
+ resume = 3;
+ } else if (status == 0) {
+ if (error == 0x53000) {
+ resume = 1;
+ }
+ else {
+ resume = 5;
+ }
+ } else {
+ resume = 5;
+ }
+ }
+
+ if (CheckCancel(resume))
+ return;
+
+ if (errorCategory == 2) {
+ __DVDStoreErrorCode(error, cbForStoreErrorCode3);
+ return;
+ }
+
+ if (errorCategory == 3) {
+ if ((error & 0x00ffffff) == 0x00031100) {
+ StampCommand(2, executing->offset, 0);
+ 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 if (status == 0) {
+ if (error == 0x53000) {
+ StampCommand(16, 0, 0);
+ DVDLowStopMotor(FALSE, FALSE, cbForStateCheckID1);
+ return;
+ } else {
+ stateError(0x1234567);
+ return;
+ }
+ } else {
+ stateError(0x1234567);
+ return;
+ }
+}
+
+static void cbForUnrecoveredError(u32 intType) {
+ StampIntType(intType);
+
+ if (intType == 16) {
+ stateTimeout();
+ return;
+ }
+
+ if (intType == 32) {
+ stateSecurityError();
+ return;
+ }
+
+ if (intType & 1) {
+ stateGoToRetry();
+ return;
+ }
+
+ ASSERTLINE(1606, intType == DVD_INTTYPE_DE);
+ StampCommand(39, 0, 0);
+ DVDLowRequestError(cbForUnrecoveredErrorRetry);
+}
+
+static void cbForUnrecoveredErrorRetry(u32 intType) {
+ StampIntType(intType);
+
+ if (intType == 16) {
+ stateTimeout();
+ return;
+ }
+
+ if (intType == 32) {
+ stateSecurityError();
+ return;
+ }
+
+ if (intType & 2) {
+ stateError(0x1234567);
+ } else {
+ stateError(DVDLowGetImmBufferReg());
+ }
+}
+
+static void stateGoToRetry() {
+ StampCommand(16, 0, 0);
+ DVDLowStopMotor(FALSE, FALSE, cbForStateGoToRetry);
+}
+
+static void cbForStateGoToRetry(u32 intType) {
+ StampIntType(intType);
+
+ if (intType == 16) {
+ stateTimeout();
+ return;
+ }
+
+ if (intType == 32) {
+ stateSecurityError();
+ return;
+ }
+
+ if (intType & 2) {
+ stateError(0x1234567);
+ return;
+ }
+
+ ASSERTLINE(1673, intType == DVD_INTTYPE_TC);
+ NumInternalRetry = 0;
+
+ if (CurrCommand == 4 || CurrCommand == 5 || CurrCommand == 13 || CurrCommand == 33 || CurrCommand == 34 || CurrCommand == 41 || CurrCommand == 42 || CurrCommand == 15 || CurrCommand == 37) {
+ ResetRequired = TRUE;
+ }
+
+ if (!CheckCancel(2)) {
+ executing->state = 11;
+ stateMotorStopped();
+ }
+}
+
+static void stateCheckID() {
+ switch(CurrCommand) {
+ case DVD_COMMAND_CHANGE_DISK:
+ ChangedDisc = FALSE;
+
+ if (DVDCompareDiskID(&CurrDiskID, executing->id)) {
+ memcpy(IDShouldBe, &CurrDiskID, sizeof(DVDDiskID));
+ executing->state = DVD_STATE_BUSY;
+ DCInvalidateRange(&BB2.bootFilePosition, 0x20);
+ NumInternalRetry = 0;
+ stateReadingTOC(executing);
+ } else {
+ StampCommand(16, 0, 0);
+ DVDLowStopMotor(FALSE, FALSE, cbForStateCheckID1);
+ }
+ break;
+ default:
+ if (memcmp(&CurrDiskID, IDShouldBe, sizeof(DVDDiskID)) != 0) {
+ StampCommand(16, 0, 0);
+ DVDLowStopMotor(FALSE, FALSE, cbForStateCheckID1);
+ } else {
+ NumInternalRetry = 0;
+ stateReadingTOC(executing);
+ }
+ break;
+ }
+}
+
+
+static ESTitleMeta* Tmd;
+
+static void cbForStateReadingTOC(u32 intType) {
+ StampIntType(intType);
+
+ if (intType == 16) {
+ stateTimeout();
+ return;
+ }
+
+ if (intType == 32) {
+ stateSecurityError();
+ return;
+ }
+
+ ASSERTLINE(1870, (intType & DVD_INTTYPE_CVR) == 0);
+
+ if (intType & 1) {
+ ASSERTLINE(1875, (intType & DVD_INTTYPE_DE) == 0);
+ NumInternalRetry = 0;
+ GameToc = (DVDGameTOC*)__DVDGameTocBuffer;
+ stateReadingPartitionInfo(executing);
+ } else {
+ ASSERTLINE(1887, intType == DVD_INTTYPE_DE);
+ stateGettingError();
+ }
+}
+
+static void stateReadingTOC(DVDCommandBlock* block) {
+ DVDLowClearCoverInterrupt(NULL);
+ StampCommand(33, 0x40000 >> 2, OSRoundUp32B(sizeof(DVDGameTOC)));
+ DVDLowUnencryptedRead(__DVDGameTocBuffer, OSRoundUp32B(sizeof(DVDGameTOC)), 0x10000, cbForStateReadingTOC);
+}
+
+static void cbForStateReadingPartitionInfo(u32 intType) {
+ StampIntType(intType);
+
+ if (intType == 16) {
+ stateTimeout();
+ return;
+ }
+
+ if (intType == 32) {
+ stateSecurityError();
+ return;
+ }
+
+ ASSERTLINE(1922, (intType & DVD_INTTYPE_CVR) == 0);
+
+ if (intType & 1) {
+ s16 i;
+ ASSERTLINE(1929, (intType & DVD_INTTYPE_DE) == 0);
+ NumInternalRetry = 0;
+ PartInfo = (DVDPartitionInfo*)__DVDPartInfoBuffer;
+ BootGameInfo = NULL;
+
+ if (*((u32*)OSPhysicalToCached(0x3198))) {
+ BootGameInfo = PartInfo;
+ BootGameInfo->type = *((u32*)OSPhysicalToCached(0x3194));
+ BootGameInfo->gamePartition = (DVDGamePartition*)*((u32*)OSPhysicalToCached(0x3198));
+ } else {
+ for (i = 0; i < GameToc->numGamePartitions; i++) {
+ if (PartInfo->type == __OSLaunchPartitionType) {
+ BootGameInfo = PartInfo;
+ }
+
+ PartInfo++;
+ }
+ }
+
+ if (BootGameInfo) {
+ switch (CurrCommand) {
+ case 3:
+ NumInternalRetry = 0;
+ stateOpenPartition(executing);
+ break;
+
+ default:
+ NumInternalRetry = 0;
+ stateOpenPartition2(executing);
+ break;
+ }
+ } else {
+ if (!CheckCancel(1)) {
+ executing->state = 6;
+ stateMotorStopped();
+ }
+ }
+ } else {
+ ASSERTLINE(1991, intType == DVD_INTTYPE_DE);
+ stateGettingError();
+ }
+}
+
+static void stateReadingPartitionInfo(DVDCommandBlock* block) {
+ DVDLowClearCoverInterrupt(0);
+ StampCommand(33, (0x40000 + OSRoundUp32B(sizeof(DVDGameTOC))) >> 2, OSRoundUp32B(sizeof(DVDPartitionInfo)));
+ DVDLowUnencryptedRead(__DVDPartInfoBuffer, OSRoundUp32B(sizeof(DVDPartitionInfo)), (0x40000 + OSRoundUp32B(sizeof(DVDGameTOC))) >> 2, cbForStateReadingPartitionInfo);
+}
+
+static void cbForStateOpenPartition(u32 intType) {
+ StampIntType(intType);
+
+ if (intType == 16) {
+ stateTimeout();
+ return;
+ }
+
+ if (intType == 32) {
+ stateSecurityError();
+ return;
+ }
+
+ ASSERTLINE(2028, (intType & DVD_INTTYPE_CVR) == 0);
+
+ if (intType & 1) {
+ ASSERTLINE(2033, (intType & DVD_INTTYPE_DE) == 0);
+ NumInternalRetry = 0;
+ stateCheckID2(executing);
+ } else {
+ ASSERTLINE(2043, intType == DVD_INTTYPE_DE);
+ stateGettingError();
+ }
+}
+
+static void stateOpenPartition(DVDCommandBlock* block) {
+ DVDLowClearCoverInterrupt(0);
+ StampCommand(34, (u32)BootGameInfo->gamePartition, 0);
+
+ if (__OSLockedFlag == 0x80) {
+ DVDLowOpenPartitionWithTmdAndTicketView((u32)BootGameInfo->gamePartition, (ESTicketView*)__DVDTicketViewBuffer, __DVDNumTmdBytes, (ESTitleMeta*)__DVDTmdBuffer, 0, NULL, cbForStateOpenPartition);
+ } else {
+ DVDLowOpenPartition((u32)BootGameInfo->gamePartition, NULL, 0, 0, (ESTitleMeta*)__DVDTmdBuffer, cbForStateOpenPartition);
+ }
+}
+
+static void cbForStateOpenPartition2(u32 intType) {
+ StampIntType(intType);
+
+ if (intType == 16) {
+ stateTimeout();
+ return;
+ }
+
+ if (intType == 32) {
+ stateSecurityError();
+ return;
+ }
+
+ ASSERTLINE(2090, (intType & DVD_INTTYPE_CVR) == 0);
+
+ if (intType & 1) {
+ ASSERTLINE(2095, (intType & DVD_INTTYPE_DE) == 0);
+ NumInternalRetry = 0;
+
+ if (!CheckCancel(0)) {
+ executing->state = 1;
+ stateBusy(executing);
+ }
+ } else {
+ ASSERTLINE(2109, intType == DVD_INTTYPE_DE);
+ stateGettingError();
+ }
+}
+
+static void stateOpenPartition2(DVDCommandBlock* block) {
+ DVDLowClearCoverInterrupt(0);
+ StampCommand(34, (u32)BootGameInfo->gamePartition, 0);
+
+ if (__OSLockedFlag == 0x80) {
+ DVDLowOpenPartitionWithTmdAndTicketView((u32)BootGameInfo->gamePartition, (ESTicketView*)__DVDTicketViewBuffer, __DVDNumTmdBytes, (ESTitleMeta*)__DVDTmdBuffer, 0, NULL, cbForStateOpenPartition2);
+ } else {
+ DVDLowOpenPartition((u32)BootGameInfo->gamePartition, NULL, 0, 0, (ESTitleMeta*)__DVDTmdBuffer, cbForStateOpenPartition2);
+ }
+}
+
+static void stateCheckID2(DVDCommandBlock* block) {
+ DVDLowClearCoverInterrupt(0);
+ StampCommand(1, (u32)(0x420 >> 2), OSRoundUp32B(sizeof(DVDBB2)));
+ DVDLowRead(&BB2, OSRoundDown32B(sizeof(DVDBB2)), (u32)(0x420 >> 2), cbForStateCheckID2);
+}
+
+static void stateCheckID3() {
+ DVDLowAudioBufferConfig(IDShouldBe->streaming, 0xA, cbForStateCheckID3);
+}
+
+static void stateCheckID2a() {
+ DVDLowAudioBufferConfig(IDShouldBe->streaming, 0xA, cbForStateCheckID2a);
+}
+
+static void cbForStateCheckID2a(u32 intType) {
+ if (intType == 16) {
+ 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 cbForStateCheckID1(u32 intType) {
+ StampIntType(intType);
+
+ if (intType == 16) {
+ stateTimeout();
+ return;
+ }
+
+ if (intType == 32) {
+ stateSecurityError();
+ return;
+ }
+
+ if (intType & DVD_INTTYPE_DE) {
+ stateError(0x01234567);
+ return;
+ }
+
+ ASSERTLINE(2184, intType == DVD_INTTYPE_TC);
+ NumInternalRetry = 0;
+
+ if (CheckCancel(1) == FALSE) {
+ executing->state = DVD_STATE_WRONG_DISK;
+ stateMotorStopped();
+ }
+}
+
+static void cbForStateCheckID2(u32 intType) {
+ StampIntType(intType);
+
+ if (intType == 16) {
+ stateTimeout();
+ return;
+ }
+
+ if (intType == 32) {
+ stateSecurityError();
+ return;
+ }
+
+ ASSERTLINE(2213, (intType & DVD_INTTYPE_CVR) == 0);
+
+ if (intType & DVD_INTTYPE_TC) {
+ ASSERTLINE(2218, (intType & DVD_INTTYPE_DE) == 0);
+ NumInternalRetry = 0;
+ stateReadingFST();
+ return;
+ }
+
+ ASSERTLINE(2234, intType == DVD_INTTYPE_DE);
+ stateGettingError();
+}
+
+static void cbForStateCheckID3(u32 intType) {
+ if (intType == 16) {
+ stateTimeout();
+ return;
+ }
+
+ ASSERTLINE(1336, (intType & DVD_INTTYPE_CVR) == 0);
+
+ if (intType & DVD_INTTYPE_TC) {
+ ASSERTLINE(1341, (intType & DVD_INTTYPE_DE) == 0);
+ NumInternalRetry = 0;
+ if (CheckCancel(0) == FALSE) {
+ executing->state = DVD_STATE_BUSY;
+ stateBusy(executing);
+ }
+ return;
+ }
+
+ ASSERTLINE(1355, 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() {
+ DVDCommandBlock* finished;
+ MotorState = 1;
+
+ switch(CurrCommand) {
+ case DVD_COMMAND_BSREAD:
+ case DVD_COMMAND_READID:
+ case DVD_COMMAND_AUDIO_BUFFER_CONFIG:
+ case DVD_COMMAND_BS_CHANGE_DISK:
+ case 33:
+ case 34:
+ case 37:
+ case 41:
+ case 42:
+ __DVDClearWaitingQueue();
+ finished = executing;
+ executing = &DummyCommandBlock;
+ if (finished->callback) {
+ finished->callback(-4, finished);
+ }
+ stateReady();
+ break;
+ case 32:
+ MotorState = 0;
+ case 35:
+ case 38:
+ case 36:
+ executing->state = 1;
+ stateBusy(executing);
+ break;
+ case 1:
+ case 2:
+ if (__OSInIPL) {
+ break;
+ }
+ default:
+ MotorState = 0;
+ DVDLowSetSpinupFlag(1);
+ DVDLowReset(cbForStateReset);
+ break;
+ }
+}
+
+static void ResetAlarmHandler(OSAlarm* alarm, OSContext* context) {
+ if (__OSDeviceCode == (u16)(0x8000 | 0x003)) {
+ LastState = stateDownRotation;
+ stateDownRotation(executing);
+ } else {
+ DCInvalidateRange(&CurrDiskID, sizeof(DVDDiskID));
+ LastState = stateCoverClosed_CMD;
+ stateCoverClosed_CMD(executing);
+ }
+}
+
+static void cbForStateReset(u32 intType) {
+ if (intType == 16) {
+ stateTimeout();
+ return;
+ }
+
+ if (intType == 32) {
+ stateSecurityError();
+ return;
+ }
+
+ ASSERTLINE(2428, (intType & DVD_INTTYPE_CVR) == 0);
+
+ if (intType & 1) {
+ ASSERTLINE(2433, (intType & DVD_INTTYPE_DE) == 0);
+ LastResetEnd = __OSGetSystemTime();
+ ResetRequired = FALSE;
+ ResumeFromHere = 0;
+ OSCreateAlarm(&ResetAlarm);
+ OSSetAlarm(&ResetAlarm, DVD_RESETCOVER_TIMELAG_TICKS2, ResetAlarmHandler);
+ } else {
+ ASSERTLINE(2447, intType == DVD_INTTYPE_DE);
+ stateGettingError();
+ }
+}
+
+static void stateDownRotation(DVDCommandBlock* block) {
+ DVDLowClearCoverInterrupt(0);
+ StampCommand(37, 0, 0);
+ DVDLowSetMaximumRotation(0x20000, cbForStateDownRotation);
+}
+
+static void cbForStateDownRotation(u32 intType) {
+ StampIntType(intType);
+
+ if (intType == 16) {
+ stateTimeout();
+ return;
+ }
+
+ if (intType == 32) {
+ stateSecurityError();
+ return;
+ }
+
+ ASSERTLINE(2480, (intType & DVD_INTTYPE_CVR) == 0);
+
+ if (intType & 1) {
+ ASSERTLINE(2485, (intType & DVD_INTTYPE_DE) == 0);
+ DCInvalidateRange(&CurrDiskID, sizeof(DVDDiskID));
+ LastState = stateCoverClosed_CMD;
+ stateCoverClosed_CMD(executing);
+ } else {
+ ASSERTLINE(2494, intType == DVD_INTTYPE_DE);
+ stateGettingError();
+ }
+}
+
+static void stateCoverClosed_CMD(DVDCommandBlock* command) {
+ if (CurrCommand == 40) {
+ NumInternalRetry = 0;
+
+ if (!CheckCancel(0)) {
+ executing->state = 1;
+ stateBusy(executing);
+ }
+ } else {
+ DVDLowClearCoverInterrupt(0);
+ StampCommand(5, 0, sizeof(DVDDiskID));
+ DVDLowReadDiskID(&CurrDiskID, cbForStateCoverClosed);
+ }
+}
+
+static void cbForStateCoverClosed(u32 intType) {
+ StampIntType(intType);
+
+ if (intType == 16) {
+ stateTimeout();
+ return;
+ }
+
+ if (intType == 32) {
+ stateSecurityError();
+ return;
+ }
+
+ ASSERTLINE(2542, (intType & DVD_INTTYPE_CVR) == 0);
+
+ if (intType & DVD_INTTYPE_TC) {
+ ASSERTLINE(2547, (intType & DVD_INTTYPE_DE) == 0);
+ NumInternalRetry = 0;
+ stateCheckID();
+ return;
+ }
+
+ ASSERTLINE(2559, intType == DVD_INTTYPE_DE);
+ stateGettingError();
+}
+
+static void cbForPrepareCoverRegister(u32 intType) {
+ PreparingCover = FALSE;
+
+ if (WaitingForCoverClose) {
+ if (!(DVDLowGetCoverRegister() & 1)) {
+ OSCancelAlarm(&CoverAlarm);
+ WaitingForCoverClose = FALSE;
+ cbForStateMotorStopped(4);
+ }
+ return;
+ }
+
+ if (DVDLowGetCoverRegister() & 1) {
+ WaitingForCoverOpen = FALSE;
+ WaitingForCoverClose = TRUE;
+
+ if (MotorState == 2) {
+ executing->state = 12;
+ }
+ else {
+ executing->state = 5;
+ }
+ } else if (DVDLowGetCoverRegister() & 4) {
+ OSCancelAlarm(&CoverAlarm);
+ WaitingForCoverOpen = FALSE;
+ DVDLowClearCoverInterrupt(0);
+ cbForStateMotorStopped(4);
+ }
+}
+
+static void CoverAlarmHandler(OSAlarm* alarm, OSContext* context) {
+ if (!PreparingCover) {
+ PreparingCover = TRUE;
+ DVDLowPrepareCoverRegister(cbForPrepareCoverRegister);
+ }
+}
+
+static void stateMotorStopped() {
+ MotorState = 1;
+
+ if (WaitingForCoverOpen || WaitingForCoverClose) {
+ return;
+ }
+
+ WaitingForCoverOpen = TRUE;
+ OSCreateAlarm(&CoverAlarm);
+ OSSetPeriodicAlarm(&CoverAlarm, OSGetTick(), OSMillisecondsToTicks(100), CoverAlarmHandler);
+}
+
+static void cbForStateMotorStopped(u32 intType) {
+ ASSERTLINE(2685, intType == DVD_INTTYPE_CVR);
+ WaitingForCoverClose = FALSE;
+
+ if (CurrCommand == 3) {
+ ChangedDisc = TRUE;
+ }
+
+ if (MotorState == 2) {
+ if (executing) {
+ executing->state = 12;
+ }
+
+ return;
+ }
+
+ DVDLowMaskCoverInterrupt();
+
+ if (executing) {
+ executing->state = 3;
+ stateCoverClosed();
+ } else {
+ ResumeFromHere = 7;
+ }
+}
+
+static void stateReady() {
+ DVDCommandBlock* finished;
+
+ if (PauseFlag != 0) {
+ PausingFlag = 1;
+ executing = NULL;
+ return;
+ }
+
+ if (__DVDCheckWaitingQueue() == 0) {
+ executing = NULL;
+ return;
+ }
+
+ executing = __DVDPopWaitingQueue();
+
+ if (FatalErrorFlag) {
+ executing->state = DVD_STATE_FATAL_ERROR;
+ finished = executing;
+ executing = &DummyCommandBlock;
+ if (finished->callback) {
+ (*finished->callback)(-1, finished);
+ }
+
+ stateReady();
+ return;
+ }
+
+ CurrCommand = executing->command;
+ if (CurrCommand == 32 || CurrCommand == 14 || CurrCommand == 35) {
+ ResumeFromHere = 0;
+ }
+
+ if (ResumeFromHere != 0) {
+ switch (ResumeFromHere) {
+ case 2:
+ executing->state = DVD_STATE_RETRY;
+ stateMotorStopped();
+ break;
+ case 3:
+ executing->state = DVD_STATE_NO_DISK;
+ stateMotorStopped();
+ break;
+ case 4:
+ executing->state = DVD_STATE_COVER_OPEN;
+ stateMotorStopped();
+ break;
+ case 1:
+ case 6:
+ case 7:
+ executing->state = DVD_STATE_COVER_CLOSED;
+ stateCoverClosed();
+ break;
+ case 5:
+ stateError(CancelLastError);
+ break;
+ }
+
+ ResumeFromHere = 0;
+ return;
+ }
+
+ switch (MotorState) {
+ case 2:
+ if (MotorStopped) {
+ executing->state = 12;
+ }
+ else {
+ executing->state = 3;
+ stateCoverClosed();
+ }
+ break;
+ case 0:
+ executing->state = 1;
+ stateBusy(executing);
+ break;
+ case 1:
+ default:
+ stateCoverClosed();
+ break;
+ }
+}
+
+static void stateBusy(DVDCommandBlock* block) {
+ DVDCommandBlock* finished;
+ LastState = stateBusy;
+
+ switch (block->command) {
+ case 5:
+ case 2:
+ case 3:
+ case 15:
+ case 14:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 16:
+ case 34:
+ case 37:
+ case 41:
+ case 42:
+ StampCommand(block->command, block->offset, block->length);
+ break;
+ default:
+ break;
+ }
+
+ switch (block->command) {
+ case 5:
+ DVDLowClearCoverInterrupt(0);
+ block->currTransferSize = sizeof(DVDDiskID);
+ DVDLowReadDiskID(block->addr, cbForStateBusy);
+ break;
+ case 1:
+ case 4:
+ if (block->length == 0) {
+ finished = executing;
+ executing = &DummyCommandBlock;
+ finished->state = 0;
+
+ if (finished->callback) {
+ (finished->callback)(0, finished);
+ }
+
+ stateReady();
+ } else {
+ DVDLowClearCoverInterrupt(0);
+ block->currTransferSize = (block->length - block->transferredSize > 0x80000) ? 0x80000 : (block->length - block->transferredSize);
+ StampCommand(block->command, ((block->offset) + (block->transferredSize >> 2)), block->currTransferSize);
+ DVDLowRead((void*)((u8*)block->addr + block->transferredSize), block->currTransferSize, ((block->offset) + (block->transferredSize >> 2)), cbForStateBusy);
+ }
+ break;
+ case 2:
+ DVDLowClearCoverInterrupt(0);
+ DVDLowSeek(block->offset, cbForStateBusy);
+ break;
+ case 3:
+ DVDLowStopMotor(FALSE, FALSE, cbForStateBusy);
+ break;
+ case 15:
+ DVDLowStopMotor(FALSE, FALSE, cbForStateBusy);
+ break;
+ case 13:
+ DVDLowClearCoverInterrupt(0);
+ DVDLowAudioBufferConfig(block->offset, block->length, cbForStateBusy);
+ break;
+ case 14:
+ DVDLowClearCoverInterrupt(0);
+ block->currTransferSize = sizeof(DVDDriveInfo);
+ DVDLowInquiry(block->addr, cbForStateBusy);
+ break;
+ case 16:
+ DVDLowClearCoverInterrupt(0);
+ DVDLowStopMotor(FALSE, FALSE, cbForStateBusy);
+ break;
+ case 32:
+ DVDLowSetSpinupFlag(1);
+ DVDLowReset(cbForStateBusy);
+ break;
+ case 33:
+ if (block->length == 0) {
+ finished = executing;
+ executing = &DummyCommandBlock;
+ finished->state = 0;
+
+ if (finished->callback) {
+ (finished->callback)(0, finished);
+ }
+
+ stateReady();
+ } else {
+ DVDLowClearCoverInterrupt(0);
+ block->currTransferSize = (block->length - block->transferredSize > 0x80000) ? 0x80000 : (block->length - block->transferredSize);
+ StampCommand(block->command, ((block->offset) + (block->transferredSize >> 2)), block->currTransferSize);
+ DVDLowUnencryptedRead((void*)((u8*)block->addr + block->transferredSize), block->currTransferSize, ((block->offset) + (block->transferredSize >> 2)), cbForStateBusy);
+ }
+ break;
+ case 34:
+ DVDLowClearCoverInterrupt(0);
+ DVDLowOpenPartition(block->offset, NULL, 0, 0, (ESTitleMeta*)block->addr, cbForStateBusy);
+ break;
+ case 35:
+ DVDLowClearCoverInterrupt(0);
+ DVDLowClosePartition(cbForStateBusy);
+ break;
+ case 38:
+ DVDLowPrepareCoverRegister(cbForStateBusy);
+ break;
+ case 36:
+ DVDLowPrepareCoverRegister(cbForStateBusy);
+ break;
+ case 37:
+ DVDLowClearCoverInterrupt(0);
+ DVDLowSetMaximumRotation(0x20000, cbForStateBusy);
+ break;
+ case 40:
+ DVDLowClearCoverInterrupt(0);
+ block->addr = &CurrDiskID;
+ block->currTransferSize = sizeof(DVDDiskID);
+ DVDLowReadDiskID(block->addr, cbForStateBusy);
+ break;
+ case 41:
+ {
+ DVDPartitionParams* params;
+ DVDLowClearCoverInterrupt(0);
+ params = block->addr;
+
+ if (!params->numTmdBytes && !params->numCertBytes) {
+ DVDLowGetNoDiscBufferSizes(block->offset, &params->numTmdBytes, &params->numCertBytes, cbForStateBusy);
+ } else {
+ DVDLowGetNoDiscOpenPartitionParams(block->offset, &params->ticket, &params->numTmdBytes,
+ &params->tmd, &params->numCertBytes, params->certificates,
+ &params->dataWordOffset, params->h3Hash, cbForStateBusy);
+ }
+ break;
+ }
+ case 42:
+ {
+ DVDPartitionParams* params;
+ DVDLowClearCoverInterrupt(0);
+ params = block->addr;
+ DVDLowOpenPartitionWithTmdAndTicketView(block->offset, &params->ticketView, params->numTmdBytes, &params->tmd,
+ params->numCertBytes, params->certificates, cbForStateBusy);
+ break;
+ }
+ default:
+ checkOptionalCommand(block, cbForStateBusy);
+ break;
+ }
+}
+
+static u32 ImmCommand[3] = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
+
+void __DVDSetImmCommand(u32 command) {
+ static u32 immCount;
+ ASSERTLINE(1790, immCount < sizeof(ImmCommand)/sizeof(ImmCommand[0]));
+ ImmCommand[immCount++] = command;
+}
+
+void __DVDSetDmaCommand(u32 command) {
+ static u32 dmaCount;
+ ASSERTLINE(1798, dmaCount < sizeof(DmaCommand)/sizeof(DmaCommand[0]));
+ DmaCommand[dmaCount++] = command;
+}
+
+static BOOL IsImmCommandWithResult(u32 command) {
+ u32 i;
+
+ if (command == 9 || command == 10 || command == 11 || command == 12) {
+ return 1;
+ }
+
+ for (i = 0; i < 3; i++) {
+ if (command == ImmCommand[i]) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static int IsDmaCommand(u32 command) {
+ u32 i;
+
+ if (command == 1 || command == 4 || command == 5 || command == 33 || command == 14) {
+ return 1;
+ }
+
+ for (i = 0; i < 1; i++) {
+ if (command == DmaCommand[i]) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void cbForStateBusy(u32 intType) {
+ DVDCommandBlock* finished;
+
+ StampIntType(intType);
+
+ if (intType == 16) {
+ stateTimeout();
+ return;
+ }
+
+ if (intType == 32) {
+ stateSecurityError();
+ return;
+ }
+
+ if ((CurrCommand == DVD_COMMAND_CHANGE_DISK) || (CurrCommand == DVD_COMMAND_BS_CHANGE_DISK)) {
+ if (intType & DVD_INTTYPE_DE) {
+ stateError(0x01234567);
+ return;
+ }
+
+ ASSERTLINE(3278, intType == DVD_INTTYPE_TC);
+ NumInternalRetry = 0;
+
+ if (CurrCommand == DVD_COMMAND_BS_CHANGE_DISK) {
+ ResetRequired = 1;
+ }
+
+ if (CheckCancel(7)) {
+ return;
+ }
+
+ if (MotorState != 2) {
+ executing->state = 7;
+ stateMotorStopped();
+ }
+ return;
+ }
+
+ ASSERTLINE(3301, (intType & DVD_INTTYPE_CVR) == 0);
+
+ if (IsDmaCommand(CurrCommand)) {
+ executing->transferredSize += (intType & (8 | 1)) ? executing->currTransferSize : 0;
+ }
+
+ if (Breaking) {
+ Breaking = FALSE;
+ 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(3345, (intType & DVD_INTTYPE_DE) == 0);
+ NumInternalRetry = 0;
+
+ if (CurrCommand == 0x10) {
+ if (executing->offset) {
+ MotorState = 2;
+ } else {
+ MotorState = 1;
+ }
+
+ finished = executing;
+ executing = &DummyCommandBlock;
+ finished->state = 0;
+
+ if (finished->callback != 0) {
+ (*finished->callback)(0, finished);
+ }
+
+ stateReady();
+ return;
+ }
+
+ if (CurrCommand == 32) {
+ LastResetEnd = __OSGetSystemTime();
+
+ ResetRequired = FALSE;
+ ResumeFromHere = 0;
+
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 0;
+ if (finished->callback) {
+ (finished->callback)(0, finished);
+ }
+
+ stateReady();
+ return;
+ }
+
+ if (CheckCancel(0) != FALSE) {
+ return;
+ }
+
+ if (CurrCommand == 38) {
+ s32 retVal;
+ u32 coverReg;
+
+ coverReg = DVDLowGetCoverRegister();
+
+ if (__OSGetSystemTime() - LastResetEnd < DVD_RESETCOVER_TIMELAG_TICKS2) {
+ retVal = 0;
+ } else if (coverReg & 1) {
+ retVal = 1;
+ } else {
+ retVal = 2;
+ }
+
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 0;
+ finished->offset = (u32)retVal;
+ if (finished->callback) {
+ (finished->callback)(retVal, finished);
+ }
+
+ stateReady();
+ return;
+ }
+
+ if (CurrCommand == 36) {
+ s32 retVal;
+ u32 coverReg;
+
+ coverReg = DVDLowGetCoverRegister();
+ if ((((u32)(coverReg) & 0x00000004) >> 2) || (coverReg & 1)) {
+ retVal = FALSE;
+ } else {
+ if (ResumeFromHere != 0) {
+ retVal = FALSE;
+ }
+ else {
+ retVal = TRUE;
+ }
+ }
+
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 0;
+ finished->offset = (u32)retVal;
+
+ if (finished->callback) {
+ (finished->callback)(retVal, finished);
+ }
+
+ stateReady();
+ return;
+ }
+
+ if (CurrCommand == 40) {
+ if (DVDCompareDiskID(&CurrDiskID, executing->id)) {
+ memcpy(IDShouldBe, &CurrDiskID, sizeof(DVDDiskID));
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 0;
+ finished->offset = (u32)TRUE;
+ if (finished->callback) {
+ (finished->callback)(TRUE, finished);
+ }
+
+ NumInternalRetry = 0;
+
+ stateReady();
+ return;
+ } else {
+ StampCommand(16, 0, 0);
+ DVDLowStopMotor(FALSE, FALSE, cbForStateCheckID1);
+ return;
+ }
+ }
+
+ if (CurrCommand == 41) {
+ DVDPartitionParams* params;
+
+ params = (DVDPartitionParams*)(executing->addr);
+ if (!params->dataWordOffset) {
+ stateBusy(executing);
+ return;
+ }
+
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 0;
+ if (finished->callback) {
+ (finished->callback)(0, finished);
+ }
+
+ stateReady();
+ return;
+ }
+
+ if (IsDmaCommand(CurrCommand)) {
+ if (executing->transferredSize != executing->length) {
+ stateBusy(executing);
+ return;
+ }
+
+ finished = executing;
+ executing = &DummyCommandBlock;
+ finished->state = DVD_STATE_END;
+ if (finished->callback) {
+ finished->callback(finished->transferredSize, finished);
+ }
+
+ stateReady();
+ return;
+ } else if (IsImmCommandWithResult(CurrCommand)) {
+ s32 result;
+ if (CurrCommand == DVD_COMMAND_REQUEST_START_ADDR || CurrCommand == DVD_COMMAND_REQUEST_PLAY_ADDR) {
+ result = (s32)(DVDLowGetImmBufferReg() << 2);
+ } else {
+ result = (s32)DVDLowGetImmBufferReg();
+ }
+
+ finished = executing;
+ executing = &DummyCommandBlock;
+ finished->state = DVD_STATE_END;
+ if (finished->callback) {
+ finished->callback(result, finished);
+ }
+
+ stateReady();
+ return;
+ } else {
+ finished = executing;
+ executing = &DummyCommandBlock;
+ finished->state = DVD_STATE_END;
+ if (finished->callback) {
+ finished->callback(0, finished);
+ }
+
+ stateReady();
+ return;
+ }
+ } else {
+ ASSERTLINE(3676, intType == DVD_INTTYPE_DE);
+
+ if (CurrCommand == 14) {
+ stateError(0x01234567);
+ return;
+ }
+
+ if ((CurrCommand == 1 || CurrCommand == 4 || CurrCommand == 5 || CurrCommand == 33 || CurrCommand == 14)
+ && (executing->transferredSize == executing->length)) {
+
+ if (CheckCancel(0)) {
+ return;
+ }
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = DVD_STATE_END;
+ if (finished->callback) {
+ (finished->callback)((s32)finished->transferredSize, finished);
+ }
+ stateReady();
+ return;
+ }
+
+ stateGettingError();
+ }
+}
+
+void* __DVDGetIssueCommandAddr(void) {
+ return issueCommand;
+}
+
+static int issueCommand(s32 prio, DVDCommandBlock* block) {
+ BOOL level;
+ int result;
+
+ if (autoInvalidation != 0 && (block->command == DVD_COMMAND_READ || block->command == DVD_COMMAND_BSREAD
+ || block->command == DVD_COMMAND_READID || block->command == 33 || block->command == DVD_COMMAND_INQUIRY)) {
+ DCInvalidateRange(block->addr, block->length);
+ }
+
+ level = OSDisableInterrupts();
+#if DEBUG
+ if (executing == block || (block->state == DVD_STATE_WAITING && __DVDIsBlockInWaitingQueue(block))) {
+ OSPanic(__FILE__, 3764, "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(DVDCommandBlock* block, void* addr, s32 length, s32 offset, DVDCBCallback callback, s32 prio) {
+ int idle;
+
+ ASSERTMSGLINE(3804, block, "DVDReadAbsAsync(): null pointer is specified to command block address.");
+ ASSERTMSGLINE(3805, addr, "DVDReadAbsAsync(): null pointer is specified to addr.");
+ ASSERTMSGLINE(3807, !OFFSET(addr, 32), "DVDReadAbsAsync(): address must be aligned with 32 byte boundary.");
+ ASSERTMSGLINE(3809, !(length & (32-1)), "DVDReadAbsAsync(): length must be a multiple of 32.");
+
+ block->command = DVD_COMMAND_READ;
+ block->addr = addr;
+ block->length = length;
+ block->offset = offset;
+ block->transferredSize = 0;
+ block->callback = callback;
+
+ idle = issueCommand(prio, block);
+ ASSERTMSGLINE(3819, idle, "DVDReadAbsAsync(): command block is used for processing previous request.");
+ return idle;
+}
+
+int DVDSeekAbsAsyncPrio(DVDCommandBlock* block, s32 offset, DVDCBCallback callback, s32 prio) {
+ int idle;
+
+ ASSERTMSGLINE(2233, block, "DVDSeekAbs(): null pointer is specified to command block address.");
+
+ block->command = DVD_COMMAND_SEEK;
+ block->offset = offset;
+ block->callback = callback;
+
+ idle = issueCommand(prio, block);
+ ASSERTMSGLINE(2242, idle, "DVDSeekAbs(): command block is used for processing previous request.");
+ return idle;
+}
+
+int DVDReadAbsAsyncForBS(DVDCommandBlock* block, void* addr, s32 length, s32 offset, DVDCBCallback callback) {
+ int idle;
+
+ ASSERTMSGLINE(2272, block, "DVDReadAbsAsyncForBS(): null pointer is specified to command block address.");
+ ASSERTMSGLINE(2273, addr, "DVDReadAbsAsyncForBS(): null pointer is specified to addr.");
+ ASSERTMSGLINE(2275, !OFFSET(addr, 32), "DVDReadAbsAsyncForBS(): address must be aligned with 32 byte boundary.");
+ ASSERTMSGLINE(2277, !(length & (32-1)), "DVDReadAbsAsyncForBS(): length must be a multiple of 32.");
+
+ block->command = DVD_COMMAND_BSREAD;
+ block->addr = addr;
+ block->length = length;
+ block->offset = offset;
+ block->transferredSize = 0;
+ block->callback = callback;
+
+ idle = issueCommand(2, block);
+ ASSERTMSGLINE(2289, idle, "DVDReadAbsAsyncForBS(): command block is used for processing previous request.");
+ return idle;
+}
+
+int DVDReadDiskID(DVDCommandBlock* block, DVDDiskID* diskID, DVDCBCallback callback) {
+ int idle;
+
+ ASSERTMSGLINE(2312, block, "DVDReadDiskID(): null pointer is specified to command block address.");
+ ASSERTMSGLINE(2313, diskID, "DVDReadDiskID(): null pointer is specified to id address.");
+ ASSERTMSGLINE(2315, !OFFSET(diskID, 32), "DVDReadDiskID(): id must be aligned with 32 byte boundary.");
+
+ block->command = DVD_COMMAND_READID;
+ block->addr = diskID;
+ block->length = 0x20;
+ block->offset = 0;
+ block->transferredSize = 0;
+ block->callback = callback;
+
+ idle = issueCommand(2, block);
+ ASSERTMSGLINE(2325, idle, "DVDReadDiskID(): command block is used for processing previous request.");
+ return idle;
+}
+
+void __DVDAudioBufferConfig(DVDCommandBlock* block, u32 enable, u32 size, DVDCBCallback callback) {
+ int idle;
+
+ block->command = DVD_COMMAND_AUDIO_BUFFER_CONFIG;
+ block->offset = enable;
+ block->length = size;
+ block->callback = callback;
+ idle = issueCommand(2, block);
+}
+
+int DVDChangeDiskAsyncForBS(DVDCommandBlock* block, DVDCBCallback callback) {
+ int idle;
+
+ ASSERTMSGLINE(2869, 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(2875, 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->id = id;
+ block->callback = callback;
+ DCInvalidateRange(bootInfo->FSTLocation, bootInfo->FSTMaxLength);
+
+ idle = issueCommand(2, block);
+ ASSERTMSGLINE(2913, idle, "DVDChangeDisk(): command block is used for processing previous request.");
+ return idle;
+}
+
+s32 DVDChangeDisk(DVDCommandBlock* block, DVDDiskID* id) {
+ int result;
+ s32 state;
+ BOOL enabled;
+ s32 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(s32 result, DVDCommandBlock* block) {
+ OSWakeupThread(&__DVDThreadQueue);
+}
+
+int DVDStopMotorAsync(DVDCommandBlock* block, DVDCBCallback callback) {
+ int idle;
+ ASSERTMSGLINE(2996, block, "DVDStopMotor(): Null address was specified for block");
+
+ block->command = DVD_COMMAND_UNK_16;
+ block->callback = callback;
+
+ idle = issueCommand(2, block);
+ ASSERTMSGLINE(3002, idle, "DVDStopMotor(): command block is used for processing previous request.");
+ return idle;
+}
+
+s32 DVDStopMotor(DVDCommandBlock* block) {
+ int result;
+ s32 state;
+ BOOL enabled;
+ s32 retVal;
+
+ result = DVDStopMotorAsync(block, cbForStopMotorSync);
+ 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 cbForStopMotorSync(s32 result, DVDCommandBlock* block) {
+ OSWakeupThread(&__DVDThreadQueue);
+}
+
+int DVDInquiryAsync(DVDCommandBlock* block, DVDDriveInfo* info, DVDCBCallback callback) {
+ int idle;
+
+ ASSERTMSGLINE(4706, block, "DVDInquiry(): Null address was specified for block");
+ ASSERTMSGLINE(4707, info, "DVDInquiry(): Null address was specified for info");
+ ASSERTMSGLINE(4709, !OFFSET(info, 32), "DVDInquiry(): Address for info is not 32 bytes aligned");
+
+ block->command = DVD_COMMAND_INQUIRY;
+ block->addr = info;
+ block->length = 0x20;
+ block->transferredSize = 0;
+ block->callback = callback;
+ idle = issueCommand(2, block);
+ return idle;
+}
+
+s32 DVDInquiry(DVDCommandBlock* block, DVDDriveInfo* info) {
+ int result;
+ s32 state;
+ BOOL enabled;
+ s32 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->transferredSize;
+ 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(s32 result, DVDCommandBlock* block) {
+ OSWakeupThread(&__DVDThreadQueue);
+}
+
+void DVDReset(void) {
+ //DVDLowReset();
+ __DIRegs[0] = 0x2A;
+ __DIRegs[1] = __DIRegs[1];
+ ResetRequired = 0;
+ ResumeFromHere = 0;
+}
+
+int DVDResetRequired(void) {
+ return ResetRequired;
+}
+
+s32 DVDGetCommandBlockStatus(const DVDCommandBlock* block) {
+ BOOL enabled;
+ s32 retVal;
+ DVDCommandBlock* next;
+
+ ASSERTMSGLINE(4907, block, "DVDGetCommandBlockStatus(): null pointer is specified to command block address.");
+ enabled = OSDisableInterrupts();
+
+ if (((volatile DVDCommandBlock*)block)->state == 3) {
+ retVal = 1;
+ } else if (((volatile DVDCommandBlock*)block)->state == 5) {
+ retVal = 4;
+ } else if (executing == &__DVDStopMotorCommandBlock) {
+ next = __DVDGetNextWaitingQueue();
+ if (next) {
+ if(block == next) {
+ retVal = 1;
+ } else {
+ retVal = ((volatile DVDCommandBlock*)block)->state;
+ }
+ } else {
+ if (block == &__DVDStopMotorCommandBlock) {
+ retVal = 0;
+ } else {
+ retVal = ((volatile DVDCommandBlock*)block)->state;
+ }
+ }
+ } else {
+ retVal = ((volatile DVDCommandBlock*)block)->state;
+ }
+
+ OSRestoreInterrupts(enabled);
+ return retVal;
+}
+
+s32 DVDGetDriveStatus(void) {
+ 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((DVDCommandBlock*)executing);
+ }
+ }
+ }
+ OSRestoreInterrupts(enabled);
+ return retVal;
+}
+
+BOOL DVDSetAutoInvalidation(BOOL autoInval) {
+ BOOL prev;
+
+ prev = autoInvalidation;
+ autoInvalidation = autoInval;
+ return prev;
+}
+
+void DVDPause(void) {
+ BOOL level;
+
+ level = OSDisableInterrupts();
+ PauseFlag = 1;
+ if (executing == NULL) {
+ PausingFlag = 1;
+ }
+ OSRestoreInterrupts(level);
+}
+
+void DVDResume(void) {
+ BOOL level;
+
+ level = OSDisableInterrupts();
+ PauseFlag = 0;
+ if (PausingFlag != 0) {
+ PausingFlag = 0;
+ stateReady();
+ }
+ OSRestoreInterrupts(level);
+}
+
+int DVDCancelAsync(DVDCommandBlock* block, DVDCBCallback callback) {
+ BOOL enabled;
+ DVDCommandBlock* finished;
+
+ 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 == 4 || block->command == 33 || block->command == 34 || block->command == 41 || block->command == 42 || block->command == 1) {
+ __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 5:
+ case 4:
+ case 13:
+ case 15:
+ case 33:
+ case 34:
+ case 37:
+ case 41:
+ case 42:
+ if (callback)
+ (*callback)(0, block);
+ break;
+ case 1:
+ case 2:
+ if (__OSInIPL) {
+ finished = executing;
+ executing = &DummyCommandBlock;
+ block->state = 10;
+
+ if (block->callback) {
+ (block->callback)(-3, block);
+ }
+
+ if (callback) {
+ (*callback)(0, block);
+ }
+
+ stateReady();
+ 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:
+ if (!(WaitingForCoverClose || WaitingForCoverOpen)) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ if (WaitingForCoverOpen) {
+ OSCancelAlarm(&CoverAlarm);
+ WaitingForCoverOpen = 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;
+
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ block->state = DVD_STATE_CANCELED;
+ if (block->callback) {
+ (block->callback)(-3, block);
+ }
+
+ if (callback) {
+ (callback)(0, block);
+ }
+ stateReady();
+ break;
+ case 12:
+ finished = executing;
+ executing = &DummyCommandBlock;
+ 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) {
+ int result;
+ s32 state;
+ u32 command;
+ BOOL enabled;
+
+ result = DVDCancelAsync((void*)block, cbForCancelSync);
+ if (result == 0) {
+ return -1;
+ }
+
+ enabled = OSDisableInterrupts();
+ while (1) {
+ state = ((volatile DVDCommandBlock*)block)->state;
+ if (state == DVD_STATE_END || state == DVD_STATE_FATAL_ERROR || state == DVD_STATE_CANCELED) {
+ break;
+ }
+
+ if (state == DVD_STATE_COVER_CLOSED) {
+ command = ((volatile DVDCommandBlock*)block)->command;
+ if ((command == 4) || (command == 5) || (command == 13) || (command == 33) || (command == 34) || (command == 41) ||
+ (command == 42) || (command == 15) || (command == 37)) {
+ break;
+ }
+ }
+ OSSleepThread(&__DVDThreadQueue);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return 0;
+}
+
+static void cbForCancelSync(s32 result, DVDCommandBlock* block) {
+ OSWakeupThread(&__DVDThreadQueue);
+}
+
+int DVDCancelAllAsync(DVDCBCallback callback) {
+ BOOL enabled;
+ 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;
+}
+
+s32 DVDCancelAll(void) {
+ int result;
+ BOOL 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(s32 result, DVDCommandBlock* block) {
+ CancelAllSyncComplete = 1;
+ OSWakeupThread(&__DVDThreadQueue);
+}
+
+DVDDiskID* DVDGetCurrentDiskID(void) {
+ return (void*)OSPhysicalToCached(0);
+}
+
+static void __BS2DVDLowCallback(u32 type) {
+ __BS2DVDLowIntType = type;
+}
+
+u32 __DVDGetCoverStatus(void) {
+ u32 reg;
+ __BS2DVDLowIntType = 0;
+ DVDLowPrepareCoverRegister(__BS2DVDLowCallback);
+
+ while (!__BS2DVDLowIntType) {
+
+ }
+
+ if (!(__BS2DVDLowIntType & 1)) {
+ return 0;
+ }
+
+ reg = DVDLowGetCoverRegister();
+
+ if (__OSGetSystemTime() - LastResetEnd < DVD_RESETCOVER_TIMELAG_TICKS2) {
+ return 0;
+ } else if (reg & 1) {
+ return 1;
+ } else {
+ return 2;
+ }
+}
+
+BOOL DVDCheckDiskAsync(DVDCommandBlock* block, DVDCBCallback callback) {
+ BOOL enabled;
+ s32 retVal, state;
+
+ enabled = OSDisableInterrupts();
+
+ if (FatalErrorFlag) {
+ state = -1;
+ } else if (PausingFlag) {
+ state = 8;
+ } else {
+ if (WaitingForCoverOpen) {
+ state = 7;
+ }
+
+ if (WaitingForCoverClose) {
+ state = 5;
+ } else if (executing == NULL) {
+ switch (ResumeFromHere) {
+ case 3: {
+ state = 4;
+ break;
+ }
+ case 4: {
+ state = 5;
+ break;
+ }
+ case 1: {
+ state = 6;
+ break;
+ }
+ case 2: {
+ state = 11;
+ break;
+ }
+ case 7: {
+ state = 7;
+ break;
+ }
+ default: {
+ state = 0;
+ break;
+ }
+ }
+ } else if (executing == &DummyCommandBlock) {
+ state = 0;
+ } else {
+ state = executing->state;
+ }
+ }
+
+ retVal = TRUE;
+ switch (state) {
+ case 1:
+ case 9:
+ case 10:
+ case 2:
+ block->state = 0;
+ if (callback) {
+ (*callback)(TRUE, block);
+ }
+ OSRestoreInterrupts(enabled);
+ break;
+ case -1:
+ case 11:
+ case 7:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 12:
+ block->state = 0;
+ if (callback) {
+ (*callback)(FALSE, block);
+ }
+ OSRestoreInterrupts(enabled);
+ break;
+ case 0:
+ case 8:
+ OSRestoreInterrupts(enabled);
+
+ block->command = 36;
+ block->callback = callback;
+
+ retVal = issueCommand(2, block);
+ break;
+ }
+
+ return retVal;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+void DVDPrepareDisk() {
+ OSReport("DVDPrepareDisk(): null pointer is specified to command block address.");
+ OSReport("DVDPrepareDisk(): null pointer is specified to id address.");
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+void DVDPrepareDiskAsync() {
+ OSReport("DVDPrepareDiskAsync(): You can't call this API from DVD application. \n");
+ OSReport("DVDPrepareDiskAsync(): You can't specify NULL to company name. \n");
+ OSReport("DVDPrepareDisk(): command block is used for processing previous request.");
+}
+
+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 == 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 if (ResumeFromHere != 0) {
+ 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);
+}
+
+static void Callback(s32 result, DVDCommandBlock* block ){
+ Prepared = TRUE;
+}
+
+void __DVDPrepareReset(void) {
+ OSDisableInterrupts();
+
+ Prepared = FALSE;
+
+ __DVDPrepareResetAsync(Callback);
+ OSEnableInterrupts();
+
+ while (!(Prepared == TRUE)) {
+
+ }
+}
+
+int __DVDTestAlarm(const OSAlarm* alarm) {
+ if (alarm == &ResetAlarm) {
+ return 1;
+ }
+ return __DVDLowTestAlarm(alarm);
+}
+
+BOOL __DVDLowBreak(void) {
+ Breaking = TRUE;
+ return TRUE;
+}
+
+BOOL __DVDStopMotorAsync(void) {
+ return TRUE;
+}
+
+void __DVDRestartMotor(void) {}
diff --git a/src/revolution/dvd/dvdDeviceError.c b/src/revolution/dvd/dvdDeviceError.c
new file mode 100644
index 0000000000..316346461f
--- /dev/null
+++ b/src/revolution/dvd/dvdDeviceError.c
@@ -0,0 +1,200 @@
+#include <revolution/os.h>
+#include <revolution/dvd.h>
+#include <revolution/sc.h>
+
+#include "__os.h"
+#include "__dvd.h"
+
+static u8 CheckBuffer[32] ATTRIBUTE_ALIGN(32);
+
+static volatile BOOL lowDone = TRUE;
+static volatile u32 lowIntType = 0;
+
+static void lowCallback(u32 intType) {
+ lowIntType = intType;
+ lowDone = TRUE;
+}
+
+void __DVDShowDeviceErrorMessage(void);
+
+BOOL __DVDCheckDevice(void) {
+ u32 checkCode = 0x460A0000;
+ u32 outOfRangeError = 0xFFFFFFFF;
+ u32 reportKeyError = 0xFFFFFFFF;
+ OSIOSRev iosRev;
+
+ if (OSGetPhysicalMem2Size() == 0x08000000) {
+ return TRUE;
+ }
+
+ __OSGetIOSRev(&iosRev);
+
+ if (iosRev.major < 30 || iosRev.major >= 254) {
+ return TRUE;
+ }
+
+ if (__OSDeviceCheckCode == 129) {
+ checkCode = 0x7ED40000;
+ }
+
+ lowDone = FALSE;
+ DVDLowUnencryptedRead((void*)CheckBuffer, 32, checkCode, lowCallback);\
+
+ while (!lowDone) {
+
+ }
+
+ switch (lowIntType) {
+ case 2:
+ break;
+ case 1:
+ goto invalid;
+ break;
+ default:
+ goto fatal;
+ break;
+ }
+
+ lowDone = FALSE;
+ DVDLowRequestError(lowCallback);
+
+ while (!lowDone) {
+
+ }
+
+ outOfRangeError = DVDLowGetImmBufferReg();
+
+ switch (lowIntType) {
+ case 1: {
+ if ((DVDLowGetImmBufferReg() & 0xFF000000) != 0) {
+ goto recover;
+ break;
+ }
+
+ switch (DVDLowGetImmBufferReg() & 0xFFFFFF) {
+ case 0x52100:
+ break;
+ default:
+ goto invalid;
+ break;
+ }
+
+ break;
+ }
+
+ default:
+ goto fatal;
+ break;
+ }
+
+ lowDone = FALSE;
+ DVDLowReportKey((DVDVideoReportKey*)CheckBuffer, 0x40000, 0, lowCallback);
+
+ while (!lowDone) {
+
+ }
+
+ switch (lowIntType) {
+ case 2:
+ break;
+ case 1:
+ goto invalid;
+ break;
+ default:
+ goto fatal;
+ break;
+ }
+
+ lowDone = FALSE;
+ DVDLowRequestError(lowCallback);
+
+ while (!lowDone) {
+
+ }
+
+ reportKeyError = DVDLowGetImmBufferReg();
+ switch (lowIntType) {
+ case 1:
+ if ((DVDLowGetImmBufferReg() & 0xFF000000) != 0) {
+ goto recover;
+ break;
+ }
+
+ switch (DVDLowGetImmBufferReg() & 0xFFFFFF) {
+ case 0x53100:
+ case 0x52000:
+ break;
+ default:
+ goto invalid;
+ break;
+ }
+
+ break;
+
+ default:
+ goto fatal;
+ break;
+ }
+
+valid:
+ return TRUE;
+
+invalid:
+ __DVDShowDeviceErrorMessage();
+ return FALSE;
+
+recover:
+ return FALSE;
+
+fatal:
+ __DVDShowFatalMessage();
+ return FALSE;
+}
+
+const char* const __DVDDeviceErrorMessage[] = {
+ "\n\n\nエラーコード001。\n"
+ "不明なデバイスが見つかりました。",
+
+ "\n\n\nError #001,\n"
+ "unauthorized device has been detected.",
+
+ "\n\n\nFehler #001:\n"
+ "Es wurde eine unzul舖sige Komponente\n"
+ "entdeckt.",
+
+ "\n\n\nErreur 001:\n"
+ "un dispositif non autoris\xE9\x20""a \xE9\x74\xE9\x20""d\xE9\x74""ect\xE9\x2E",
+
+ "\n\n\nError 001:\n"
+ "Se ha detectado un dispositivo no\n"
+ "autorizado.",
+
+ "\n\n\nErrore #001:\n"
+ "rilevato un dispositivo non autorizzato.",
+
+ "\n\n\nFout #001:\n"
+ "ongeoorloofd onderdeel gevonden."
+};
+
+void __DVDShowDeviceErrorMessage(void) {
+ const char* message;
+ const char* const* messageList;
+ GXColor bg = { 0, 0, 0, 0 };
+ GXColor fg = { 255, 255, 255, 0 };
+
+ if (SCGetLanguage() == 0) {
+ OSSetFontEncode(1);
+ } else {
+ OSSetFontEncode(0);
+ }
+
+ messageList = __DVDDeviceErrorMessage;
+
+ if (SCGetLanguage() > 6) {
+ message = messageList[1];
+ } else {
+ message = messageList[SCGetLanguage()];
+ }
+
+ OSFatal(fg, bg, message);
+}
diff --git a/src/revolution/dvd/dvdFatal.c b/src/revolution/dvd/dvdFatal.c
new file mode 100644
index 0000000000..4172ac10ac
--- /dev/null
+++ b/src/revolution/dvd/dvdFatal.c
@@ -0,0 +1,211 @@
+#include <revolution.h>
+#include <revolution/dvd.h>
+#include <revolution/sc.h>
+
+#include "__dvd.h"
+
+static void (*FatalFunc)();
+
+const char* const __DVDErrorMessageDefault[] = {
+ "\n"
+ "\n"
+ "\n"
+ "エラーが発生しました。\n"
+ "\n"
+ "イジェクトボタンを押してディスクを取り出してか\n"
+ "ら、本体の電源をOFFにして、本体の取扱説明書の\n"
+ "指示に従ってください。",
+
+ "\n"
+ "\n"
+ "\n"
+ "An error has occurred.\n"
+ "Press the Eject Button, remove the\n"
+ "Game Disc, and turn off the power to the\n"
+ "console. Please read the Wii Operations\n"
+ "Manual for further instructions.",
+
+ "\n"
+ "\n"
+ "\n"
+ "Ein Fehler ist aufgetreten.\n"
+ "Dr""\xFC""cke den Ausgabeknopf, entnimm die\n"
+ "Disc und schalte die Wii-Konsole aus.\n"
+ "Bitte lies die Wii-Bedienungsanleitung,\n"
+ "um weitere Informationen zu erhalten.",
+
+ "\n"
+ "\n"
+ "\n"
+ "Une erreur est survenue.\n"
+ "Appuyez sur le bouton EJECT, retirez\n"
+ "le disque et ""\xE9""teignez la console.\n"
+ "Veuillez vous r""\xE9""f""\xE9""rer au Mode d'emploi\n"
+ "de la Wii pour plus de d""\xE9""tails.",
+
+ "\n"
+ "\n"
+ "\n"
+ "Ocurri\xF3 un Error.\n"
+ "Oprime el Bot\xF3n EJECT, saca el disco\n"
+ "y apaga la consola. Consulta el manual\n"
+ "de operaciones de la consola Wii para\n"
+ "obtener m\xE1s informaci\xF3n.",
+
+ "\n"
+ "\n"
+ "\n"
+ "Si \xE8 verificato un errore.\n"
+ "Premi il pulsante EJECT, estrai il disco\n"
+ "e spegni la console. Per maggiori\n"
+ "informazioni, consulta il manuale di\n"
+ "istruzioni della console Wii.",
+
+ "\n"
+ "\n"
+ "\n"
+ "Er is een fout opgetreden.\n"
+ "Druk op de EJECT-knop, verwijder de\n"
+ "disk en zet het Wii-systeem uit. Lees\n"
+ "de Wii-handleiding voor meer informatie."
+};
+
+const char* const __DVDErrorMessageEurope[] = {
+ "\n"
+ "\n"
+ "\n"
+ "エラーが発生しました。\n"
+ "\n"
+ "イジェクトボタンを押してディスクを取り出してか\n"
+ "ら、本体の電源をOFFにして、本体の取扱説明書の\n"
+ "指示に従ってください。",
+
+ "\n"
+ "\n"
+ "\n"
+ "An error has occurred.\n"
+ "Press the EJECT Button, remove the Disc,\n"
+ "and turn off the power to the console.\n"
+ "Please refer to the Wii Operations Manual\n"
+ "for details.",
+
+ "\n"
+ "\n"
+ "\n"
+ "Ein Fehler ist aufgetreten.\n"
+ "Dr""\xFC""cke den Ausgabeknopf, entnimm die\n"
+ "Disc und schalte die Wii-Konsole aus.\n"
+ "Bitte lies die Wii-Bedienungsanleitung,\n"
+ "um weitere Informationen zu erhalten.",
+
+ "\n"
+ "\n"
+ "\n"
+ "Une erreur est survenue.\n"
+ "Appuyez sur le bouton EJECT, retirez\n"
+ "le disque et ""\xE9""teignez la console.\n"
+ "Veuillez vous r""\xE9""f""\xE9""rer au mode d'emploi\n"
+ "Wii pour plus de d""\xE9""tails.",
+
+ "\n"
+ "\n"
+ "\n"
+ "Se ha producido un error.\n"
+ "Pulsa el Bot\xF3n EJECT, extrae el disco y\n"
+ "apaga la consola. Consulta el manual de\n"
+ "instrucciones de la consola Wii para\n"
+ "obtener m\xE1s informaci\xF3n.",
+
+ "\n"
+ "\n"
+ "\n"
+ "Si \xE8 verificato un errore.\n"
+ "Premi il pulsante EJECT, estrai il disco\n"
+ "e spegni la console. Per maggiori\n"
+ "informazioni, consulta il manuale di\n"
+ "istruzioni della console Wii.",
+
+ "\n"
+ "\n"
+ "\n"
+ "Er is een fout opgetreden.\n"
+ "Druk op de EJECT-knop, verwijder de\n"
+ "disk en zet het Wii-systeem uit. Lees\n"
+ "de Wii-handleiding voor meer informatie."
+};
+
+const char* __DVDErrorMessageChinaKorea [] = {
+ "\n"
+ "\n"
+ "エラーコード104。\n"
+ "エラーが発生しました。\n"
+ "\n"
+ "イジェクトボタンを押してディスクを取り出してか\n"
+ "ら、本体の電源をOFFにして、本体の取扱説明書の\n"
+ "指示に従ってください。",
+
+ "\n"
+ "\n"
+ "Error #104,\n"
+ "An error has occurred.\n"
+ "Press the EJECT Button, remove the\n"
+ "Game Disc, and turn the power off.\n"
+ "Please read the Wii Operations Manual\n"
+ "for more information."
+};
+
+void __DVDShowFatalMessage(void) {
+ const char* message;
+ const char* const* messageList;
+ GXColor bg = { 0, 0, 0, 0 };
+ GXColor fg = { 255, 255, 255, 0 };
+
+ if (SCGetLanguage() == SC_LANG_JAPANESE) {
+ OSSetFontEncode(OS_FONT_ENCODE_SJIS);
+ } else {
+ OSSetFontEncode(OS_FONT_ENCODE_ANSI);
+ }
+
+ switch (SCGetProductGameRegion()) {
+ case 0:
+ case 1:
+ case 3:
+ default:
+ messageList = __DVDErrorMessageDefault;
+ break;
+ case 2:
+ messageList = __DVDErrorMessageEurope;
+ break;
+ case 4:
+ case 5:
+ messageList = __DVDErrorMessageChinaKorea;
+ break;
+ }
+
+ if (SCGetLanguage() > SC_LANG_DUTCH) {
+ message = messageList[1];
+ } else {
+ message = messageList[SCGetLanguage()];
+ }
+
+ OSFatal(fg, bg, message);
+}
+
+BOOL DVDSetAutoFatalMessaging(BOOL enable) {
+ BOOL enabled, prev;
+ enabled = OSDisableInterrupts();
+ prev = FatalFunc ? TRUE : FALSE;
+ FatalFunc = enable ? __DVDShowFatalMessage : NULL;
+ OSRestoreInterrupts(enabled);
+ return prev;
+}
+
+BOOL __DVDGetAutoFatalMessaging(void) {
+ return FatalFunc ? TRUE : FALSE;
+}
+
+void __DVDPrintFatalMessage(void) {
+ if (FatalFunc) {
+ FatalFunc();
+ }
+}
diff --git a/src/revolution/dvd/dvd_broadway.c b/src/revolution/dvd/dvd_broadway.c
new file mode 100644
index 0000000000..ab4840791f
--- /dev/null
+++ b/src/revolution/dvd/dvd_broadway.c
@@ -0,0 +1,978 @@
+#include <revolution/os.h>
+#include <revolution/esp.h>
+#include <revolution/ipc.h>
+
+IOSFd DiFD = -1;
+
+static volatile u8 requestInProgress = FALSE;
+static u8 breakRequested;
+static u8 callbackInProgress;
+
+static u32 registerBuf[8] ATTRIBUTE_ALIGN(32);
+static u32 statusRegister[8] ATTRIBUTE_ALIGN(32);
+static u32 controlRegister[8] ATTRIBUTE_ALIGN(32);
+static s32 lastTicketError[8] ATTRIBUTE_ALIGN(32);
+
+static u32 readLength;
+static u32 spinUpValue;
+
+static diRegVals_t diRegValCache ATTRIBUTE_ALIGN(32);
+
+static u8 DVDLowInitCalled = FALSE;
+
+typedef enum callbackType {
+ BOGUS_TYPE = 0,
+ TRANSACTION_CB,
+ COVER_CB,
+ COVER_REG_CB
+} callbackType_t;
+
+static int freeCommandBuf = 0;
+static diCommand_t* diCommand;
+static char* pathBuf;
+
+typedef struct dvdContext {
+ DVDLowCallback callback;
+ callbackType_t callbackType;
+ u8 inUse;
+ u32 contextMagic;
+ u32 contextNum;
+ u32 pad[3];
+} dvdContext_t;
+
+static int freeDvdContext = 0;
+static u8 dvdContextsInited = FALSE;
+static dvdContext_t dvdContexts[4] ATTRIBUTE_ALIGN(32);
+static IOSIoVector ioVec[10] ATTRIBUTE_ALIGN(32);
+
+static void* ddrAllocAligned32(const int size) {
+ void* low, *high;
+
+ if ((size & 0x1F) != 0) {
+ OSReport("(ddrAllocAligned32) size is not a multiple of 32 !!! \n");
+ return 0;
+ }
+
+ low = IPCGetBufferLo();
+ high = IPCGetBufferHi();
+
+ if (((u32)low & 0x1F) != 0) {
+ low = (void*)(((u32)low + 31) & 0x1F);
+ }
+
+ if ((u32)low + size > (u32)high) {
+ OSReport("(ddrAllocAligned32) Not enough space to allocate %d bytes\n", size);
+ }
+
+ IPCSetBufferLo((void*)((u32)low + size));
+ return low;
+}
+
+static BOOL allocateStructures(void) {
+ if ((diCommand = ddrAllocAligned32(sizeof(diCommand_t) * 4)) == 0) {
+ OSReport("Allocation of diCommand blocks failed\n");
+ return FALSE;
+ }
+
+ if ((pathBuf = ddrAllocAligned32(32)) == 0) {
+ OSReport("Allocation of pathBuf failed\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void initDvdContexts(void) {
+ {
+ u32 i;
+
+ for (i = 0; i < 4; i++) {
+ dvdContexts[i].callback = 0;
+ dvdContexts[i].callbackType = 0;
+ dvdContexts[i].inUse = FALSE;
+ dvdContexts[i].contextMagic = 0xFEEBDAED;
+ dvdContexts[i].contextNum = i;
+ }
+ }
+
+ freeDvdContext = 0;
+}
+
+IOSError doTransactionCallback(IOSError ret, void* context) {
+ dvdContext_t* dvdContext = context;
+
+ if (dvdContext->contextMagic != 0xFEEBDAED) {
+ OSReport("(doTransactionCallback) Error - context mangled!\n");
+ dvdContext->contextMagic = 0xFEEBDAED;
+ goto out;
+ }
+
+ requestInProgress = FALSE;
+
+ if (dvdContext->callback != NULL) {
+ int callbackArg;
+ callbackInProgress = TRUE;
+ callbackArg = ret;
+
+ if (breakRequested == TRUE) {
+ breakRequested = FALSE;
+ callbackArg |= 8;
+ }
+
+ if (callbackArg & 1) {
+ readLength = 0;
+ }
+
+ dvdContext->callback((u32)callbackArg);
+ callbackInProgress = FALSE;
+ }
+
+out:
+ dvdContext->inUse = FALSE;
+ return 0;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+IOSError doCoverCallback(IOSError err, void* context) {
+ OSReport("(doCoverCallback) Error - context mangled!\n");
+ return 0;
+}
+
+IOSError doPrepareCoverRegisterCallback(IOSError err, void* context) {
+ dvdContext_t* dvdContext;
+ requestInProgress = FALSE;
+
+ diRegValCache.CoverRegVal = registerBuf[0];
+ dvdContext = (dvdContext_t*)context;
+
+ if (dvdContext->contextMagic != 0xFEEBDAED) {
+ OSReport("(doTransactionCallback) Error - context mangled!\n");
+ dvdContext->contextMagic = 0xFEEBDAED;
+ } else {
+ IOSError ret;
+ if (dvdContext->callback != 0) {
+ callbackInProgress = TRUE;
+ ret = err;
+
+ if (breakRequested == TRUE) {
+ breakRequested = FALSE;
+ ret |= 8;
+ }
+
+ dvdContext->callback(ret);
+ callbackInProgress = FALSE;
+ }
+ }
+
+ dvdContext->inUse = FALSE;
+ return 0;
+}
+
+static inline void nextCommandBuf(int* bufNum) {
+ (*bufNum)++;
+
+ if (*bufNum >= 4) {
+ *bufNum = 0;
+ }
+}
+
+static inline dvdContext_t* newContext(const DVDLowCallback callback, const callbackType_t type) {
+ int returnIndex;
+
+ if ((dvdContexts[freeDvdContext].inUse != 0) == (u32)1) {
+ OSReport("(newContext) ERROR: freeDvdContext.inUse (#%d) is true\n", freeDvdContext);
+ OSReport("(newContext) Now spinning in infinite loop\n");
+
+ while (1) {
+
+ }
+ }
+
+ if (dvdContexts[freeDvdContext].contextMagic != 0xFEEBDAED) {
+ OSReport("(newContext) Something overwrote the context magic - spinning \n");
+
+ while (1) {
+
+ }
+ }
+
+ dvdContexts[freeDvdContext].callback = callback;
+ dvdContexts[freeDvdContext].callbackType = type;
+ dvdContexts[freeDvdContext].inUse = TRUE;
+ returnIndex = freeDvdContext;
+ freeDvdContext++;
+
+ if (freeDvdContext >= 4) {
+ freeDvdContext = 0;
+ }
+
+ return(dvdContexts + returnIndex);
+}
+
+BOOL DVDLowFinalize(void) {
+ IOSError ret = IOS_Close(DiFD);
+
+ if (ret != IOS_ERROR_OK) {
+ OSReport("(DVDLowFinish) Error: IOS_Close failed\n");
+ return FALSE;
+ }
+
+ DVDLowInitCalled = FALSE;
+ return TRUE;
+}
+
+BOOL DVDLowInit(void) {
+ IOSError retVal;
+
+ if (DVDLowInitCalled == FALSE) {
+ DVDLowInitCalled = TRUE;
+ retVal = IPCCltInit();
+
+ if (retVal != IOS_ERROR_OK) {
+ OSReport("IPCCltInit returned error: %d\n", retVal);
+ return FALSE;
+ }
+
+ if (allocateStructures() == FALSE) {
+ return FALSE;
+ }
+
+ if (dvdContextsInited == FALSE) {
+ initDvdContexts();
+ dvdContextsInited = TRUE;
+ }
+ }
+
+ strncpy(pathBuf, "/dev/di", 32);
+ DiFD = IOS_Open(pathBuf, 0);
+
+ if (DiFD >= 0) {
+ return TRUE;
+ } else {
+ switch (DiFD) {
+ case IOS_ERROR_NOEXISTS:
+ OSReport("(DVDLowInit) Error: IOS_Open failed - pathname '/dev/di' does not exist\n");
+ return FALSE;
+ break;
+ case IOS_ERROR_ACCESS:
+ OSReport("(DVDLowInit) Error: IOS_Open failed - calling thread lacks permission\n");
+ return FALSE;
+ break;
+ case IOS_ERROR_MAX:
+ OSReport("(DVDLowInit) Error: IOS_Open failed - connection limit has been reached\n");
+ return FALSE;
+ break;
+ default:
+ OSReport("(DVDLowInit) IOS_Open failed, errorcode = %d\n", DiFD);
+ return FALSE;
+ break;
+ }
+ }
+}
+
+BOOL DVDLowReadDiskID(DVDDiskID* diskID, DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+
+ if (diskID == 0) {
+ OSReport("@@@@@@ WARNING - Calling DVDLowReadDiskId with NULL ptr\n");
+ }
+
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0x70;
+
+ rv = IOS_IoctlAsync(DiFD, 0x70, &diCommand[freeCommandBuf], sizeof(diCommand_t), diskID, sizeof(DVDDiskID), doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (DVDLowReadDiskID) IOS_IoctlAsync returned error: %d\n", rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL DVDLowOpenPartition(const u32 partitionWordOffset, const ESTicket* const eTicket, const u32 numCertBytes, const u8* const certificates, ESTitleMeta* tmd, DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+
+ if (eTicket != 0 && ((u32)eTicket & 0x1F)) {
+ OSReport("(DVDLowOpenPartition) eTicket memory is unaligned\n");
+ return FALSE;
+ }
+
+ if (certificates != 0 && ((u32)certificates & 0x1F)) {
+ OSReport("(DVDLowOpenPartition) certificates memory is unaligned\n");
+ return FALSE;
+ }
+
+ if (tmd != 0 && ((u32)tmd & 0x1F)) {
+ OSReport("(DVDLowOpenPartition) certificates memory is unaligned\n");
+ return FALSE;
+ }
+
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0x8B;
+ diCommand[freeCommandBuf].arg[0] = partitionWordOffset;
+ ioVec[0].base = (u8*)&diCommand[freeCommandBuf];
+ ioVec[0].length = sizeof(diCommand_t);
+
+ ioVec[1].base = (u8*)eTicket;
+ if (eTicket == 0) {
+ ioVec[1].length = 0;
+ } else {
+ ioVec[1].length = sizeof(ESTicket);
+ }
+
+ ioVec[2].base = (u8*)certificates;
+ if (certificates == 0) {
+ ioVec[2].length = 0;
+ } else {
+ ioVec[2].length = numCertBytes;
+ }
+
+ ioVec[3].base = (u8*)tmd;
+ ioVec[3].length = sizeof(ESTitleMeta);
+
+ ioVec[4].base = (u8*)&lastTicketError[0];
+ ioVec[4].length = sizeof(lastTicketError);
+
+ rv = IOS_IoctlvAsync(DiFD, 0x8B, 3, 2, ioVec, doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (DVDLowOpenPartition) IOS_IoctlvAsync returned error: %d\n", rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+BOOL DVDLowOpenPartitionWithTmdAndTicket() {
+ OSReport("(%s) eTicket memory is unaligned\n", __FUNCTION__);
+ OSReport("(%s) tmd parameter cannot be NULL\n", __FUNCTION__);
+ OSReport("(%s) tmd memory is unaligned\n", __FUNCTION__);
+ OSReport("(%s) eTicket parameter cannot be NULL\n", __FUNCTION__);
+ return TRUE;
+}
+
+BOOL DVDLowOpenPartitionWithTmdAndTicketView(const u32 partitionWordOffset, const ESTicketView* const eTicketView, const u32 numTmdBytes, const ESTitleMeta* const tmd, const u32 numCertBytes, const u8* const certificates, DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+
+ if (certificates != 0 && ((u32)certificates & 0x1F)) {
+ return FALSE;
+ }
+
+ if (tmd == 0) {
+ OSReport("(%s) tmd parameter cannot be NULL\n", __FUNCTION__);
+ return FALSE;
+ } else if (((u32)tmd & 0x1F)) {
+ OSReport("(%s) tmd memory is unaligned\n", __FUNCTION__);
+ return FALSE;
+ }
+
+ if (eTicketView == 0) {
+ OSReport("(%s) eTicketView parameter cannot be NULL\n", __FUNCTION__);
+ return FALSE;
+ } else if (((u32)eTicketView & 0x1F)) {
+ OSReport("(%s) eTicketView memory is unaligned\n", __FUNCTION__);
+ return FALSE;
+ }
+
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0x94;
+ diCommand[freeCommandBuf].arg[0] = partitionWordOffset;
+ ioVec[0].base = (u8*)&diCommand[freeCommandBuf];
+ ioVec[0].length = sizeof(diCommand_t);
+
+ ioVec[1].base = (u8*)eTicketView;
+ ioVec[1].length = sizeof(ESTicketView);
+
+ ioVec[2].base = (u8*)tmd;
+ ioVec[2].length = numTmdBytes;
+
+ ioVec[3].base = (u8*)certificates;
+ if (certificates == 0) {
+ ioVec[3].length = 0;
+ } else {
+ ioVec[3].length = numCertBytes;
+ }
+
+ ioVec[4].base = (u8*)&lastTicketError[0];
+ ioVec[4].length = sizeof(lastTicketError);
+
+ rv = IOS_IoctlvAsync(DiFD, 0x94, 4, 1, ioVec, doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (DVDLowOpenPartition) IOS_IoctlvAsync returned error: %d\n", rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL DVDLowGetNoDiscBufferSizes(const u32 partitionWordOffset, u32* numTmdBytes, u32* numCertBytes, DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+
+ if (numTmdBytes == 0 || numCertBytes == 0) {
+ OSReport("(%s) Error: NULL pointer argument\n", __FUNCTION__);
+ return FALSE;
+ }
+
+ if (((u32)numTmdBytes & 0x1F)) {
+ OSReport("(%s) numTmdBytes memory is unaligned\n", __FUNCTION__);
+ return FALSE;
+ }
+
+ if (((u32)numCertBytes & 0x1F)) {
+ OSReport("(%s) certificates memory is unaligned\n", __FUNCTION__);
+ return FALSE;
+ }
+
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0x92;
+ diCommand[freeCommandBuf].arg[0] = partitionWordOffset;
+
+ ioVec[0].base = (u8*)&diCommand[freeCommandBuf];
+ ioVec[0].length = sizeof(diCommand_t);
+
+ ioVec[1].base = (u8*)numTmdBytes;
+ ioVec[1].length = 4;
+
+ ioVec[2].base = (u8*)numCertBytes;
+ ioVec[2].length = 4;
+
+ rv = IOS_IoctlvAsync(DiFD, 0x92, 1, 2, ioVec, doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (%s) IOS_IoctlvAsync returned error: %d\n", __FUNCTION__, rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#define is_aligned(addr) (((u32)(addr) & 0x1F) == 0)
+
+BOOL DVDLowGetNoDiscOpenPartitionParams(const u32 partitionWordOffset, ESTicket* eTicket, u32* numTmdBytes, ESTitleMeta* tmd, u32* numCertBytes, u8* certificates, u32* dataWordOffset, u8* h3HashPtr, DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+
+ if (eTicket == 0 || numTmdBytes == 0 || tmd == 0 || numCertBytes == 0 || certificates == 0 || dataWordOffset == 0 || h3HashPtr == 0) {
+ OSReport("(%s) Error: NULL pointer argument\n", __FUNCTION__);
+ return FALSE;
+ }
+
+ if (!is_aligned(eTicket) || !is_aligned(numTmdBytes) || !is_aligned(tmd) || !is_aligned(numCertBytes) || !is_aligned(certificates) || !is_aligned(dataWordOffset) || !is_aligned(h3HashPtr)) {
+ OSReport("(%s) pointer argument is unaligned\n", __FUNCTION__);
+ return FALSE;
+ }
+
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0x90;
+ diCommand[freeCommandBuf].arg[0] = partitionWordOffset;
+
+ ioVec[0].base = (u8*)&diCommand[freeCommandBuf];
+ ioVec[0].length = sizeof(diCommand_t);
+
+ ioVec[1].base = (u8*)numTmdBytes;
+ ioVec[1].length = 4;
+
+ ioVec[2].base = (u8*)numCertBytes;
+ ioVec[2].length = 4;
+
+ ioVec[3].base = (u8*)eTicket;
+ ioVec[3].length = sizeof(ESTicket);
+
+ ioVec[4].base = (u8 *) numTmdBytes;
+ ioVec[4].length = 4;
+
+ ioVec[5].base = (u8*)tmd;
+ ioVec[5].length = *numTmdBytes;
+
+ ioVec[6].base = (u8*)numCertBytes;
+ ioVec[6].length = 4;
+
+ ioVec[7].base = certificates;
+ ioVec[7].length = *numCertBytes;
+
+ ioVec[8].base = (u8*)dataWordOffset;
+ ioVec[8].length = 4;
+
+ ioVec[9].base = h3HashPtr;
+ ioVec[9].length = 98304;
+
+ rv = IOS_IoctlvAsync(DiFD, 0x90, 3, 7, ioVec, doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (%s) IOS_IoctlvAsync returned error: %d\n", __FUNCTION__, rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+BOOL DVDLowNoDiscOpenPartition() {
+ OSReport("DVDLowNoDiscOpenPartition");
+ return TRUE;
+}
+
+BOOL DVDLowClosePartition(DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0x8C;
+
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+ rv = IOS_IoctlAsync(DiFD, 0x8C, &diCommand[freeCommandBuf], sizeof(diCommand_t), 0, 0, doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (DVDLowClosePartition) IOS_IoctlAsync returned error: %d\n", rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL DVDLowUnencryptedRead(void* destAddr, u32 length, u32 wordOffset, DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+ readLength = length;
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0x8D;
+ diCommand[freeCommandBuf].arg[0] = length;
+ diCommand[freeCommandBuf].arg[1] = wordOffset;
+
+ rv = IOS_IoctlAsync(DiFD, 0x8D, &diCommand[freeCommandBuf], sizeof(diCommand_t), destAddr, length, doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (DVDLowUnencryptedRead) IOS_IoctlAsync returned error: %d\n", rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL DVDLowStopMotor(u8 eject, u8 saving, DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0xE3;
+ diCommand[freeCommandBuf].arg[0] = eject;
+ diCommand[freeCommandBuf].arg[1] = saving;
+
+ rv = IOS_IoctlAsync(DiFD, 0xE3, &diCommand[freeCommandBuf], sizeof(diCommand_t), &diRegValCache, sizeof(diRegVals_t), doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (DVDLowStopMotor) IOS_IoctlAsync returned error: %d\n", rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+BOOL DVDLowWaitForCoverClose() {
+ OSReport("@@@ (DVDLowWaitForCoverClose) IOS_IoctlAsync returned error: %d\n");
+ return TRUE;
+}
+
+BOOL DVDLowInquiry(DVDDriveInfo* info, DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+
+ if (breakRequested == 0) {}
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0x12;
+ rv = IOS_IoctlAsync(DiFD, 0x12, &diCommand[freeCommandBuf], sizeof(diCommand_t), info, sizeof(DVDDriveInfo), doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (DVDLowInquiry) IOS_IoctlAsync returned error: %d\n", rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL DVDLowRequestError(DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0xE0;
+ rv = IOS_IoctlAsync(DiFD, 0xE0, &diCommand[freeCommandBuf], sizeof(diCommand_t), &diRegValCache, sizeof(diRegVals_t), doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (DVDLowRequestError) IOS_IoctlAsync returned error: %d\n", rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL DVDLowSetSpinupFlag(u32 spinUp) {
+ "(DVDLowSetSpinupFlag): Synch functions can't be called in callbacks\n"; // dead string
+
+ spinUpValue = spinUp;
+ return TRUE;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+BOOL DVDLowNotifyReset() {
+ OSReport("@@@ (DVDLowNotifyReset) IOS_IoctlAsync returned error: %d\n");
+ return TRUE;
+}
+
+BOOL DVDLowReset(DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0x8A;
+ diCommand[freeCommandBuf].arg[0] = spinUpValue;
+ rv = IOS_IoctlAsync(DiFD, 0x8A, &diCommand[freeCommandBuf], sizeof(diCommand_t), 0, 0, doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (DVDLowReset) IOS_IoctlAsync returned error: %d\n", rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL DVDLowAudioBufferConfig(u8 enable, u32 size, DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0xE4;
+ diCommand[freeCommandBuf].arg[0] = enable;
+ diCommand[freeCommandBuf].arg[1] = size;
+ rv = IOS_IoctlAsync(DiFD, 0xE4, &diCommand[freeCommandBuf], sizeof(diCommand_t), &diRegValCache, sizeof(diRegVals_t), doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (DVDLowAudioBufferConfig) IOS_IoctlAsync returned error: %d\n", rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+u32 DVDLowGetCoverStatus() {
+ OSReport("(DVDLowGetCoverStatus): Synch functions can't be called in callbacks\n");
+ OSReport("@@@ (DVDLowGetCoverStatus) IOS_Ioctl returned error: %d\n");
+ return TRUE;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+u32 DVDLowReadDVD() {
+ OSReport("@@@ (DVDLowReadDVD) IOS_IoctlAsync returned error: %d\n");
+ return TRUE;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+BOOL DVDLowReadDVDConfig() {
+ OSReport("@@@ (DVDLowReadDVDConfig) IOS_IoctlAsync returned error: %d\n");
+ return TRUE;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+BOOL DVDLowReadDvdCopyright() {
+ OSReport("@@@ (DVDLowReadDvdCopyright) IOS_IoctlAsync returned error: %d\n");
+ return TRUE;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+BOOL DVDLowReadDvdPhysical() {
+ OSReport("@@@ (DVDLowReadDvdPhysical) IOS_IoctlAsync returned error: %d\n");
+ return TRUE;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+BOOL DVDLowReadDvdDiscKey() {
+ OSReport("@@@ (DVDLowReadDvdDiscKey) IOS_IoctlAsync returned error: %d\n");
+ return TRUE;
+}
+
+BOOL DVDLowReportKey(DVDVideoReportKey* reportKey, u32 format, u32 lsn, DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0xA4;
+ diCommand[freeCommandBuf].arg[0] = format >> 16;
+ diCommand[freeCommandBuf].arg[1] = lsn;
+
+ rv = IOS_IoctlAsync(DiFD, 0xA4, &diCommand[freeCommandBuf], sizeof(diCommand_t), reportKey, sizeof(DVDVideoReportKey), doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (DVDLowReportKey) IOS_IoctlAsync returned error: %d\n", rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+BOOL DVDLowOffset() {
+ OSReport("@@@ (DVDLowOffset) IOS_IoctlAsync returned error: %d\n");
+ return TRUE;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+BOOL DVDLowStopLaser() {
+ OSReport("@@@ (DVDLowStopLaser) IOS_IoctlAsync returned error: %d\n");
+ return TRUE;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+BOOL DVDLowReadDiskBca() {
+ OSReport("@@@ (DVDLowReadDiskBca) IOS_IoctlAsync returned error: %d\n");
+ return TRUE;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+BOOL DVDLowSerMeasControl() {
+ OSReport("@@@ (DVDLowSerMeasControl) IOS_IoctlAsync returned error: %d\n");
+ return TRUE;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+BOOL DVDLowRequestDiscStatus() {
+ OSReport("@@@ (DVDLowRequestDiscStatus) IOS_IoctlAsync returned error: %d\n");
+ return TRUE;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+BOOL DVDLowRequestRetryNumber() {
+ OSReport("@@@ (DVDLowRequestRetryNumber) IOS_IoctlAsync returned error: %d\n");
+ return TRUE;
+}
+
+BOOL DVDLowSetMaximumRotation(u32 subcmd, DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0xDD;
+ diCommand[freeCommandBuf].arg[0] = (subcmd >> 16) & 3;
+ rv = IOS_IoctlAsync(DiFD, 0xDD, &diCommand[freeCommandBuf], sizeof(diCommand_t), 0, 0, doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (DVDLowSetMaxRotation) IOS_IoctlAsync returned error: %d\n", rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL DVDLowRead(void* destAddr, u32 length, u32 wordOffset, DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+
+ if (((u32)destAddr & 0x1F) != 0) {
+ OSReport("(DVDLowRead): ERROR - destAddr buffer is not 32 byte aligned\n");
+ return FALSE;
+ }
+
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+ readLength = length;
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0x71;
+ diCommand[freeCommandBuf].arg[0] = length;
+ diCommand[freeCommandBuf].arg[1] = wordOffset;
+ rv = IOS_IoctlAsync(DiFD, 0x71, &diCommand[freeCommandBuf], sizeof(diCommand_t), destAddr, length, doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (DVDLowRead) IOS_IoctlAsync returned error: %d\n", rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL DVDLowSeek(u32 wordOffset, DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0xAB;
+ diCommand[freeCommandBuf].arg[0] = wordOffset;
+ rv = IOS_IoctlAsync(DiFD, 0xAB, &diCommand[freeCommandBuf], sizeof(diCommand_t), 0, 0, doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (DVDLowSeek) IOS_IoctlAsync returned error: %d\n", rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+BOOL DVDLowGetCoverReg() {
+ OSReport("(DVDLowGetCoverReg): Synch functions can't be called in callbacks\n");
+ OSReport("@@@ (DVDLowGetCoverReg) IOS_Ioctl returned error: %d\n");
+ return TRUE;
+}
+
+u32 DVDLowGetCoverRegister(void) {
+ return diRegValCache.CoverRegVal;
+}
+
+u32 DVDLowGetStatusRegister(void) {
+ return statusRegister[0];
+}
+
+u32 DVDLowGetControlRegister(void) {
+ return controlRegister[0];
+}
+
+BOOL DVDLowPrepareCoverRegister(DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0x7A;
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+ rv = IOS_IoctlAsync(DiFD, 0x7A, &diCommand[freeCommandBuf], sizeof(diCommand_t), registerBuf, sizeof(registerBuf), doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (DVDLowPrepareCoverRegsiter) IOS_IoctlAsync returned error: %d\n", rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL DVDLowPrepareStatusRegister(DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0x95;
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+ rv = IOS_IoctlAsync(DiFD, 0x95, &diCommand[freeCommandBuf], sizeof(diCommand_t), statusRegister, sizeof(statusRegister), doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (DVDLowPrepareStatusRegsiter) IOS_IoctlAsync returned error: %d\n", rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL DVDLowPrepareControlRegister(DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0x96;
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+ rv = IOS_IoctlAsync(DiFD, 0x96, &diCommand[freeCommandBuf], sizeof(diCommand_t), controlRegister, sizeof(controlRegister), doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (DVDLowPrepareControlRegister) IOS_IoctlAsync returned error: %d\n", rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+u32 DVDLowGetImmBufferReg(void) {
+ return diRegValCache.ImmRegVal;
+}
+
+BOOL DVDLowUnmaskStatusInterrupts(void) {
+ return TRUE;
+}
+
+BOOL DVDLowMaskCoverInterrupt(void) {
+ return TRUE;
+}
+
+BOOL DVDLowClearCoverInterrupt(DVDLowCallback callback) {
+ dvdContext_t* dvdContext;
+ IOSError rv;
+
+ nextCommandBuf(&freeCommandBuf);
+ diCommand[freeCommandBuf].theCommand = 0x86;
+ requestInProgress = TRUE;
+ dvdContext = newContext(callback, 1);
+ rv = IOS_IoctlAsync(DiFD, 0x86, &diCommand[freeCommandBuf], sizeof(diCommand_t), 0, 0, doTransactionCallback, dvdContext);
+
+ if (rv != IOS_ERROR_OK) {
+ OSReport("@@@ (DVDLowClearCoverInterrupt) IOS_IoctlAsync returned error: %d\n", rv);
+ dvdContext->inUse = FALSE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL __DVDLowTestAlarm(const OSAlarm *) {
+ return FALSE;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+BOOL DVDLowEnableDvdVideo() {
+ OSReport("@@@ (DVDLowEnableDvdVideo) IOS_IoctlAsync returned error: %d\n");
+ return TRUE;
+}
diff --git a/src/revolution/dvd/dvderror.c b/src/revolution/dvd/dvderror.c
new file mode 100644
index 0000000000..7f1e8e52ad
--- /dev/null
+++ b/src/revolution/dvd/dvderror.c
@@ -0,0 +1,202 @@
+#include <revolution.h>
+#include <revolution/os.h>
+#include <revolution/dvd.h>
+#include <revolution/nand.h>
+
+#include "os/__os.h"
+#include "__dvd.h"
+
+static BOOL ExistFlag = FALSE;
+static NANDCommandBlock NandCb;
+static NANDFileInfo NandInfo;
+static DVDCBCallback Callback;
+static u32 NextOffset;
+DVDErrorInfo __ErrorInfo ATTRIBUTE_ALIGN(32);
+DVDErrorInfo __FirstErrorInfo ATTRIBUTE_ALIGN(32);
+
+void cbForNandClose(s32 result, NANDCommandBlock* block) {
+ if (Callback) {
+ Callback((result == 0) ? 1 : 2, NULL);
+ }
+}
+
+void cbForNandWrite(s32 result, NANDCommandBlock* block) {
+ if (NANDCloseAsync(&NandInfo, cbForNandClose, &NandCb) != 0) {
+ if (Callback) {
+ Callback(2, NULL);
+ }
+ }
+}
+
+void cbForNandSeek(s32 result, NANDCommandBlock* block) {
+ if (result == sizeof(DVDErrorInfo) * (1 + NextOffset)) {
+ if (NextOffset == 0) {
+ __ErrorInfo.nextOffset = (1 + NextOffset) % 7;
+ }
+
+ DCFlushRange((void*)&__ErrorInfo, sizeof(__ErrorInfo));
+
+ if (NANDWriteAsync(&NandInfo, (void*)&__ErrorInfo, sizeof(__ErrorInfo), cbForNandWrite, &NandCb) != 0) {
+ cbForNandWrite(-1, NULL);
+ }
+ } else {
+ if (Callback) {
+ Callback(2, NULL);
+ }
+ }
+}
+
+void cbForNandWrite0(s32 result, NANDCommandBlock* block) {
+ if (result == sizeof(__FirstErrorInfo)) {
+ if (NANDSeekAsync(&NandInfo, (s32)(sizeof(DVDErrorInfo) * (1 + NextOffset)), 0, cbForNandSeek, &NandCb) != 0) {
+ cbForNandSeek(-1, NULL);
+ }
+ } else {
+ if (Callback) {
+ Callback(2, NULL);
+ }
+ }
+}
+
+void cbForNandSeek2(s32 result, NANDCommandBlock* block) {
+ if (result == sizeof(DVDErrorInfo)) {
+ __FirstErrorInfo.nextOffset = (__FirstErrorInfo.nextOffset + 1) % 7;
+
+ if (NANDWriteAsync(&NandInfo, (void*)&__FirstErrorInfo, sizeof(__FirstErrorInfo), cbForNandWrite0, &NandCb) != 0) {
+ cbForNandWrite0(-1, NULL);
+ }
+ } else {
+ if (Callback) {
+ Callback(2, NULL);
+ }
+ }
+}
+
+void cbForNandRead(s32 result, NANDCommandBlock* block) {
+ if (result == sizeof(DVDErrorInfo)) {
+ NextOffset = __FirstErrorInfo.nextOffset;
+
+ if (NANDSeekAsync(&NandInfo, sizeof(DVDErrorInfo), 0, cbForNandSeek2, &NandCb) != 0) {
+ cbForNandSeek2(-1, NULL);
+ }
+ } else {
+ __ErrorInfo.nextOffset = 1;
+ if (NANDWriteAsync(&NandInfo, (void*)&__ErrorInfo, sizeof(__ErrorInfo), cbForNandWrite, &NandCb) != 0) {
+ cbForNandWrite(-1, NULL);
+ }
+ }
+}
+
+void cbForNandSeek0(s32 result, NANDCommandBlock* block) {
+ if (result == 0) {
+ NextOffset = 0;
+ __ErrorInfo.nextOffset = 1;
+
+ if (NANDWriteAsync(&NandInfo, (void*)&__FirstErrorInfo, sizeof(__FirstErrorInfo), cbForNandWrite0, &NandCb) != 0) {
+ cbForNandWrite0(-1, NULL);
+ }
+ } else {
+ if (Callback) {
+ Callback(2, NULL);
+ }
+ }
+}
+
+void cbForNandSeek1(s32 result, NANDCommandBlock* block) {
+ if (result == sizeof(DVDErrorInfo)) {
+ if (NANDReadAsync(&NandInfo, (void*)&__FirstErrorInfo, sizeof(__FirstErrorInfo), cbForNandRead, &NandCb) != 0) {
+ cbForNandRead(-1, NULL);
+ }
+ } else {
+ if (NANDSeekAsync(&NandInfo, 0, 0, cbForNandSeek0, &NandCb) != 0) {
+ cbForNandSeek0(-1, NULL);
+ }
+ }
+}
+
+void cbForNandOpen(s32 result, NANDCommandBlock* block) {
+ if (result == 0) {
+ if (ExistFlag) {
+ if (NANDSeekAsync(&NandInfo, sizeof(DVDErrorInfo), 0, cbForNandSeek1, &NandCb) != 0) {
+ cbForNandSeek1(-1, NULL);
+ }
+ } else {
+ NextOffset = 0;
+ __ErrorInfo.nextOffset = 1;
+ if (NANDWriteAsync(&NandInfo, (void*)&__FirstErrorInfo, sizeof(__FirstErrorInfo), cbForNandWrite0, &NandCb) != 0) {
+ cbForNandWrite0(-1, NULL);
+ }
+ }
+ } else {
+ if (Callback) {
+ Callback(2, NULL);
+ }
+ }
+}
+
+void cbForNandCreate(s32 result, NANDCommandBlock* block) {
+ if (result == 0 || result == -6) {
+ if (result == -6) {
+ ExistFlag = TRUE;
+ }
+
+ if (NANDPrivateOpenAsync("/shared2/test2/dvderror.dat", &NandInfo, 3, cbForNandOpen, &NandCb) != 0) {
+ if (Callback) {
+ Callback(2, NULL);
+ }
+ }
+ } else {
+ if (Callback) {
+ Callback(2, NULL);
+ }
+ }
+}
+
+void cbForNandCreateDir(s32 result, NANDCommandBlock* block) {
+ if (result == 0 || result == -6) {
+ if (NANDPrivateCreateAsync("/shared2/test2/dvderror.dat", 0x3F, 0, cbForNandCreate, &NandCb) != 0) {
+ if (Callback) {
+ Callback(2, NULL);
+ }
+ }
+ } else {
+ if (Callback) {
+ Callback(2, NULL);
+ }
+ }
+}
+
+void cbForPrepareControlRegister(u32 intType) {
+ if (intType == 1) {
+ __ErrorInfo.unk_0x14 = DVDLowGetControlRegister();
+ } else {
+ __ErrorInfo.unk_0x14 = 0xFFFFFFFF;
+ }
+
+ if (NANDPrivateCreateDirAsync("/shared2/test2", 0x3F, 0, cbForNandCreateDir, &NandCb) != 0) {
+ if (Callback) {
+ Callback(2, NULL);
+ }
+ }
+}
+
+void cbForPrepareStatusRegister(u32 intType) {
+ if (intType == 1) {
+ __ErrorInfo.status = DVDLowGetStatusRegister();
+ } else {
+ __ErrorInfo.status = 0xFFFFFFFF;
+ }
+
+ if (DVDLowPrepareControlRegister(cbForPrepareControlRegister) == 0) {
+ if (Callback) {
+ Callback(2, NULL);
+ }
+ }
+}
+
+void __DVDStoreErrorCode(u32 error, DVDCBCallback callback) {
+ __ErrorInfo.error = error;
+ __ErrorInfo.dateTime = (u32)OSTicksToSeconds(OSGetTime());
+ Callback = callback;
+ DVDLowPrepareStatusRegister(cbForPrepareStatusRegister);
+}
diff --git a/src/revolution/dvd/dvdfs.c b/src/revolution/dvd/dvdfs.c
new file mode 100644
index 0000000000..5be8b8297c
--- /dev/null
+++ b/src/revolution/dvd/dvdfs.c
@@ -0,0 +1,557 @@
+#include <revolution.h>
+#include <revolution/dvd.h>
+
+#include "__dvd.h"
+
+typedef struct FSTEntry {
+ /* 0x00 */ unsigned int isDirAndStringOff;
+ /* 0x04 */ unsigned int parentOrPosition;
+ /* 0x08 */ unsigned int nextEntryOrLength;
+} FSTEntry;
+
+static OSBootInfo* BootInfo;
+static FSTEntry* FstStart;
+static char* FstStringStart;
+static u32 MaxEntryNum;
+static u32 currentDirectory;
+
+OSThreadQueue __DVDThreadQueue;
+u32 __DVDLongFileNameFlag = 1;
+
+// prototypes
+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(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(void) {
+ BootInfo = (void*)OSPhysicalToCached(0);
+ FstStart = BootInfo->FSTLocation;
+ if (FstStart) {
+ MaxEntryNum = FstStart->nextEntryOrLength;
+ FstStringStart = (char*)FstStart + (MaxEntryNum* sizeof(FSTEntry));
+ }
+}
+
+/* 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(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(383, 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__, 452,
+ "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(536, fileInfo, "DVDFastOpen(): null pointer is specified to file info address ");
+ ASSERTMSG1LINE(539, (entrynum >= 0) && ((u32) entrynum < (u32) MaxEntryNum), "DVDFastOpen(): specified entry number '%d' is out of range ", entrynum);
+ ASSERTMSG1LINE(542, !entryIsDir(entrynum), "DVDFastOpen(): entry number '%d' is assigned to a directory ", entrynum);
+
+ if (entrynum < 0 || entrynum >= MaxEntryNum || entryIsDir(entrynum)) {
+ return FALSE;
+ }
+
+ fileInfo->startAddr = filePosition(entrynum) >> __DVDLayoutFormat;
+ fileInfo->length = fileLength(entrynum);
+ fileInfo->callback = (DVDCallback)NULL;
+ fileInfo->cb.state = DVD_STATE_END;
+
+ return TRUE;
+}
+
+BOOL DVDOpen(const char* fileName, DVDFileInfo* fileInfo) {
+ s32 entry;
+ char currentDir[128];
+
+ ASSERTMSGLINE(572, fileName, "DVDOpen(): null pointer is specified to file name ");
+ ASSERTMSGLINE(573, 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(587, !entryIsDir(entry), "DVDOpen(): directory '%s' is specified as a filename ", fileName);
+ return FALSE;
+ }
+
+ fileInfo->startAddr = filePosition(entry) >> __DVDLayoutFormat;
+ fileInfo->length = fileLength(entry);
+ fileInfo->callback = (DVDCallback)NULL;
+ fileInfo->cb.state = DVD_STATE_END;
+
+ return TRUE;
+}
+
+BOOL DVDClose(DVDFileInfo* fileInfo) {
+ ASSERTMSGLINE(611, fileInfo, "DVDClose(): null pointer is specified to file info address ");
+ DVDCancel(&(fileInfo->cb));
+ 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) {
+ u32 i;
+ char* name;
+ u32 loc;
+
+ if (entry == 0) {
+ return 0;
+ }
+
+ name = FstStringStart + stringOff(entry);
+ if (entryIsDir(entry)) {
+ i = parentDir(entry);
+ } else {
+ i = entry;
+ while (i != 0) {
+ if (entryIsDir(i) && nextDir(i) > entry) {
+ break;
+ }
+
+ i--;
+ }
+ }
+
+ loc = entryToPath(i, 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(721, (entrynum >= 0) && (entrynum < MaxEntryNum), "DVDConvertEntrynumToPath: specified entrynum(%d) is out of range ", entrynum);
+ ASSERTMSG1LINE(723, maxlen > 1, "DVDConvertEntrynumToPath: maxlen should be more than 1 (%d is specified)", maxlen);
+
+ 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(765, (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(787, dirName, "DVDChangeDir(): null pointer is specified to directory name ");
+ entry = DVDConvertPathToEntrynum(dirName);
+ ASSERTMSG2LINE(795, entry >= 0, "DVDChangeDir(): directory '%s' is not found under %s ", dirName, (DVDGetCurrentDir(currentDir, 128), currentDir));
+ ASSERTMSG1LINE(799, 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(831, fileInfo, "DVDReadAsync(): null pointer is specified to file info address ");
+ ASSERTMSGLINE(832, addr, "DVDReadAsync(): null pointer is specified to addr ");
+ ASSERTMSGLINE(836, !OFFSET(addr, 32), "DVDReadAsync(): address must be aligned with 32 byte boundaries ");
+ ASSERTMSGLINE(838, !(length & 0x1F), "DVDReadAsync(): length must be multiple of 32 byte ");
+ ASSERTMSGLINE(840, !(offset & 3), "DVDReadAsync(): offset must be multiple of 4 byte ");
+
+ DVD_ASSERTMSGLINE(845, (0 <= offset) && (offset <= fileInfo->length), "DVDReadAsync(): specified area is out of the file ");
+
+ DVD_ASSERTMSGLINE(851, (0 <= offset + length) && (offset + length < fileInfo->length + DVD_MIN_TRANSFER_SIZE), "DVDReadAsync(): specified area is out of the file ");
+
+ fileInfo->callback = callback;
+ DVDReadAbsAsyncPrio(&(fileInfo->cb), addr, length, (s32)(fileInfo->startAddr + (offset >> 2)),
+ 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));
+ ASSERTLINE(869, (void*) &fileInfo->cb == (void*) block);
+ if (fileInfo->callback) {
+ (fileInfo->callback)(result, fileInfo);
+ }
+}
+
+s32 DVDReadPrio(DVDFileInfo* fileInfo, void* addr, s32 length, s32 offset, s32 prio) {
+ int result;
+ DVDCommandBlock* block;
+ s32 state;
+ BOOL enabled;
+ s32 retVal;
+
+ ASSERTMSGLINE(901, fileInfo, "DVDRead(): null pointer is specified to file info address ");
+ ASSERTMSGLINE(902, addr, "DVDRead(): null pointer is specified to addr ");
+ ASSERTMSGLINE(906, !OFFSET(addr, 32), "DVDRead(): address must be aligned with 32 byte boundaries ");
+ ASSERTMSGLINE(908, !(length & 0x1F), "DVDRead(): length must be multiple of 32 byte ");
+ ASSERTMSGLINE(910, !(offset & 3), "DVDRead(): offset must be multiple of 4 byte ");
+
+ DVD_ASSERTMSGLINE(915, (0 <= offset) && (offset <= fileInfo->length), "DVDRead(): specified area is out of the file ");
+ DVD_ASSERTMSGLINE(921, (0 <= offset + length) && (offset + length < fileInfo->length + DVD_MIN_TRANSFER_SIZE), "DVDRead(): specified area is out of the file ");
+
+ block = &fileInfo->cb;
+ result = DVDReadAbsAsyncPrio(block, addr, length, fileInfo->startAddr + (offset >> 2), cbForReadSync, prio);
+ if (result == 0) {
+ return -1;
+ }
+ enabled = OSDisableInterrupts();
+
+ while (1) {
+ state = ((volatile DVDCommandBlock*)block)->state;
+ if (state == 0) {
+ retVal = (s32)block->transferredSize;
+ break;
+ } else if (state == -1) {
+ retVal = -1;
+ break;
+ } else if (state == 10) {
+ retVal = -3;
+ break;
+ }
+ OSSleepThread(&__DVDThreadQueue);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return retVal;
+}
+
+static void cbForReadSync(s32 result, DVDCommandBlock* block) {
+ OSWakeupThread(&__DVDThreadQueue);
+}
+
+int DVDSeekAsyncPrio(DVDFileInfo* fileInfo, s32 offset, DVDCallback callback, s32 prio) {
+ ASSERTMSGLINE(898, fileInfo, "DVDSeek(): null pointer is specified to file info address ");
+ ASSERTMSGLINE(902, !(offset & 3), "DVDSeek(): offset must be multiple of 4 byte ");
+
+ DVD_ASSERTMSGLINE(907, (0 <= offset) && (offset <= fileInfo->length), "DVDSeek(): offset is out of the file ");
+
+ fileInfo->callback = callback;
+ DVDSeekAbsAsyncPrio(&fileInfo->cb, (u32)(char*)fileInfo->startAddr + offset, cbForSeekAsync, prio);
+ return 1;
+}
+
+static void cbForSeekAsync(s32 result, DVDCommandBlock* block) {
+ DVDFileInfo* fileInfo;
+
+ fileInfo = (DVDFileInfo *)&block->next;
+ ASSERTLINE(925, (void*) &fileInfo->cb == (void*) block);
+ if (fileInfo->callback) {
+ (fileInfo->callback)(result, fileInfo);
+ }
+}
+
+s32 DVDSeekPrio(DVDFileInfo* fileInfo, s32 offset, s32 prio) {
+ int result;
+ DVDCommandBlock* block;
+ s32 state;
+ BOOL enabled;
+ s32 retVal;
+
+ ASSERTMSGLINE(955, fileInfo, "DVDSeek(): null pointer is specified to file info address ");
+ ASSERTMSGLINE(959, !(offset & 3), "DVDSeek(): offset must be multiple of 4 byte ");
+ ASSERTMSGLINE(963, (offset >= 0) && ((u32) offset <= (u32) fileInfo->length), "DVDSeek(): offset is out of the file ");
+
+ block = &fileInfo->cb;
+ result = DVDSeekAbsAsyncPrio(block, (u32)(char*)fileInfo->startAddr + 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(s32 result, DVDCommandBlock* block) {
+ OSWakeupThread(&__DVDThreadQueue);
+}
+
+s32 DVDGetFileInfoStatus(const DVDFileInfo* fileInfo) {
+ return DVDGetCommandBlockStatus(&fileInfo->cb);
+}
+
+BOOL DVDFastOpenDir(s32 entrynum, DVDDir* dir) {
+ ASSERTMSGLINE(1048, dir, "DVDFastOpenDir(): null pointer is specified to dir structure address ");
+ ASSERTMSG1LINE(1051, entrynum >= 0 && entrynum < MaxEntryNum, "DVDFastOpenDir(): specified entry number \'%d\' is out of range ", entrynum);
+ ASSERTMSG1LINE(1054, entryIsDir(entrynum), "DVDFastOpenDir(): entry number \'%d\' is assigned to a file ", entrynum);
+
+ if (entrynum < 0 || entrynum >= MaxEntryNum || !entryIsDir(entrynum)) {
+ return FALSE;
+ }
+
+ dir->entryNum = entrynum;
+ dir->location = entrynum + 1;
+ dir->next = FstStart[entrynum].nextEntryOrLength;
+ return TRUE;
+}
+
+BOOL DVDOpenDir(const char* dirName, DVDDir* dir) {
+ s32 entry;
+ char currentDir[128];
+
+ ASSERTMSGLINE(1178, dirName, "DVDOpendir(): null pointer is specified to directory name ");
+ ASSERTMSGLINE(1179, 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(1193, entryIsDir(entry), "DVDOpendir(): file \'%s\' is specified as a directory name ", dirName);
+ return FALSE;
+ }
+
+ dir->entryNum = entry;
+ dir->location = entry + 1;
+ dir->next = nextDir(entry);
+ return TRUE;
+}
+
+int DVDReadDir(DVDDir* dir, DVDDirEntry* dirent) {
+ u32 loc;
+
+ loc = dir->location;
+ if ((loc <= (u32) dir->entryNum) || ((u32) dir->next <= loc)) {
+ return 0;
+ }
+ dirent->entryNum = loc;
+ dirent->isDir = entryIsDir(loc);
+ dirent->name = FstStringStart + stringOff(loc);
+ dir->location = entryIsDir(loc) ? nextDir(loc) : loc + 1;
+ return 1;
+}
+
+int DVDCloseDir(DVDDir* dir) {
+ return 1;
+}
+
+void DVDRewindDir(DVDDir* dir) {
+ dir->location = dir->entryNum + 1;
+}
+
+void* DVDGetFSTLocation(void) {
+ return BootInfo->FSTLocation;
+}
+
+s32 DVDGetTransferredSize(DVDFileInfo* fileinfo) {
+ s32 bytes;
+ DVDCommandBlock* cb;
+
+ cb = &(fileinfo->cb);
+
+ 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->transferredSize;
+ break;
+ case DVD_STATE_WAITING:
+ bytes = 0;
+ break;
+ case DVD_STATE_BUSY:
+ bytes = (s32)(cb->transferredSize + (cb->currTransferSize - __DIRegs[6]));
+ break;
+ default:
+ ASSERTMSG1LINE(1391, FALSE, "DVDGetTransferredSize(): Illegal state (%d)", cb->state);
+ break;
+ }
+
+ return bytes;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+void DVDGetEntrynum() {
+ OSReport("DVDGetEntrynum(): null pointer is specified to file info address ");
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+void DVDGetEntryName() {
+ OSReport("DVDGetEntryName: specified entrynum(%d) is out of range ");
+}
diff --git a/src/revolution/dvd/dvdidutils.c b/src/revolution/dvd/dvdidutils.c
new file mode 100644
index 0000000000..2742086b58
--- /dev/null
+++ b/src/revolution/dvd/dvdidutils.c
@@ -0,0 +1,98 @@
+#include <revolution.h>
+#include <revolution/dvd.h>
+#include <string.h>
+
+#include "__dvd.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(const DVDDiskID* id1, const 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(70, id1, "DVDCompareDiskID(): Specified id1 is NULL\n");
+ ASSERTMSGLINE(71, id2, "DVDCompareDiskID(): Specified id2 is NULL\n");
+
+ game1 = id1->gameName;
+ game2 = id2->gameName;
+ company1 = id1->company;
+ company2 = id2->company;
+ diskNum1 = id1->diskNumber;
+ diskNum2 = id2->diskNumber;
+ version1 = id1->gameVersion;
+ version2 = id2->gameVersion;
+
+ length = strnlen(game1, sizeof(game1));
+ ASSERTMSGLINE(84, length == 0 || length == 4, "DVDCompareDiskID(): Specified game name for id1 is neither NULL nor 4 character long\n");
+ ASSERTMSGLINE(85, company1, "DVDCompareDiskID(): Specified company name for id1 is NULL\n");
+ ASSERTMSGLINE(86, company1[1] != 0, "DVDCompareDiskID(): Specified company name for id1 is not 2 character long\n");
+ ASSERTMSGLINE(87, diskNum1 == 0xFF || ((diskNum1 / 16) < 10 && diskNum1 % 16 < 10), "DVDCompareDiskID(): Specified disk number for id1 is neither 0xff nor a BCD number");
+ ASSERTMSGLINE(88, 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(91, length == 0 || length == 4, "DVDCompareDiskID(): Specified game name for id2 is neither NULL nor 4 character long\n");
+ ASSERTMSGLINE(92, company2, "DVDCompareDiskID(): Specified company name for id2 is NULL\n");
+ ASSERTMSGLINE(93, company2[1] != 0, "DVDCompareDiskID(): Specified company name for id2 is not 2 character long\n");
+ ASSERTMSGLINE(94, diskNum2 == 0xFF || ((diskNum2 / 16) < 10 && diskNum2 % 16 < 10), "DVDCompareDiskID(): Specified disk number for id2 is neither 0xff nor a BCD number");
+ ASSERTMSGLINE(95, version2 == 0xFF || ((version2 / 16) < 10 && version2 % 16 < 10), "DVDCompareDiskID(): Specified version number for id2 is neither 0xff nor a BCD number");
+#endif
+
+ if (id1->gameName[0] != 0 && id2->gameName[0] != 0 && strncmp(id1->gameName, id2->gameName, 4) != 0) {
+ return 0;
+ }
+
+ if (id1->company[0] == 0 || id2->company[0] == 0 || strncmp(id1->company, id2->company, 2) != 0) {
+ return 0;
+ }
+
+ if (id1->diskNumber != 0xFF && id2->diskNumber != 0xFF && id1->diskNumber != id2->diskNumber) {
+ return 0;
+ }
+
+ if (id1->gameVersion != 0xFF && id2->gameVersion != 0xFF && id1->gameVersion != id2->gameVersion) {
+ 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->gameName, game, 4);
+ }
+
+ if (company != NULL) {
+ strncpy(id->company, company, 2);
+ }
+
+ id->diskNumber = diskNum;
+ id->gameVersion = version;
+ return id;
+}
diff --git a/src/revolution/dvd/dvdqueue.c b/src/revolution/dvd/dvdqueue.c
new file mode 100644
index 0000000000..aa60da0d7a
--- /dev/null
+++ b/src/revolution/dvd/dvdqueue.c
@@ -0,0 +1,192 @@
+#include <revolution.h>
+#include <revolution/dvd.h>
+
+#include "__dvd.h"
+
+static struct {
+ /* 0x00 */ DVDCommandBlock* next;
+ /* 0x04 */ DVDCommandBlock* prev;
+} WaitingQueue[4];
+
+// prototypes
+static DVDCommandBlock* PopWaitingQueuePrio(s32 prio);
+
+void __DVDClearWaitingQueue(void) {
+ u32 i;
+
+ for(i = 0; i < 4; i++) {
+ DVDCommandBlock* q = (DVDCommandBlock*)&WaitingQueue[i].next;
+ q->next = q;
+ q->prev = q;
+ }
+}
+
+int __DVDPushWaitingQueue(s32 prio, DVDCommandBlock* block) {
+ BOOL enabled = OSDisableInterrupts();
+ DVDCommandBlock* q = (DVDCommandBlock*)&WaitingQueue[prio];
+
+ q->prev->next = block;
+ block->prev = q->prev;
+ block->next = q;
+ q->prev = block;
+ OSRestoreInterrupts(enabled);
+ return 1;
+}
+
+static DVDCommandBlock* PopWaitingQueuePrio(s32 prio) {
+ DVDCommandBlock* tmp;
+ BOOL enabled;
+ DVDCommandBlock* q;
+
+ enabled = OSDisableInterrupts();
+ q = (DVDCommandBlock*)&WaitingQueue[prio];
+ ASSERTLINE(99, q->next != q);
+
+ tmp = q->next;
+ q->next = tmp->next;
+ tmp->next->prev = q;
+ OSRestoreInterrupts(enabled);
+ tmp->next = 0;
+ tmp->prev = 0;
+ return tmp;
+}
+
+DVDCommandBlock* __DVDPopWaitingQueue(void) {
+ u32 i;
+ BOOL enabled;
+ DVDCommandBlock* q;
+
+ enabled = OSDisableInterrupts();
+ for (i = 0; i < 4; i++) {
+ q = (DVDCommandBlock*)&WaitingQueue[i];
+ if (q->next != q) {
+ OSRestoreInterrupts(enabled);
+ return PopWaitingQueuePrio(i);
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+ return NULL;
+}
+
+int __DVDCheckWaitingQueue(void) {
+ u32 i;
+ BOOL enabled;
+ DVDCommandBlock* q;
+
+ enabled = OSDisableInterrupts();
+ for (i = 0; i < 4; i++) {
+ q = (DVDCommandBlock*)&WaitingQueue[i];
+ if (q->next != q) {
+ OSRestoreInterrupts(enabled);
+ return 1;
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+ return 0;
+}
+
+DVDCommandBlock* __DVDGetNextWaitingQueue(void) {
+ u32 i;
+ BOOL enabled;
+ DVDCommandBlock* q, *tmp;
+
+ enabled = OSDisableInterrupts();
+
+ for (i = 0; i < 4; i++) {
+ q = (DVDCommandBlock*)&(WaitingQueue[i]);
+
+ if (q->next != q) {
+ tmp = q->next;
+ OSRestoreInterrupts(enabled);
+ return tmp;
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+ return NULL;
+}
+
+int __DVDDequeueWaitingQueue(DVDCommandBlock* block) {
+ BOOL enabled;
+ DVDCommandBlock* prev;
+ 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(DVDCommandBlock* block) {
+ u32 i;
+ DVDCommandBlock* start;
+ DVDCommandBlock* q;
+
+ for (i = 0; i < 4; i++) {
+ start = (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) {
+ u32 i;
+ DVDCommandBlock* start;
+ DVDCommandBlock* q;
+
+ OSReport("==== DVD Waiting Queue Status ====\n");
+ for (i = 0; i < 4; i++) {
+ OSReport("< Queue #%d > ", i);
+ start = (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->addr);
+ } else {
+ OSReport("\n");
+ }
+ }
+ }
+ }
+}
diff --git a/src/revolution/fs/fs.c b/src/revolution/fs/fs.c
new file mode 100644
index 0000000000..5ea6eecd54
--- /dev/null
+++ b/src/revolution/fs/fs.c
@@ -0,0 +1,973 @@
+#include <revolution/fs.h>
+#include <revolution/ipc.h>
+#include <string.h>
+
+static IOSFd __fsFd = -1;
+static u32 __fsInitialized = FALSE;
+static char* __devfs = 0;
+static IOSHeapId hId;
+static s32 _asynCnt = 0;
+
+#define ROUNDUP(sz) (((u32)(sz) + 31) & ~(u32)(31))
+
+typedef struct isfs_GetAttr {
+ IOSUid* ownerId;
+ IOSGid* groupId;
+ u32* attr;
+ u32* ownerAcc;
+ u32* groupAcc;
+ u32* othersAcc;
+} isfs_GetAttr;
+
+typedef struct isfs_GetUsage {
+ u32* nblocks;
+ u32* ninodes;
+} isfs_GetUsage;
+
+typedef struct __isfsCtxt {
+ u8 ioBuf[ROUNDUP(256)] ATTRIBUTE_ALIGN(32);
+ ISFSCallback cb;
+ void* ctxt;
+ u32 func;
+
+ union {
+ ISFSStats* stats;
+ ISFSFileStats* fstats;
+ u32* num;
+ isfs_GetAttr ga;
+ isfs_GetUsage gu;
+ } args;
+} __isfsCtxt;
+
+static IOSError _FSGetStatsCb(IOSError ret, void* ctxt);
+static IOSError _FSReadDirCb(IOSError ret, void* ctxt);
+static IOSError _FSGetAttrCb(IOSError ret, void* ctxt);
+static IOSError _FSGetUsageCb(IOSError ret, void* ctxt);
+static IOSError _FSGetFileStatsCb(IOSError ret, void* ctxt);
+
+ISFSError ISFS_OpenLib(void) {
+ ISFSError rc = 0;
+ static void* lo = 0, *hi = 0;
+ __isfsCtxt* __fsCtxt = 0;
+
+ if (!__fsInitialized) {
+ lo = IPCGetBufferLo();
+ hi = IPCGetBufferHi();
+ }
+
+ __devfs = (char*)ROUNDUP(lo);
+ if (!__fsInitialized && ((u32)__devfs + ROUNDUP(64)) > (u32)hi) {
+ OSReport("APP ERROR: Not enough IPC arena\n");
+ rc = -22;
+ goto out;
+ }
+
+ strcpy(__devfs, "/dev/fs");
+ __fsFd = IOS_Open(__devfs, 0);
+
+ if (__fsFd < 0) {
+ rc = __fsFd;
+ goto out;
+ }
+
+ __fsCtxt = (__isfsCtxt*)((u32)__devfs);
+
+ if (!__fsInitialized && ((u32)__fsCtxt + (16 + 1) * ROUNDUP(sizeof(__isfsCtxt))) > (u32)hi) {
+ OSReport("APP ERROR: Not enough IPC arena\n");
+ rc = -22;
+ goto out;
+ }
+
+ if (!__fsInitialized) {
+ IPCSetBufferLo((void*)((u32)__fsCtxt + (17) * ROUNDUP(sizeof(__isfsCtxt))));
+ __fsInitialized = TRUE;
+ }
+
+ hId = iosCreateHeap(__fsCtxt, 17 * ROUNDUP(sizeof(__isfsCtxt)));
+
+ if (hId < 0) {
+ rc = -22;
+ goto out;
+ }
+
+out:
+ return rc;
+}
+
+IOSError _isfsFuncCb(IOSError ret, void* ctxt) {
+ ISFSError rc = 0;
+ __isfsCtxt* _ctxt = (__isfsCtxt*)ctxt;
+ rc = ret;
+
+ if (rc >= 0) {
+ switch (_ctxt->func) {
+ case 1:
+ _FSGetStatsCb(ret, ctxt);
+ break;
+ case 2:
+ _FSReadDirCb(ret, ctxt);
+ break;
+ case 3:
+ _FSGetAttrCb(ret, ctxt);
+ break;
+ case 4:
+ _FSGetUsageCb(ret, ctxt);
+ break;
+ case 5:
+ _FSGetFileStatsCb(ret, ctxt);
+ break;
+ default:
+ break;
+ }
+ }
+
+ _asynCnt = 0;
+
+ if (_ctxt->cb) {
+ _ctxt->cb(rc, _ctxt->ctxt);
+ }
+
+ if (ctxt) {
+ iosFree(hId, ctxt);
+ }
+
+ return rc;
+}
+
+static IOSError _FSGetStatsCb(IOSError ret, void* ctxt) {
+ __isfsCtxt* _ctxt = (__isfsCtxt*)ctxt;
+ ISFSError rc = ISFS_ERROR_OK;
+
+ if (ret == 0) {
+ memcpy(_ctxt->args.stats, _ctxt->ioBuf, sizeof(*_ctxt->args.stats));
+ }
+
+ return rc;
+}
+
+s32 ISFS_CreateDir(const u8* dname, u32 dirAttr, u32 ownerAcc, u32 groupAcc, u32 othersAcc) {
+ ISFSError rc = ISFS_ERROR_OK;
+ ISFSPathAttrArgs* pathAttrArgs;
+ __isfsCtxt* blCtxt = 0;
+ u32 len;
+
+ if (dname == NULL || __fsFd < 0 || (len = strnlen(dname, 64)) == 64) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ blCtxt = iosAllocAligned(hId, sizeof(*blCtxt), 32);
+
+ if (blCtxt == 0) {
+ rc = IOS_ERROR_FAIL_ALLOC;
+ goto out;
+ }
+
+ pathAttrArgs = (ISFSPathAttrArgs*)blCtxt->ioBuf;
+ memcpy(pathAttrArgs->path, dname, len + 1);
+
+ pathAttrArgs->attr = (u8)dirAttr;
+ pathAttrArgs->ownerAccess = (u8)ownerAcc;
+ pathAttrArgs->groupAccess = (u8)groupAcc;
+ pathAttrArgs->othersAccess = (u8)othersAcc;
+ rc = IOS_Ioctl(__fsFd, 3, pathAttrArgs, sizeof(*pathAttrArgs), NULL, 0);
+
+out:
+ /* they seem to have had a macro for this, and the programmer forgot it did a NULL check */
+ if (blCtxt != 0) {
+ if (blCtxt != 0) {
+ iosFree(hId, blCtxt);
+ }
+
+ }
+
+ return rc;
+}
+
+s32 ISFS_CreateDirAsync(const u8* dname, u32 dirAttr, u32 ownerAcc, u32 groupAcc, u32 othersAcc, ISFSCallback cb, void* fsCtxt) {
+ ISFSError rc = ISFS_ERROR_OK;
+ u32 len;
+ ISFSPathAttrArgs* pathAttrArgs;
+ __isfsCtxt* ctxt;
+
+ if (dname == NULL || __fsFd < 0 || (len = strnlen(dname, 64)) == 64) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32);
+
+ if (ctxt == 0) {
+ rc = ISFS_ERROR_BUSY;
+ goto out;
+ }
+
+ ctxt->cb = cb;
+ ctxt->ctxt = fsCtxt;
+ ctxt->func = 0;
+ pathAttrArgs = (ISFSPathAttrArgs*)ctxt->ioBuf;
+ memcpy(pathAttrArgs->path, (void*)dname, len + 1);
+ pathAttrArgs->attr = dirAttr;
+ pathAttrArgs->ownerAccess = ownerAcc;
+ pathAttrArgs->groupAccess = groupAcc;
+ pathAttrArgs->othersAccess = othersAcc;
+ rc = IOS_IoctlAsync(__fsFd, 3, pathAttrArgs, sizeof(*pathAttrArgs), NULL, 0, _isfsFuncCb, ctxt);
+
+out:
+ return rc;
+}
+
+s32 ISFS_ReadDir(const u8* dname, u8* nameList, u32* num) {
+ ISFSError rc = ISFS_ERROR_OK;
+ IOSIoVector* v = 0;
+ u32 len, numInputs, numOutputs, *numPtr;
+ char* dnPtr;
+ __isfsCtxt* blCtxt = 0;
+
+ if (dname == NULL || num == NULL || __fsFd < 0 || (u32)nameList & 31 || (len = strnlen(dname, 64)) == 64) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ blCtxt = iosAllocAligned(hId, sizeof(*blCtxt), 32);
+ if (blCtxt == 0) {
+ rc = IOS_ERROR_FAIL_ALLOC;
+ goto out;
+ }
+
+ v = (IOSIoVector*)blCtxt->ioBuf;
+ dnPtr = (char*)ROUNDUP((u8*)&v[4]);
+ memcpy(dnPtr, dname, len + 1);
+ v[0].base = (u8*)dnPtr;
+ v[0].length = 64;
+ numPtr = (u32*)ROUNDUP((u32)dnPtr + 64);
+ v[1].base = (u8*)numPtr;
+ v[1].length = 4;
+
+ if (nameList != 0) {
+ numInputs = 2;
+ numOutputs = 2;
+ *numPtr = *num;
+ v[2].base = nameList;
+ v[2].length = *num * 13;
+ v[3].base = (u8*)numPtr;
+ v[3].length = 4;
+ } else {
+ numInputs = 1;
+ numOutputs = 1;
+ }
+
+ rc = IOS_Ioctlv(__fsFd, 4, numInputs, numOutputs, v);
+
+ if (rc != ISFS_ERROR_OK) {
+ goto out;
+ }
+
+ *num = *numPtr;
+
+out:
+ if (blCtxt != 0) {
+ if (blCtxt != 0) {
+ iosFree(hId, blCtxt);
+ }
+ }
+
+ return rc;
+}
+
+static IOSError _FSReadDirCb(IOSError ret, void* ctxt) {
+ ISFSError rc = ISFS_ERROR_OK;
+ __isfsCtxt* _ctxt = (__isfsCtxt*)ctxt;
+
+ if (ret == 0) {
+ u8* ptr;
+ IOSIoVector* v = (IOSIoVector*)_ctxt->ioBuf;
+ ptr = (u8*)ROUNDUP((u8*)&v[4]);
+ ptr = (u8*)ROUNDUP(ptr + 64);
+ *_ctxt->args.num = *(u32 *) ptr;
+ }
+
+ return rc;
+}
+
+s32 ISFS_ReadDirAsync(const u8* dname, u8* nameList, u32* num, ISFSCallback cb, void* fsCtxt) {
+ ISFSError rc = ISFS_ERROR_OK;
+ u32 len, numOutputs, numInputs, *numPtr;
+ IOSIoVector* v;
+ __isfsCtxt* ctxt;
+ char* dnPtr;
+
+ if (dname == NULL || num == NULL || __fsFd < 0 || (u32)nameList & 31 || (len = strnlen(dname, 64)) == 64) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32);
+ if (ctxt == 0) {
+ rc = ISFS_ERROR_BUSY;
+ goto out;
+ }
+
+ ctxt->cb = cb;
+ ctxt->ctxt = fsCtxt;
+ ctxt->func = 2;
+ ctxt->args.num = num;
+ v = (IOSIoVector*)ctxt->ioBuf;
+ dnPtr = (char*)ROUNDUP((u8*)&v[4]);
+ memcpy(dnPtr, dname, len + 1);
+ v[0].base = (u8*)dnPtr;
+ v[0].length = 64;
+ numPtr = (u32*)ROUNDUP((u32)dnPtr + 64);
+ v[1].base = (u8*)numPtr;
+ v[1].length = 4;
+
+ if (nameList != 0) {
+ numInputs = 2;
+ numOutputs = 2;
+ *numPtr = *num;
+ v[2].base = nameList;
+ v[2].length = *num * 13;
+ v[3].base = (u8*)numPtr;
+ v[3].length = 4;
+ } else {
+ numInputs = 1;
+ numOutputs = 1;
+ }
+
+ rc = IOS_IoctlvAsync(__fsFd, 4, numInputs, numOutputs, v, _isfsFuncCb, ctxt);
+
+out:
+ return rc;
+}
+
+s32 ISFS_GetAttr(const u8* name, IOSUid* ownerId, IOSGid* groupId, u32* attr, u32* ownerAcc, u32* groupAcc, u32* othersAcc) {
+ ISFSError rc = ISFS_ERROR_OK;
+ ISFSPathAttrArgs* pathAttrArgs;
+ u8* ptr;
+ u32 len;
+ __isfsCtxt* blCtxt = 0;
+
+ if (name == NULL || __fsFd < 0 ||
+ (len = strnlen(name, 64)) == 64 ||
+ ownerId == NULL || groupId == NULL || attr == NULL ||
+ ownerAcc == NULL || groupAcc == NULL || othersAcc == NULL) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ blCtxt = iosAllocAligned(hId, sizeof(*blCtxt), 32);
+ if (blCtxt == 0) {
+ rc = IOS_ERROR_FAIL_ALLOC;
+ goto out;
+ }
+
+ ptr = (u8*)blCtxt->ioBuf;
+ memcpy(ptr, name, len + 1);
+ pathAttrArgs = (ISFSPathAttrArgs*)ROUNDUP(ptr + 64);
+ rc = IOS_Ioctl(__fsFd, 6, ptr, 64, pathAttrArgs, sizeof(*pathAttrArgs));
+
+ if (rc != IOS_ERROR_OK) {
+ goto out;
+ }
+
+ *ownerId = pathAttrArgs->ownerId;
+ *groupId = pathAttrArgs->groupId;
+ *attr = pathAttrArgs->attr;
+ *ownerAcc = pathAttrArgs->ownerAccess;
+ *groupAcc = pathAttrArgs->groupAccess;
+ *othersAcc = pathAttrArgs->othersAccess;
+
+out:
+ if (blCtxt != 0) {
+ if (blCtxt != 0) {
+ iosFree(hId, blCtxt);
+ }
+ }
+
+ return rc;
+}
+
+static IOSError _FSGetAttrCb(IOSError ret, void* ctxt) {
+ ISFSError rc = ret;
+
+ if (ret == 0) {
+ __isfsCtxt* _ctxt = (__isfsCtxt*)ctxt;
+ ISFSPathAttrArgs* pathAttrArgs = (ISFSPathAttrArgs*)ROUNDUP(_ctxt->ioBuf + 64);
+
+ *_ctxt->args.ga.ownerId = pathAttrArgs->ownerId;
+ *_ctxt->args.ga.groupId = pathAttrArgs->groupId;
+ *_ctxt->args.ga.attr = pathAttrArgs->attr;
+ *_ctxt->args.ga.ownerAcc = pathAttrArgs->ownerAccess;
+ *_ctxt->args.ga.groupAcc = pathAttrArgs->groupAccess;
+ *_ctxt->args.ga.othersAcc = pathAttrArgs->othersAccess;
+ }
+
+ return rc;
+}
+
+s32 ISFS_GetAttrAsync(const u8* name, IOSUid* ownerId, IOSGid* groupId, u32* attr, u32* ownerAcc, u32* groupAcc, u32* othersAcc, ISFSCallback cb, void* fsCtxt) {
+ ISFSError rc = ISFS_ERROR_OK;
+ __isfsCtxt *ctxt;
+ ISFSPathAttrArgs *pathAttrArgs;
+ u8 *ptr;
+ u32 len;
+
+ if (name == NULL || __fsFd < 0 ||
+ (len = strnlen(name, 64)) == 64 ||
+ ownerId == NULL || groupId == NULL || attr == NULL ||
+ ownerAcc == NULL || groupAcc == NULL || othersAcc == NULL) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32);
+ if (ctxt == 0) {
+ rc = ISFS_ERROR_BUSY;
+ goto out;
+ }
+
+ ctxt->args.ga.ownerId = ownerId;
+ ctxt->args.ga.groupId = groupId;
+ ctxt->args.ga.attr = attr;
+ ctxt->args.ga.ownerAcc = ownerAcc;
+ ctxt->args.ga.groupAcc = groupAcc;
+ ctxt->args.ga.othersAcc = othersAcc;
+ ctxt->cb = cb;
+ ctxt->ctxt = fsCtxt;
+ ctxt->func = 3;
+
+ ptr = (u8*)ctxt->ioBuf;
+ memcpy(ptr, name, len + 1);
+ pathAttrArgs = (ISFSPathAttrArgs*)ROUNDUP(ptr + 64);
+
+ rc = IOS_IoctlAsync(__fsFd, 6, ptr, 64, pathAttrArgs, sizeof(*pathAttrArgs), _isfsFuncCb, ctxt);
+out:
+ return rc;
+}
+
+s32 ISFS_Delete(const u8* name) {
+ ISFSError rc = ISFS_ERROR_OK;
+ u32 len;
+ __isfsCtxt* blCtxt = 0;
+
+ if (name == NULL || __fsFd < 0 || (len = strnlen(name, 64)) == 64) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ blCtxt = iosAllocAligned(hId, sizeof(*blCtxt), 32);
+ if (blCtxt == 0) {
+ rc = IOS_ERROR_FAIL_ALLOC;
+ goto out;
+ }
+
+ memcpy(blCtxt->ioBuf, name, len + 1);
+ rc = IOS_Ioctl(__fsFd, 7, blCtxt->ioBuf, 64, NULL, 0);
+
+out:
+ if (blCtxt != 0) {
+ if (blCtxt != 0) {
+ iosFree(hId, blCtxt);
+ }
+ }
+
+ return rc;
+}
+
+s32 ISFS_DeleteAsync(const u8* name, ISFSCallback cb, void* fsCtxt) {
+ ISFSError rc = ISFS_ERROR_OK;
+ u32 len;
+ __isfsCtxt* ctxt;
+
+ if (name == NULL || __fsFd < 0 || (len = strnlen(name, 64)) == 64) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32);
+ if (ctxt == 0) {
+ rc = ISFS_ERROR_BUSY;
+ goto out;
+ }
+
+ memcpy(ctxt->ioBuf, name, len + 1);
+ ctxt->cb = cb;
+ ctxt->ctxt = fsCtxt;
+ ctxt->func = 0;
+ rc = IOS_IoctlAsync(__fsFd, 7, ctxt->ioBuf, 64, NULL, 0, _isfsFuncCb, ctxt);
+
+out:
+ return rc;
+}
+
+s32 ISFS_Rename(const u8* oldName, const u8* newName) {
+ ISFSError rc = ISFS_ERROR_OK;
+ ISFSPathsArgs* pathsArgs;
+ u32 oldLen, newLen;
+ __isfsCtxt* blCtxt = 0;
+
+ if (oldName == NULL || newName == NULL || __fsFd < 0 ||
+ (oldLen = strnlen(oldName, 64)) == 64 ||
+ (newLen = strnlen(newName, 64)) == 64) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ blCtxt = iosAllocAligned(hId, sizeof(*blCtxt), 32);
+ if (blCtxt == 0) {
+ rc = IOS_ERROR_FAIL_ALLOC;
+ goto out;
+ }
+
+ pathsArgs = (ISFSPathsArgs*)blCtxt->ioBuf;
+ memcpy(pathsArgs->path1, oldName, oldLen + 1);
+ memcpy(pathsArgs->path2, newName, newLen + 1);
+ rc = IOS_Ioctl(__fsFd, 8, pathsArgs, sizeof(*pathsArgs), NULL, 0);
+
+out:
+ if (blCtxt != 0) {
+ if (blCtxt != 0) {
+ iosFree(hId, blCtxt);
+ }
+ }
+
+ return rc;
+}
+
+s32 ISFS_RenameAsync(const u8* oldName, const u8* newName, ISFSCallback cb, void* fsCtxt) {
+ ISFSError rc = ISFS_ERROR_OK;
+ ISFSPathsArgs* pathsArgs;
+ u32 oldLen, newLen;
+ __isfsCtxt* ctxt;
+
+ if ((oldName == NULL) || newName == NULL || __fsFd < 0 ||
+ (oldLen = strnlen(oldName, 64)) == 64 ||
+ (newLen = strnlen(newName, 64)) == 64) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32);
+ if (ctxt == NULL) {
+ rc = ISFS_ERROR_BUSY;
+ goto out;
+ }
+
+ ctxt->cb = cb;
+ ctxt->ctxt = fsCtxt;
+ ctxt->func = 0;
+
+ pathsArgs = (ISFSPathsArgs*)ctxt->ioBuf;
+ memcpy(pathsArgs->path1, oldName, oldLen + 1);
+ memcpy(pathsArgs->path2, newName, newLen + 1);
+ rc = IOS_IoctlAsync(__fsFd, 8, pathsArgs, sizeof(*pathsArgs), NULL, 0, _isfsFuncCb, ctxt);
+
+out:
+ return rc;
+}
+
+s32 ISFS_GetUsage(const u8* dname, u32* nblocks, u32* ninodes) {
+ ISFSError rc = ISFS_ERROR_OK;
+ IOSIoVector* v = 0;
+ u32 len, *blkPtr, *inodePtr;
+ char* dnPtr;
+ __isfsCtxt* blCtxt = 0;
+
+ if (dname == NULL || __fsFd < 0 ||
+ nblocks == NULL || ninodes == NULL ||
+ (len = strnlen(dname, 64)) == 64) {
+
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ blCtxt = iosAllocAligned(hId, sizeof(*blCtxt), 32);
+ if (blCtxt == 0) {
+ rc = IOS_ERROR_FAIL_ALLOC;
+ goto out;
+ }
+
+ v = (IOSIoVector*)blCtxt->ioBuf;
+ dnPtr = (char*)ROUNDUP((u8*)&v[3]);
+ memcpy(dnPtr, dname, len + 1);
+ v[0].base = (u8*)dnPtr;
+ v[0].length = 64;
+
+ blkPtr = (u32*) ROUNDUP(((u32)dnPtr) + 64);
+ inodePtr = (u32*) ROUNDUP(((u32)blkPtr) + 4);
+ v[1].base = (u8*) blkPtr;
+ v[1].length = 4;
+ v[2].base = (u8*)inodePtr;
+ v[2].length = 4;
+
+ rc = IOS_Ioctlv(__fsFd, 12, 1, 2, v);
+ if (rc != ISFS_ERROR_OK) {
+ goto out;
+ }
+
+ *nblocks = *blkPtr;
+ *ninodes = *inodePtr;
+
+out:
+ if (blCtxt != 0) {
+ if (blCtxt != 0) {
+ iosFree(hId, blCtxt);
+ }
+ }
+
+ return rc;
+}
+
+static IOSError _FSGetUsageCb(IOSError ret, void* ctxt) {
+ ISFSError rc = ISFS_ERROR_OK;
+ __isfsCtxt* _ctxt = (__isfsCtxt*)ctxt;
+
+ if (ret == 0) {
+ u8* ptr;
+ IOSIoVector* v = (IOSIoVector*)_ctxt->ioBuf;
+ ptr = (u8*)ROUNDUP((u8*)&v[4]);
+ ptr = (u8*)ROUNDUP(ptr + 64);
+ *_ctxt->args.gu.nblocks = *(u32*)ptr;
+ ptr = (u8*)ROUNDUP(ptr + 4);
+ *_ctxt->args.gu.ninodes = *(u32*)ptr;
+ }
+
+ return rc;
+}
+
+s32 ISFS_CreateFile(const u8* fname, u32 fileAttr, u32 ownerAcc, u32 groupAcc, u32 othersAcc) {
+ ISFSError rc = ISFS_ERROR_OK;
+ ISFSPathAttrArgs* pathAttrArgs;
+ u32 len;
+ __isfsCtxt* blCtxt = NULL;
+
+ if (fname == NULL || __fsFd < 0 || (len = strnlen(fname, 64)) == 64) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ blCtxt = iosAllocAligned(hId, sizeof(*blCtxt), 32);
+ if (blCtxt == 0) {
+ rc = IOS_ERROR_FAIL_ALLOC;
+ goto out;
+ }
+
+ pathAttrArgs = (ISFSPathAttrArgs*)blCtxt->ioBuf;
+ memcpy(pathAttrArgs->path, fname, len + 1);
+ pathAttrArgs->attr = fileAttr;
+ pathAttrArgs->ownerAccess = ownerAcc;
+ pathAttrArgs->groupAccess = groupAcc;
+ pathAttrArgs->othersAccess = othersAcc;
+ rc = IOS_Ioctl(__fsFd, 9, pathAttrArgs, sizeof(*pathAttrArgs), NULL, 0);
+
+out:
+ if (blCtxt != 0) {
+ if (blCtxt != 0) {
+ iosFree(hId, blCtxt);
+ }
+ }
+
+ return rc;
+}
+
+s32 ISFS_CreateFileAsync(const u8* fname, u32 fileAttr, u32 ownerAcc, u32 groupAcc, u32 othersAcc, ISFSCallback cb, void* fsCtxt) {
+ ISFSError rc = ISFS_ERROR_OK;
+ ISFSPathAttrArgs* pathAttrArgs;
+ u32 len;
+ __isfsCtxt* ctxt;
+
+ if (fname == NULL || __fsFd < 0 || (len = strnlen(fname, 64)) == 64) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32);
+ if (ctxt == 0) {
+ rc = ISFS_ERROR_BUSY;
+ goto out;
+ }
+
+ ctxt->cb = cb;
+ ctxt->ctxt = fsCtxt;
+ ctxt->func = 0;
+
+ pathAttrArgs = (ISFSPathAttrArgs*)ctxt->ioBuf;
+ memcpy(pathAttrArgs->path, fname, len + 1);
+ pathAttrArgs->attr = fileAttr;
+ pathAttrArgs->ownerAccess = ownerAcc;
+ pathAttrArgs->groupAccess = groupAcc;
+ pathAttrArgs->othersAccess = othersAcc;
+ rc = IOS_IoctlAsync(__fsFd, 9, pathAttrArgs, sizeof(*pathAttrArgs), NULL, 0, _isfsFuncCb, ctxt);
+
+out:
+ return rc;
+}
+
+IOSFd ISFS_Open(const u8* fname, u32 access) {
+ ISFSError rc = ISFS_ERROR_OK;
+ u32 len;
+ __isfsCtxt* blCtxt = 0;
+
+ if (fname == NULL || (len = strnlen(fname, 64)) == 64) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ blCtxt = iosAllocAligned(hId, sizeof(*blCtxt), 32);
+ if (blCtxt == 0) {
+ rc = IOS_ERROR_FAIL_ALLOC;
+ goto out;
+ }
+
+ memcpy(blCtxt->ioBuf, fname, len + 1);
+ rc = IOS_Open((const char*)blCtxt->ioBuf, access);
+
+out:
+ if (blCtxt != 0) {
+ if (blCtxt != 0) {
+ iosFree(hId, blCtxt);
+ }
+ }
+
+ return rc;
+}
+
+IOSFd ISFS_OpenAsync(const u8* fname, u32 access, ISFSCallback cb, void* fsCtxt) {
+ ISFSError rc = ISFS_ERROR_OK;
+ u32 len;
+ __isfsCtxt* ctxt;
+
+ if (fname == NULL || (len = strnlen(fname, 64)) == 64) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32);
+ if (ctxt == 0) {
+ rc = ISFS_ERROR_BUSY;
+ goto out;
+ }
+
+ ctxt->cb = cb;
+ ctxt->ctxt = fsCtxt;
+ ctxt->func = 0;
+ memcpy(ctxt->ioBuf, fname, len + 1);
+ rc = IOS_OpenAsync((const char*)ctxt->ioBuf, access, _isfsFuncCb, ctxt);
+
+out:
+ return rc;
+}
+
+static IOSError _FSGetFileStatsCb(IOSError ret, void* ctxt) {
+ __isfsCtxt* _ctxt = (__isfsCtxt*)ctxt;
+ ISFSError rc = ISFS_ERROR_OK;
+
+ if (ret == 0) {
+ memcpy(_ctxt->args.fstats, _ctxt->ioBuf, sizeof(*_ctxt->args.fstats));
+ }
+
+ return rc;
+}
+
+s32 ISFS_GetFileStats(IOSFd fd, ISFSFileStats* stats) {
+ ISFSError rc = ISFS_ERROR_OK;
+ __isfsCtxt* blCtxt = 0;
+
+ if (stats == NULL || ((u32)stats & 31)) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ blCtxt = iosAllocAligned(hId, sizeof(*blCtxt), 32);
+ if (blCtxt == 0) {
+ rc = IOS_ERROR_FAIL_ALLOC;
+ goto out;
+ }
+
+ rc = IOS_Ioctl(fd, 11, NULL, 0, blCtxt->ioBuf, sizeof(*stats));
+
+ if (rc != IOS_ERROR_OK) {
+ goto out;
+ }
+
+ memcpy(stats, blCtxt->ioBuf, sizeof(*stats));
+
+out:
+ if (blCtxt != 0) {
+ if (blCtxt != 0) {
+ iosFree(hId, blCtxt);
+ }
+ }
+
+ return rc;
+}
+
+s32 ISFS_GetFileStatsAsync(IOSFd fd, ISFSFileStats* stats, ISFSCallback cb, void* fsCtxt) {
+ ISFSError rc = ISFS_ERROR_OK;
+ __isfsCtxt* ctxt;
+
+ if (stats == NULL || ((u32)stats & 31)) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32);
+ if (ctxt == 0) {
+ rc = ISFS_ERROR_BUSY;
+ goto out;
+ }
+
+ ctxt->cb = cb;
+ ctxt->ctxt = fsCtxt;
+ ctxt->func = 5;
+ ctxt->args.fstats = stats;
+ rc = IOS_IoctlAsync(fd, 11, NULL, 0, ctxt->ioBuf, sizeof(*stats), _isfsFuncCb, ctxt);
+
+out:
+ return rc;
+}
+
+s32 ISFS_Seek(IOSFd fd, s32 offset, u32 whence) {
+ ISFSError rc = ISFS_ERROR_OK;
+ rc = IOS_Seek(fd, offset, whence);
+ return rc;
+}
+
+s32 ISFS_SeekAsync(IOSFd fd, s32 offset, u32 whence, ISFSCallback cb, void* fsCtxt) {
+ ISFSError rc;
+ __isfsCtxt* ctxt;
+
+ ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32);
+ if (ctxt == 0) {
+ rc = ISFS_ERROR_BUSY;
+ goto out;
+ }
+
+ ctxt->cb = cb;
+ ctxt->ctxt = fsCtxt;
+ ctxt->func = 0;
+ rc = IOS_SeekAsync(fd, offset, whence, _isfsFuncCb, ctxt);
+
+out:
+ return rc;
+}
+
+s32 ISFS_Read(s32 fd, u8* pBuffer, u32 bufSize) {
+ ISFSError rc = ISFS_ERROR_OK;
+
+ /* nullptr check and alignment to 0x20 */
+ if (pBuffer == NULL || (u32)pBuffer & 31) {
+ rc = ISFS_ERROR_INVALID;
+ } else {
+ rc = IOS_Read(fd, pBuffer, bufSize);;
+ }
+
+ return rc;
+}
+
+s32 ISFS_ReadAsync(IOSFd fd, u8* buf, u32 size, ISFSCallback cb, void* fsCtxt) {
+ ISFSError rc = ISFS_ERROR_OK;
+ __isfsCtxt* ctxt;
+
+ if (buf == NULL || ((u32)buf & 31)) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32);
+ if (ctxt == 0) {
+ rc = ISFS_ERROR_BUSY;
+ goto out;
+ }
+
+ ctxt->cb = cb;
+ ctxt->ctxt = fsCtxt;
+ ctxt->func = 0;
+ rc = IOS_ReadAsync(fd, buf, size, _isfsFuncCb, ctxt);
+
+out:
+ return rc;
+}
+
+s32 ISFS_Write(IOSFd fd, const u8* buf, u32 size) {
+ ISFSError rc = ISFS_ERROR_OK;
+
+ if (buf == NULL || ((u32)buf & 31)) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ rc = IOS_Write(fd, (u8*)buf, size);
+out:
+ return rc;
+}
+
+s32 ISFS_WriteAsync(IOSFd fd, const u8* buf, u32 size, ISFSCallback cb, void* fsCtxt) {
+ ISFSError rc = ISFS_ERROR_OK;
+ __isfsCtxt* ctxt;
+
+ if (buf == NULL || ((u32)buf & (u32)(31))) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32);
+ if (ctxt == 0) {
+ rc = ISFS_ERROR_BUSY;
+ goto out;
+ }
+
+ ctxt->cb = cb;
+ ctxt->ctxt = fsCtxt;
+ ctxt->func = 0;
+ rc = IOS_WriteAsync(fd, (void*)buf, size, _isfsFuncCb, ctxt);
+
+out:
+ return rc;
+}
+
+s32 ISFS_Close(IOSFd fd) {
+ ISFSError rc = ISFS_ERROR_OK;
+ rc = IOS_Close(fd);
+ return rc;
+}
+
+s32 ISFS_CloseAsync(IOSFd fd, ISFSCallback cb, void* fsCtxt) {
+ ISFSError rc;
+ __isfsCtxt* ctxt;
+
+ ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32);
+
+ if (ctxt == 0) {
+ rc = ISFS_ERROR_BUSY;
+ goto out;
+ }
+
+ ctxt->cb = cb;
+ ctxt->ctxt = fsCtxt;
+ ctxt->func = 0;
+ rc = IOS_CloseAsync(fd, _isfsFuncCb, ctxt);
+
+out:
+ return rc;
+}
+
+s32 ISFS_ShutdownAsync(ISFSCallback cb, void* fsCtxt) {
+ __isfsCtxt* ctxt;
+ s32 rc = ISFS_ERROR_OK;
+
+ ctxt = iosAllocAligned(hId, sizeof(*ctxt), 32);
+
+ if (__fsFd < 0) {
+ rc = ISFS_ERROR_INVALID;
+ goto out;
+ }
+
+ ctxt->cb = cb;
+ ctxt->ctxt = fsCtxt;
+ ctxt->func = 0;
+ rc = IOS_IoctlAsync(__fsFd, 13, NULL, 0, NULL, 0, _isfsFuncCb, ctxt);
+
+out:
+ return rc;
+}
diff --git a/src/revolution/ipc/ipcMain.c b/src/revolution/ipc/ipcMain.c
new file mode 100644
index 0000000000..f126cb5b1f
--- /dev/null
+++ b/src/revolution/ipc/ipcMain.c
@@ -0,0 +1,51 @@
+#include <revolution/ipc.h>
+#include <revolution/os/OSIpc.h>
+#include <revolution/os.h>
+
+static void* IPCBufferHi;
+static void* IPCBufferLo;
+
+static void* IPCCurrentBufferHi;
+static void* IPCCurrentBufferLo;
+
+static u8 Initialized;
+
+void IPCInit(void) {
+ if (Initialized) {
+ return;
+ }
+
+ IPCBufferHi = __OSGetIPCBufferHi();
+ IPCBufferLo = __OSGetIPCBufferLo();
+ IPCCurrentBufferHi = IPCBufferHi;
+ IPCCurrentBufferLo = IPCBufferLo;
+
+ Initialized = TRUE;
+}
+
+void IPCReInit(void) {
+ Initialized = FALSE;
+ IPCInit();
+}
+
+u32 IPCReadReg(u32 regIdx) {
+ u32 reg = __IPCRegs[regIdx];
+ return reg;
+}
+
+void IPCWriteReg(u32 regIdx, u32 data) {
+ __IPCRegs[regIdx] = data;
+}
+
+void* IPCGetBufferHi(void) {
+ return IPCCurrentBufferHi;
+}
+
+void* IPCGetBufferLo(void) {
+ return IPCCurrentBufferLo;
+}
+
+void IPCSetBufferLo(void* newLo) {
+ ASSERTLINE(296, IPCBufferLo <= newLo);
+ IPCCurrentBufferLo = newLo;
+}
diff --git a/src/revolution/ipc/ipcProfile.c b/src/revolution/ipc/ipcProfile.c
new file mode 100644
index 0000000000..60bf758a75
--- /dev/null
+++ b/src/revolution/ipc/ipcProfile.c
@@ -0,0 +1,53 @@
+#include <revolution.h>
+#include <string.h>
+
+static u32 IpcReqPtrArray[96] = {0};
+static u32 IpcReqArray[0x300] = {0};
+static OSTime IpcStartTimeArray[96];
+static u8 IpcHandlePathBuf[0x1800];
+static u8 IpcOpenPathBuf[0x1200];
+
+static u32 IpcNumPendingReqs = 0;
+static u32 IpcNumUnIssuedReqs = 0;
+
+static void AddReqInfo(void* req);
+static void DelReqInfo(void* req);
+
+void IPCiProfInit(void) {
+ u32 i;
+
+ IpcNumPendingReqs = 0;
+ IpcNumUnIssuedReqs = 0;
+
+ for (i = 0; i < 96; ++i) {
+ IpcReqPtrArray[i] = 0;
+ IpcStartTimeArray[i] = 0;
+ }
+
+ memset(IpcHandlePathBuf, 0, sizeof(IpcHandlePathBuf));
+ memset(IpcOpenPathBuf, 0, sizeof(IpcOpenPathBuf));
+ memset(IpcReqArray, 0, sizeof(IpcReqArray));
+}
+
+void IPCiProfQueueReq(void* req, s32 handle) {
+ ++IpcNumPendingReqs;
+ ++IpcNumUnIssuedReqs;
+ AddReqInfo(req);
+}
+
+void IPCiProfAck(void) {
+ --IpcNumUnIssuedReqs;
+}
+
+void IPCiProfReply(void* req, s32 handle) {
+ --IpcNumPendingReqs;
+ DelReqInfo(req);
+}
+
+static void AddReqInfo(void* ptr) {
+ // NONMATCHING
+}
+
+static void DelReqInfo(void* ptr) {
+ // NONMATCHING
+}
diff --git a/src/revolution/ipc/ipcclt.c b/src/revolution/ipc/ipcclt.c
new file mode 100644
index 0000000000..39bc8b2f69
--- /dev/null
+++ b/src/revolution/ipc/ipcclt.c
@@ -0,0 +1,835 @@
+#include <revolution/private/iostypes.h>
+#include <revolution/private/iosrestypes.h>
+#include <revolution/os.h>
+#include <revolution/ipc.h>
+#include <string.h>
+
+/* macro for matching __ipcQueueRequest */
+#define diff(a, b) \
+ ((a) < (b)) ? ((u32)0xffffffff - (b) + (a) + 1) : ((a) - (b))
+
+static s32 __mailboxAck = 1;
+static u32 __relnchFl = 0;
+
+#define OSVirtualToPhysical(addr) OSCachedToPhysical(addr)
+#define OSPhysicalToVirtual(addr) OSPhysicalToCached(addr)
+
+typedef struct IOSRpcRequest {
+ IOSResourceRequest request;
+ IOSIpcCb cb ATTRIBUTE_ALIGN(32); // I am assuming this is aligned due to where cbArg is stored, and I see nothing between cb and callback_arg?
+ void* callback_arg;
+ u32 relaunch_flag;
+ OSThreadQueue thread_queue;
+} IOSRpcRequest;
+
+static IOSRpcRequest* __relnchRpc = 0;
+static IOSRpcRequest* __relnchRpcSave = 0;
+
+#define ROUNDUP(sz) (((u32)(sz) + 31) & ~(u32)(31))
+
+static u8 __rpcBuf[ROUNDUP(sizeof(IOSRpcRequest))] ATTRIBUTE_ALIGN(32);
+
+static struct {
+ u32 rcount;
+ u32 wcount;
+ u32 rptr;
+ u32 wptr;
+ IOSResourceRequest* buf[48];
+} __responses;
+
+static OSAlarm __timeout_alarm;
+
+static IOSHeapId hid = -1;
+
+extern void ACRWriteReg(u32 param_0, u32 param_1);
+extern void IPCiProfQueueReq(void*, s32);
+
+/* the MSL_C version of strnlen doesn't match when inlined, cool */
+u32 strnlen(const u8 *str, u32 n) {
+ const u8 *s = str;
+ while (*s && n-- > 0)
+ ++s;
+ return (s - str);
+}
+
+static IOSRpcRequest* ipcAllocReq(void) {
+ IOSRpcRequest* req = NULL;
+ req = iosAllocAligned(hid, 0x40, 0x20);
+ return req;
+}
+
+static IOSError ipcFree(IOSRpcRequest *rpc) {
+ IOSError ret = 0;
+ iosFree(hid, rpc);
+ return ret;
+}
+
+static IOSError __ipcQueueRequest(IOSResourceRequest *req) {
+ IOSError ret = 0;
+
+ if (diff(__responses.wcount, __responses.rcount) >= sizeof(__responses.buf) / sizeof(__responses.buf[0])) {
+ ret = -8;
+ } else {
+ __responses.buf[__responses.wptr] = req;
+ __responses.wptr = (__responses.wptr + 1) % (sizeof(__responses.buf) / sizeof(__responses.buf[0]));
+ __responses.wcount++;
+ IPCiProfQueueReq(req, (s32)req->handle);
+ }
+
+ return ret;
+}
+
+static void __ipcSendRequest(void) {
+ IOSRpcRequest *rpc;
+
+ if (diff(__responses.wcount, __responses.rcount) <= 0) {
+ return;
+ }
+
+ rpc = (IOSRpcRequest*)__responses.buf[__responses.rptr];
+
+ if (rpc == 0) {
+ return;
+ }
+
+ if (rpc->relaunch_flag) {
+ __mailboxAck--;
+ }
+
+ IPCWriteReg(0, OSVirtualToPhysical(rpc));
+ __responses.rptr = (__responses.rptr + 1) % (sizeof(__responses.buf) / sizeof(__responses.buf[0]));
+ __responses.rcount++;
+ __mailboxAck--;
+
+ IPCWriteReg(1, (IPCReadReg(1) & (1 << 5 | 1 << 4)) | 1 << 0);
+}
+
+void IpcReplyHandler(__OSInterrupt interrupt, OSContext* context) {
+ OSContext exceptionContext;
+ IOSResourceRequest* req;
+ IOSRpcRequest* rep;
+ u32 addr;
+
+ addr = IPCReadReg(2);
+
+ if (!addr) {
+ goto err;
+ }
+
+ rep = (IOSRpcRequest*)OSPhysicalToVirtual(addr);
+ IPCWriteReg(1, (IPCReadReg(1) & (1 << 5 | 1 << 4) | 1 << 2));
+ ACRWriteReg(0x30, 0x40000000);
+ req = &rep->request;
+
+ DCInvalidateRange(req, sizeof(*req));
+
+ switch (req->handle) {
+ case 3:
+ req->args.read.outPtr = (req->args.read.outPtr) ? OSPhysicalToVirtual((u32)req->args.read.outPtr) : 0;
+
+ if (req->status > 0) {
+ DCInvalidateRange(req->args.read.outPtr, (u32)req->status);
+ }
+
+ break;
+ case 6:
+ req->args.ioctl.outPtr = (req->args.ioctl.outPtr) ? OSPhysicalToVirtual((u32)req->args.ioctl.outPtr) : 0;
+ DCInvalidateRange(req->args.ioctl.inPtr, req->args.ioctl.inLen);
+ DCInvalidateRange(req->args.ioctl.outPtr, req->args.ioctl.outLen);
+ break;
+ case 7:
+ {
+ int i;
+ IOSResourceIoctlv* v = &req->args.ioctlv;
+ req->args.ioctlv.vector = (req->args.ioctlv.vector) ? (IOSIoVector*)OSPhysicalToVirtual((u32)req->args.ioctlv.vector) : 0;
+ DCInvalidateRange(&v->vector[0], (req->args.ioctlv.readCount + req->args.ioctlv.writeCount) * sizeof(IOSIoVector));
+
+ for (i = 0; i < (req->args.ioctlv.readCount + req->args.ioctlv.writeCount); ++i) {
+ v->vector[i].base = (v->vector[i].base) ? (u8*)OSPhysicalToVirtual((u32)v->vector[i].base) : 0;
+ DCInvalidateRange(v->vector[i].base, v->vector[i].length);
+ }
+
+ if (__relnchFl && __relnchRpcSave == rep) {
+ __relnchFl = 0;
+
+ if (__mailboxAck < 1) {
+ __mailboxAck++;
+ }
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (rep->cb) {
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+ rep->cb(req->status, (void*)rep->callback_arg);
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+ ipcFree(rep);
+ } else {
+ OSWakeupThread(&rep->thread_queue);
+ }
+
+ IPCWriteReg(1, (IPCReadReg(1) & (1 << 5 | 1 << 4)) | 1 << 3);
+ IPCiProfReply(req, (s32)req->handle);
+
+err:
+ return;
+}
+
+static void IpcAckHandler(__OSInterrupt interrupt, OSContext* context) {
+ IPCWriteReg(1, (IPCReadReg(1) & (1 << 5 | 1 << 4)) | 1 << 1);
+ ACRWriteReg(0x30, 0x40000000);
+
+ if (__mailboxAck < 1) {
+ __mailboxAck++;
+ IPCiProfAck();
+ }
+
+ if (__mailboxAck > 0) {
+ if (__relnchFl) {
+ IOSResourceRequest* req = &__relnchRpc->request;
+ req->status = 0;
+ __relnchFl = 0;
+
+ OSWakeupThread(&__relnchRpc->thread_queue);
+ IPCWriteReg(1, (IPCReadReg(1) & (1 << 5 | 1 << 4)) | 1 << 3);
+ }
+
+ __ipcSendRequest();
+ }
+}
+
+
+void IPCInterruptHandler(__OSInterrupt interrupt, OSContext* context) {
+ if ((IPCReadReg(1) & (1 << 4 | 1 << 2)) == (1 << 4 | 1 << 2)) {
+ IpcReplyHandler(interrupt, context);
+ }
+
+ if ((IPCReadReg(1) & (1 << 5 | 1 << 1)) == (1 << 5 | 1 << 1)) {
+ IpcAckHandler(interrupt, context);
+ }
+}
+
+IOSError IPCCltInit(void) {
+ static u32 initialized = 0;
+ u32 i;
+ IOSError ret = 0;
+ void* bufferLo;
+
+ if (initialized) {
+ goto out;
+ }
+
+ initialized = 1;
+
+ IPCInit();
+
+ i = ROUNDUP(64 * (ROUNDUP(sizeof(IOSRpcRequest)) + 64));
+ bufferLo = IPCGetBufferLo();
+
+ if ((void*)((u8*)bufferLo + i) > IPCGetBufferHi()) {
+ ret = -22;
+ goto out;
+ }
+
+ hid = iosCreateHeap(bufferLo, i);
+ IPCSetBufferLo((void*)((u8*)bufferLo + i));
+
+ __OSSetInterruptHandler(27, IPCInterruptHandler);
+ __OSUnmaskInterrupts(16);
+
+ IPCWriteReg(1, (1 << 5 | 1<< 4 | 1 << 3));
+ IPCiProfInit();
+ OSCreateAlarm(&__timeout_alarm);
+
+out:
+ return ret;
+}
+
+IOSError IPCCltReInit(void) {
+ u32 i;
+ IOSError ret = 0;
+ void* bufferLo;
+
+ i = ROUNDUP(64 * ROUNDUP(sizeof(IOSRpcRequest)));
+ bufferLo = IPCGetBufferLo();
+
+ if ((void*)((u8*)bufferLo + i) > IPCGetBufferHi()) {
+ ret = -22;
+ goto out;
+ }
+
+ hid = iosCreateHeap(bufferLo, i);
+ IPCSetBufferLo((void*)((u8*)bufferLo + i));
+
+out:
+ return ret;
+}
+
+static IOSError __ios_Ipc1(IOSFd fd, u32 cmd, IOSIpcCb cb, void* cbArg, IOSRpcRequest** rpc) {
+ IOSError ret = 0;
+ IOSResourceRequest *req;
+
+ if (rpc == 0) {
+ ret = -4;
+ goto error;
+ }
+
+ *rpc = (IOSRpcRequest*)ipcAllocReq();
+
+ if (*rpc == 0) {
+ ret = -22;
+ goto error;
+ }
+
+ req = &(*rpc)->request;
+ (*rpc)->cb = cb;
+ (*rpc)->callback_arg = cbArg;
+ (*rpc)->relaunch_flag = 0;
+ req->cmd = cmd;
+ req->handle = (u32)fd;
+
+error:
+ return ret;
+}
+
+static IOSError __ios_Ipc2(IOSRpcRequest* rpc, IOSIpcCb cb) {
+ IOSError ret = 0;
+ u32 inten;
+ IOSResourceRequest* req;
+
+ if (rpc == 0) {
+ ret = -4;
+ } else {
+ req = &rpc->request;
+
+ if (!cb) {
+ OSInitThreadQueue(&rpc->thread_queue);
+ }
+
+ DCFlushRange(req, sizeof(*req));
+ inten = OSDisableInterrupts();
+ ret = __ipcQueueRequest(req);
+
+ if (ret != 0) {
+ OSRestoreInterrupts(inten);
+
+ if (cb) {
+ ipcFree(rpc);
+ }
+ } else {
+ if (__mailboxAck > 0) {
+ __ipcSendRequest();
+ }
+
+ if (!cb) {
+ OSSleepThread(&rpc->thread_queue);
+ }
+
+ OSRestoreInterrupts(inten);
+
+ if (!cb) {
+ ret = req->status;
+ }
+ }
+ }
+
+ if (rpc && !cb) {
+ ipcFree(rpc);
+ }
+
+ return ret;
+}
+
+static IOSError __ios_Open(IOSRpcRequest* rpc, const char* path, u32 flags) {
+ IOSError ret = 0;
+ IOSResourceRequest* req;
+
+ if (!rpc) {
+ ret = -4;
+ goto error;
+ }
+
+ req = &rpc->request;
+ DCFlushRange((void*)path, strnlen((const u8*)path, 64) + 1);
+ req->args.open.path = (u8*)OSVirtualToPhysical((void*)path);
+ req->args.open.flags = flags;
+
+error:
+ return ret;
+}
+
+IOSError IOS_OpenAsync(const char* pPath, u32 flags, IOSIpcCb cb, void* callback_arg) {
+ IOSRpcRequest* rpc;
+ IOSError ret = 0;
+
+ ret = __ios_Ipc1(0, 1, cb, callback_arg, &rpc);
+
+ if (ret != 0) {
+ goto error;
+ }
+
+ ret = __ios_Open(rpc, pPath, flags);
+
+ if (ret != 0) {
+ goto error;
+ }
+
+ ret = __ios_Ipc2(rpc, cb);
+error:
+ return ret;
+}
+
+IOSError IOS_Open(const char* path, u32 flags) {
+ IOSRpcRequest* rpc;
+ IOSError ret = 0;
+
+ ret = __ios_Ipc1(0, 1, 0, 0, &rpc);
+
+ if (ret != 0) {
+ goto error;
+ }
+
+ ret = __ios_Open(rpc, path, flags);
+
+ if (ret != 0) {
+ goto error;
+ }
+
+ ret = __ios_Ipc2(rpc, 0);
+
+error:
+ return ret;
+}
+
+IOSError IOS_CloseAsync(IOSFd fd, IOSIpcCb cb, void* cbArg) {
+ IOSRpcRequest* rpc;
+ IOSError ret = 0;
+
+ ret = __ios_Ipc1(fd, 2, cb, cbArg, &rpc);
+
+ if (ret == 0) {
+ ret = __ios_Ipc2(rpc, cb);
+ }
+
+ return ret;
+}
+
+IOSError IOS_Close(IOSFd fd) {
+ IOSRpcRequest* rpc;
+ IOSError ret = 0;
+
+ ret = __ios_Ipc1(fd, 2, 0, 0, &rpc);
+
+ if (ret == 0) {
+ ret = __ios_Ipc2(rpc, 0);
+ }
+
+ return ret;
+}
+
+static IOSError __ios_Read(IOSRpcRequest* rpc, void* buf, u32 len) {
+ IOSError ret = 0;
+ IOSResourceRequest* req;
+
+ if (!rpc) {
+ ret = -4;
+ goto error;
+ }
+
+ req = &rpc->request;
+
+ DCInvalidateRange(buf, len);
+ req->args.read.outPtr = (buf) ? (u8*)OSVirtualToPhysical(buf) : 0;
+ req->args.read.outLen = len;
+
+error:
+ return ret;
+}
+
+IOSError IOS_ReadAsync(IOSFd fd, void* buf, u32 len, IOSIpcCb cb, void* cbArg) {
+ IOSRpcRequest* rpc;
+ IOSError ret = 0;
+
+ ret = __ios_Ipc1(fd, 3, cb, cbArg, &rpc);
+
+ if (ret != 0) {
+ goto error;
+ }
+
+ ret = __ios_Read(rpc, buf, len);
+
+ if (ret != 0) {
+ goto error;
+ }
+
+ ret = __ios_Ipc2(rpc, cb);
+
+error:
+ return ret;
+}
+
+IOSError IOS_Read(IOSFd fd, void* buf, u32 len) {
+ IOSRpcRequest* rpc;
+ IOSError ret = 0;
+
+ ret = __ios_Ipc1(fd, 3, 0, 0, &rpc);
+
+ if (ret != 0) {
+ goto error;
+ }
+
+ ret = __ios_Read(rpc, buf, len);
+
+ if (ret != 0) {
+ goto error;
+ }
+
+ ret = __ios_Ipc2(rpc, 0);
+
+error:
+ return ret;
+}
+
+static IOSError __ios_Write(IOSRpcRequest* rpc, void* buf, u32 len) {
+ IOSError ret = 0;
+ IOSResourceRequest* req;
+
+ if (!rpc) {
+ ret = -4;
+ goto error;
+ }
+
+ req = &rpc->request;
+ req->args.write.inPtr = (buf) ? (u8*)OSVirtualToPhysical(buf) : 0;
+ req->args.write.inLen = len;
+ DCFlushRange(buf, len);
+
+error:
+ return ret;
+}
+
+IOSError IOS_WriteAsync(IOSFd fd, void* buf, u32 len, IOSIpcCb cb, void* cbArg) {
+ IOSRpcRequest* rpc;
+ IOSError ret = 0;
+
+ ret = __ios_Ipc1(fd, 4, cb, cbArg, &rpc);
+
+ if (ret != 0) {
+ goto error;
+ }
+
+ ret = __ios_Write(rpc, buf, len);
+
+ if (ret != 0) {
+ goto error;
+ }
+
+ ret = __ios_Ipc2(rpc, cb);
+
+error:
+ return ret;
+}
+
+IOSError IOS_Write(IOSFd fd, void* buf, u32 len) {
+ IOSRpcRequest* rpc;
+ IOSError ret = 0;
+
+ ret = __ios_Ipc1(fd, 4, 0, 0, &rpc);
+
+ if (ret != 0) {
+ goto error;
+ }
+
+ ret = __ios_Write(rpc, buf, len);
+
+ if (ret != 0) {
+ goto error;
+ }
+
+ ret = __ios_Ipc2(rpc, 0);
+
+error:
+ return ret;
+}
+
+static IOSError __ios_Seek(IOSRpcRequest* rpc, s32 offset, u32 whence) {
+ IOSError ret = 0;
+ IOSResourceRequest* req;
+
+ if (!rpc) {
+ ret = -4;
+ goto error;
+ }
+
+ req = &rpc->request;
+ req->args.seek.offset = offset;
+ req->args.seek.whence = whence;
+
+error:
+ return ret;
+}
+
+IOSError IOS_SeekAsync(IOSFd fd, s32 offset, u32 whence, IOSIpcCb cb, void* cbArg) {
+ IOSRpcRequest* rpc;
+ IOSError ret = 0;
+
+ ret = __ios_Ipc1(fd, 5, cb, cbArg, &rpc);
+
+ if (ret != 0) {
+ goto error;
+ }
+
+ ret = __ios_Seek(rpc, offset, whence);
+
+ if (ret != 0) {
+ goto error;
+ }
+
+ ret = __ios_Ipc2(rpc, cb);
+
+error:
+ return ret;
+}
+
+IOSError IOS_Seek(IOSFd fd, s32 offset, u32 whence) {
+ IOSRpcRequest* rpc;
+ IOSError ret = 0;
+
+ ret = __ios_Ipc1(fd, 5, 0, 0, &rpc);
+
+ if (ret != 0) {
+ goto error;
+ }
+
+ ret = __ios_Seek(rpc, offset, whence);
+
+ if (ret != 0) {
+ goto error;
+ }
+
+ ret = __ios_Ipc2(rpc, 0);
+
+error:
+ return ret;
+}
+
+static IOSError __ios_Ioctl(IOSRpcRequest* rpc, s32 cmd, void* input, u32 inputLen, void* output, u32 outputLen) {
+ IOSError ret = 0;
+ IOSResourceRequest* req;
+
+ if (!rpc) {
+ ret = -4;
+ goto error;
+ }
+
+ req = &rpc->request;
+
+ req->args.ioctl.cmd = (u32)cmd;
+ req->args.ioctl.outPtr = (output) ? (u8*)OSVirtualToPhysical(output) : 0;
+ req->args.ioctl.outLen = outputLen;
+ req->args.ioctl.inPtr = (input) ? (u8*)OSVirtualToPhysical(input) : 0;
+ req->args.ioctl.inLen = inputLen;
+
+ DCFlushRange(input, inputLen);
+ DCFlushRange(output, outputLen);
+
+error:
+ return ret;
+}
+
+IOSError IOS_IoctlAsync(IOSFd fd, s32 cmd, void* input, u32 inputLen, void* output, u32 outputLen, IOSIpcCb cb, void* cbArg) {
+ IOSRpcRequest* rpc;
+ IOSError ret = 0;
+
+ ret = __ios_Ipc1(fd, 6, cb, cbArg, &rpc);
+
+ if (ret != 0) {
+ goto err;
+ }
+
+ ret = __ios_Ioctl(rpc, cmd, input, inputLen, output, outputLen);
+
+ if (ret != 0) {
+ goto err;
+ }
+
+ ret = __ios_Ipc2(rpc, cb);
+
+err:
+ return ret;
+}
+
+IOSError IOS_Ioctl(IOSFd fd, s32 cmd, void* input, u32 inputLen, void* output, u32 outputLen) {
+ IOSRpcRequest* rpc;
+ IOSError ret = 0;
+
+ ret = __ios_Ipc1(fd, 6, 0, 0, &rpc);
+
+ if (ret != 0) {
+ goto error;
+ }
+
+ ret = __ios_Ioctl(rpc, cmd, input, inputLen, output, outputLen);
+
+ if (ret != 0) {
+ goto error;
+ }
+
+ ret = __ios_Ipc2(rpc, 0);
+
+error:
+ return ret;
+}
+
+static IOSError __ios_Ioctlv(IOSRpcRequest* rpc, s32 cmd, u32 readCount, u32 writeCount, IOSIoVector* vect) {
+ IOSError ret = 0;
+ IOSResourceRequest* req;
+ IOSResourceIoctlv* v;
+ u32 i, j;
+
+ if (!rpc) {
+ ret = -4;
+ goto err;
+ }
+
+ req = &rpc->request;
+ req->args.ioctlv.cmd = (u32)cmd;
+ req->args.ioctlv.readCount = readCount;
+ req->args.ioctlv.writeCount = writeCount;
+ req->args.ioctlv.vector = vect;
+
+ v = &req->args.ioctlv;
+
+ for (i = 0, j = v->readCount; i < req->args.ioctlv.writeCount; ++i) {
+ DCFlushRange(v->vector[j + i].base, v->vector[j + i].length);
+ v->vector[j + i].base = (v->vector[j + i].base) ? (u8*)OSVirtualToPhysical(v->vector[j + i].base) : 0;
+ }
+
+ for (i = 0; i < req->args.ioctlv.readCount; ++i) {
+ DCFlushRange(v->vector[i].base, v->vector[i].length);
+ v->vector[i].base = (v->vector[i].base) ? (u8*)OSVirtualToPhysical(v->vector[i].base) : 0;
+ }
+
+ DCFlushRange(&v->vector[0], (v->readCount + v->writeCount) * sizeof(IOSIoVector));
+ req->args.ioctlv.vector = (vect) ? (IOSIoVector*)OSVirtualToPhysical(vect) : 0;
+
+err:
+ return ret;
+}
+
+IOSError IOS_IoctlvAsync(IOSFd fd, s32 cmd, u32 readCount, u32 writeCount, IOSIoVector* vect, IOSIpcCb cb, void* cbArg) {
+ IOSRpcRequest* rpc;
+ IOSError ret = 0;
+
+ ret = __ios_Ipc1(fd, 7, cb, cbArg, &rpc);
+
+ if (ret != 0) {
+ goto err;
+ }
+
+ ret = __ios_Ioctlv(rpc, cmd, readCount, writeCount, vect);
+
+ if (ret != 0) {
+ goto err;
+ }
+
+ ret = __ios_Ipc2(rpc, cb);
+
+err:
+ return ret;
+}
+
+IOSError IOS_Ioctlv(IOSFd fd, s32 cmd, u32 readCount, u32 writeCount, IOSIoVector* vect) {
+ IOSRpcRequest* rpc;
+ IOSError ret = 0;
+
+ ret = __ios_Ipc1(fd, 7, 0, 0, &rpc);
+
+ if (ret != 0) {
+ goto err;
+ }
+
+ ret = __ios_Ioctlv(rpc, cmd, readCount, writeCount, vect);
+
+ if (ret != 0) {
+ goto err;
+ }
+
+ ret = __ios_Ipc2(rpc, 0);
+
+err:
+ return ret;
+}
+
+IOSError IOS_IoctlvReboot(IOSFd fd, s32 cmd, u32 readCount, u32 writeCount, IOSIoVector* vect) {
+ IOSRpcRequest* rpc;
+ IOSError ret = 0;
+ u32 inten;
+ IOSResourceRequest* req;
+
+ inten = OSDisableInterrupts();
+
+ if (__relnchFl) {
+ OSRestoreInterrupts(inten);
+ ret = -10;
+ goto finish;
+ }
+
+ __relnchFl = 1;
+ OSRestoreInterrupts(inten);
+
+ ret = __ios_Ipc1(fd, 7, 0, 0, &rpc);
+
+ if (ret != 0) {
+ goto err;
+ }
+
+ __relnchRpcSave = rpc;
+ rpc->relaunch_flag = 1;
+
+ ret = __ios_Ioctlv(rpc, cmd, readCount, writeCount, vect);
+
+ if (ret != 0) {
+ goto err;
+ }
+
+ memcpy(&__rpcBuf, rpc, sizeof(IOSRpcRequest));
+ __relnchRpc = (IOSRpcRequest*)&__rpcBuf;
+ req = &rpc->request;
+
+ OSInitThreadQueue(&__relnchRpc->thread_queue);
+ DCFlushRange(req, sizeof(*req));
+
+ inten = OSDisableInterrupts();
+ ret = __ipcQueueRequest(req);
+
+ if (ret != 0) {
+ OSRestoreInterrupts(inten);
+ goto err;
+ }
+
+ if (__mailboxAck > 0) {
+ __ipcSendRequest();
+ }
+
+ OSSleepThread(&__relnchRpc->thread_queue);
+ OSRestoreInterrupts(inten);
+ ret = (&__relnchRpc->request)->status;
+
+err:
+ __relnchFl = 0;
+ __relnchRpcSave = NULL;
+
+ if (rpc && (ret != 0)) {
+ ipcFree(rpc);
+ }
+
+finish:
+ return ret;
+}
diff --git a/src/revolution/ipc/memory.c b/src/revolution/ipc/memory.c
new file mode 100644
index 0000000000..29625a117e
--- /dev/null
+++ b/src/revolution/ipc/memory.c
@@ -0,0 +1,240 @@
+#include <revolution.h>
+#include <revolution/private/iostypes.h>
+
+typedef struct IOSChunk {
+ u32 status;
+ u32 size;
+ struct IOSChunk* prevFree;
+ struct IOSChunk* nextFree;
+} IOSChunk;
+
+typedef struct {
+ void* base;
+ u32 owner;
+ u32 size;
+ IOSChunk *freeList;
+} IOSHeap;
+
+static IOSHeap __heaps[8];
+
+static void __iosCoalesceChunk(IOSChunk* p) {
+ if (p && ((u32)p->nextFree == ((u32)p + p->size + sizeof(IOSChunk)))) {
+ IOSChunk* next = p->nextFree;
+ p->nextFree = next->nextFree;
+
+ if (p->nextFree != 0) {
+ p->nextFree->prevFree = p;
+ }
+
+ p->size += next->size + sizeof(IOSChunk);
+ }
+}
+
+IOSHeapId iosCreateHeap(void* ptr, u32 size) {
+ IOSError rv = -4;
+ u32 mask;
+ s32 i;
+ IOSHeap* h;
+
+ mask = OSDisableInterrupts();
+
+ if ((u32)ptr & 31) {
+ goto finish;
+ }
+
+ for (i = 0; i < sizeof(__heaps) / sizeof(__heaps[0]); ++i) {
+ if (__heaps[i].base == 0) {
+ break;
+ }
+ }
+
+ if (i == 8) {
+ rv = -5;
+ goto finish;
+ }
+
+ h = __heaps + i;
+ h->base = ptr;
+ h->size = size;
+ h->freeList = ptr;
+ h->freeList->status = 0xBABE0000;
+ h->freeList->size = size - sizeof(IOSChunk);
+ h->freeList->prevFree = NULL;
+ h->freeList->nextFree = NULL;
+ rv = i;
+
+finish:
+ OSRestoreInterrupts(mask);
+ return rv;
+}
+
+#define CHECK_HEAP_ID(id, err) \
+ if (((id) < 0) || ((id) >= 8) || (!__heaps[id].base)) { \
+ rv = err; \
+ goto finish; \
+ }
+
+#define GET_HEAP(h, id, err) \
+ do { \
+ CHECK_HEAP_ID(id, err); \
+ h = __heaps + id; \
+ } while (0)
+
+void* __iosAlloc(IOSHeapId id, u32 size, u32 alignment) {
+ u32 mask;
+ IOSChunk* p, *best;
+ IOSHeap* h;
+ void* rv = NULL;
+
+ mask = OSDisableInterrupts();
+
+ if (!size) {
+ goto finish;
+ }
+
+ if (!alignment || (alignment & (alignment - 1))) {
+ goto finish;
+ }
+
+ if (alignment < 32) {
+ alignment = 32;
+ }
+
+ size = (size + 31) & ~31;
+
+ GET_HEAP(h, id, NULL);
+
+ p = h->freeList;
+ best = NULL;
+
+ while (p != 0) {
+ u32 ptr = (u32)p + sizeof(IOSChunk);
+ u32 extra = (alignment - (ptr & (alignment - 1))) & (alignment - 1);
+
+ if ((p->size == size) && !extra) {
+ best = p;
+ break;
+ } else if ((p->size >= (size + extra)) && (!best || (p->size < best->size))) {
+ best = p;
+ }
+
+ p = p->nextFree;
+ }
+
+ p = best;
+
+ if (p != 0) {
+ u32 ptr = (u32)p + sizeof(IOSChunk);
+ u32 extra = (alignment - (ptr & (alignment - 1))) & (alignment - 1);
+
+ if (p->size > (size + extra + sizeof(IOSChunk))) {
+ IOSChunk *n = (IOSChunk *)((u8 *)p + size + extra + sizeof(IOSChunk));
+ n->status = 0xBABE0000;
+ n->size = p->size - size - extra - sizeof(IOSChunk);
+ n->nextFree = p->nextFree;
+
+ if (n->nextFree) {
+ n->nextFree->prevFree = n;
+ }
+
+ p->nextFree = n;
+ p->size = size + extra;
+ }
+
+ p->status = 0xBABE0001;
+
+ if (p->prevFree) {
+ p->prevFree->nextFree = p->nextFree;
+ } else {
+ h->freeList = p->nextFree;
+ }
+
+ if (p->nextFree) {
+ p->nextFree->prevFree = p->prevFree;
+ }
+
+ p->prevFree = p->nextFree = NULL;
+ rv = (u8*)p + extra + sizeof(IOSChunk);
+
+ if (extra) {
+ IOSChunk* n = (IOSChunk*)rv - 1;
+ n->status = 0xBABE0002;
+ n->prevFree = p;
+ }
+ }
+
+finish:
+ OSRestoreInterrupts(mask);
+ return rv;
+}
+
+void* iosAllocAligned(IOSHeapId id, u32 size, u32 alignment) {
+ return __iosAlloc(id, size, alignment);
+}
+
+IOSError iosFree(IOSHeapId id, void* ptr) {
+ u32 mask;
+ IOSChunk* p;
+ IOSHeap* h;
+ IOSError rv = -4;
+ IOSChunk* prev;
+
+ mask = OSDisableInterrupts();
+
+ if (!ptr) {
+ goto finish;
+ }
+
+ GET_HEAP(h, id, -4);
+
+ if (((u32)ptr) < (((u32)h->base) + sizeof(IOSChunk)) || ((u32)ptr) > ((u32)h->base + h->size)) {
+ goto finish;
+ }
+
+ p = (IOSChunk*)ptr - 1;
+
+ if (p->status == 0xBABE0002) {
+ p = p->prevFree;
+ }
+
+ if (p->status != 0xBABE0001) {
+ goto finish;
+ }
+
+ p->status = 0xBABE0000;
+ prev = h->freeList;
+
+ while (prev != 0) {
+ if (!prev->nextFree || prev->nextFree > p) {
+ break;
+ }
+
+ prev = prev->nextFree;
+ }
+
+ if (prev && p > prev) {
+ p->prevFree = prev;
+ p->nextFree = prev->nextFree;
+ prev->nextFree = p;
+
+ if (p->nextFree) {
+ p->nextFree->prevFree = p;
+ }
+ } else {
+ p->nextFree = h->freeList;
+ h->freeList = p;
+ p->prevFree = NULL;
+
+ if (p->nextFree) {
+ p->nextFree->prevFree = p;
+ }
+ }
+
+ __iosCoalesceChunk(p);
+ __iosCoalesceChunk(p->prevFree);
+ rv = 0;
+
+finish:
+ OSRestoreInterrupts(mask);
+ return rv;
+}
diff --git a/src/revolution/nand/NANDCheck.c b/src/revolution/nand/NANDCheck.c
new file mode 100644
index 0000000000..3f7630af12
--- /dev/null
+++ b/src/revolution/nand/NANDCheck.c
@@ -0,0 +1,94 @@
+#include <revolution/nand.h>
+#include <revolution/fs.h>
+
+static const char* USER_DIR_LIST[] = {
+ "/meta",
+ "/ticket",
+ "/title/00010000",
+ "/title/00010001",
+ "/title/00010003",
+ "/title/00010004",
+ "/title/00010005",
+ "/title/00010006",
+ "/title/00010007",
+ "/shared2/title",
+ NULL,
+};
+
+s32 nandCalcUsage(u32* fsBlock, u32* inode, const char* dir_list[]) {
+ ISFSError ret = ISFS_ERROR_UNKNOWN;
+ *fsBlock = 0;
+ *inode = 0;
+
+ while (*dir_list) {
+ u32 blk = 0;
+ u32 node = 0;
+ ret = ISFS_GetUsage((const u8*)*dir_list, &blk, &node);
+
+ if (ret == ISFS_ERROR_OK) {
+ *fsBlock += blk;
+ *inode += node;
+ } else if (ret == ISFS_ERROR_NOEXISTS) {
+ ret = ISFS_ERROR_OK;
+ } else {
+ break;
+ }
+
+ ++dir_list;
+ }
+
+ return ret;
+}
+
+ISFSError nandCalcUserUsage(u32* fsBlock, u32* inode) {
+ return nandCalcUsage(fsBlock, inode, USER_DIR_LIST);
+}
+
+static u32 nandCheck(const u32 reqBlock, const u32 reqInode, const u32 homeBlock, const u32 homeInode, const u32 userBlock, const u32 userInode) {
+ u32 answer = 0;
+
+ if (homeBlock + reqBlock > 0x400) {
+ answer |= 1;
+ }
+
+ if (homeInode + reqInode > 0x21) {
+ answer |= 2;
+ }
+
+ if (userBlock + reqBlock > 0x4400) {
+ answer |= 4;
+ }
+
+ if (userInode + reqInode > 0xFA0) {
+ answer |= 8;
+ }
+
+ return answer;
+}
+
+s32 NANDCheck(const u32 fsBlock, const u32 inode, u32* answer) {
+ ISFSError ret = ISFS_ERROR_UNKNOWN;
+ u32 homeBlocks = 0xFFFFFFFF;
+ u32 homeInodes = 0xFFFFFFFF;
+ u32 userBlocks = 0xFFFFFFFF;
+ u32 userInodes = 0xFFFFFFFF;
+
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ ret = ISFS_GetUsage((const u8*)nandGetHomeDir(), &homeBlocks, &homeInodes);
+
+ if (ret != ISFS_ERROR_OK) {
+ return nandConvertErrorCode(ret);
+ }
+
+ ret = nandCalcUserUsage(&userBlocks, &userInodes);
+
+ if (ret != ISFS_ERROR_OK) {
+ return nandConvertErrorCode(ret);
+ }
+
+ *answer = nandCheck(fsBlock, inode, homeBlocks, homeInodes, userBlocks, userInodes);
+ return NAND_RESULT_OK;
+}
diff --git a/src/revolution/nand/NANDCore.c b/src/revolution/nand/NANDCore.c
new file mode 100644
index 0000000000..575ebecb40
--- /dev/null
+++ b/src/revolution/nand/NANDCore.c
@@ -0,0 +1,514 @@
+#include <revolution/nand.h>
+#include <revolution/esp.h>
+#include <revolution/fs.h>
+#include <revolution/os.h>
+#include <string.h>
+
+#ifdef SDK_AUG2010
+#define BUILD_DATE "Aug 23 2010"
+#if DEBUG
+#define BUILD_TIME "17:28:24"
+#else
+#define BUILD_TIME "17:33:06"
+#endif
+#elif SDK_SEP2006
+#define BUILD_DATE "Sep 21 2006"
+#define BUILD_TIME "14:32:13"
+#endif
+
+#ifdef SDK_AUG2010
+#if DEBUG
+const char* __NANDVersion = "<< RVL_SDK - NAND \tdebug build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#else
+const char* __NANDVersion = "<< RVL_SDK - NAND \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#endif
+#elif SDK_SEP2006
+const char* __NANDVersion = "<< RVL_SDK - NAND \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4200_60422) >>";
+#endif
+
+enum LibState {
+ STATE_NOT_INITIALIZED,
+ STATE_WORKING,
+ STATE_INITIALIZED
+};
+
+static enum LibState s_libState = STATE_NOT_INITIALIZED;
+static char s_currentDir[64] ATTRIBUTE_ALIGN(32) = "/";
+static char s_homeDir[64] ATTRIBUTE_ALIGN(32) = "";
+
+static BOOL nandOnShutdown(BOOL final, u32 event);
+void nandConvertPath(char* abspath, const char* wd, const char* relpath);
+static void nandShutdownCallback(ISFSError result, void* ctxt);
+
+static OSShutdownFunctionInfo s_shutdownFuncInfo = {
+ nandOnShutdown,
+ 255
+};
+
+void nandRemoveTailToken(char* newpath, const char* oldpath) {
+ ASSERTMSGLINE(189, newpath, "null pointer is detected in argument of newpath\n");
+ ASSERTMSGLINE(190, oldpath, "null pointer is detected in argument of oldpath\n");
+ ASSERTMSGLINE(191, oldpath[0] == '/', "Head of path must be \'/\' .\n");
+
+ if (oldpath[0] == '/' && oldpath[1] == '\0') {
+ newpath[0] = '/';
+ newpath[1] = '\0';
+ } else {
+ int i = 0;
+
+ for (i = (int)(strlen(oldpath)) - 1; i >= 0; --i) {
+ if (oldpath[i] == '/') {
+ if (i != 0) {
+ strncpy(newpath, oldpath, (u32)i);
+ newpath[i] = '\0';
+ break;
+ } else {
+ newpath[0] = '/';
+ newpath[1] = '\0';
+ break;
+ }
+ }
+ }
+ }
+}
+
+void nandGetHeadToken(char* token, char* newpath, const char* oldpath) {
+ unsigned int i = 0;
+
+ ASSERTMSGLINE(224, token && newpath && oldpath, "Null pointer detected!\n");
+ ASSERTMSGLINE(225, strlen(oldpath) != 0, "Null string was detected!\n");
+ ASSERTMSGLINE(226, oldpath[0] != '/', "Head of relative path must not be \'/\' .\n");
+
+ for (i = 0; i <= strlen(oldpath); ++i) {
+ if (oldpath[i] == '/') {
+ strncpy(token, oldpath, i);
+ token[i] = '\0';
+
+ if (oldpath[i + 1] == '\0') {
+ newpath[0] = '\0';
+ } else {
+ strcpy(newpath, oldpath + i + 1);
+ }
+
+ break;
+ } else if (oldpath[i] == '\0') {
+ strncpy(token, oldpath, i);
+ token[i] = '\0';
+ newpath[0] = '\0';
+ break;
+ }
+ }
+}
+
+void nandGetRelativeName(char* name, const char* path) {
+ if (strcmp("/", path) == 0) {
+ strcpy(name, "");
+ } else {
+ int i = 0;
+
+ ASSERTMSGLINE(260, nandIsAbsolutePath(path), "path must be absolute path.");
+ ASSERTMSGLINE(261, path[strlen(path) - 1] != '/', "path must not be terminated with \'/\'.");
+
+ for (i = (int)(strlen(path) - 1); i >= 0; --i) {
+ if (path[i] == '/') {
+ break;
+ }
+ }
+
+ ASSERTMSGLINE(270, strlen(path + i + 1) <= ISFS_INODE_NAMELEN, "path must be smaller than or equal to ISFS_INODE_NAMELEN.");
+
+ strcpy(name, path + i + 1);
+ }
+}
+
+void nandConvertPath(char* abspath, const char* wd, const char* relpath) {
+ char token[128];
+ char new_relpath[128];
+
+ ASSERTMSGLINE(283, abspath && wd && relpath, "Null pointer detected!\n");
+ ASSERTMSGLINE(284, wd[0] == '/', "Head of \'wd\' must be \'/\'.\n");
+
+ if (strlen(relpath) == 0) {
+ strcpy(abspath, wd);
+ return;
+ }
+
+ nandGetHeadToken(token, new_relpath, relpath);
+
+ if (strcmp(token, ".") == 0) {
+ nandConvertPath(abspath, wd, new_relpath);
+ } else if (strcmp(token, "..") == 0) {
+ char new_wd[128];
+ nandRemoveTailToken(new_wd, wd);
+ nandConvertPath(abspath, new_wd, new_relpath);
+ } else if (token[0] != '\0') {
+ char new_wd[128];
+ if (strcmp(wd, "/") == 0) {
+ sprintf(new_wd, "/%s", token);
+ } else {
+ sprintf(new_wd, "%s/%s", wd, token);
+ }
+
+ nandConvertPath(abspath, new_wd, new_relpath);
+ } else {
+ strcpy(abspath, wd);
+ }
+}
+
+BOOL nandIsRelativePath(const char* path) {
+ if (path[0] == '/') {
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+}
+
+BOOL nandIsAbsolutePath(const char* path) {
+ if (nandIsRelativePath(path)) {
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+}
+
+BOOL nandIsPrivatePath(const char* path) {
+ if (strncmp(path, "/shared2", 8) == 0) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+BOOL nandIsUnderPrivatePath(const char* path) {
+ if (strncmp(path, "/shared2/", 9) == 0 && path[9] != '\0') {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+BOOL nandIsInitialized(void) {
+ if (s_libState == STATE_INITIALIZED) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+void nandReportErrorCode(const ISFSError err) {
+ // NONMATCHING
+ return;
+}
+
+void nandLoggingCallback(BOOL, ISFSError err) {
+ if (err == ISFS_ERROR_UNKNOWN || err == IOS_ERROR_UNKNOWN) {
+ __NANDPrintErrorMessage(err);
+ }
+}
+
+s32 nandConvertErrorCode(const ISFSError err) {
+ const int ERRMAP[] = {
+ ISFS_ERROR_OK, NAND_RESULT_OK,
+ ISFS_ERROR_ACCESS, NAND_RESULT_ACCESS,
+ ISFS_ERROR_CORRUPT, NAND_RESULT_CORRUPT,
+ ISFS_ERROR_ECC_CRIT, NAND_RESULT_ECC_CRIT,
+ ISFS_ERROR_EXISTS, NAND_RESULT_EXISTS,
+ ISFS_ERROR_HMAC, NAND_RESULT_AUTHENTICATION,
+ ISFS_ERROR_INVALID, NAND_RESULT_INVALID,
+ ISFS_ERROR_MAXBLOCKS, NAND_RESULT_MAXBLOCKS,
+ ISFS_ERROR_MAXFD, NAND_RESULT_MAXFD,
+ ISFS_ERROR_MAXFILES, NAND_RESULT_MAXFILES,
+ ISFS_ERROR_MAXDEPTH, NAND_RESULT_MAXDEPTH,
+ ISFS_ERROR_NOEXISTS, NAND_RESULT_NOEXISTS,
+ ISFS_ERROR_NOTEMPTY, NAND_RESULT_NOTEMPTY,
+ ISFS_ERROR_NOTREADY, NAND_RESULT_UNKNOWN,
+ ISFS_ERROR_OPENFD, NAND_RESULT_OPENFD,
+ ISFS_ERROR_UNKNOWN, NAND_RESULT_UNKNOWN,
+ ISFS_ERROR_BUSY, NAND_RESULT_BUSY,
+ ISFS_ERROR_SHUTDOWN, NAND_RESULT_FATAL_ERROR,
+
+ IOS_ERROR_ACCESS, NAND_RESULT_ACCESS,
+ IOS_ERROR_EXISTS, NAND_RESULT_EXISTS,
+ IOS_ERROR_INTR, NAND_RESULT_UNKNOWN,
+ IOS_ERROR_INVALID, NAND_RESULT_INVALID,
+ IOS_ERROR_MAX, NAND_RESULT_UNKNOWN,
+ IOS_ERROR_NOEXISTS, NAND_RESULT_NOEXISTS,
+ IOS_ERROR_QEMPTY, NAND_RESULT_UNKNOWN,
+ IOS_ERROR_QFULL, NAND_RESULT_BUSY,
+ IOS_ERROR_UNKNOWN, NAND_RESULT_UNKNOWN,
+ IOS_ERROR_NOTREADY, NAND_RESULT_UNKNOWN,
+ IOS_ERROR_ECC, NAND_RESULT_UNKNOWN,
+ IOS_ERROR_ECC_CRIT, NAND_RESULT_ECC_CRIT,
+ IOS_ERROR_BADBLOCK, NAND_RESULT_UNKNOWN,
+
+ IOS_ERROR_INVALID_OBJTYPE, NAND_RESULT_UNKNOWN,
+ IOS_ERROR_INVALID_RNG, NAND_RESULT_UNKNOWN,
+ IOS_ERROR_INVALID_FLAG, NAND_RESULT_UNKNOWN,
+ IOS_ERROR_INVALID_FORMAT, NAND_RESULT_UNKNOWN,
+ IOS_ERROR_INVALID_VERSION, NAND_RESULT_UNKNOWN,
+ IOS_ERROR_INVALID_SIGNER, NAND_RESULT_UNKNOWN,
+ IOS_ERROR_FAIL_CHECKVALUE, NAND_RESULT_UNKNOWN,
+ IOS_ERROR_FAIL_INTERNAL, NAND_RESULT_UNKNOWN,
+ IOS_ERROR_FAIL_ALLOC, NAND_RESULT_ALLOC_FAILED,
+ IOS_ERROR_INVALID_SIZE, NAND_RESULT_UNKNOWN,
+ };
+
+ int i = 0;
+
+ if (err >= 0) {
+ return err;
+ }
+
+ for (; i < sizeof(ERRMAP) / 4; i = i + 2) {
+ if (ERRMAP[i] == err) {
+ if (err == ISFS_ERROR_ECC_CRIT || err == ISFS_ERROR_HMAC || err == ISFS_ERROR_UNKNOWN || err == IOS_ERROR_UNKNOWN || err == IOS_ERROR_ECC_CRIT) {
+ char buf[128] ATTRIBUTE_ALIGN(64);
+ sprintf(buf, "ISFS error code: %d", err);
+ NANDLoggingAddMessageAsync(nandLoggingCallback, err, buf);
+ }
+
+ nandReportErrorCode(err);
+
+ if (err == ISFS_ERROR_MAXBLOCKS || err == ISFS_ERROR_MAXFILES || err == ISFS_ERROR_CORRUPT || err == ISFS_ERROR_BUSY || err == IOS_ERROR_QFULL || err == IOS_ERROR_FAIL_ALLOC) {
+ __NANDPrintErrorMessage(err);
+ }
+
+ return ERRMAP[i + 1];
+ }
+ }
+
+ OSReport("CAUTION! Unexpected error code [%d] was found.\n", err);
+ {
+ char buf[128] ATTRIBUTE_ALIGN(64);
+ sprintf(buf, "ISFS unexpected error code: %d", err);
+ NANDLoggingAddMessageAsync(nandLoggingCallback, err, buf);
+ }
+
+ nandReportErrorCode(err);
+ return -64;
+}
+
+void nandGenerateAbsPath(char* absPath, const char* path) {
+ if (strlen(path) == 0) {
+ strcpy(absPath, "");
+ } else if (nandIsRelativePath(path)) {
+ nandConvertPath(absPath, s_currentDir, path);
+ } else {
+ u32 len = 0xFFFFFFFF;
+ strcpy(absPath, path);
+
+ len = strlen(absPath);
+ if (len > 0) {
+ if ((absPath[len - 1] == '/') && (len - 1 != 0)) {
+ absPath[len - 1] = '\0';
+ }
+ }
+ }
+}
+
+void nandGetParentDirectory(char* parentDir, const char* absPath) {
+ int i = 0;
+ for (i = (int)strlen(absPath); i >= 0; --i) {
+ if (absPath[i] == '/') {
+ break;
+ }
+ }
+
+ if (i == 0) {
+ strcpy(parentDir, "/");
+ } else {
+ strncpy(parentDir, absPath, (u32)i);
+ parentDir[i] = '\0';
+ }
+}
+
+s32 NANDInit(void) {
+ BOOL enabled = OSDisableInterrupts();
+
+ if (s_libState == STATE_WORKING) {
+ OSRestoreInterrupts(enabled);
+ return -3;
+ } else if (s_libState == 2) {
+ OSRestoreInterrupts(enabled);
+ return 0;
+ } else {
+ ISFSError result = ISFS_ERROR_UNKNOWN;
+ s_libState = STATE_WORKING;
+ OSRestoreInterrupts(enabled);
+
+ result = ISFS_OpenLib();
+ if (result == 0) {
+ s32 rv;
+ ESTitleId id;
+
+ rv = ESP_InitLib();
+
+ if (rv == 0) {
+ rv = ESP_GetTitleId(&id);
+ }
+
+ if (rv == 0) {
+ rv = ESP_GetDataDir(id, s_homeDir);
+ }
+
+ if (rv == 0) {
+ strcpy(s_currentDir, s_homeDir);
+ }
+
+ ESP_CloseLib();
+
+ if (rv != 0) {
+ OSReport("Failed to set home directory.\n");
+ }
+
+ OSRegisterShutdownFunction(&s_shutdownFuncInfo);
+ enabled = OSDisableInterrupts();
+ s_libState = STATE_INITIALIZED;
+ OSRestoreInterrupts(enabled);
+ NANDSetAutoErrorMessaging(1);
+ OSRegisterVersion(__NANDVersion);
+ return 0;
+ } else {
+ enabled = OSDisableInterrupts();
+ s_libState = STATE_NOT_INITIALIZED;
+ OSRestoreInterrupts(enabled);
+ return nandConvertErrorCode(result);
+ }
+ }
+}
+
+static BOOL nandOnShutdown(BOOL final, u32 event) {
+ if (!final) {
+ if (event == 2) {
+ volatile BOOL flag = FALSE;
+ OSTime t = OSGetTime();
+ ISFS_ShutdownAsync(nandShutdownCallback, (void*)&flag);
+
+ while (OSTicksToMilliseconds(OSGetTime() - t) < 500) {
+ if (flag) {
+ break;
+ }
+ }
+ }
+
+ return TRUE;
+ } else {
+ return TRUE;
+ }
+}
+
+static void nandShutdownCallback(ISFSError result, void* ctxt) {
+ (void)result;
+ *(BOOL*)ctxt = TRUE;
+}
+
+// NOTE: function doesn't exist in TP debug, needed for string data
+s32 NANDGetCurrentDir() {
+ OSReport("NANDGetCurrentDir");
+}
+
+s32 NANDGetHomeDir(char* path) {
+ ASSERTMSG1LINE(0, path, "NULL pointer is detected: %s()", "NANDGetHomeDir");
+
+ if (!nandIsInitialized()) {
+ return -128;
+ }
+
+ strcpy(path, s_homeDir);
+ return 0;
+}
+
+void nandCallback(ISFSError result, void* ctxt) {
+ NANDCommandBlock* b = (NANDCommandBlock*)ctxt;
+ ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b);
+}
+
+void nandGetTypeCallback(ISFSError result, void* ctxt);
+
+static ISFSError nandGetType(const char* path, u8* type, NANDCommandBlock* block, const BOOL async_flag, const BOOL privilege_flag) {
+ ASSERTMSG1LINE(1133, path, "NULL pointer is detected: %s()", __FUNCTION__);
+ ASSERTMSG1LINE(1134, type, "NULL pointer is detected: %s()", __FUNCTION__);
+
+ if (strlen(path) == 0) {
+ return ISFS_ERROR_INVALID;
+ }
+
+ if (async_flag) {
+ nandGenerateAbsPath(block->absPath, path);
+ if (!privilege_flag && nandIsUnderPrivatePath(block->absPath)) {
+ return ISFS_ERROR_ACCESS;
+ } else {
+ block->type = type;
+ return ISFS_ReadDirAsync((u8*)(block->absPath), NULL, &(block->num), nandGetTypeCallback, block);
+ }
+ } else {
+ char absPath[64] = "";
+ nandGenerateAbsPath(absPath, path);
+
+ if (!privilege_flag && nandIsUnderPrivatePath(absPath)) {
+ return ISFS_ERROR_ACCESS;
+ } else {
+ u32 dmy = 0;
+ ISFSError err = ISFS_ReadDir((u8*)absPath, NULL, &dmy);
+
+ if (err == ISFS_ERROR_OK || err == ISFS_ERROR_ACCESS) {
+ *type = 2;
+ err = ISFS_ERROR_OK;
+ } else if (err == ISFS_ERROR_INVALID) {
+ *type = 1;
+ err = ISFS_ERROR_OK;
+ }
+
+ return err;
+ }
+ }
+}
+
+s32 NANDPrivateGetTypeAsync(const char* path, u8* type, NANDCallback cb, NANDCommandBlock* block) {
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ block->callback = cb;
+ return nandConvertErrorCode(nandGetType(path, type, block, TRUE, TRUE));
+}
+
+void nandGetTypeCallback(ISFSError result, void* ctxt) {
+ NANDCommandBlock* b = (NANDCommandBlock*)ctxt;
+
+ if (result == ISFS_ERROR_OK || result == ISFS_ERROR_ACCESS) {
+ *(b->type) = 2;
+ result = ISFS_ERROR_OK;
+ } else if (result == ISFS_ERROR_INVALID) {
+ *(b->type) = 1;
+ result = ISFS_ERROR_OK;
+ }
+
+ ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b);
+}
+
+const char* nandGetHomeDir(void) {
+ return s_homeDir;
+}
+
+void NANDInitBanner(NANDBanner* bnr, u32 const flag, const u16* title, const u16* comment) {
+ ASSERTMSG1LINE(1253, bnr, "Null pointer is detected at %s()", __FUNCTION__);
+ ASSERTMSG1LINE(1254, title, "Null pointer is detected at %s()", __FUNCTION__);
+ ASSERTMSG1LINE(1255, comment, "Null pointer is detected at %s()", __FUNCTION__);
+
+ memset(bnr, 0, sizeof(NANDBanner));
+ bnr->signature = 0x5749424E;
+ bnr->flag = flag;
+
+ if (wcscmp((wchar_t*)title, L"") == 0) {
+ wcsncpy(bnr->comment[0], L" ", 32);
+ } else {
+ wcsncpy(bnr->comment[0], (wchar_t*)title, 32);
+ }
+
+ if (wcscmp((wchar_t*)comment, L"") == 0) {
+ wcsncpy(bnr->comment[1], L" ", 32);
+ } else {
+ wcsncpy(bnr->comment[1], (wchar_t*)comment, 32);
+ }
+}
diff --git a/src/revolution/nand/NANDErrorMessage.c b/src/revolution/nand/NANDErrorMessage.c
new file mode 100644
index 0000000000..1cb0f0e961
--- /dev/null
+++ b/src/revolution/nand/NANDErrorMessage.c
@@ -0,0 +1,235 @@
+#include <revolution/os.h>
+#include <revolution/nand.h>
+#include <revolution/fs.h>
+#include <revolution/sc.h>
+
+#define REGION_DEFAULT 0
+#define REGION_EUROPE 1
+#define REGION_CHNKOR 2
+
+const char* const __NANDMaxBlocksErrorMessageDefault[] = {
+ "\n\n\nWii本体保存メモリの空き容量が異常です。\nくわしくはWii本体の取扱説明書をお読み\nください。",
+ "\n\n\nThere is no more available space in\nWii system memory. Refer to the Wii\nOperations Manual for details.",
+ "\n\n\nDer Speicher der Wii-Konsole ist belegt.\nBitte lies die Wii-Bedienungsanleitung,\num weitere Informationen zu erhalten.",
+ "\n\n\nIl n'y a pas assez d'espace libre\ndans la m駑oire de la console Wii.\nVeuillez vous r馭駻er au Mode d'emploi\nde la Wii pour plus de d騁ails.",
+ "\n\n\nNo queda espacio libre en la memoria\nde la consola Wii. Consulta el manual\nde operaciones de la consola Wii para\nobtener m疽 informaci\xF3n.",
+ "\n\n\nNon c'\xE8 pi\xF9 spazio libero nella memoria\ndella console Wii. Per maggiori\ninformazioni, consulta il manuale di\nistruzioni della console Wii.",
+ "\n\n\nEr is geen vrije ruimte meer in het\ninterne geheugen van het Wii-systeem.\nLees de handleiding voor meer informatie.",
+};
+
+const char* const __NANDMaxBlocksErrorMessageEurope[] = {
+ "\n\n\nWii本体保存メモリの空き容量が異常です。\nくわしくはWii本体の取扱説明書をお読み\nください。",
+ "\n\n\nThere is no more available space in\nthe Wii System Memory. Please refer to\nthe Wii Operations Manual for details.",
+ "\n\n\nDer Speicher der Wii-Konsole ist belegt.\nBitte lies die Wii-Bedienungsanleitung,\num weitere Informationen zu erhalten.",
+ "\n\n\nIl n'y a pas assez d'espace libre dans\nla m駑oire de la console Wii. Veuillez\nvous r馭駻er au mode d'emploi Wii pour\nplus de d騁ails.",
+ "\n\n\nNo queda espacio libre en la memoria de\nla consola Wii. Consulta el manual de\ninstrucciones de la consola Wii para\nobtener m疽 informaci\xF3n.",
+ "\n\n\nNon c'\xE8 pi\xF9 spazio libero nella memoria\ndella console Wii. Per maggiori\ninformazioni, consulta il manuale di\nistruzioni della console Wii.",
+ "\n\n\nEr is geen vrije ruimte meer in het\ninterne geheugen van het Wii-systeem.\nLees de handleiding voor meer informatie.",
+};
+
+const char* const __NANDMaxBlocksErrorMessageChinaKorea[] = {
+ "\n\nエラーコード405。\n\nWii本体保存メモリの空き容量が異常です。\nくわしくはWii本体の取扱説明書をお読み\nください。",
+ "\n\nError #405,\n\nThere is no more available space in\nWii system memory. Refer to the Wii\nOperations Manual for details.",
+};
+
+const char* const __NANDMaxFilesErrorMessageDefault[] = {
+ "\n\n\nWii本体保存メモリの空きファイル数が異常です。\nくわしくはWii本体の取扱説明書をお読み\nください。",
+ "\n\n\nThere is no more available space in\nWii system memory. Refer to the Wii\nOperations Manual for details.",
+ "\n\n\nDer Speicher der Wii-Konsole ist belegt.\nBitte lies die Wii-Bedienungsanleitung,\num weitere Informationen zu erhalten.",
+ "\n\n\nIl n'y a pas assez d'espace libre dans\nla m駑oire de la console Wii. Veuillez\nvous r馭駻er au Mode d'emploi de la Wii\npour plus de d騁ails.",
+ "\n\n\nNo queda espacio libre en la memoria de\nla consola Wii. Consulta el manual de\noperaciones de la consola Wii para\nobtener m疽 informaci\xF3n.",
+ "\n\n\nImpossibile salvare altri dati nella\nmemoria della console Wii. Per maggiori\ninformazioni, consulta il manuale di\nistruzioni della console Wii.",
+ "\n\n\nEr is geen ruimte meer beschikbaar\nin het interne geheugen van het\nWii-systeem. Lees de handleiding voor\nmeer informatie.",
+};
+
+const char* const __NANDMaxFilesErrorMessageEurope[] = {
+ "\n\n\nWii本体保存メモリの空きファイル数が異常です。\nくわしくはWii本体の取扱説明書をお読み\nください。",
+ "\n\n\nThere is no more available space in\nthe Wii System Memory. Please refer to\nthe Wii Operations Manual for details.",
+ "\n\n\nDer Speicher der Wii-Konsole ist belegt.\nBitte lies die Wii-Bedienungsanleitung,\num weitere Informationen zu erhalten.",
+ "\n\n\nIl n'y a pas assez d'espace libre dans\nla m駑oire de la console Wii. Veuillez\nvous r馭駻er au mode d'emploi Wii pour\nplus de d騁ails.",
+ "\n\n\nNo queda espacio libre en la memoria de\nla consola Wii. Consulta el manual de\ninstrucciones de la consola Wii para\nobtener m疽 informaci\xF3n.",
+ "\n\n\nImpossibile salvare altri dati nella\nmemoria della console Wii. Per maggiori\ninformazioni, consulta il manuale di\nistruzioni della console Wii.",
+ "\n\n\nEr is geen ruimte meer beschikbaar\nin het interne geheugen van het\nWii-systeem. Lees de handleiding voor\nmeer informatie.",
+};
+
+const char* const __NANDMaxFilesErrorMessageChinaKorea[] = {
+ "\n\nエラーコード406。\n\nWii本体保存メモリの空きファイル数が異常です。\nくわしくはWii本体の取扱説明書をお読み\nください。",
+ "\n\nError #406,\n\nThere is no more available space in\nWii system memory. Refer to the Wii\nOperations Manual for details.",
+};
+
+const char* const __NANDCorruptErrorMessageDefault[] = {
+ "\n\n\nWii本体保存メモリが壊れました。\nくわしくはWii本体の取扱説明書をお読み\nください。",
+ "\n\n\nThe Wii system memory has been damaged.\nRefer to the Wii Operations Manual for\ndetails.",
+ "\n\n\nDer Speicher der Wii-Konsole\nist besch臈igt. Bitte lies die\nWii-Bedienungsanleitung, um weitere\nInformationen zu erhalten.",
+ "\n\n\nLa m駑oire de la console Wii a 騁\xE9 \nendommag馥. Veuillez vous r馭駻er au\nMode d'emploi de la Wii pour plus de\nd騁ails.",
+ "\n\n\nLa memoria de la consola Wii\nest\xE1 da\xF1""ada. Consulta el manual de\noperaciones de la consola Wii para\nobtener m疽 informaci\xF3n.",
+ "\n\n\nLa memoria della console Wii e\ndanneggiata. Per maggiori informazioni,\nconsulta il manuale di istruzioni della\nconsole Wii.",
+ "\n\n\nHet interne geheugen van het\nWii-systeem is beschadigd. Lees de\nWii-handleiding voor meer informatie.",
+};
+
+const char* const __NANDCorruptErrorMessageEurope[] = {
+ "\n\n\nWii本体保存メモリが壊れました。\nくわしくはWii本体の取扱説明書をお読み\nください。",
+ "\n\n\nThe Wii System Memory has been damaged.\nPlease refer to the Wii Operations Manual\nfor details.",
+ "\n\n\nDer Speicher der Wii-Konsole\nist beschadigt. Bitte lies die\nWii-Bedienungsanleitung, um weitere\nInformationen zu erhalten.",
+ "\n\n\nLa m駑oire de la console Wii est\nendommag馥. Veuillez vous r馭駻er au\nmode d'emploi Wii pour plus de d騁ails.\n",
+ "\n\n\nLa memoria de la consola Wii est\xE1 da\xF1""ada.\nConsulta el manual de instrucciones de la\nconsola Wii para obtener m疽 informaci\xF3n.",
+ "\n\n\nLa memoria della console Wii e\ndanneggiata. Per maggiori informazioni,\nconsulta il manuale di istruzioni della\nconsole Wii.",
+ "\n\n\nHet interne geheugen van het\nWii-systeem is beschadigd. Lees de\nWii-handleiding voor meer informatie.",
+};
+
+const char* const __NANDCorruptErrorMessageChinaKorea[] = {
+ "\n\nエラーコード408。\n\nWii本体保存メモリが壊れました。\nくわしくはWii本体の取扱説明書をお読み\nください。",
+ "\n\nError #408,\n\nThe Wii system memory has been damaged.\nRefer to the Wii Operations Manual for\ndetails.",
+};
+
+const char* const __NANDBusyErrorMessageDefault[] = {
+ "\n\n\nWii本体保存メモリの書き込み/読み出しが\nできませんでした。\nくわしくはWii本体の取扱説明書をお読み\nください。",
+ "\n\n\nCould not access Wii system memory.\nRefer to the Wii Operations Manual for\ndetails.",
+ "\n\n\nAuf den Speicher der Wii-Konsole konnte\nnicht zugegriffen werden. Bitte lies die\nWii-Bedienungsanleitung, um weitere\nInformationen zu erhalten.",
+ "\n\n\nImpossible d'acc馘er \xE0 la m駑oire de\nla console Wii. Veuillez vous r馭駻er\nau Mode d'emploi de la Wii pour plus\nde d騁ails.",
+ "\n\n\nNo se ha podido acceder a la memoria de\nla consola Wii. Consulta el manual de\noperaciones de la consola Wii para\nobtener m疽 informaci\xF3n.",
+ "\n\n\nImpossibile accedere alla memoria della\nconsole Wii. Per maggiori informazioni,\nconsulta il manuale di istruzioni della\nconsole Wii.",
+ "\n\n\nHet interne geheugen van het Wii-systeem\nkan niet worden gelezen of beschreven.\nLees de Wii-handleiding voor meer\ninformatie.",
+};
+
+const char* const __NANDBusyErrorMessageEurope[] = {
+ "\n\n\nWii本体保存メモリの書き込み/読み出しが\nできませんでした。\nくわしくはWii本体の取扱説明書をお読み\nください。",
+ "\n\n\nCould not access the Wii System Memory.Please refer to the Wii Operations Manual\nfor details.",
+ "\n\n\nAuf den Speicher der Wii-Konsole konnte\nnicht zugegriffen werden. Bitte lies die\nWii-Bedienungsanleitung, um weitere\nInformationen zu erhalten.",
+ "\n\n\nImpossible d'acc馘er \xE0 la m駑oire de la\nconsole Wii. Veuillez vous r馭駻er au\nmode d'emploi Wii pour plus de d騁ails.",
+ "\n\n\nNo se ha podido acceder a la memoria de\nla consola Wii. Consulta el manual de\ninstrucciones de la consola Wii para\nobtener m疽 informaci\xF3n.",
+ "\n\n\nImpossibile accedere alla memoria della\nconsole Wii. Per maggiori informazioni,\nconsulta il manuale di istruzioni della\nconsole Wii.",
+ "\n\n\nHet interne geheugen van het Wii-systeem\nkan niet worden gelezen of beschreven.\nLees de Wii-handleiding voor meer\ninformatie.",
+};
+
+const char* const __NANDBusyErrorMessageChinaKorea[] = {
+ "\n\nエラーコード411。\n\nWii本体保存メモリの書き込み/読み出しが\nできませんでした。\nくわしくはWii本体の取扱説明書をお読み\nください。",
+ "\n\nError #411,\n\nCould not access Wii system memory.\nRefer to the Wii Operations Manual for\ndetails.",
+};
+
+const char* const __NANDUnknownErrorMessageDefault[] = {
+ "\n\n\nWii本体保存メモリの書き込み/読み出し中に\nエラーが発生しました。\nくわしくはWii本体の取扱説明書をお読み\nください。",
+ "\n\n\nAn error occurred while accessing Wii\nsystem memory. Refer to the Wii\nOperations Manual for details.",
+ "\n\n\nBeim Zugriff auf den Speicher der\nWii-Konsole ist ein Fehler aufgetreten.\nBitte lies die Wii-Bedienungsanleitung,\num weitere Informationen zu erhalten.",
+ "\n\n\nUne erreur est survenue pendant le\nprocessus de lecture ou d'馗riture\ndans la m駑oire de la console Wii.\nVeuillez vous r馭駻er au Mode d'emploi\nde la Wii pour plus de d騁ails.",
+ "\n\n\nSe ha producido un error al intentar\nacceder a la memoria de la consola Wii.\nConsulta el manual de operaciones\nde la consola Wii para obtener m疽\ninformaci\xF3n.",
+ "\n\n\nSi \xE8 verificato un errore durante la\nlettura o la modifica dei dati\nall'interno della memoria della\nconsole Wii. Per maggiori informazioni,\nconsulta il manuale di istruzioni della\nconsole Wii.",
+ "\n\n\nEr is een fout opgetreden tijdens het\nlezen of beschrijven van het interne\ngeheugen van het Wii-systeem. Lees de\nWii-handleiding voor meer informatie.",
+};
+
+const char* const __NANDUnknownErrorMessageEurope[] = {
+ "\n\n\nWii本体保存メモリの書き込み/読み出し中に\nエラーが発生しました。\nくわしくはWii本体の取扱説明書をお読み\nください。",
+ "\n\n\nAn error occurred during the process of\nreading from or writing to the Wii System\nMemory. Please refer to the Wii Operations\nManual for details.",
+ "\n\n\nBeim Zugriff auf den Speicher der\nWii-Konsole ist ein Fehler aufgetreten.\nBitte lies die Wii-Bedienungsanleitung,\num weitere Informationen zu erhalten.",
+ "\n\n\nUne erreur est survenue avec la m駑oire\nde la console Wii pendant le processus\nde lecture ou d'馗riture. Veuillez vous\nr馭駻er au mode d'emploi Wii pour plus\nde d鑼ails.",
+ "\n\n\nSe ha producido un error durante la\nlectura o escritura de la memoria de\nla consola Wii. Consulta el manual de\ninstrucciones de la consola Wii para\nobtener m疽 informaci\xF3n.",
+ "\n\n\nSi \xE8 verificato un errore durante la\nlettura o la modifica dei dati\nall'interno della memoria della\nconsole Wii. Per maggiori informazioni,\nconsulta il manuale di istruzioni della\nconsole Wii.",
+ "\n\n\nEr is een fout opgetreden tijdens het\nlezen of beschrijven van het interne\ngeheugen van het Wii-systeem. Lees de\nWii-handleiding voor meer informatie.",
+};
+
+const char* const __NANDUnknownErrorMessageChinaKorea[] = {
+ "\n\nエラーコード412。\n\nWii本体保存メモリの書き込み/読み出し中に\nエラーが発生しました。\nくわしくはWii本体の取扱説明書をお読み\nください。",
+ "\n\nError #412,\n\nAn error occurred while accessing Wii\nsystem memory. Refer to the Wii\nOperations Manual for details.",
+};
+
+static void (*NANDErrorFunc)(s32 errorCode);
+
+const char* const* __NANDSetErrorMessageList(s32 errorCode, s32 region) {
+ u8 i = 0;
+ struct {
+ int errorCode;
+ int region;
+ const char* const* messageList;
+ } errorMessageList[] = {
+ {ISFS_ERROR_MAXBLOCKS, REGION_DEFAULT, __NANDMaxBlocksErrorMessageDefault},
+ {ISFS_ERROR_MAXFILES, REGION_DEFAULT, __NANDMaxFilesErrorMessageDefault},
+ {ISFS_ERROR_CORRUPT, REGION_DEFAULT, __NANDCorruptErrorMessageDefault},
+ {ISFS_ERROR_BUSY, REGION_DEFAULT, __NANDBusyErrorMessageDefault},
+ {IOS_ERROR_FAIL_ALLOC, REGION_DEFAULT, __NANDBusyErrorMessageDefault},
+ {ISFS_ERROR_UNKNOWN, REGION_DEFAULT, __NANDUnknownErrorMessageDefault},
+ {IOS_ERROR_UNKNOWN, REGION_DEFAULT, __NANDUnknownErrorMessageDefault},
+
+ {ISFS_ERROR_MAXBLOCKS, REGION_EUROPE, __NANDMaxBlocksErrorMessageEurope},
+ {ISFS_ERROR_MAXFILES, REGION_EUROPE, __NANDMaxFilesErrorMessageEurope},
+ {ISFS_ERROR_CORRUPT, REGION_EUROPE, __NANDCorruptErrorMessageEurope},
+ {ISFS_ERROR_BUSY, REGION_EUROPE, __NANDBusyErrorMessageEurope},
+ {IOS_ERROR_FAIL_ALLOC, REGION_EUROPE, __NANDBusyErrorMessageEurope},
+ {ISFS_ERROR_UNKNOWN, REGION_EUROPE, __NANDUnknownErrorMessageEurope},
+ {IOS_ERROR_UNKNOWN, REGION_EUROPE, __NANDUnknownErrorMessageEurope},
+
+ {ISFS_ERROR_MAXBLOCKS, REGION_CHNKOR, __NANDMaxBlocksErrorMessageChinaKorea},
+ {ISFS_ERROR_MAXFILES, REGION_CHNKOR, __NANDMaxFilesErrorMessageChinaKorea},
+ {ISFS_ERROR_CORRUPT, REGION_CHNKOR, __NANDCorruptErrorMessageChinaKorea},
+ {ISFS_ERROR_BUSY, REGION_CHNKOR, __NANDBusyErrorMessageChinaKorea},
+ {IOS_ERROR_FAIL_ALLOC, REGION_CHNKOR, __NANDBusyErrorMessageChinaKorea},
+ {ISFS_ERROR_UNKNOWN, REGION_CHNKOR, __NANDUnknownErrorMessageChinaKorea},
+ {IOS_ERROR_UNKNOWN, REGION_CHNKOR, __NANDUnknownErrorMessageChinaKorea},
+ };
+
+ while (i < 21) {
+ if (region == errorMessageList[i].region) {
+ if (errorCode == errorMessageList[i].errorCode) {
+ return errorMessageList[i].messageList;
+ }
+ i++;
+ continue;
+ }
+
+ i = i + 7;
+ }
+
+ return __NANDUnknownErrorMessageDefault;
+}
+
+void __NANDShowErrorMessage(s32 errorCode) {
+ const char* message;
+ const char* const* messageList;
+ GXColor bg = { 0, 0, 0, 0 };
+ GXColor fg = { 255, 255, 255, 0 };
+
+ if (SCGetLanguage() == SC_LANG_JAPANESE) {
+ OSSetFontEncode(OS_FONT_ENCODE_SJIS);
+ } else {
+ OSSetFontEncode(OS_FONT_ENCODE_ANSI);
+ }
+
+ switch (SCGetProductGameRegion()) {
+ case 0:
+ case 1:
+ case 3:
+ default:
+ messageList = __NANDSetErrorMessageList(errorCode, REGION_DEFAULT);
+ break;
+ case 2:
+ messageList = __NANDSetErrorMessageList(errorCode, REGION_EUROPE);
+ break;
+ case 4:
+ case 5:
+ messageList = __NANDSetErrorMessageList(errorCode, REGION_CHNKOR);
+ break;
+ }
+
+ if (SCGetLanguage() > SC_LANG_DUTCH) {
+ message = messageList[1];
+ } else {
+ message = messageList[SCGetLanguage()];
+ }
+
+ OSFatal(fg, bg, message);
+}
+
+BOOL NANDSetAutoErrorMessaging(BOOL show) {
+ BOOL enabled = OSDisableInterrupts();
+ BOOL var_r30 = NANDErrorFunc != NULL ? TRUE : FALSE;
+
+ NANDErrorFunc = show ? __NANDShowErrorMessage : NULL;
+
+ OSRestoreInterrupts(enabled);
+ return var_r30;
+}
+
+void __NANDPrintErrorMessage(s32 errorCode) {
+ if (NANDErrorFunc) {
+ NANDErrorFunc(errorCode);
+ }
+}
diff --git a/src/revolution/nand/NANDLogging.c b/src/revolution/nand/NANDLogging.c
new file mode 100644
index 0000000000..7d0d862c81
--- /dev/null
+++ b/src/revolution/nand/NANDLogging.c
@@ -0,0 +1,181 @@
+#include <revolution/os.h>
+#include <revolution/fs.h>
+#include <revolution/nand.h>
+#include <string.h>
+#include <printf.h>
+
+static IOSFd s_fd = -255;
+static IOSError s_err = ISFS_ERROR_UNKNOWN;
+static int s_stage;
+static char s_message[256] ATTRIBUTE_ALIGN(64);
+static NANDLoggingCallback s_callback = 0;
+
+static void asyncRoutine(ISFSError, void*);
+static void prepareLine(char line[256], int, const char*);
+
+BOOL reserveFileDescriptor(void) {
+ BOOL enabled = FALSE;
+ BOOL busy_flag = FALSE;
+
+ enabled = OSDisableInterrupts();
+
+ if (s_fd == -255) {
+ s_fd = -254;
+ busy_flag = FALSE;
+ } else if (s_fd == -254) {
+ busy_flag = TRUE;
+ } else if (s_fd >= 0) {
+ busy_flag = TRUE;
+ } else {
+ busy_flag = TRUE;
+ }
+
+ OSRestoreInterrupts(enabled);
+ return busy_flag ? FALSE : TRUE;
+}
+
+BOOL NANDLoggingAddMessageAsync(NANDLoggingCallback cb, s32 errorCode, const char* fmt, ...) {
+ va_list ap;
+ ISFSError err = ISFS_ERROR_UNKNOWN;
+
+ if (!reserveFileDescriptor()) {
+ return FALSE;
+ }
+
+ va_start(ap, fmt);
+ vsnprintf(s_message, 256, fmt, ap);
+ va_end(ap);
+
+ s_callback = cb;
+ s_stage = 1;
+
+ if (errorCode == ISFS_ERROR_UNKNOWN || errorCode == IOS_ERROR_UNKNOWN) {
+ s_err = errorCode;
+ }
+
+ err = ISFS_OpenAsync((const u8*)"/shared2/test2/nanderr.log", 3, asyncRoutine, 0);
+
+ if (err == ISFS_ERROR_OK) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+static void callbackRoutine(BOOL result) {
+ if (s_callback) {
+ s_callback(result, s_err);
+ }
+}
+
+static void asyncRoutine(ISFSError result, void *ctxt) {
+ ISFSError ret = ISFS_ERROR_UNKNOWN;
+ static char s_rBuf[256] ATTRIBUTE_ALIGN(64);
+ static char s_wBuf[256] ATTRIBUTE_ALIGN(64);
+ ++s_stage;
+
+ if (s_stage == 2) {
+ if (result >= 0) {
+ s_fd = result;
+ ret = ISFS_SeekAsync(s_fd, 0, 0, asyncRoutine, 0);
+
+ if (ret != ISFS_ERROR_OK) {
+ callbackRoutine(FALSE);
+ }
+ } else {
+ callbackRoutine(FALSE);
+ }
+ } else if (s_stage == 3) {
+ if (result == 0) {
+ ret = ISFS_ReadAsync(s_fd, (u8*)s_rBuf, 256, asyncRoutine, 0);
+
+ if (ret != ISFS_ERROR_OK) {
+ callbackRoutine(FALSE);
+ }
+ } else {
+ callbackRoutine(FALSE);
+ }
+ } else if (s_stage == 4) {
+ if (result == 256) {
+ ret = ISFS_SeekAsync(s_fd, 0, 0, asyncRoutine, 0);
+
+ if (ret != ISFS_ERROR_OK) {
+ callbackRoutine(FALSE);
+ }
+ } else {
+ callbackRoutine(FALSE);
+ }
+ } else if (s_stage == 5) {
+ if (result == 0) {
+ int n = 0;
+ s_rBuf[255] = '\0';
+ n = atoi(s_rBuf);
+ prepareLine(s_wBuf, n, s_message);
+ ret = ISFS_WriteAsync(s_fd, (const u8*)s_wBuf, 256, asyncRoutine, 0);
+
+ if (ret != ISFS_ERROR_OK) {
+ callbackRoutine(FALSE);
+ }
+ } else {
+ callbackRoutine(FALSE);
+ }
+ } else if (s_stage == 6) {
+ if (result == 256) {
+ int n = atoi(s_rBuf);
+ ret = ISFS_SeekAsync(s_fd, n * 256, 0, asyncRoutine, 0);
+
+ if (ret != ISFS_ERROR_OK) {
+ callbackRoutine(FALSE);
+ }
+ } else {
+ callbackRoutine(FALSE);
+ }
+ } else if (s_stage == 7) {
+ int n = atoi(s_rBuf);
+ if (result == n * 256) {
+ ret = ISFS_WriteAsync(s_fd, (const u8*)s_wBuf, 256, asyncRoutine, 0);
+ if (ret != ISFS_ERROR_OK) {
+ callbackRoutine(FALSE);
+ }
+ } else {
+ callbackRoutine(FALSE);
+ }
+ } else if (s_stage == 8) {
+ if (result == 256) {
+ ret = ISFS_CloseAsync(s_fd, asyncRoutine, 0);
+
+ if (ret != ISFS_ERROR_OK) {
+ callbackRoutine(FALSE);
+ }
+ } else {
+ callbackRoutine(FALSE);
+ }
+ } else if (s_stage == 9) {
+ if (result == ISFS_ERROR_OK) {
+ s_fd = -255;
+ callbackRoutine(TRUE);
+ } else {
+ callbackRoutine(FALSE);
+ }
+ }
+}
+
+static void prepareLine(char line[256], int n, const char* msg) {
+ char titleID[64];
+ int end = 0;
+ struct OSCalendarTime cal;
+
+ memset(line, ' ', 254);
+ OSTicksToCalendarTime(OSGetTime(), &cal);
+ strncpy(titleID, nandGetHomeDir() + 7, 8 + 1 + 8);
+ titleID[8] = '-';
+ titleID[8 + 1 + 8] = '\0';
+ end = snprintf(line, 256, "%d %04d/%02d/%02d %02d:%02d:%02d %s %s", n % (64 - 1) + 1, cal.year, cal.mon + 1, cal.mday, cal.hour, cal.min, cal.sec, titleID, msg);
+
+ if (end < 256) {
+ line[end] = ' ';
+ }
+
+ line[254] = '\r';
+ line[255] = '\n';
+}
diff --git a/src/revolution/nand/NANDOpenClose.c b/src/revolution/nand/NANDOpenClose.c
new file mode 100644
index 0000000000..cd3ed01142
--- /dev/null
+++ b/src/revolution/nand/NANDOpenClose.c
@@ -0,0 +1,683 @@
+#include <revolution/os.h>
+#include <revolution/nand.h>
+#include <revolution/fs.h>
+
+static void nandOpenCallback(ISFSError, void *);
+static void nandReadCloseCallback(ISFSError, void *);
+static void nandCloseCallback(ISFSError, void *);
+static void nandSafeCloseCallback(ISFSError, void *);
+static void nandSafeOpenCallback(const ISFSError, void *);
+void nandReadOpenCallback(ISFSError, void *);
+s32 nandSafeCloseAsync(NANDFileInfo *, NANDCallback, NANDCommandBlock *, BOOL);
+s32 nandSafeOpenAsync(const char *, NANDFileInfo *, const u8, void *, const u32, NANDCallback, NANDCommandBlock *, BOOL, BOOL);
+s32 nandSafeOpen(const char* path, NANDFileInfo* info, const u8 accType, void* buf, const u32 length, BOOL privilege_flag, BOOL simple_flag);
+s32 nandSafeClose(NANDFileInfo* info, BOOL simple_flag);
+static ISFSError nandCopy(s32 fd, s32 origFd, void* buf, const u32 length);
+static u32 nandGetUniqueNumber(void);
+
+IOSFd nandOpen(const char* path, const u8 accType, NANDCommandBlock* block, const BOOL async_flag, const BOOL privilege_flag) {
+ IOSFd fd = ISFS_ERROR_UNKNOWN;
+ char absPath[64] = "";
+ u32 access = 0;
+
+ ASSERTMSGLINE(188, path, "NULL pointer is detected.");
+ ASSERTMSGLINE(192, accType == 1 || accType == 2 || accType == 3, "Access type is illegal.");
+
+ nandGenerateAbsPath(absPath, path);
+
+ if (!privilege_flag && nandIsPrivatePath(absPath)) {
+ return ISFS_ERROR_ACCESS;
+ } else {
+ switch (accType) {
+ case 3:
+ access = 3;
+ break;
+ case 1:
+ access = 1;
+ break;
+ case 2:
+ access = 2;
+ break;
+ default:
+ break;
+ }
+
+ if (async_flag) {
+ fd = ISFS_OpenAsync((const u8*)absPath, access, nandOpenCallback, block);
+ } else {
+ fd = ISFS_Open((const u8*)absPath, access);
+ }
+
+ return fd;
+ }
+}
+
+s32 NANDOpen(const char* path, NANDFileInfo* info, const u8 accType) {
+ IOSFd fd = ISFS_ERROR_UNKNOWN;
+
+ ASSERTMSGLINE(234, info, "NULL pointer is detected.");
+
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ fd = nandOpen(path, accType, NULL, FALSE, FALSE);
+
+ if (fd >= 0) {
+ info->fileDescriptor = fd;
+ info->mark = 1;
+ return NAND_RESULT_OK;
+ } else {
+ return nandConvertErrorCode(fd);
+ }
+}
+
+s32 NANDPrivateOpen(const char* path, NANDFileInfo* info, const u8 accType) {
+ IOSFd fd = ISFS_ERROR_UNKNOWN;
+
+ ASSERTMSGLINE(259, info, "NULL pointer is detected.");
+
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ fd = nandOpen(path, accType, NULL, FALSE, TRUE);
+
+ if (fd >= 0) {
+ info->fileDescriptor = fd;
+ info->mark = 1;
+ return NAND_RESULT_OK;
+ } else {
+ return nandConvertErrorCode(fd);
+ }
+}
+
+s32 NANDOpenAsync(const char* path, NANDFileInfo* info, const u8 accType, NANDCallback cb, NANDCommandBlock* block) {
+ IOSFd fd = ISFS_ERROR_UNKNOWN;
+
+ ASSERTMSGLINE(284, info, "NULL pointer is detected.");
+
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ block->callback = cb;
+ block->fileInfo = info;
+ fd = nandOpen(path, accType, block, TRUE, FALSE);
+ return nandConvertErrorCode(fd);
+}
+
+s32 NANDPrivateOpenAsync(const char* path, NANDFileInfo* info, const u8 accType, NANDCallback cb, NANDCommandBlock* block) {
+ IOSFd fd = ISFS_ERROR_UNKNOWN;
+
+ ASSERTMSGLINE(301, info, "NULL pointer is detected.");
+
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ block->callback = cb;
+ block->fileInfo = info;
+ fd = nandOpen(path, accType, block, TRUE, TRUE);
+ return nandConvertErrorCode(fd);
+}
+
+void nandOpenCallback(ISFSError result, void* ctxt) {
+ NANDCommandBlock* b = (NANDCommandBlock*)ctxt;
+
+ if (result >= 0) {
+ ((NANDFileInfo*)(b->fileInfo))->fileDescriptor = result;
+ ((NANDFileInfo*)(b->fileInfo))->stage = 2;
+ ((NANDFileInfo*)(b->fileInfo))->mark = 1;
+ ((NANDCallback)(b->callback))(NAND_RESULT_OK, b);
+ } else {
+ ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b);
+ }
+}
+
+s32 NANDClose(NANDFileInfo* info) {
+ ISFSError err = ISFS_ERROR_UNKNOWN;
+
+ ASSERTMSGLINE(341, info, "NULL pointer is detected.");
+
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ if (info->mark != 1) {
+ return NAND_RESULT_INVALID;
+ }
+
+ err = ISFS_Close(info->fileDescriptor);
+
+ if (err == ISFS_ERROR_OK) {
+ info->mark = 2;
+ }
+
+ return nandConvertErrorCode(err);
+}
+
+s32 NANDCloseAsync(NANDFileInfo* info, NANDCallback cb, NANDCommandBlock* block) {
+ ISFSError err = ISFS_ERROR_UNKNOWN;
+
+ ASSERTMSGLINE(364, info, "NULL pointer is detected.");
+
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ if (info->mark != 1) {
+ return NAND_RESULT_INVALID;
+ }
+
+ block->callback = cb;
+ block->fileInfo = info;
+ err = ISFS_CloseAsync(info->fileDescriptor, nandCloseCallback, block);
+ return nandConvertErrorCode(err);
+}
+
+s32 NANDSimpleSafeOpen(const char* path, NANDFileInfo* info, const u8 accType, void* buf, const u32 length) {
+ return nandSafeOpen(path, info, accType, buf, length, FALSE, TRUE);
+}
+
+s32 nandSafeOpen(const char* path, NANDFileInfo* info, const u8 accType, void* buf, const u32 length, BOOL privilege_flag, BOOL simple_flag) {
+ ASSERTMSGLINE(411, path, "NULL pointer detected.\n");
+ ASSERTMSGLINE(412, info, "NULL pointer detected.\n");
+ ASSERTMSGLINE(413, accType == 1 || accType == 2 || accType == 3, "Illegal access type.\n");
+ ASSERTMSGLINE(414, buf, "NULL pointer detected.\n");
+ ASSERTMSGLINE(415, !((u32)buf & 0x1F), "32byte alignment is required.\n");
+ ASSERTMSGLINE(416, !(length & 0x1F), "Buffer size must be multiples of 32.\n");
+
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ if (simple_flag && ((length & 0x3FFF) != 0)) {
+ return NAND_RESULT_INVALID;
+ }
+
+ info->accType = accType;
+ info->stage = 0;
+ nandGenerateAbsPath(info->origPath, path);
+
+ if (!privilege_flag && nandIsPrivatePath(info->origPath)) {
+ return NAND_RESULT_ACCESS;
+ }
+
+ if (accType == 1) {
+ IOSFd fd = ISFS_Open((u8*)(info->origPath), 1);
+ if (fd >= ISFS_ERROR_OK) {
+ info->fileDescriptor = fd;
+ info->stage = 2;
+
+ if (!simple_flag) {
+ info->mark = 3;
+ } else {
+ info->mark = 5;
+ }
+
+ return NAND_RESULT_OK;
+ } else {
+ return nandConvertErrorCode(fd);
+ }
+ } else if (accType == 2 || accType == 3) {
+ char dirName[64];
+ char relName[13] = {0};
+ ISFSError ret = ISFS_ERROR_UNKNOWN;
+ IOSUid ownerId;
+ IOSGid groupId;
+ u32 attr;
+ u32 ownerAcc;
+ u32 groupAcc;
+ u32 othersAcc;
+ u32 sp48 = -1;
+
+ ret = ISFS_CreateDir((u8*)"/tmp/sys", 0, 3, 3, 3);
+ if (ret != ISFS_ERROR_OK && ret != ISFS_ERROR_EXISTS) {
+ return nandConvertErrorCode(ret);
+ } else {
+ info->stage = 1;
+ ret = ISFS_GetAttr((const u8*)info->origPath, &ownerId, &groupId, &attr, &ownerAcc, &groupAcc, &othersAcc);
+ if (ret != ISFS_ERROR_OK) {
+ return nandConvertErrorCode(ret);
+ }
+
+ info->origFd = ISFS_Open((u8*)info->origPath, 1);
+ if (info->origFd < 0) {
+ return nandConvertErrorCode(info->origFd);
+ }
+
+ info->stage = 2;
+
+ if (!simple_flag) {
+ sp48 = nandGetUniqueNumber();
+ sprintf(dirName, "%s/%08x", "/tmp/sys", sp48);
+ ret = ISFS_CreateDir((const u8*)dirName, 0, 3, 0, 0);
+ if (ret != 0) {
+ return nandConvertErrorCode(ret);
+ }
+
+ info->stage = 3;
+ }
+
+ nandGetRelativeName(relName, info->origPath);
+
+ if (!simple_flag) {
+ sprintf(info->tmpPath, "%s/%08x/%s", "/tmp/sys", sp48, relName);
+ } else {
+ sprintf(info->tmpPath, "%s/%s", "/tmp/sys", relName);
+ }
+
+ ret = ISFS_CreateFile((const u8*)info->tmpPath, attr, ownerAcc, groupAcc, othersAcc);
+ if (ret != 0) {
+ return nandConvertErrorCode(ret);
+ }
+
+ info->stage = 4;
+
+ if (accType == 2) {
+ info->fileDescriptor = ISFS_Open((const u8*)info->tmpPath, 2);
+ } else if (accType == 3) {
+ info->fileDescriptor = ISFS_Open((const u8*)info->tmpPath, 3);
+ }
+
+ if (info->fileDescriptor < 0) {
+ return nandConvertErrorCode(info->fileDescriptor);
+ }
+
+ info->stage = 5;
+
+ ret = nandCopy(info->fileDescriptor, info->origFd, buf, length);
+ if (ret != 0) {
+ return nandConvertErrorCode(ret);
+ }
+
+ ret = ISFS_Seek(info->fileDescriptor, 0, 0);
+ if (ret == 0) {
+ ret = 0;
+ } else {
+ return nandConvertErrorCode(ret);
+ }
+
+ if (ret == 0) {
+ if (simple_flag) {
+ info->mark = 5;
+ } else {
+ info->mark = 3;
+ }
+ }
+
+ return nandConvertErrorCode(ret);
+ }
+ } else {
+ return NAND_RESULT_INVALID;
+ }
+}
+
+s32 NANDSimpleSafeClose(NANDFileInfo* info) {
+ return nandSafeClose(info, TRUE);
+}
+
+s32 nandSafeClose(NANDFileInfo* info, BOOL simple_flag) {
+ ISFSError err = ISFS_ERROR_UNKNOWN;
+ char tmpdir[64] = "";
+ ASSERTMSGLINE(612, info, "NULL pointer detected.\n");
+
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ if (!((info->mark == 3 && !simple_flag) || (info->mark == 5 && simple_flag))) {
+ return NAND_RESULT_INVALID;
+ }
+
+ if (info->accType == 1) {
+ err = ISFS_Close(info->fileDescriptor);
+ if (err == 0) {
+ info->stage = 7;
+
+ if (!simple_flag) {
+ info->mark = 4;
+ } else {
+ info->mark = 6;
+ }
+ }
+
+ return nandConvertErrorCode(err);
+ } else if (info->accType == 2 || info->accType == 3) {
+ err = ISFS_Close(info->fileDescriptor);
+ if (err != 0) {
+ return nandConvertErrorCode(err);
+ }
+
+ info->stage = 6;
+
+ err = ISFS_Close(info->origFd);
+ if (err != 0) {
+ return nandConvertErrorCode(err);
+ }
+
+ info->stage = 7;
+
+ err = ISFS_Rename((const u8*)info->tmpPath, (const u8*)info->origPath);
+ if (err != 0) {
+ return nandConvertErrorCode(err);
+ }
+
+ info->stage = 8;
+
+ if (!simple_flag) {
+ nandGetParentDirectory(tmpdir, info->tmpPath);
+ err = ISFS_Delete((const u8*)tmpdir);
+ if (err == 0) {
+ info->stage = 9;
+ info->mark = 4;
+ }
+ } else {
+ info->mark = 6;
+ }
+
+ return nandConvertErrorCode(err);
+ } else {
+ OSReport("Illegal NANDFileInfo.\n");
+ }
+
+ return NAND_RESULT_INVALID;
+}
+
+s32 NANDPrivateSafeOpenAsync(const char* path, NANDFileInfo* info, const u8 accType, void* buf, const u32 length, NANDCallback cb, NANDCommandBlock* block) {
+ return nandSafeOpenAsync(path, info, accType, buf, length, cb, block, TRUE, FALSE);
+}
+
+s32 nandSafeOpenAsync(const char* path, NANDFileInfo* info, const u8 accType, void* buf, const u32 length, NANDCallback cb, NANDCommandBlock* block, BOOL privilege_flag, BOOL simple_flag) {
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ if (simple_flag && ((length & 0x3FFF) != 0)) {
+ return NAND_RESULT_INVALID;
+ }
+
+ info->accType = accType;
+ info->stage = 0;
+ block->simpleFlag = simple_flag;
+ nandGenerateAbsPath(info->origPath, path);
+
+ if (!privilege_flag && nandIsPrivatePath(info->origPath)) {
+ return NAND_RESULT_ACCESS;
+ }
+
+ if (accType == 1) {
+ IOSFd fd = -1;
+ block->fileInfo = info;
+ block->callback = cb;
+ fd = ISFS_OpenAsync((u8*)(info->origPath), 1, nandReadOpenCallback, block);
+
+ if (fd == ISFS_ERROR_OK) {
+ return NAND_RESULT_OK;
+ } else {
+ return nandConvertErrorCode(fd);
+ }
+ } else if (accType == 2 || accType == 3) {
+ ISFSError ret = ISFS_ERROR_UNKNOWN;
+ block->fileInfo = info;
+ block->callback = cb;
+ block->state = 0;
+ block->copyBuf = buf;
+ block->bufLength = length;
+ ret = ISFS_CreateDirAsync((u8*)"/tmp/sys", 0, 3, 3, 3, nandSafeOpenCallback, block);
+
+ if (ret == ISFS_ERROR_OK) {
+ return NAND_RESULT_OK;
+ } else {
+ return nandConvertErrorCode(ret);
+ }
+ } else {
+ return NAND_RESULT_INVALID;
+ }
+}
+
+void nandSafeOpenCallback(const ISFSError result, void* ctxt) {
+ NANDCommandBlock* b = (NANDCommandBlock*)ctxt;
+
+ if (result >= 0 || (result == ISFS_ERROR_EXISTS && b->state == 0)) {
+ NANDFileInfo* info = b->fileInfo;
+ ISFSError ret = ISFS_ERROR_UNKNOWN;
+
+ if (b->state == 0) {
+ info->stage = 1;
+ }
+
+ if (b->state == 2) {
+ info->origFd = result;
+ info->stage = 2;
+ }
+
+ if (b->state == 2 && b->simpleFlag) {
+ b->state += 2;
+ } else {
+ ++b->state;
+ }
+
+ if (b->state == 1) {
+ ret = ISFS_GetAttrAsync((u8*)(info->origPath), &b->ownerId, &b->groupId, &b->attr, &b->ownerAcc, &b->groupAcc, &b->othersAcc, nandSafeOpenCallback, ctxt);
+ } else if (b->state == 2) {
+ ret = ISFS_OpenAsync((u8*)(info->origPath), 1, nandSafeOpenCallback, ctxt);
+ } else if (b->state == 3) {
+ char tmpDir[64];
+ b->uniqNo = nandGetUniqueNumber();
+ sprintf(tmpDir, "%s/%08x", "/tmp/sys", b->uniqNo);
+ ret = ISFS_CreateDirAsync((u8*)tmpDir, 0, 3, 0, 0, nandSafeOpenCallback, ctxt);
+ } else if (b->state == 4) {
+ char filename[13];
+ nandGetRelativeName(filename, info->origPath);
+
+ if (!b->simpleFlag) {
+ info->stage = 3;
+ sprintf(info->tmpPath, "%s/%08x/%s", "/tmp/sys", b->uniqNo, filename);
+ }
+ else {
+ sprintf(info->tmpPath, "%s/%s", "/tmp/sys", filename);
+ }
+
+ ret = ISFS_CreateFileAsync((u8*)info->tmpPath, b->attr, b->ownerAcc, b->groupAcc, b->othersAcc, nandSafeOpenCallback, ctxt);
+ } else if (b->state == 5) {
+ info->stage = 4;
+
+ if (info->accType == 2) {
+ ret = ISFS_OpenAsync((u8*)info->tmpPath, 2, nandSafeOpenCallback, ctxt);
+ } else if (info->accType == 3) {
+ ret = ISFS_OpenAsync((u8*)info->tmpPath, 3, nandSafeOpenCallback, ctxt);
+ } else {
+ ret = ISFS_ERROR_UNKNOWN;
+ }
+ } else if (b->state == 6) {
+ info->fileDescriptor = result;
+ info->stage = 5;
+ b->state = 7;
+ ret = ISFS_ReadAsync(info->origFd, b->copyBuf, b->bufLength, nandSafeOpenCallback, ctxt);
+ } else if (b->state == 7) {
+ ret = ISFS_ReadAsync(info->origFd, b->copyBuf, b->bufLength, nandSafeOpenCallback, ctxt);
+ } else if (b->state == 8) {
+ if (result > 0) {
+ b->state = 6;
+ ret = ISFS_WriteAsync(info->fileDescriptor, b->copyBuf, (u32)result, nandSafeOpenCallback, ctxt);
+ } else if (result == 0) {
+ ret = ISFS_SeekAsync(info->fileDescriptor, 0, 0, nandSafeOpenCallback, ctxt);
+ }
+ } else if (b->state == 9) {
+ if (result == 0) {
+ if (!b->simpleFlag) {
+ info->mark = 3;
+ } else {
+ info->mark = 5;
+ }
+
+ ((NANDCallback)(b->callback))(nandConvertErrorCode(ISFS_ERROR_OK), b);
+ } else {
+ ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b);
+ }
+ return;
+ }
+
+ if (ret != ISFS_ERROR_OK) {
+ ((NANDCallback)(b->callback))(nandConvertErrorCode(ret), b);
+ }
+ } else {
+ ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b);
+ }
+}
+
+void nandReadOpenCallback(ISFSError result, void* ctxt) {
+ NANDCommandBlock* b = (NANDCommandBlock*)ctxt;
+
+ if (result >= 0) {
+ ((NANDFileInfo*)(b->fileInfo))->fileDescriptor = result;
+ ((NANDFileInfo*)(b->fileInfo))->stage = 2;
+
+ if (!(b->simpleFlag)) {
+ ((NANDFileInfo*)(b->fileInfo))->mark = 3;
+ } else {
+ ((NANDFileInfo*)(b->fileInfo))->mark = 5;
+ }
+
+ ((NANDCallback)(b->callback))(NAND_RESULT_OK, b);
+ } else {
+ ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b);
+ }
+}
+
+void dummyStrings() {
+ OSReport("NULL pointer detected.");
+}
+
+s32 NANDSafeCloseAsync(NANDFileInfo* info, NANDCallback cb, NANDCommandBlock* block) {
+ return nandSafeCloseAsync(info, cb, block, FALSE);
+}
+
+s32 nandSafeCloseAsync(NANDFileInfo* info, NANDCallback cb, NANDCommandBlock* block, BOOL simple_flag) {
+ ISFSError err = ISFS_ERROR_UNKNOWN;
+
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ if (!((info->mark == 3 && !simple_flag) || (info->mark == 5 && simple_flag))) {
+ return NAND_RESULT_INVALID;
+ }
+
+ block->simpleFlag = simple_flag;
+ if (info->accType == 1) {
+ block->fileInfo = info;
+ block->callback = cb;
+ err = ISFS_CloseAsync(info->fileDescriptor, nandReadCloseCallback, block);
+ } else if (info->accType == 2 || info->accType == 3) {
+ block->fileInfo = info;
+ block->callback = cb;
+ block->state = 10;
+ err = ISFS_CloseAsync(info->fileDescriptor, nandSafeCloseCallback, block);
+ } else {
+ err = ISFS_ERROR_INVALID;
+ }
+
+ return nandConvertErrorCode(err);
+}
+
+void nandSafeCloseCallback(ISFSError result, void* ctxt) {
+ NANDCommandBlock* b = (NANDCommandBlock*)ctxt;
+
+ if (result == 0) {
+ NANDFileInfo* info = b->fileInfo;
+ ISFSError ret = ISFS_ERROR_UNKNOWN;
+
+ if (b->state == 12) {
+ info->stage = 8;
+ }
+
+ if (b->state == 12 && b->simpleFlag) {
+ b->state += 2;
+ } else {
+ ++b->state;
+ }
+
+ if (b->state == 11) {
+ info->stage = 6;
+ ret = ISFS_CloseAsync(info->origFd, nandSafeCloseCallback, ctxt);
+ } else if (b->state == 12) {
+ info->stage = 7;
+ ret = ISFS_RenameAsync((u8*)(info->tmpPath), (u8*)(info->origPath), nandSafeCloseCallback, ctxt);
+ } else if (b->state == 13) {
+ char tmpdir[64] = "";
+ nandGetParentDirectory(tmpdir, info->tmpPath);
+ ret = ISFS_DeleteAsync((u8*)tmpdir, nandSafeCloseCallback, ctxt);
+ } else if (b->state == 14) {
+ if (!(b->simpleFlag)) {
+ info->stage = 9;
+ }
+
+ info->mark = 4;
+ ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b);
+ return;
+ }
+
+ if (ret != 0) {
+ ((NANDCallback)(b->callback))(nandConvertErrorCode(ret), b);
+ }
+ } else {
+ ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b);
+ }
+}
+
+static void nandReadCloseCallback(ISFSError result, void* ctxt) {
+ NANDCommandBlock* b = (NANDCommandBlock*)ctxt;
+
+ if (result == 0) {
+ ((NANDFileInfo*)(b->fileInfo))->stage = 7;
+ ((NANDFileInfo*)(b->fileInfo))->mark = 4;
+ }
+
+ ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b);
+}
+
+static void nandCloseCallback(ISFSError result, void* ctxt) {
+ NANDCommandBlock* b = (NANDCommandBlock*)ctxt;
+
+ if (result == 0) {
+ ((NANDFileInfo*)(b->fileInfo))->stage = 7;
+ ((NANDFileInfo*)(b->fileInfo))->mark = 2;
+ }
+
+ ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b);
+}
+
+static ISFSError nandCopy(s32 fd, s32 origFd, void* buf, const u32 length) {
+ while (1) {
+ ISFSError ret;
+ ISFSError ret2;
+
+ ret = ISFS_Read(origFd, buf, length);
+ if (ret == 0) {
+ return 0;
+ }
+
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret2 = ISFS_Write(fd, buf, ret);
+ if (ret2 < 0) {
+ return ret2;
+ }
+
+ if (ret2 != ret) {}
+ }
+}
+
+static u32 nandGetUniqueNumber(void) {
+ static u32 s_counter = 0;
+ u32 ret;
+ BOOL enabled = OSDisableInterrupts();
+ ret = s_counter++;
+ OSRestoreInterrupts(enabled);
+ return ret;
+}
diff --git a/src/revolution/nand/nand.c b/src/revolution/nand/nand.c
new file mode 100644
index 0000000000..c81f226f84
--- /dev/null
+++ b/src/revolution/nand/nand.c
@@ -0,0 +1,565 @@
+#include <revolution/os.h>
+#include <revolution/fs.h>
+#include <revolution/nand.h>
+
+#include <string.h>
+
+static BOOL nandInspectPermission(u8);
+static void nandSplitPerm(u8, u32*, u32*, u32*);
+static void nandGetTypeCallback(ISFSError, void*);
+static void nandGetStatusCallback(ISFSError, void*);
+BOOL nandCheckPathName(const char* path);
+BOOL nandCheckCharacter(char character);
+void nandCallback(ISFSError, void*);
+
+ISFSError nandCreate(const char* path, const u8 perm, const u8 attr, NANDCommandBlock* block, BOOL async_flag,
+ BOOL privilege_flag) {
+ char absPath[64] = "";
+ u32 owner = 0, group = 0, others = 0;
+
+ ASSERTMSGLINE(254, path, "NULL pointer is detected.");
+
+ if (!nandCheckPathName(path)) {
+ return ISFS_ERROR_INVALID;
+ }
+
+ nandGenerateAbsPath(absPath, path);
+
+ if (!privilege_flag && nandIsPrivatePath(absPath)) {
+ return ISFS_ERROR_ACCESS;
+ } else if (!nandInspectPermission(perm)) {
+ return ISFS_ERROR_INVALID;
+ } else {
+ nandSplitPerm(perm, &owner, &group, &others);
+
+ if (async_flag) {
+ return ISFS_CreateFileAsync((const u8*)absPath, attr, owner, group, others, nandCallback, block);
+ } else {
+ return ISFS_CreateFile((const u8*)absPath, attr, owner, group, others);
+ }
+ }
+}
+
+s32 NANDCreate(const char* path, const u8 perm, const u8 attr) {
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ return nandConvertErrorCode(nandCreate(path, perm, attr, NULL, FALSE, FALSE));
+}
+
+s32 NANDPrivateCreate(const char* path, u8 perm, u8 attr) {
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ return nandConvertErrorCode(nandCreate(path, perm, attr, NULL, FALSE, TRUE));
+}
+
+s32 NANDPrivateCreateAsync(const char* path, u8 perm, u8 attr, NANDCallback cb,
+ NANDCommandBlock* block) {
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ block->callback = cb;
+ return nandConvertErrorCode(nandCreate(path, perm, attr, block, TRUE, TRUE));
+}
+
+static ISFSError nandDelete(const char* path, NANDCommandBlock* block,
+ BOOL async_flag, BOOL privilege_flag) {
+ char absPath[64] = "";
+ nandGenerateAbsPath(absPath, path);
+
+ if (!privilege_flag && nandIsPrivatePath(absPath)) {
+ return ISFS_ERROR_ACCESS;
+ } else {
+ if (async_flag) {
+ return ISFS_DeleteAsync((const u8*)absPath, nandCallback, block);
+ } else {
+ return ISFS_Delete((const u8*)absPath);
+ }
+ }
+}
+
+s32 NANDDelete(const char* path) {
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ return nandConvertErrorCode(nandDelete(path, NULL, FALSE, FALSE));
+}
+
+s32 NANDPrivateDelete(const char* path) {
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ return nandConvertErrorCode(nandDelete(path, NULL, FALSE, TRUE));
+}
+
+s32 NANDPrivateDeleteAsync(const char* path, NANDCallback cb, NANDCommandBlock* block) {
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ block->callback = cb;
+ return nandConvertErrorCode(nandDelete(path, block, TRUE, TRUE));
+}
+
+s32 NANDRead(NANDFileInfo* info, void* buf, const u32 length) {
+ ASSERTMSGLINE(412, info, "*info is NULL pointer!");
+ ASSERTMSGLINE(413, buf, "*buf is NULL pointer!");
+ ASSERTMSGLINE(414, !((u32)buf & 0x1F), "Buffer must be 32 bytes aligned.");
+ ASSERTMSGLINE(415, !(length & 0x1F), "Buffer length must be multiples of 32 bytes.");
+
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ return nandConvertErrorCode(ISFS_Read(info->fileDescriptor, (u8*)buf, length));
+}
+
+s32 NANDReadAsync(NANDFileInfo* info, void* buf, const u32 length, NANDCallback cb, NANDCommandBlock* block) {
+ ASSERTMSGLINE(428, info, "*info is NULL pointer!");
+ ASSERTMSGLINE(429, buf, "*buf is NULL pointer!");
+ ASSERTMSGLINE(430, !((u32)buf & 0x1F), "Buffer must be 32 bytes aligned.");
+ ASSERTMSGLINE(431, !(length & 0x1F), "Buffer length must be multiples of 32 bytes.");
+
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ block->callback = cb;
+ return nandConvertErrorCode(ISFS_ReadAsync(info->fileDescriptor, (u8*)buf, length, nandCallback, block));
+}
+
+s32 NANDWrite(NANDFileInfo* info, const void* buf, const u32 length) {
+ ASSERTMSGLINE(448, info, "*info is NULL pointer!");
+ ASSERTMSGLINE(449, buf, "*buf is NULL pointer!");
+ ASSERTMSGLINE(450, !((u32)buf & 0x1F), "Buffer must be 32 bytes aligned.");
+
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ return nandConvertErrorCode(ISFS_Write(info->fileDescriptor, (const u8*)buf, length));
+}
+
+s32 NANDWriteAsync(NANDFileInfo* info, const void* buf, const u32 length,
+ NANDCallback cb, NANDCommandBlock* block) {
+ ASSERTMSGLINE(463, info, "*info is NULL pointer!");
+ ASSERTMSGLINE(464, buf, "*buf is NULL pointer!");
+ ASSERTMSGLINE(465, !((u32)buf & 0x1F), "Buffer must be 32 bytes aligned.");
+
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ block->callback = cb;
+ return nandConvertErrorCode(ISFS_WriteAsync(info->fileDescriptor, (const u8*)buf, length, nandCallback, block));
+}
+
+static ISFSError nandSeek(const IOSFd fd, const s32 offset, const s32 whence,
+ NANDCommandBlock* block, const BOOL async_flag) {
+ u32 w = 0xFFFFFFFF;
+
+ switch (whence) {
+ case 0:
+ w = 0;
+ break;
+ case 1:
+ w = 1;
+ break;
+ case 2:
+ w = 2;
+ break;
+ default:
+ break;
+ }
+
+ if (async_flag) {
+ return ISFS_SeekAsync(fd, offset, w, nandCallback, block);
+ } else {
+ return ISFS_Seek(fd, offset, w);
+ }
+}
+
+s32 NANDSeek(NANDFileInfo* info, const s32 offset, const s32 whence) {
+ ASSERTMSGLINE(513, info, "*info is NULL pointer!");
+ ASSERTMSGLINE(514, whence == 0 || whence == 1 || whence == 2, "Illegal whence parameter.");
+
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ return nandConvertErrorCode(nandSeek(info->fileDescriptor, offset, whence, NULL, FALSE));
+}
+
+s32 NANDSeekAsync(NANDFileInfo* info, const s32 offset, const s32 whence,
+ NANDCallback cb, NANDCommandBlock* block) {
+ ASSERTMSGLINE(527, info, "*info is NULL pointer!");
+ ASSERTMSGLINE(528, whence == 0 || whence == 1 || whence == 2, "Illegal whence parameter.");
+
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ block->callback = cb;
+ return nandConvertErrorCode(nandSeek(info->fileDescriptor, offset, whence, block, TRUE));
+}
+
+ISFSError nandCreateDir(const char* path, const u8 perm, const u8 attr,
+ NANDCommandBlock* block, const BOOL async_flag,
+ const BOOL privilege_flag) {
+ char absPath[64] = "";
+
+ if (!nandCheckPathName(path)) {
+ return ISFS_ERROR_INVALID;
+ }
+
+ nandGenerateAbsPath(absPath, path);
+ if (!privilege_flag && nandIsPrivatePath(absPath)) {
+ return ISFS_ERROR_ACCESS;
+ } else if (!nandInspectPermission(perm)) {
+ return ISFS_ERROR_INVALID;
+ } else {
+ u32 owner = 0;
+ u32 group = 0;
+ u32 others = 0;
+ nandSplitPerm(perm, &owner, &group, &others);
+
+ if (async_flag) {
+ return ISFS_CreateDirAsync((const u8*)absPath, attr, owner, group, others, nandCallback, block);
+ } else {
+ return ISFS_CreateDir((const u8*)absPath, attr, owner, group, others);
+ }
+ }
+}
+
+s32 NANDPrivateCreateDir(const char* path, u8 perm, u8 attr) {
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ return nandConvertErrorCode(nandCreateDir(path, perm, attr, NULL, FALSE, TRUE));
+}
+
+s32 NANDPrivateCreateDirAsync(const char* path, u8 perm, u8 attr,
+ NANDCallback cb, NANDCommandBlock* block) {
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ block->callback = cb;
+ return nandConvertErrorCode(nandCreateDir(path, perm, attr, block, TRUE, TRUE));
+}
+
+ISFSError nandMove(const char* path, const char* destDir,
+ NANDCommandBlock* block, const BOOL async_flag,
+ const BOOL privilege_flag) {
+ char absOldPath[64] = "";
+ char absNewPath[64] = "";
+ char relativeName[13] = "";
+
+ nandGenerateAbsPath(absOldPath, path);
+ nandGetRelativeName(relativeName, absOldPath);
+ nandGenerateAbsPath(absNewPath, destDir);
+
+ if (strcmp(absNewPath, "/") == 0) {
+ sprintf(absNewPath, "/%s", relativeName);
+ } else {
+ strcat(absNewPath, "/");
+ strcat(absNewPath, relativeName);
+ }
+
+ if (!privilege_flag && (nandIsPrivatePath(absOldPath) || nandIsPrivatePath(absNewPath))) {
+ return ISFS_ERROR_ACCESS;
+ } else {
+ if (async_flag) {
+ return ISFS_RenameAsync((const u8*)absOldPath, (const u8*)absNewPath, nandCallback, block);
+ } else {
+ return ISFS_Rename((const u8*)absOldPath, (const u8*)absNewPath);
+ }
+ }
+}
+
+s32 NANDMove(const char* path, const char* destDir) {
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ return nandConvertErrorCode(nandMove(path, destDir, NULL, FALSE, FALSE));
+}
+
+static ISFSError nandGetFileStatus(IOSFd fd, u32* length, u32* pos) {
+ ISFSFileStats fstat ATTRIBUTE_ALIGN(32);
+ ISFSError result = ISFS_GetFileStats(fd, &fstat);
+ if (result == ISFS_ERROR_OK) {
+ if (length) {
+ *length = fstat.size;
+ }
+ if (pos) {
+ *pos = fstat.offset;
+ }
+ }
+
+ return result;
+}
+
+s32 NANDGetLength(NANDFileInfo* info, u32* length) {
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ return nandConvertErrorCode(nandGetFileStatus(info->fileDescriptor, length, NULL));
+}
+
+void nandGetFileStatusAsyncCallback(ISFSError result, void* ctxt) {
+ NANDCommandBlock* b = (NANDCommandBlock*)ctxt;
+ ISFSFileStats* fstat = (ISFSFileStats*)OSRoundUp32B((u32)(b->absPath));
+
+ if (result == ISFS_ERROR_OK) {
+ if (b->length) {
+ *(b->length) = fstat->size;
+ }
+ if (b->pos) {
+ *(b->pos) = fstat->offset;
+ }
+ }
+
+ ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b);
+}
+
+static ISFSError nandGetFileStatusAsync(IOSFd fd, NANDCommandBlock* block) {
+ ISFSError result = ISFS_ERROR_UNKNOWN;
+ ISFSFileStats* fstat = (ISFSFileStats*)OSRoundUp32B((u32)(block->absPath));
+ return ISFS_GetFileStatsAsync(fd, fstat, nandGetFileStatusAsyncCallback, block);
+}
+
+s32 NANDGetLengthAsync(NANDFileInfo* info, u32* length, NANDCallback cb, NANDCommandBlock* block) {
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ block->callback = cb;
+ block->length = length;
+ block->pos = NULL;
+ return nandConvertErrorCode(nandGetFileStatusAsync(info->fileDescriptor, block));
+}
+
+void dummyStrings() {
+ OSReport("NULL pointer is detected.\n");
+ OSReport("NAND library internal error: bytes value must be illegal.");
+ OSReport("NAND library internal error: inodes value must be illegal.");
+ OSReport("NAND library internal error: length value must be illegal.");
+ OSReport("NULL pointer detected.");
+}
+
+void nandComposePerm(u8* perm, const u32 ownerAcc, const u32 groupAcc, const u32 othersAcc) {
+ u32 p = 0;
+
+ if (ownerAcc & 1) {
+ p = p | 0x10;
+ }
+
+ if (ownerAcc & 2) {
+ p = p | 0x20;
+ }
+
+ if (groupAcc & 1) {
+ p = p | 4;
+ }
+
+ if (groupAcc & 2) {
+ p = p | 8;
+ }
+
+ if (othersAcc & 1) {
+ p = p | 1;
+ }
+
+ if (othersAcc & 2) {
+ p = p | 2;
+ }
+
+ ASSERTMSGLINE(1007, (p & ~0xFF) == 0, "NAND library internal error.\n");
+ *perm = (u8)p;
+}
+
+static void nandSplitPerm(u8 perm, u32* ownerAcc, u32* groupAcc, u32* othersAcc) {
+ *ownerAcc = 0;
+ *groupAcc = 0;
+ *othersAcc = 0;
+
+ if (perm & 0x10) {
+ *ownerAcc = *ownerAcc | 1;
+ }
+
+ if (perm & 0x20) {
+ *ownerAcc = *ownerAcc | 2;
+ }
+
+ if (perm & 4) {
+ *groupAcc = *groupAcc | 1;
+ }
+
+ if (perm & 8) {
+ *groupAcc = *groupAcc | 2;
+ }
+
+ if (perm & 1) {
+ *othersAcc = *othersAcc | 1;
+ }
+
+ if (perm & 2) {
+ *othersAcc = *othersAcc | 2;
+ }
+}
+
+ISFSError nandGetStatus(const char* path, NANDStatus* stat,
+ NANDCommandBlock* block, const BOOL async_flag,
+ const BOOL privilege_flag) {
+ char absPath[64] = "";
+
+ ASSERTMSGLINE(1052, path, "NULL pointer detected.");
+ ASSERTMSGLINE(1053, stat, "NULL pointer detected.");
+
+ nandGenerateAbsPath(absPath, path);
+ if (!privilege_flag && nandIsUnderPrivatePath(absPath)) {
+ return ISFS_ERROR_ACCESS;
+ } else {
+ if (async_flag) {
+ return ISFS_GetAttrAsync(
+ (const u8*)absPath, &(stat->ownerId), &(stat->groupId),
+ &(block->attr), &(block->ownerAcc), &(block->groupAcc),
+ &(block->othersAcc), nandGetStatusCallback, block);
+ } else {
+ u32 attr = 0, ownerAcc = 0, groupAcc = 0, othersAcc = 0;
+ ISFSError result =
+ ISFS_GetAttr((const u8*)absPath, &stat->ownerId, &stat->groupId,
+ &attr, &ownerAcc, &groupAcc, &othersAcc);
+ if (result == ISFS_ERROR_OK) {
+ nandComposePerm(&stat->permission, ownerAcc, groupAcc, othersAcc);
+ stat->attribute = (u8)attr;
+ }
+
+ return result;
+ }
+ }
+}
+
+void nandGetStatusCallback(ISFSError result, void* ctxt) {
+ NANDCommandBlock* b = (NANDCommandBlock*)ctxt;
+ if (result == ISFS_ERROR_OK) {
+ NANDStatus* stat = (NANDStatus*)(b->status);
+ stat->attribute = (u8)(b->attr);
+ nandComposePerm(&(stat->permission), b->ownerAcc, b->groupAcc,
+ b->othersAcc);
+ }
+ ((NANDCallback)(b->callback))(nandConvertErrorCode(result), b);
+}
+
+s32 NANDGetStatus(const char* path, NANDStatus* stat) {
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ return nandConvertErrorCode(nandGetStatus(path, stat, NULL, FALSE, FALSE));
+}
+
+s32 NANDPrivateGetStatus(const char* path, NANDStatus* stat) {
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ return nandConvertErrorCode(nandGetStatus(path, stat, NULL, FALSE, TRUE));
+}
+
+s32 NANDPrivateGetStatusAsync(const char* path, NANDStatus* stat,
+ NANDCallback cb, NANDCommandBlock* block) {
+ if (!nandIsInitialized()) {
+ return NAND_RESULT_FATAL_ERROR;
+ }
+
+ block->callback = cb;
+ block->status = stat;
+ return nandConvertErrorCode(nandGetStatus(path, stat, block, TRUE, TRUE));
+}
+
+void NANDSetUserData(NANDCommandBlock* block, void* data) {
+ block->userData = data;
+}
+
+void* NANDGetUserData(const NANDCommandBlock* block) {
+ return block->userData;
+}
+
+static BOOL nandInspectPermission(const u8 perm) {
+ if (perm & 0x10) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+BOOL nandCheckPathName(const char* path) {
+ char absNewPath[64];
+ char relativeName[13];
+ int len = -1;
+
+ ASSERTMSGLINE(1266, path, "null pointer is detected in argument of path\n");
+
+ len = strlen(path);
+ if (len == 0) {
+ return 0;
+ } else {
+ u32 i;
+ if (nandIsRelativePath(path)) {
+ for (i = 0; i < len; i++) {
+ if (!nandCheckCharacter(path[i])) {
+ return 0;
+ }
+ }
+ } else {
+ strcpy(absNewPath, path);
+ if (absNewPath[len - 1] == '/' && len - 1 != 0) {
+ absNewPath[len - 1] = 0;
+ }
+
+ nandGetRelativeName(relativeName, absNewPath);
+ len = strlen(relativeName);
+
+ for (i = 0; i < len; i++) {
+ if (!nandCheckCharacter(relativeName[i])) {
+ return 0;
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+BOOL nandCheckCharacter(char character) {
+ u32 i;
+ const char legal_characters[66] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_./";
+
+ for (i = 0; i < sizeof(legal_characters); i++) {
+ if (character == legal_characters[i]) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+void dummyStrings2() {
+ OSReport("/tmp/count_tmpdir");
+ OSReport("countfile");
+ OSReport("*number is NULL pointer!");
+ OSReport("*cleaning is NULL pointer!");
+ OSReport("NAND library internal error: free_inodes value must be illegal.");
+ OSReport("%s/%s_%.2d");
+ OSReport("count_tmp.count must be less than ISFS_FD_ENTRIES.!");
+}
diff --git a/src/revolution/os/OS.c b/src/revolution/os/OS.c
index fc769082da..006870ca43 100644
--- a/src/revolution/os/OS.c
+++ b/src/revolution/os/OS.c
@@ -4,16 +4,17 @@
#include <revolution/si.h>
#include <revolution/db.h>
#include <revolution/sc.h>
+#include <revolution/ipc.h>
#include "__os.h"
+#include "__dvd.h"
+
+#include <string.h>
#define NOP 0x60000000
// external functions
extern void EnableMetroTRKInterrupts(void);
-extern void __OSInitMemoryProtection(void);
-extern void IPCCltInit(void);
-extern BOOL __DVDCheckDevice(void);
#define DB_EXCEPTIONRET_OFFSET 0xC
#define DB_EXCEPTIONDEST_OFFSET 0x8
diff --git a/src/revolution/os/OSAlarm.c b/src/revolution/os/OSAlarm.c
index fd50cb3199..235ce0d18d 100644
--- a/src/revolution/os/OSAlarm.c
+++ b/src/revolution/os/OSAlarm.c
@@ -16,7 +16,7 @@ static void DecrementerExceptionCallback(__REGISTER __OSException exception, __R
static void DecrementerExceptionHandler(__OSException exception, OSContext* context);
static BOOL OnReset(BOOL final, u32 event);
-static OSShutdownFunctionInfo ResetFunctionInfo = {OnReset, 0xFFFFFFFF, NULL, NULL};
+static OSShutdownFunctionInfo ShutdownFunctionInfo = {OnReset, 0xFFFFFFFF, NULL, NULL};
static OSAlarmQueue AlarmQueue;
#define ASSERTREPORT(line, cond) \
@@ -52,7 +52,7 @@ void __OSInitAlarm(void) {
if (__OSGetExceptionHandler(8) != DecrementerExceptionHandler) {
AlarmQueue.head = AlarmQueue.tail = NULL;
__OSSetExceptionHandler(8, DecrementerExceptionHandler);
- OSRegisterShutdownFunction(&ResetFunctionInfo);
+ OSRegisterShutdownFunction(&ShutdownFunctionInfo);
}
}
diff --git a/src/revolution/os/OSAudioSystem.c b/src/revolution/os/OSAudioSystem.c
index d9e1df68c9..a16bf21954 100644
--- a/src/revolution/os/OSAudioSystem.c
+++ b/src/revolution/os/OSAudioSystem.c
@@ -1,6 +1,8 @@
#include <revolution.h>
#include <revolution/os.h>
+#include <string.h>
+
#include "__os.h"
// prototypes
diff --git a/src/revolution/os/OSExec.c b/src/revolution/os/OSExec.c
index b4b8b2d6cb..50441daab8 100644
--- a/src/revolution/os/OSExec.c
+++ b/src/revolution/os/OSExec.c
@@ -1,13 +1,14 @@
#include <revolution.h>
#include <revolution/os.h>
#include <revolution/esp.h>
+#include <revolution/ipc.h>
+#include <string.h>
#include "__os.h"
#include "__dvd.h"
// extern
extern size_t wcslen(const wchar_t *);
-extern int __DVDLayoutFormat;
extern volatile u32 BOOT_REGION_START AT_ADDRESS(0x812FDFF0);
extern volatile u32 BOOT_REGION_END AT_ADDRESS(0x812FDFEC);
diff --git a/src/revolution/os/OSInterrupt.c b/src/revolution/os/OSInterrupt.c
index cc5e30cb1c..6250a07300 100644
--- a/src/revolution/os/OSInterrupt.c
+++ b/src/revolution/os/OSInterrupt.c
@@ -1,5 +1,6 @@
#include <revolution.h>
#include <revolution/os.h>
+#include <string.h>
#include "__os.h"
diff --git a/src/revolution/os/OSLaunch.c b/src/revolution/os/OSLaunch.c
index ce81619f6b..77bc9f8572 100644
--- a/src/revolution/os/OSLaunch.c
+++ b/src/revolution/os/OSLaunch.c
@@ -1,5 +1,6 @@
#include <revolution.h>
#include <revolution/os.h>
+#include <string.h>
#include "__os.h"
diff --git a/src/revolution/os/OSLink.c b/src/revolution/os/OSLink.c
index 7ec245ae58..4abc2f64c2 100644
--- a/src/revolution/os/OSLink.c
+++ b/src/revolution/os/OSLink.c
@@ -1,5 +1,6 @@
#include <revolution.h>
#include <revolution/os.h>
+#include <string.h>
#include "__os.h"
diff --git a/src/revolution/os/OSNet.c b/src/revolution/os/OSNet.c
index be1f33f184..e68c789734 100644
--- a/src/revolution/os/OSNet.c
+++ b/src/revolution/os/OSNet.c
@@ -1,6 +1,8 @@
#include <revolution/os.h>
#include <revolution/sc.h>
-#include <revolution/private/iosresclt.h>
+#include <revolution/ipc.h>
+
+#include "__os.h"
static OSShutdownFunctionInfo ShutdownFuncInfo;
@@ -8,21 +10,6 @@ static IOSFd nwc24ShtFd = -1;
static int nwc24ShtRetryRest = 0;
static BOOL NWC24iIsRequestPending = FALSE;
-typedef enum NWC24Err {
- NWC24_OK = 0,
-} NWC24Err;
-
-NWC24Err NWC24iPrepareShutdown(void);
-NWC24Err NWC24iSynchronizeRtcCounter(BOOL);
-NWC24Err NWC24iOpenResourceManager_(const char* callerName, const char* path, IOSFd* fd, u32 flags);
-NWC24Err NWC24SuspendScheduler(void);
-NWC24Err NWC24iSetRtcCounter_(int rtc, BOOL);
-NWC24Err NWC24iIoctlResourceManager_(const char* callerName, IOSFd fd, s32 cmd, void* input, u32 inputLen, void* output, u32 outputLen);
-NWC24Err NWC24iCloseResourceManager_(const char* callerName, IOSFd);
-NWC24Err NWC24iRequestShutdown(u32 event, s32* result);
-NWC24Err NWC24iIoctlResourceManagerAsync_(const char* callerName, IOSFd fd, s32 cmd, void* input, u32 inputLen, void* output, u32 outputLen, void* cbArg);
-BOOL NWC24iIsAsyncRequestPending_(void);
-
BOOL NWC24Shutdown_(BOOL, u32);
static void REXInit(void);
diff --git a/src/revolution/os/OSPlayTime.c b/src/revolution/os/OSPlayTime.c
index 9eddeb8b48..78a10b2ca8 100644
--- a/src/revolution/os/OSPlayTime.c
+++ b/src/revolution/os/OSPlayTime.c
@@ -2,6 +2,10 @@
#include <revolution/os.h>
#include <revolution/esp.h>
#include <revolution/nand.h>
+#include <revolution/vi.h>
+#include <string.h>
+
+#include "__os.h"
OSAlarm __OSExpireAlarm;
OSTime __OSExpireTime;
diff --git a/src/revolution/os/OSReset.c b/src/revolution/os/OSReset.c
index 12cd1ae138..1685475a18 100644
--- a/src/revolution/os/OSReset.c
+++ b/src/revolution/os/OSReset.c
@@ -1,6 +1,8 @@
#include <revolution.h>
#include <revolution/os.h>
+#include <revolution/os/OSPlayRecord.h>
#include <revolution/sc.h>
+#include <string.h>
#include "__os.h"
#include "__dvd.h"
diff --git a/src/revolution/os/OSStateFlags.c b/src/revolution/os/OSStateFlags.c
index 5bb81fbcd6..da4b199cd5 100644
--- a/src/revolution/os/OSStateFlags.c
+++ b/src/revolution/os/OSStateFlags.c
@@ -1,6 +1,6 @@
#include <revolution/os.h>
#include <revolution/nand.h>
-#include <stdio.h>
+#include <string.h>
static OSStateFlags StateFlags ATTRIBUTE_ALIGN(32);
diff --git a/src/revolution/os/OSStateTM.c b/src/revolution/os/OSStateTM.c
index 1b4e491e67..3961ff7d98 100644
--- a/src/revolution/os/OSStateTM.c
+++ b/src/revolution/os/OSStateTM.c
@@ -1,6 +1,7 @@
#include <revolution/os.h>
#include <revolution/os/OSResetSW.h>
#include <revolution/vi.h>
+#include <revolution/ipc.h>
#include <revolution/private/iosrestypes.h>
@@ -29,6 +30,7 @@ static void __OSDefaultResetCallback(void);
static void __OSDefaultPowerCallback(void);
static void __OSRegisterStateEvent(void);
+static int AccessVIDimRegs(void);
static void LockUp(void);
OSPowerCallback OSSetPowerCallback(OSPowerCallback callback) {
diff --git a/src/revolution/os/OSSync.c b/src/revolution/os/OSSync.c
index 7bf90140c2..094efbe1ba 100644
--- a/src/revolution/os/OSSync.c
+++ b/src/revolution/os/OSSync.c
@@ -1,5 +1,6 @@
#include <revolution.h>
#include <revolution/os.h>
+#include <string.h>
#include "__os.h"
diff --git a/src/revolution/os/OSThread.c b/src/revolution/os/OSThread.c
index 6bf9b1a6c3..dbe745d17a 100644
--- a/src/revolution/os/OSThread.c
+++ b/src/revolution/os/OSThread.c
@@ -542,7 +542,7 @@ void OSCancelThread(OSThread* thread) {
int OSJoinThread(OSThread* thread, void* val) {
BOOL enabled = OSDisableInterrupts();
- ASSERTMSG1LINE(LINE(1061, 1092, 1092), __OSIsThreadActive(thread) != 0, "OSJoinThread(): thread %p is not active.", thread);
+ ASSERTMSG1LINE(1061, __OSIsThreadActive(thread) != 0, "OSJoinThread(): thread %p is not active.", thread);
if (!(thread->attr & 1) && (thread->state != OS_THREAD_STATE_MORIBUND) && (thread->queueJoin.head == NULL)) {
OSSleepThread(&thread->queueJoin);
@@ -851,7 +851,7 @@ void OSSetThreadSpecific(s32 index, void* ptr) {
OSThread* thread;
thread = __OSCurrentThread;
- ASSERTLINE(LINE(1573, 1604, 1604), 0 <= index && index < OS_THREAD_SPECIFIC_MAX);
+ ASSERTLINE(1573, 0 <= index && index < OS_THREAD_SPECIFIC_MAX);
if (thread != 0 && index >= 0 && index < OS_THREAD_SPECIFIC_MAX) {
thread->specific[index] = ptr;
@@ -862,7 +862,7 @@ void* OSGetThreadSpecific(s32 index) {
OSThread* thread;
thread = __OSCurrentThread;
- ASSERTLINE(LINE(1584, 1615, 1615), 0 <= index && index < OS_THREAD_SPECIFIC_MAX);
+ ASSERTLINE(1584, 0 <= index && index < OS_THREAD_SPECIFIC_MAX);
if (thread != 0 && index >= 0 && index < OS_THREAD_SPECIFIC_MAX) {
return thread->specific[index];
diff --git a/src/revolution/os/__os.h b/src/revolution/os/__os.h
index 75e76f5df3..3c41d2158a 100644
--- a/src/revolution/os/__os.h
+++ b/src/revolution/os/__os.h
@@ -13,11 +13,7 @@ extern char* __OSExceptionNames[17]; // D ONLY
u32 __OSIsDebuggerPresent(void);
void __OSPSInit(void);
-void __OSInitIPCBuffer(void);
-void __OSInitSTM(void);
-void __OSInitNet(void);
-void __OSInitPlayTime(void);
-void __OSStartPlayRecord(void);
+void __OSGetIOSRev(OSIOSRev* rev);
// OSAlarm
void __OSInitAlarm(void);
@@ -44,6 +40,7 @@ void __OSGetExecParams(OSExecParams* params);
void __OSSetExecParams(const OSExecParams* params, OSExecParams* addr);
void __OSBootDolSimple(u32 doloffset, u32 restartCode, void* regionStart, void* regionEnd, BOOL argsUseDefault, s32 argc, char** argv);
void __OSBootDol(u32 doloffset, u32 restartCode, const char** argv);
+void __OSLaunchMenu();
extern u32 __OSNextPartitionType;
// OSInterrupt
@@ -62,25 +59,37 @@ OSInterruptMask __OSUnmaskInterrupts(OSInterruptMask global);
void __OSDispatchInterrupt(__OSException exception, OSContext* context);
void __OSModuleInit(void);
+// OSIpc
+void __OSInitIPCBuffer(void);
+
+// OSLaunch
+void __OSRelaunchTitle(u32 resetCode);
+
// OSMemory
void __OSInitMemoryProtection(void);
+void __OSRestoreCodeExecOnMEM1(u32 param_0);
// OSMutex
void __OSUnlockAllMutex(OSThread* thread);
int __OSCheckDeadLock(OSThread* thread);
int __OSCheckMutexes(OSThread* thread);
+// OSNet
+void __OSInitNet(void);
+
// OSPlayTime
-void __OSGetPlayTime(ESTicketView* ticket, __OSPlayTimeType* type, u32* playTime);
+void __OSInitPlayTime(void);
+s32 __OSGetPlayTime(ESTicketView* ticket, __OSPlayTimeType* type, u32* playTime);
+BOOL __OSWriteExpiredFlagIfSet(void);
+
+// OSPlayRecord
+void __OSStartPlayRecord(void);
// OSReset
-void __OSDoHotReset(u32 resetCode);
void __OSShutdownDevices(u32 event);
int __OSCallShutdownFunctions(BOOL final, u32 event);
-
-// OSResetSW
-void __OSResetSWInterruptHandler(s16 exception, OSContext* context);
-void __OSSetResetButtonTimer(u8 min);
+void __OSReturnToMenuForError(void);
+void __OSHotResetForError(void);
// OSRtc
int __OSGetRTC(u32* rtc);
@@ -96,6 +105,8 @@ int __OSReadROM(void* buffer, s32 length, s32 offset);
int __OSReadROMAsync(void* buffer, s32 length, s32 offset, void (*callback)());
u8 __OSGetBootMode(void);
void __OSSetBootMode(u8 ntd);
+BOOL __OSGetRTCFlags(u32* flags);
+BOOL __OSClearRTCFlags(void);
// OSSync
extern void __OSSystemCallVectorStart();
@@ -103,6 +114,14 @@ extern void __OSSystemCallVectorEnd();
void __OSInitSystemCall(void);
+// OSStateTM
+void __OSInitSTM(void);
+BOOL __OSWriteExpiredFlag(void);
+int __OSSetVIForceDimming(BOOL isEnabled, u32 yShift, u32 xShift);
+s32 __OSUnRegisterStateEvent(void);
+void __OSShutdownToSBY(void);
+void __OSHotReset(void);
+
// OSThread
void __OSThreadInit(void);
s32 __OSGetEffectivePriority(OSThread* thread);
@@ -137,12 +156,6 @@ OSTime __get_clock(void);
u32 __get_time(void);
int __to_gm_time(void);
-
-// unsorted
-void __OSWriteExpiredFlag(void);
-void __OSReturnToMenuForError(void);
-void __OSHotResetForError(void);
-
#ifdef __cplusplus
}
#endif
diff --git a/src/revolution/pad/Pad.c b/src/revolution/pad/Pad.c
new file mode 100644
index 0000000000..81105f2b8f
--- /dev/null
+++ b/src/revolution/pad/Pad.c
@@ -0,0 +1,894 @@
+#include <revolution.h>
+#include <revolution/pad.h>
+#include <revolution/si.h>
+
+#include <string.h>
+
+#include "__si.h"
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#ifdef SDK_AUG2010
+#define BUILD_DATE "Aug 23 2010"
+#if DEBUG
+#define BUILD_TIME "17:27:55"
+#else
+#define BUILD_TIME "17:33:06"
+#endif
+#elif SDK_SEP2006
+#define BUILD_DATE "Sep 21 2006"
+#define BUILD_TIME "14:32:13"
+#endif
+
+#ifdef SDK_AUG2010
+#if DEBUG
+const char* __PADVersion = "<< RVL_SDK - PAD \tdebug build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#else
+const char* __PADVersion = "<< RVL_SDK - PAD \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4302_145) >>";
+#endif
+#elif SDK_SEP2006
+const char* __PADVersion = "<< RVL_SDK - PAD \trelease build: "BUILD_DATE" "BUILD_TIME" (0x4200_60422) >>";
+#endif
+
+#define PAD_ALL \
+ ( \
+ PAD_BUTTON_LEFT | \
+ PAD_BUTTON_RIGHT | \
+ PAD_BUTTON_DOWN | \
+ PAD_BUTTON_UP | \
+ PAD_TRIGGER_Z | \
+ PAD_TRIGGER_R | \
+ PAD_TRIGGER_L | \
+ PAD_BUTTON_A | \
+ PAD_BUTTON_B | \
+ PAD_BUTTON_X | \
+ PAD_BUTTON_Y | \
+ PAD_BUTTON_MENU | \
+ 0x2000 | \
+ 0x0080 \
+ )
+
+static s32 ResettingChan = 0x20;
+static u32 XPatchBits = PAD_CHAN0_BIT | PAD_CHAN1_BIT | PAD_CHAN2_BIT | PAD_CHAN3_BIT;
+static u32 AnalogMode = 0x300;
+static u32 Spec = PAD_SPEC_5;
+
+static BOOL Initialized;
+static u32 EnabledBits;
+static u32 ResettingBits;
+static u32 RecalibrateBits;
+static u32 WaitingBits;
+static u32 CheckingBits;
+static u32 PendingBits;
+static u32 BarrelBits;
+
+static u32 Type[4];
+static PADStatus Origin[4];
+
+u32 __PADSpec;
+
+// prototypes
+static void PADTypeAndStatusCallback(s32 chan, u32 type);
+static u16 GetWirelessID(s32 chan);
+static void SetWirelessID(s32 chan, u16 id);
+static void DoReset();
+static void PADEnable(s32 chan);
+static void ProbeWireless(s32 chan);
+static void PADProbeCallback(s32 chan, u32 error, OSContext *context);
+static void PADDisable(s32 chan);
+static void UpdateOrigin(s32 chan);
+static void PADOriginCallback(s32 chan, u32 error, OSContext *context);
+static void PADFixCallback(s32 unused, u32 error, struct OSContext *context);
+static void PADResetCallback(s32 unused, u32 error, struct OSContext *context);
+static void PADReceiveCheckCallback(s32 chan, u32 error);
+static void SPEC0_MakeStatus(s32 chan, PADStatus *status, u32 data[2]);
+static void SPEC1_MakeStatus(s32 chan, PADStatus *status, u32 data[2]);
+static s8 ClampS8(s8 var, s8 org);
+static u8 ClampU8(u8 var, u8 org);
+static void SPEC2_MakeStatus(s32 chan, PADStatus *status, u32 data[2]);
+static BOOL OnShutdown(BOOL f, u32);
+void __PADDisableXPatch(void);
+BOOL __PADDisableRumble(BOOL disable);
+
+typedef void (*SPECCallback)(s32, PADStatus*, u32*);
+static SPECCallback MakeStatus = SPEC2_MakeStatus;
+
+static u32 CmdTypeAndStatus;
+static u32 CmdReadOrigin = 0x41000000;
+static u32 CmdCalibrate = 0x42000000;
+static u32 CmdProbeDevice[4];
+
+static OSShutdownFunctionInfo ShutdownFunctionInfo = {
+ OnShutdown,
+ 127,
+ NULL,
+ NULL,
+};
+
+static void PADEnable(s32 chan) {
+ u32 cmd;
+ u32 chanBit;
+ u32 data[2];
+
+ chanBit = PAD_CHAN0_BIT >> chan;
+ EnabledBits |= chanBit;
+ SIGetResponse(chan, &data);
+ cmd = (AnalogMode | 0x400000);
+ SISetCommand(chan, cmd);
+ SIEnablePolling(EnabledBits);
+}
+
+static void PADDisable(s32 chan) {
+ BOOL enabled;
+ u32 chanBit;
+
+ enabled = OSDisableInterrupts();
+ chanBit = PAD_CHAN0_BIT >> chan;
+ SIDisablePolling(chanBit);
+ EnabledBits &= ~chanBit;
+ WaitingBits &= ~chanBit;
+ CheckingBits &= ~chanBit;
+ PendingBits &= ~chanBit;
+ BarrelBits &= ~chanBit;
+ OSSetWirelessID(chan, 0);
+ OSRestoreInterrupts(enabled);
+}
+
+static void DoReset() {
+ u32 chanBit;
+
+ ResettingChan = __cntlzw(ResettingBits);
+ if (ResettingChan != 32) {
+ ASSERTLINE(589, 0 <= ResettingChan && ResettingChan < SI_MAX_CHAN);
+ chanBit = (PAD_CHAN0_BIT >> ResettingChan);
+ ResettingBits &= ~chanBit;
+
+ memset(&Origin[ResettingChan], 0, sizeof(PADStatus));
+ SIGetTypeAsync(ResettingChan, PADTypeAndStatusCallback);
+ }
+}
+
+static void UpdateOrigin(s32 chan) {
+ PADStatus* origin;
+ u32 chanBit = PAD_CHAN0_BIT >> chan;
+
+ origin = &Origin[chan];
+ switch (AnalogMode & 0x00000700u) {
+ case 0x00000000u:
+ case 0x00000500u:
+ case 0x00000600u:
+ case 0x00000700u:
+ origin->triggerLeft &= ~15;
+ origin->triggerRight &= ~15;
+ origin->analogA &= ~15;
+ origin->analogB &= ~15;
+ break;
+ case 0x00000100u:
+ origin->substickX &= ~15;
+ origin->substickY &= ~15;
+ origin->analogA &= ~15;
+ origin->analogB &= ~15;
+ break;
+ case 0x00000200u:
+ origin->substickX &= ~15;
+ origin->substickY &= ~15;
+ origin->triggerLeft &= ~15;
+ origin->triggerRight &= ~15;
+ break;
+ case 0x00000300u: break;
+ case 0x00000400u: break;
+ }
+
+ origin->stickX -= 128;
+ origin->stickY -= 128;
+ origin->substickX -= 128;
+ origin->substickY -= 128;
+
+ if (XPatchBits & chanBit) {
+ if (64 < origin->stickX && (SIGetType(chan) & 0xFFFF0000) == SI_GC_CONTROLLER) {
+ origin->stickX = 0;
+ }
+ }
+}
+
+static void PADOriginCallback(s32 chan, u32 error, OSContext* context) {
+ ASSERTLINE(671, 0 <= ResettingChan && ResettingChan < SI_MAX_CHAN);
+ ASSERTLINE(672, chan == ResettingChan);
+
+ if (!(error & (SI_ERROR_UNDER_RUN | SI_ERROR_OVER_RUN | SI_ERROR_NO_RESPONSE | SI_ERROR_COLLISION)))
+ {
+ UpdateOrigin(ResettingChan);
+ PADEnable(ResettingChan);
+ }
+
+ DoReset();
+}
+
+static void PADOriginUpdateCallback(s32 chan, u32 error, OSContext* context) {
+ ASSERTLINE(701, 0 <= chan && chan < SI_MAX_CHAN);
+ if (!(EnabledBits & (PAD_CHAN0_BIT >> chan)))
+ return;
+ if (!(error & (SI_ERROR_UNDER_RUN | SI_ERROR_OVER_RUN | SI_ERROR_NO_RESPONSE | SI_ERROR_COLLISION)))
+ UpdateOrigin(chan);
+ if (error & SI_ERROR_NO_RESPONSE) {
+ PADDisable(chan);
+ }
+}
+
+static void PADProbeCallback(s32 chan, u32 error, OSContext* context) {
+ u32 type;
+ ASSERTLINE(740, 0 <= ResettingChan && ResettingChan < SI_MAX_CHAN);
+ ASSERTLINE(741, chan == ResettingChan);
+ ASSERTLINE(743, (Type[chan] & SI_WIRELESS_CONT_MASK) == SI_WIRELESS_CONT && !(Type[chan] & SI_WIRELESS_LITE));
+
+ if (!(error & (SI_ERROR_UNDER_RUN | SI_ERROR_OVER_RUN | SI_ERROR_NO_RESPONSE | SI_ERROR_COLLISION)))
+ {
+ PADEnable(ResettingChan);
+ WaitingBits |= PAD_CHAN0_BIT >> ResettingChan;
+ }
+
+ DoReset();
+}
+
+static void PADTypeAndStatusCallback(s32 chan, u32 type) {
+ u32 chanBit;
+ u32 recalibrate;
+ BOOL rc = TRUE;
+ u32 error;
+
+ ASSERTLINE(776, 0 <= ResettingChan && ResettingChan < SI_MAX_CHAN);
+ ASSERTLINE(777, chan == ResettingChan);
+
+ chanBit = PAD_CHAN0_BIT >> ResettingChan;
+ error = type & 0xFF;
+ ASSERTLINE(786, !(error & SI_ERROR_BUSY));
+
+ recalibrate = RecalibrateBits & chanBit;
+ RecalibrateBits &= ~chanBit;
+
+ if (error & (SI_ERROR_UNDER_RUN | SI_ERROR_OVER_RUN | SI_ERROR_NO_RESPONSE | SI_ERROR_COLLISION))
+ {
+ DoReset();
+ return;
+ }
+
+ type &= ~0xFF;
+ Type[ResettingChan] = type;
+
+ if ((type & SI_TYPE_MASK) != SI_TYPE_GC || !(type & SI_GC_STANDARD)) {
+ DoReset();
+ return;
+ }
+
+ if (Spec < PAD_SPEC_2) {
+ PADEnable(ResettingChan);
+ DoReset();
+ return;
+ }
+
+ if (!(type & SI_GC_WIRELESS) || (type & SI_WIRELESS_IR)) {
+ if (recalibrate) {
+ rc = SITransfer(ResettingChan, &CmdCalibrate, 3, &Origin[ResettingChan], 10,
+ PADOriginCallback, 0);
+ } else {
+ rc = SITransfer(ResettingChan, &CmdReadOrigin, 1, &Origin[ResettingChan], 10,
+ PADOriginCallback, 0);
+ }
+ } else if ((type & SI_WIRELESS_FIX_ID) && (type & SI_WIRELESS_CONT_MASK) == SI_WIRELESS_CONT &&
+ !(type & SI_WIRELESS_LITE))
+ {
+ if (type & SI_WIRELESS_RECEIVED) {
+ rc = SITransfer(ResettingChan, &CmdReadOrigin, 1, &Origin[ResettingChan], 10,
+ PADOriginCallback, 0);
+ } else {
+ rc = SITransfer(ResettingChan, &CmdProbeDevice[ResettingChan], 3,
+ &Origin[ResettingChan], 8, PADProbeCallback, 0);
+ }
+ }
+
+ if (!rc) {
+ PendingBits |= chanBit;
+ DoReset();
+ return;
+ }
+}
+
+static void PADReceiveCheckCallback(s32 chan, u32 type) {
+ u32 error;
+ u32 chanBit;
+
+ chanBit = PAD_CHAN0_BIT >> chan;
+
+ if (EnabledBits & chanBit) {
+ error = type & 0xFF;
+ type &= ~0xFF;
+
+ WaitingBits &= ~chanBit;
+ CheckingBits &= ~chanBit;
+
+ if (!(error &
+ (SI_ERROR_UNDER_RUN | SI_ERROR_OVER_RUN | SI_ERROR_NO_RESPONSE | SI_ERROR_COLLISION)) &&
+ (type & SI_GC_WIRELESS) && (type & SI_WIRELESS_FIX_ID) && (type & SI_WIRELESS_RECEIVED) &&
+ !(type & SI_WIRELESS_IR) && (type & SI_WIRELESS_CONT_MASK) == SI_WIRELESS_CONT &&
+ !(type & SI_WIRELESS_LITE))
+ {
+ SITransfer(chan, &CmdReadOrigin, 1, &Origin[chan], 10, PADOriginUpdateCallback, 0);
+ } else {
+ PADDisable(chan);
+ }
+ }
+}
+
+int PADReset(u32 mask) {
+ BOOL enabled;
+ u32 disableBits;
+
+ ASSERTMSGLINE(927, !(mask & 0x0FFFFFFF), "PADReset(): invalid mask");
+
+ enabled = OSDisableInterrupts();
+ mask |= PendingBits;
+ PendingBits = 0;
+ mask &= ~(WaitingBits | CheckingBits);
+ ResettingBits |= mask;
+ disableBits = ResettingBits & EnabledBits;
+ EnabledBits &= ~mask;
+ BarrelBits &= ~mask;
+
+ if (Spec == 4) {
+ RecalibrateBits |= mask;
+ }
+
+ SIDisablePolling(disableBits);
+
+ if (ResettingChan == 0x20) {
+ DoReset();
+ }
+
+ OSRestoreInterrupts(enabled);
+ return 1;
+}
+
+BOOL PADRecalibrate(u32 mask) {
+ BOOL enabled;
+ u32 disableBits;
+
+ ASSERTMSGLINE(969, !(mask & 0x0FFFFFFF), "PADReset(): invalid mask");
+ enabled = OSDisableInterrupts();
+
+ mask |= PendingBits;
+ PendingBits = 0;
+ mask &= ~(WaitingBits | CheckingBits);
+ ResettingBits |= mask;
+ disableBits = ResettingBits & EnabledBits;
+ EnabledBits &= ~mask;
+ BarrelBits &= ~mask;
+
+ if (!(__gUnknown800030E3 & 0x40)) {
+ RecalibrateBits |= mask;
+ }
+
+ SIDisablePolling(disableBits);
+ if (ResettingChan == 32)
+ DoReset();
+
+ OSRestoreInterrupts(enabled);
+ return 1;
+}
+
+BOOL PADInit() {
+ s32 chan;
+ if (Initialized) {
+ return 1;
+ }
+
+ OSRegisterVersion(__PADVersion);
+
+ if (__PADSpec)
+ PADSetSpec(__PADSpec);
+
+ Initialized = TRUE;
+
+ if (__PADFixBits != 0) {
+ OSTime time = OSGetTime();
+ __OSWirelessPadFixMode
+ = (u16)((((time)&0xffff) + ((time >> 16) & 0xffff) + ((time >> 32) & 0xffff) + ((time >> 48) & 0xffff))
+ & 0x3fffu);
+
+ RecalibrateBits = PAD_CHAN0_BIT | PAD_CHAN1_BIT | PAD_CHAN2_BIT | PAD_CHAN3_BIT;
+ }
+
+ for (chan = 0; chan < SI_MAX_CHAN; ++chan) {
+ CmdProbeDevice[chan] = (0x4D << 24) | (chan << 22) | ((__OSWirelessPadFixMode & 0x3fffu) << 8);
+ }
+
+ SIRefreshSamplingRate();
+ OSRegisterShutdownFunction(&ShutdownFunctionInfo);
+
+ return PADReset(PAD_CHAN0_BIT | PAD_CHAN1_BIT | PAD_CHAN2_BIT | PAD_CHAN3_BIT);
+}
+
+u32 PADRead(PADStatus* status) {
+ BOOL enabled;
+ s32 chan;
+ u32 data[2];
+ u32 chanBit;
+ u32 sr;
+ int chanShift;
+ u32 motor;
+ static PADStatus pre_status[4];
+ int threshold;
+
+ threshold = 3;
+ enabled = OSDisableInterrupts();
+ motor = 0;
+
+ for (chan = 0; chan < 4; chan++, status++) {
+ chanBit = PAD_CHAN0_BIT >> chan;
+ chanShift = 8 * (SI_MAX_CHAN - 1 - chan);
+
+ if (PendingBits & chanBit) {
+ PADReset(0);
+ status->err = PAD_ERR_NOT_READY;
+ memset(status, 0, offsetof(PADStatus, err));
+ } else if ((ResettingBits & chanBit) || ResettingChan == chan) {
+ status->err = PAD_ERR_NOT_READY;
+ memset(status, 0, offsetof(PADStatus, err));
+ } else if (!(EnabledBits & chanBit)) {
+ status->err = PAD_ERR_NO_CONTROLLER;
+ memset(status, 0, offsetof(PADStatus, err));
+ } else if (SIIsChanBusy(chan)) {
+ status->err = PAD_ERR_TRANSFER;
+ memset(status, 0, offsetof(PADStatus, err));
+ } else {
+ sr = SIGetStatus(chan);
+ if (sr & SI_ERROR_NO_RESPONSE) {
+ SIGetResponse(chan, data);
+
+ if (WaitingBits & chanBit) {
+ status->err = PAD_ERR_NONE;
+ memset(status, 0, offsetof(PADStatus, err));
+
+ if (!(CheckingBits & chanBit)) {
+ CheckingBits |= chanBit;
+ SIGetTypeAsync(chan, PADReceiveCheckCallback);
+ }
+ } else {
+ PADDisable(chan);
+ status->err = PAD_ERR_NO_CONTROLLER;
+ memset(status, 0, offsetof(PADStatus, err));
+ }
+ } else {
+ if (!(SIGetType(chan) & SI_GC_NOMOTOR)) {
+ motor |= chanBit;
+ }
+
+ if (!SIGetResponse(chan, &data)) {
+ status->err = PAD_ERR_TRANSFER;
+ memset(status, 0, offsetof(PADStatus, err));
+ } else if (data[0] & 0x80000000) {
+ status->err = PAD_ERR_TRANSFER;
+ memset(status, 0, offsetof(PADStatus, err));
+ } else {
+
+
+ MakeStatus(chan, status, data);
+
+ if (((Type[chan] & (0xFFFF0000)) == SI_GC_CONTROLLER) && ((status->button & 0x80) ^ 0x80)) {
+ threshold = 10;
+ } else {
+ threshold = 3;
+ }
+
+ #ifdef __MWERKS__
+ #define abs(x) __abs(x)
+ #else
+ #define abs(x) __builtin_abs(x)
+ #endif
+
+ if (abs(abs(status->stickX) - abs(pre_status[chan].stickX)) >= threshold ||
+ abs(abs(status->stickY) - abs(pre_status[chan].stickY)) >= threshold ||
+ abs(abs(status->substickX) - abs(pre_status[chan].substickX)) >= threshold ||
+ abs(abs(status->substickY) - abs(pre_status[chan].substickY)) >= threshold ||
+ abs(abs(status->triggerLeft) - abs(pre_status[chan].triggerLeft)) >= threshold ||
+ abs(abs(status->triggerRight) - abs(pre_status[chan].triggerRight)) >= threshold ||
+ pre_status[chan].button != status->button)
+ {
+ __VIResetSIIdle();
+ }
+
+ #undef abs
+
+ memcpy(&pre_status[chan], status, sizeof(PADStatus));
+
+ // Check and clear PAD_ORIGIN bit
+ if (status->button & 0x2000) {
+ status->err = PAD_ERR_TRANSFER;
+ memset(status, 0, offsetof(PADStatus, err));
+
+ // Get origin. It is okay if the following transfer fails
+ // since the PAD_ORIGIN bit remains until the read origin
+ // command complete.
+ SITransfer(chan, &CmdReadOrigin, 1, &Origin[chan], 10, PADOriginUpdateCallback, 0);
+ } else {
+ status->err = PAD_ERR_NONE;
+
+ // Clear PAD_INTERFERE bit
+ status->button &= ~0x0080;
+ }
+ }
+ }
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+ return motor;
+}
+
+typedef struct XY {
+ u8 line;
+ u8 count;
+} XY;
+
+void PADSetSamplingRate(u32 msec) {
+ SISetSamplingRate(msec);
+}
+
+#if DEBUG
+void __PADTestSamplingRate(u32 tvmode) {
+ __SITestSamplingRate(tvmode);
+}
+#endif
+
+void PADControlAllMotors(const u32* commandArray) {
+ BOOL enabled;
+ int chan;
+ u32 command;
+ BOOL commit;
+ u32 chanBit;
+
+ enabled = OSDisableInterrupts();
+ commit = FALSE;
+
+ for (chan = 0; chan < SI_MAX_CHAN; chan++, commandArray++) {
+ chanBit = PAD_CHAN0_BIT >> chan;
+ if ((EnabledBits & chanBit) && !(SIGetType(chan) & 0x20000000)) {
+ command = *commandArray;
+ ASSERTMSGLINE(0x4B5, !(command & 0xFFFFFFFC), "PADControlAllMotors(): invalid command");
+ if (Spec < PAD_SPEC_2 && command == PAD_MOTOR_STOP_HARD)
+ command = PAD_MOTOR_STOP;
+ if (__gUnknown800030E3 & 0x20)
+ command = PAD_MOTOR_STOP;
+ SISetCommand(chan, (0x40 << 16) | AnalogMode | (command & (0x00000001 | 0x00000002)));
+ commit = TRUE;
+ }
+ }
+
+ if (commit)
+ SITransferCommands();
+
+ OSRestoreInterrupts(enabled);
+}
+
+void PADControlMotor(s32 chan, u32 command) {
+ BOOL enabled;
+ u32 chanBit;
+
+ ASSERTMSGLINE(1308, !(command & 0xFFFFFFFC), "PADControlMotor(): invalid command");
+
+ enabled = OSDisableInterrupts();
+ chanBit = PAD_CHAN0_BIT >> chan;
+ if ((EnabledBits & chanBit) && !(SIGetType(chan) & SI_GC_NOMOTOR)) {
+ if (Spec < PAD_SPEC_2 && command == PAD_MOTOR_STOP_HARD)
+ command = PAD_MOTOR_STOP;
+ if (__gUnknown800030E3 & 0x20)
+ command = PAD_MOTOR_STOP;
+ SISetCommand(chan, (0x40 << 16) | AnalogMode | (command & (0x00000001 | 0x00000002)));
+ SITransferCommands();
+ }
+
+ OSRestoreInterrupts(enabled);
+}
+
+void PADSetSpec(u32 spec) {
+ ASSERTLINE(1346, !Initialized);
+ __PADSpec = 0;
+
+ switch (spec) {
+ case PAD_SPEC_0:
+ MakeStatus = SPEC0_MakeStatus;
+ break;
+ case PAD_SPEC_1:
+ MakeStatus = SPEC1_MakeStatus;
+ break;
+ case PAD_SPEC_2:
+ case PAD_SPEC_3:
+ case PAD_SPEC_4:
+ case PAD_SPEC_5:
+ MakeStatus = SPEC2_MakeStatus;
+ break;
+ }
+ Spec = spec;
+}
+
+u32 PADGetSpec(void) {
+ return Spec;
+}
+
+static void SPEC0_MakeStatus(s32 chan, PADStatus* status, u32 data[2]) {
+ status->button = 0;
+ status->button |= ((data[0] >> 16) & 0x0008) ? PAD_BUTTON_A : 0;
+ status->button |= ((data[0] >> 16) & 0x0020) ? PAD_BUTTON_B : 0;
+ status->button |= ((data[0] >> 16) & 0x0100) ? PAD_BUTTON_X : 0;
+ status->button |= ((data[0] >> 16) & 0x0001) ? PAD_BUTTON_Y : 0;
+ status->button |= ((data[0] >> 16) & 0x0010) ? PAD_BUTTON_START : 0;
+ status->stickX = (s8)(data[1] >> 16);
+ status->stickY = (s8)(data[1] >> 24);
+ status->substickX = (s8)(data[1]);
+ status->substickY = (s8)(data[1] >> 8);
+ status->triggerLeft = (u8)(data[0] >> 8);
+ status->triggerRight = (u8)data[0];
+ status->analogA = 0;
+ status->analogB = 0;
+ if (170 <= status->triggerLeft)
+ status->button |= PAD_TRIGGER_L;
+ if (170 <= status->triggerRight)
+ status->button |= PAD_TRIGGER_R;
+ status->stickX -= 128;
+ status->stickY -= 128;
+ status->substickX -= 128;
+ status->substickY -= 128;
+}
+
+static void SPEC1_MakeStatus(s32 chan, PADStatus* status, u32 data[2]) {
+ status->button = 0;
+ status->button |= ((data[0] >> 16) & 0x0080) ? PAD_BUTTON_A : 0;
+ status->button |= ((data[0] >> 16) & 0x0100) ? PAD_BUTTON_B : 0;
+ status->button |= ((data[0] >> 16) & 0x0020) ? PAD_BUTTON_X : 0;
+ status->button |= ((data[0] >> 16) & 0x0010) ? PAD_BUTTON_Y : 0;
+ status->button |= ((data[0] >> 16) & 0x0200) ? PAD_BUTTON_START : 0;
+
+ status->stickX = (s8)(data[1] >> 16);
+ status->stickY = (s8)(data[1] >> 24);
+ status->substickX = (s8)(data[1]);
+ status->substickY = (s8)(data[1] >> 8);
+
+ status->triggerLeft = (u8)(data[0] >> 8);
+ status->triggerRight = (u8)data[0];
+
+ status->analogA = 0;
+ status->analogB = 0;
+
+ if (170 <= status->triggerLeft)
+ status->button |= PAD_TRIGGER_L;
+ if (170 <= status->triggerRight)
+ status->button |= PAD_TRIGGER_R;
+
+ status->stickX -= 128;
+ status->stickY -= 128;
+ status->substickX -= 128;
+ status->substickY -= 128;
+}
+
+static s8 ClampS8(s8 var, s8 org) {
+ if (0 < org) {
+ s8 min = (s8)(-128 + org);
+ if (var < min)
+ var = min;
+ } else if (org < 0) {
+ s8 max = (s8)(127 + org);
+ if (max < var)
+ var = max;
+ }
+ return var -= org;
+}
+
+static u8 ClampU8(u8 var, u8 org) {
+ if (var < org)
+ var = org;
+ return var -= org;
+}
+
+static void SPEC2_MakeStatus(s32 chan, PADStatus* status, u32 data[2]) {
+ PADStatus* origin;
+
+ status->button = (u16)((data[0] >> 16) & PAD_ALL);
+ status->stickX = (s8)(data[0] >> 8);
+ status->stickY = (s8)(data[0]);
+
+ switch (AnalogMode & 0x00000700) {
+ case 0x00000000:
+ case 0x00000500:
+ case 0x00000600:
+ case 0x00000700:
+ status->substickX = (s8)(data[1] >> 24);
+ status->substickY = (s8)(data[1] >> 16);
+ status->triggerLeft = (u8)(((data[1] >> 12) & 0x0f) << 4);
+ status->triggerRight = (u8)(((data[1] >> 8) & 0x0f) << 4);
+ status->analogA = (u8)(((data[1] >> 4) & 0x0f) << 4);
+ status->analogB = (u8)(((data[1] >> 0) & 0x0f) << 4);
+ break;
+ case 0x00000100:
+ status->substickX = (s8)(((data[1] >> 28) & 0x0f) << 4);
+ status->substickY = (s8)(((data[1] >> 24) & 0x0f) << 4);
+ status->triggerLeft = (u8)(data[1] >> 16);
+ status->triggerRight = (u8)(data[1] >> 8);
+ status->analogA = (u8)(((data[1] >> 4) & 0x0f) << 4);
+ status->analogB = (u8)(((data[1] >> 0) & 0x0f) << 4);
+ break;
+ case 0x00000200:
+ status->substickX = (s8)(((data[1] >> 28) & 0x0f) << 4);
+ status->substickY = (s8)(((data[1] >> 24) & 0x0f) << 4);
+ status->triggerLeft = (u8)(((data[1] >> 20) & 0x0f) << 4);
+ status->triggerRight = (u8)(((data[1] >> 16) & 0x0f) << 4);
+ status->analogA = (u8)(data[1] >> 8);
+ status->analogB = (u8)(data[1] >> 0);
+ break;
+ case 0x00000300:
+ status->substickX = (s8)(data[1] >> 24);
+ status->substickY = (s8)(data[1] >> 16);
+ status->triggerLeft = (u8)(data[1] >> 8);
+ status->triggerRight = (u8)(data[1] >> 0);
+ status->analogA = 0;
+ status->analogB = 0;
+ break;
+ case 0x00000400:
+ status->substickX = (s8)(data[1] >> 24);
+ status->substickY = (s8)(data[1] >> 16);
+ status->triggerLeft = 0;
+ status->triggerRight = 0;
+ status->analogA = (u8)(data[1] >> 8);
+ status->analogB = (u8)(data[1] >> 0);
+ break;
+ }
+
+ status->stickX -= 128;
+ status->stickY -= 128;
+ status->substickX -= 128;
+ status->substickY -= 128;
+
+ if (((Type[chan] & (0xFFFF0000)) == SI_GC_CONTROLLER) && ((status->button & 0x80) ^ 0x80)) {
+ BarrelBits |= (PAD_CHAN0_BIT >> chan);
+ status->stickX = 0;
+ status->stickY = 0;
+ status->substickX = 0;
+ status->substickY = 0;
+ return;
+ } else {
+ BarrelBits &= ~(PAD_CHAN0_BIT >> chan);
+ }
+
+ origin = &Origin[chan];
+ status->stickX = ClampS8(status->stickX, origin->stickX);
+ status->stickY = ClampS8(status->stickY, origin->stickY);
+ status->substickX = ClampS8(status->substickX, origin->substickX);
+ status->substickY = ClampS8(status->substickY, origin->substickY);
+ status->triggerLeft = ClampU8(status->triggerLeft, origin->triggerLeft);
+ status->triggerRight = ClampU8(status->triggerRight, origin->triggerRight);
+}
+
+int PADGetType(s32 chan, u32* type) {
+ u32 chanBit;
+
+ *type = SIGetType(chan);
+ chanBit = PAD_CHAN0_BIT >> chan;
+ if (ResettingBits & chanBit || ResettingChan == chan || !(EnabledBits & chanBit)) {
+ return 0;
+ }
+ return 1;
+}
+
+BOOL PADSync(void) {
+ return ResettingBits == 0 && (s32)ResettingChan == 32 && !SIBusy();
+}
+
+void PADSetAnalogMode(u32 mode) {
+ BOOL enabled;
+ u32 mask;
+
+ ASSERTMSGLINE(1681, (mode < 8), "PADSetAnalogMode(): invalid mode");
+
+ enabled = OSDisableInterrupts();
+ AnalogMode = mode << 8;
+ mask = EnabledBits;
+
+ EnabledBits &= ~mask;
+ WaitingBits &= ~mask;
+ CheckingBits &= ~mask;
+
+ SIDisablePolling(mask);
+ OSRestoreInterrupts(enabled);
+}
+
+static void (*SamplingCallback)();
+
+static BOOL OnShutdown(BOOL final, u32 event) {
+ BOOL sync;
+ static BOOL recalibrated = FALSE;
+
+ if (SamplingCallback)
+ PADSetSamplingCallback(NULL);
+
+ if (!final) {
+ sync = PADSync();
+ if (!recalibrated && sync) {
+ recalibrated = PADRecalibrate(PAD_CHAN0_BIT | PAD_CHAN1_BIT | PAD_CHAN2_BIT | PAD_CHAN3_BIT);
+ return FALSE;
+ }
+ return sync;
+ } else
+ recalibrated = FALSE;
+
+ return TRUE;
+}
+
+void __PADDisableXPatch(void) {
+ XPatchBits = 0;
+}
+
+static void SamplingHandler(__OSInterrupt interrupt, OSContext* context) {
+ OSContext exceptionContext;
+
+ if (SamplingCallback) {
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+ SamplingCallback();
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+ }
+}
+
+PADSamplingCallback PADSetSamplingCallback(PADSamplingCallback callback) {
+ PADSamplingCallback prev;
+
+ prev = SamplingCallback;
+ SamplingCallback = callback;
+ if (callback) {
+ SIRegisterPollingHandler(SamplingHandler);
+ } else {
+ SIUnregisterPollingHandler(SamplingHandler);
+ }
+
+ return prev;
+}
+
+BOOL __PADDisableRecalibration(BOOL disable) {
+ BOOL enabled;
+ BOOL prev;
+
+ enabled = OSDisableInterrupts();
+ prev = (__gUnknown800030E3 & 0x40) ? TRUE : FALSE;
+ __gUnknown800030E3 &= ~0x40;
+ if (disable) {
+ __gUnknown800030E3 |= 0x40;
+ }
+
+ OSRestoreInterrupts(enabled);
+ return prev;
+}
+
+BOOL __PADDisableRumble(BOOL disable) {
+ BOOL enabled;
+ BOOL prev;
+
+ enabled = OSDisableInterrupts();
+ prev = (__gUnknown800030E3 & 0x20) ? TRUE : FALSE;
+ __gUnknown800030E3 &= ~0x20;
+ if (disable) {
+ __gUnknown800030E3 |= 0x20;
+ }
+ OSRestoreInterrupts(enabled);
+ return prev;
+}
+
+BOOL PADIsBarrel(s32 chan) {
+ if (chan < 0 || chan >= 4) {
+ return FALSE;
+ }
+
+ if (BarrelBits & (PAD_CHAN0_BIT >> chan)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/src/revolution/pad/Padclamp.c b/src/revolution/pad/Padclamp.c
new file mode 100644
index 0000000000..1348cf5d9e
--- /dev/null
+++ b/src/revolution/pad/Padclamp.c
@@ -0,0 +1,153 @@
+#include <revolution.h>
+#include <revolution/pad.h>
+
+extern f32 sqrtf(f32);
+
+static const PADClampRegion ClampRegion = {
+ // Triggers
+ 30,
+ 180,
+
+ // Left stick
+ 15,
+ 72,
+ 40,
+
+ // Right stick
+ 15,
+ 59,
+ 31,
+
+ // Stick radii
+ 56,
+ 44,
+};
+
+// prototypes
+static void ClampStick(s8* px, s8* py, s8 max, s8 xy, s8 min);
+static void ClampCircle(s8* px, s8* py, s8 radius, s8 min);
+static void ClampTrigger(u8* trigger, u8 min, u8 max);
+
+static void ClampStick(s8* px, s8* py, s8 max, s8 xy, s8 min) {
+ int x = *px;
+ int y = *py;
+ int signX;
+ int signY;
+ int d;
+
+ if (0 <= x) {
+ signX = 1;
+ } else {
+ signX = -1;
+ x = -x;
+ }
+
+ if (0 <= y) {
+ signY = 1;
+ } else {
+ signY = -1;
+ y = -y;
+ }
+
+ if (x <= min) {
+ x = 0;
+ } else {
+ x -= min;
+ }
+ if (y <= min) {
+ y = 0;
+ } else {
+ y -= min;
+ }
+
+ if (x == 0 && y == 0) {
+ *px = *py = 0;
+ return;
+ }
+
+ if (xy * y <= xy * x) {
+ d = xy * x + (max - xy) * y;
+ if (xy * max < d) {
+ x = (s8)(xy * max * x / d);
+ y = (s8)(xy * max * y / d);
+ }
+ } else {
+ d = xy * y + (max - xy) * x;
+ if (xy * max < d) {
+ x = (s8)(xy * max * x / d);
+ y = (s8)(xy * max * y / d);
+ }
+ }
+
+ *px = (s8)(signX * x);
+ *py = (s8)(signY * y);
+}
+
+static void ClampCircle(s8* px, s8* py, s8 radius, s8 min) {
+ int x = *px;
+ int y = *py;
+ int squared;
+ int length;
+
+ if (-min < x && x < min) {
+ x = 0;
+ } else if (0 < x) {
+ x -= min;
+ } else {
+ x += min;
+ }
+
+ if (-min < y && y < min) {
+ y = 0;
+ } else if (0 < y) {
+ y -= min;
+ } else {
+ y += min;
+ }
+
+ squared = x * x + y * y;
+ if (radius * radius < squared) {
+ length = sqrtf(squared);
+ x = (x * radius) / length;
+ y = (y * radius) / length;
+ }
+
+ *px = x;
+ *py = y;
+}
+
+static void ClampTrigger(u8* trigger, u8 min, u8 max) {
+ if (*trigger <= min) {
+ *trigger = 0;
+ } else {
+ if (max < *trigger) {
+ *trigger = max;
+ }
+ *trigger -= min;
+ }
+}
+
+void PADClamp(PADStatus * status) {
+ int i;
+
+ for (i = 0; i < 4; i++, status++) {
+ if (status->err == PAD_ERR_NONE) {
+ ClampStick(&status->stickX, &status->stickY, ClampRegion.maxStick, ClampRegion.xyStick, ClampRegion.minStick);
+ ClampStick(&status->substickX, &status->substickY, ClampRegion.maxSubstick, ClampRegion.xySubstick, ClampRegion.minSubstick);
+ ClampTrigger(&status->triggerLeft, ClampRegion.minTrigger, ClampRegion.maxTrigger);
+ ClampTrigger(&status->triggerRight, ClampRegion.minTrigger, ClampRegion.maxTrigger);
+ }
+ }
+}
+
+void PADClampCircle(PADStatus* status) {
+ int i;
+ for (i = 0; i < 4; ++i, status++) {
+ if (status->err == PAD_ERR_NONE) {
+ ClampCircle(&status->stickX, &status->stickY, ClampRegion.radStick, ClampRegion.minStick);
+ ClampCircle(&status->substickX, &status->substickY, ClampRegion.radSubstick, ClampRegion.minSubstick);
+ ClampTrigger(&status->triggerLeft, ClampRegion.minTrigger, ClampRegion.maxTrigger);
+ ClampTrigger(&status->triggerRight, ClampRegion.minTrigger, ClampRegion.maxTrigger);
+ }
+ }
+}
diff --git a/src/revolution/si/__si.h b/src/revolution/si/__si.h
new file mode 100644
index 0000000000..52aebce923
--- /dev/null
+++ b/src/revolution/si/__si.h
@@ -0,0 +1,21 @@
+#ifndef _REVOLUTION_SI_INTERNAL_H_
+#define _REVOLUTION_SI_INTERNAL_H_
+
+#include <revolution/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void __SISteeringEnable(s32 chan);
+s32 __SISteeringTransfer(s32 chan, u32 outputBytes, u32 inputBytes, void (*proc)(s32));
+void __SISteeringSyncCallback(s32 chan, s32);
+s32 __SISteeringSync(s32 chan);
+void __SISteeringDisable(s32 chan);
+void __SITestSamplingRate(u32 tvmode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif