summaryrefslogtreecommitdiff
path: root/src/Dolphin/card
diff options
context:
space:
mode:
authormrb0nk500 <b0nk@b0nk.xyz>2023-02-01 18:45:02 -0400
committermrb0nk500 <b0nk@b0nk.xyz>2023-02-01 18:50:25 -0400
commit9fa0a7f1da1b70bee995f53c6c96c43189018772 (patch)
tree114548896790eaff23cdca84a025281de86bbb51 /src/Dolphin/card
parent2ba3289286bbfcf9fcc13fd135d976058d8b6c2e (diff)
global: Import Dolphin SDK
This version comes from the Metroid Prime decompilation project. https://github.com/PrimeDecomp/prime
Diffstat (limited to 'src/Dolphin/card')
-rw-r--r--src/Dolphin/card/CARDBios.c775
-rw-r--r--src/Dolphin/card/CARDBlock.c159
-rw-r--r--src/Dolphin/card/CARDCheck.c688
-rw-r--r--src/Dolphin/card/CARDCreate.c115
-rw-r--r--src/Dolphin/card/CARDDelete.c97
-rw-r--r--src/Dolphin/card/CARDDir.c92
-rw-r--r--src/Dolphin/card/CARDFormat.c127
-rw-r--r--src/Dolphin/card/CARDMount.c354
-rw-r--r--src/Dolphin/card/CARDNet.c33
-rw-r--r--src/Dolphin/card/CARDOpen.c123
-rw-r--r--src/Dolphin/card/CARDRdwr.c104
-rw-r--r--src/Dolphin/card/CARDRead.c141
-rw-r--r--src/Dolphin/card/CARDRename.c70
-rw-r--r--src/Dolphin/card/CARDStat.c144
-rw-r--r--src/Dolphin/card/CARDUnlock.c396
-rw-r--r--src/Dolphin/card/CARDWrite.c117
16 files changed, 3535 insertions, 0 deletions
diff --git a/src/Dolphin/card/CARDBios.c b/src/Dolphin/card/CARDBios.c
new file mode 100644
index 0000000..0a1fb45
--- /dev/null
+++ b/src/Dolphin/card/CARDBios.c
@@ -0,0 +1,775 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+const char* __CARDVersion =
+ "<< Dolphin SDK - CARD\trelease build: Sep 5 2002 05:35:20 (0x2301) >>";
+
+CARDControl __CARDBlock[2];
+DVDDiskID __CARDDiskNone;
+
+static u16 __CARDEncode;
+
+s32 __CARDReadStatus(s32 chan, u8* status);
+s32 __CARDClearStatus(s32 chan);
+void __CARDSetDiskID(const DVDDiskID* id);
+static s32 Retry(s32 chan);
+
+BOOL OnReset(BOOL f);
+static OSResetFunctionInfo ResetFunctionInfo = {OnReset, 127};
+
+void __CARDDefaultApiCallback(s32 chan, s32 result) {}
+
+void __CARDExtHandler(s32 chan, OSContext* context) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ if (card->attached) {
+ card->attached = FALSE;
+ EXISetExiCallback(chan, 0);
+ OSCancelAlarm(&card->alarm);
+ callback = card->exiCallback;
+
+ if (callback) {
+ card->exiCallback = 0;
+ callback(chan, CARD_RESULT_NOCARD);
+ }
+
+ if (card->result != CARD_RESULT_BUSY) {
+ card->result = CARD_RESULT_NOCARD;
+ }
+
+ callback = card->extCallback;
+ if (callback && CARD_MAX_MOUNT_STEP <= card->mountStep) {
+ card->extCallback = 0;
+ callback(chan, CARD_RESULT_NOCARD);
+ }
+ }
+}
+
+void __CARDExiHandler(s32 chan, OSContext* context) {
+ CARDControl* card;
+ CARDCallback callback;
+ u8 status;
+ s32 result;
+
+ card = &__CARDBlock[chan];
+
+ OSCancelAlarm(&card->alarm);
+
+ if (!card->attached) {
+ return;
+ }
+
+ if (!EXILock(chan, 0, 0)) {
+ result = CARD_RESULT_FATAL_ERROR;
+ goto fatal;
+ }
+
+ if ((result = __CARDReadStatus(chan, &status)) < 0 || (result = __CARDClearStatus(chan)) < 0) {
+ goto error;
+ }
+
+ if ((result = (status & 0x18) ? CARD_RESULT_IOERROR : CARD_RESULT_READY) == CARD_RESULT_IOERROR &&
+ --card->retry > 0) {
+ result = Retry(chan);
+ if (result >= 0) {
+ return;
+ }
+ goto fatal;
+ }
+
+error:
+ EXIUnlock(chan);
+
+fatal:
+ callback = card->exiCallback;
+ if (callback) {
+ card->exiCallback = 0;
+ callback(chan, result);
+ }
+}
+
+void __CARDTxHandler(s32 chan, OSContext* context) {
+ CARDControl* card;
+ CARDCallback callback;
+ BOOL err;
+
+ card = &__CARDBlock[chan];
+ err = !EXIDeselect(chan);
+ EXIUnlock(chan);
+ callback = card->txCallback;
+ if (callback) {
+ card->txCallback = 0;
+ callback(chan, (!err && EXIProbe(chan)) ? CARD_RESULT_READY : CARD_RESULT_NOCARD);
+ }
+}
+
+void __CARDUnlockedHandler(s32 chan, OSContext* context) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ callback = card->unlockCallback;
+ if (callback) {
+ card->unlockCallback = 0;
+ callback(chan, EXIProbe(chan) ? CARD_RESULT_UNLOCKED : CARD_RESULT_NOCARD);
+ }
+}
+
+s32 __CARDEnableInterrupt(s32 chan, BOOL enable) {
+ BOOL err;
+ u32 cmd;
+
+ if (!EXISelect(chan, 0, 4)) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ cmd = enable ? 0x81010000 : 0x81000000;
+ err = FALSE;
+ err |= !EXIImm(chan, &cmd, 2, 1, NULL);
+ err |= !EXISync(chan);
+ err |= !EXIDeselect(chan);
+ return err ? CARD_RESULT_NOCARD : CARD_RESULT_READY;
+}
+
+s32 __CARDReadStatus(s32 chan, u8* status) {
+ BOOL err;
+ u32 cmd;
+
+ if (!EXISelect(chan, 0, 4)) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ cmd = 0x83000000;
+ err = FALSE;
+ err |= !EXIImm(chan, &cmd, 2, 1, NULL);
+ err |= !EXISync(chan);
+ err |= !EXIImm(chan, status, 1, 0, NULL);
+ err |= !EXISync(chan);
+ err |= !EXIDeselect(chan);
+ return err ? CARD_RESULT_NOCARD : CARD_RESULT_READY;
+}
+
+s32 __CARDClearStatus(s32 chan) {
+ BOOL err;
+ u32 cmd;
+
+ if (!EXISelect(chan, 0, 4)) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ cmd = 0x89000000;
+ err = FALSE;
+ err |= !EXIImm(chan, &cmd, 1, 1, 0);
+ err |= !EXISync(chan);
+ err |= !EXIDeselect(chan);
+
+ return err ? CARD_RESULT_NOCARD : CARD_RESULT_READY;
+}
+
+static void TimeoutHandler(OSAlarm* alarm, OSContext* context) {
+ s32 chan;
+ CARDControl* card;
+ CARDCallback callback;
+ for (chan = 0; chan < 2; ++chan) {
+ card = &__CARDBlock[chan];
+ if (alarm == &card->alarm) {
+ break;
+ }
+ }
+
+ if (!card->attached) {
+ return;
+ }
+
+ EXISetExiCallback(chan, NULL);
+ callback = card->exiCallback;
+ if (callback) {
+ card->exiCallback = 0;
+ callback(chan, CARD_RESULT_IOERROR);
+ }
+}
+
+static void SetupTimeoutAlarm(CARDControl* card) {
+ OSCancelAlarm(&card->alarm);
+ switch (card->cmd[0]) {
+ case 0xF2:
+ OSSetAlarm(&card->alarm, OSMillisecondsToTicks(100), TimeoutHandler);
+ break;
+ case 0xF3:
+ break;
+ case 0xF4:
+ case 0xF1:
+ OSSetAlarm(&card->alarm, OSSecondsToTicks((OSTime)2) * (card->sectorSize / 0x2000),
+ TimeoutHandler);
+ break;
+ }
+}
+
+static s32 Retry(s32 chan) {
+ CARDControl* card;
+ card = &__CARDBlock[chan];
+
+ if (!EXISelect(chan, 0, 4)) {
+ EXIUnlock(chan);
+ return CARD_RESULT_NOCARD;
+ }
+
+ SetupTimeoutAlarm(card);
+
+ if (!EXIImmEx(chan, card->cmd, card->cmdlen, 1)) {
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ return CARD_RESULT_NOCARD;
+ }
+
+ if (card->cmd[0] == 0x52 &&
+ !EXIImmEx(chan, (u8*)card->workArea + sizeof(CARDID), card->latency, 1)) {
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ return CARD_RESULT_NOCARD;
+ }
+
+ if (card->mode == 0xffffffff) {
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ return CARD_RESULT_READY;
+ }
+
+ if (!EXIDma(chan, card->buffer, (s32)((card->cmd[0] == 0x52) ? 512 : 128), card->mode,
+ __CARDTxHandler)) {
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ return CARD_RESULT_NOCARD;
+ }
+
+ return CARD_RESULT_READY;
+}
+
+static void UnlockedCallback(s32 chan, s32 result) {
+ CARDCallback callback;
+ CARDControl* card;
+
+ card = &__CARDBlock[chan];
+ if (result >= 0) {
+ card->unlockCallback = UnlockedCallback;
+ if (!EXILock(chan, 0, __CARDUnlockedHandler)) {
+ result = CARD_RESULT_READY;
+ } else {
+ card->unlockCallback = 0;
+ result = Retry(chan);
+ }
+ }
+
+ if (result < 0) {
+ switch (card->cmd[0]) {
+ case 0x52:
+ callback = card->txCallback;
+ if (callback) {
+ card->txCallback = 0;
+ callback(chan, result);
+ }
+
+ break;
+ case 0xF2:
+ case 0xF4:
+ case 0xF1:
+ callback = card->exiCallback;
+ if (callback) {
+ card->exiCallback = 0;
+ callback(chan, result);
+ }
+ break;
+ }
+ }
+}
+
+static s32 __CARDStart(s32 chan, CARDCallback txCallback, CARDCallback exiCallback) {
+ BOOL enabled;
+ CARDControl* card;
+ s32 result;
+
+ enabled = OSDisableInterrupts();
+
+ card = &__CARDBlock[chan];
+ if (!card->attached) {
+ result = CARD_RESULT_NOCARD;
+ } else {
+
+ if (txCallback) {
+ card->txCallback = txCallback;
+ }
+ if (exiCallback) {
+ card->exiCallback = exiCallback;
+ }
+ card->unlockCallback = UnlockedCallback;
+ if (!EXILock(chan, 0, __CARDUnlockedHandler)) {
+ result = CARD_RESULT_BUSY;
+ } else {
+ card->unlockCallback = 0;
+
+ if (!EXISelect(chan, 0, 4)) {
+ EXIUnlock(chan);
+ result = CARD_RESULT_NOCARD;
+ } else {
+ SetupTimeoutAlarm(card);
+ result = CARD_RESULT_READY;
+ }
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+ return result;
+}
+
+#define AD1(x) ((u8)(((x) >> 17) & 0x7f))
+#define AD1EX(x) ((u8)(AD1(x) | 0x80));
+#define AD2(x) ((u8)(((x) >> 9) & 0xff))
+#define AD3(x) ((u8)(((x) >> 7) & 0x03))
+#define BA(x) ((u8)((x)&0x7f))
+
+s32 __CARDReadSegment(s32 chan, CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+
+ card = &__CARDBlock[chan];
+ card->cmd[0] = 0x52;
+ card->cmd[1] = AD1(card->addr);
+ card->cmd[2] = AD2(card->addr);
+ card->cmd[3] = AD3(card->addr);
+ card->cmd[4] = BA(card->addr);
+ card->cmdlen = 5;
+ card->mode = 0;
+ card->retry = 0;
+
+ result = __CARDStart(chan, callback, 0);
+ if (result == CARD_RESULT_BUSY) {
+ result = CARD_RESULT_READY;
+ } else if (result >= 0) {
+ if (!EXIImmEx(chan, card->cmd, card->cmdlen, 1) ||
+ !EXIImmEx(chan, (u8*)card->workArea + sizeof(CARDID), card->latency,
+ 1) || // XXX use DMA if possible
+ !EXIDma(chan, card->buffer, 512, card->mode, __CARDTxHandler)) {
+ card->txCallback = 0;
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ result = CARD_RESULT_NOCARD;
+ } else {
+ result = CARD_RESULT_READY;
+ }
+ }
+ return result;
+}
+
+s32 __CARDWritePage(s32 chan, CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+
+ card = &__CARDBlock[chan];
+ card->cmd[0] = 0xF2;
+ card->cmd[1] = AD1(card->addr);
+ card->cmd[2] = AD2(card->addr);
+ card->cmd[3] = AD3(card->addr);
+ card->cmd[4] = BA(card->addr);
+ card->cmdlen = 5;
+ card->mode = 1;
+ card->retry = 3;
+
+ result = __CARDStart(chan, 0, callback);
+ if (result == CARD_RESULT_BUSY) {
+ result = CARD_RESULT_READY;
+ } else if (result >= 0) {
+ if (!EXIImmEx(chan, card->cmd, card->cmdlen, 1) ||
+ !EXIDma(chan, card->buffer, 128, card->mode, __CARDTxHandler)) {
+ card->exiCallback = 0;
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ result = CARD_RESULT_NOCARD;
+ } else {
+ result = CARD_RESULT_READY;
+ }
+ }
+ return result;
+}
+
+#ifdef FULL_FRANK
+/* TODO: Needs frank fix for disconnected stack epilogue */
+s32 __CARDEraseSector(s32 chan, u32 addr, CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+
+ card = &__CARDBlock[chan];
+ card->cmd[0] = 0xF1;
+ card->cmd[1] = AD1(addr);
+ card->cmd[2] = AD2(addr);
+ card->cmdlen = 3;
+ card->mode = -1;
+ card->retry = 3;
+
+ result = __CARDStart(chan, 0, callback);
+
+ if (result == CARD_RESULT_BUSY) {
+ result = CARD_RESULT_READY;
+ } else if (result >= 0) {
+ if (!EXIImmEx(chan, card->cmd, card->cmdlen, 1)) {
+ card->exiCallback = NULL;
+ result = CARD_RESULT_NOCARD;
+ } else {
+ result = CARD_RESULT_READY;
+ }
+
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ }
+ return result;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm s32 __CARDEraseSector(s32 chan, u32 addr, CARDCallback callback) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x28(r1)
+ stw r31, 0x24(r1)
+ stw r30, 0x20(r1)
+ stw r29, 0x1c(r1)
+ addi r29, r3, 0
+ mulli r6, r29, 0x110
+ lis r3, __CARDBlock@ha
+ addi r0, r3, __CARDBlock@l
+ add r31, r0, r6
+ li r0, 0xf1
+ stb r0, 0x94(r31)
+ rlwinm r3, r4, 0xf, 0x19, 0x1f
+ rlwinm r0, r4, 0x17, 0x18, 0x1f
+ stb r3, 0x95(r31)
+ li r6, 3
+ addi r3, r29, 0
+ stb r0, 0x96(r31)
+ li r0, -1
+ li r4, 0
+ stw r6, 0xa0(r31)
+ stw r0, 0xa4(r31)
+ stw r6, 0xa8(r31)
+ bl __CARDStart
+ addi r30, r3, 0
+ cmpwi r30, -1
+ bne lbl_803B8C4C
+ li r30, 0
+ b lbl_803B8C94
+lbl_803B8C4C:
+ cmpwi r30, 0
+ blt lbl_803B8C94
+ lwz r5, 0xa0(r31)
+ addi r3, r29, 0
+ addi r4, r31, 0x94
+ li r6, 1
+ bl EXIImmEx
+ cmpwi r3, 0
+ bne lbl_803B8C80
+ li r0, 0
+ stw r0, 0xcc(r31)
+ li r30, -3
+ b lbl_803B8C84
+lbl_803B8C80:
+ li r30, 0
+lbl_803B8C84:
+ mr r3, r29
+ bl EXIDeselect
+ mr r3, r29
+ bl EXIUnlock
+lbl_803B8C94:
+ mr r3, r30
+ lwz r0, 0x2c(r1)
+ lwz r31, 0x24(r1)
+ lwz r30, 0x20(r1)
+ lwz r29, 0x1c(r1)
+ addi r1, r1, 0x28
+ mtlr r0
+ blr
+}
+/* clang-format on */
+#pragma pop
+#endif
+void CARDInit(void) {
+ int chan;
+
+ if (__CARDBlock[0].diskID && __CARDBlock[1].diskID) {
+ return;
+ }
+
+ __CARDEncode = OSGetFontEncode();
+
+ OSRegisterVersion(__CARDVersion);
+
+ DSPInit();
+ OSInitAlarm();
+
+ for (chan = 0; chan < 2; ++chan) {
+ CARDControl* card = &__CARDBlock[chan];
+
+ card->result = CARD_RESULT_NOCARD;
+ OSInitThreadQueue(&card->threadQueue);
+ OSCreateAlarm(&card->alarm);
+ }
+ __CARDSetDiskID((DVDDiskID*)OSPhysicalToCached(0x0));
+
+ OSRegisterResetFunction(&ResetFunctionInfo);
+}
+
+u16 __CARDGetFontEncode() { return __CARDEncode; }
+
+void __CARDSetDiskID(const DVDDiskID* id) {
+ __CARDBlock[0].diskID = id ? id : &__CARDDiskNone;
+ __CARDBlock[1].diskID = id ? id : &__CARDDiskNone;
+}
+
+s32 __CARDGetControlBlock(s32 chan, CARDControl** pcard) {
+ BOOL enabled;
+ s32 result;
+ CARDControl* card;
+
+ card = &__CARDBlock[chan];
+ if (chan < 0 || chan >= 2 || card->diskID == NULL) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+
+ enabled = OSDisableInterrupts();
+ if (!card->attached) {
+ result = CARD_RESULT_NOCARD;
+ } else if (card->result == CARD_RESULT_BUSY) {
+ result = CARD_RESULT_BUSY;
+ } else {
+ card->result = CARD_RESULT_BUSY;
+ result = CARD_RESULT_READY;
+ card->apiCallback = 0;
+ *pcard = card;
+ }
+ OSRestoreInterrupts(enabled);
+ return result;
+}
+
+#ifdef FULL_FRANK
+/* TODO: Needs frank fix for disconnected stack epilogue */
+s32 __CARDPutControlBlock(CARDControl* card, s32 result) {
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ if (card->attached) {
+ card->result = result;
+ } else if (card->result == CARD_RESULT_BUSY) {
+ card->result = result;
+ }
+ OSRestoreInterrupts(enabled);
+ return result;
+}
+#else
+#pragma push
+/* clang-format off */
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm s32 __CARDPutControlBlock(CARDControl* card, s32 result) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x18(r1)
+ stw r31, 0x14(r1)
+ addi r31, r4, 0
+ stw r30, 0x10(r1)
+ addi r30, r3, 0
+ bl OSDisableInterrupts
+ lwz r0, 0(r30)
+ cmpwi r0, 0
+ beq lbl_803B8E8C
+ stw r31, 4(r30)
+ b lbl_803B8E9C
+lbl_803B8E8C:
+ lwz r0, 4(r30)
+ cmpwi r0, -1
+ bne lbl_803B8E9C
+ stw r31, 4(r30)
+lbl_803B8E9C:
+ bl OSRestoreInterrupts
+ mr r3, r31
+ lwz r0, 0x1c(r1)
+ lwz r31, 0x14(r1)
+ lwz r30, 0x10(r1)
+ addi r1, r1, 0x18
+ mtlr r0
+ blr
+}
+/* clang-format on */
+#pragma pop
+#endif
+
+s32 CARDGetResultCode(s32 chan) {
+ CARDControl* card;
+ if (chan < 0 || chan >= 2) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+ card = &__CARDBlock[chan];
+ return card->result;
+}
+
+#ifdef FULL_FRANK
+s32 CARDFreeBlocks(s32 chan, s32* byteNotUsed, s32* filesNotUsed) {
+ CARDControl* card;
+ s32 result;
+ u16* fat;
+ CARDDir* dir;
+ CARDDir* ent;
+ u16 fileNo;
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ fat = __CARDGetFatBlock(card);
+ dir = __CARDGetDirBlock(card);
+ if (fat == 0 || dir == 0) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+
+ if (byteNotUsed) {
+ *byteNotUsed = (s32)(card->sectorSize * fat[CARD_FAT_FREEBLOCKS]);
+ }
+
+ if (filesNotUsed) {
+ *filesNotUsed = 0;
+ for (fileNo = 0; fileNo < CARD_MAX_FILE; fileNo++) {
+ ent = &dir[fileNo];
+ if (ent->fileName[0] == 0xff) {
+ ++*filesNotUsed;
+ }
+ }
+ }
+
+ return __CARDPutControlBlock(card, CARD_RESULT_READY);
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm s32 CARDFreeBlocks(s32 chan, s32* byteNotUsed, s32* filesNotUsed) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x30(r1)
+ stw r31, 0x2c(r1)
+ addi r31, r5, 0
+ stw r30, 0x28(r1)
+ stw r29, 0x24(r1)
+ addi r29, r4, 0
+ addi r4, r1, 0x18
+ bl __CARDGetControlBlock
+ cmpwi r3, 0
+ bge lbl_803B8F20
+ b lbl_803B9020
+lbl_803B8F20:
+ lwz r3, 0x18(r1)
+ bl __CARDGetFatBlock
+ mr r30, r3
+ lwz r3, 0x18(r1)
+ bl __CARDGetDirBlock
+ cmplwi r30, 0
+ beq lbl_803B8F44
+ cmplwi r3, 0
+ bne lbl_803B8F84
+lbl_803B8F44:
+ lwz r30, 0x18(r1)
+ bl OSDisableInterrupts
+ lwz r0, 0(r30)
+ cmpwi r0, 0
+ beq lbl_803B8F64
+ li r0, -6
+ stw r0, 4(r30)
+ b lbl_803B8F78
+lbl_803B8F64:
+ lwz r0, 4(r30)
+ cmpwi r0, -1
+ bne lbl_803B8F78
+ li r0, -6
+ stw r0, 4(r30)
+lbl_803B8F78:
+ bl OSRestoreInterrupts
+ li r3, -6
+ b lbl_803B9020
+lbl_803B8F84:
+ cmplwi r29, 0
+ beq lbl_803B8FA0
+ lwz r4, 0x18(r1)
+ lhz r0, 6(r30)
+ lwz r4, 0xc(r4)
+ mullw r0, r4, r0
+ stw r0, 0(r29)
+lbl_803B8FA0:
+ cmplwi r31, 0
+ beq lbl_803B8FE4
+ li r0, 0
+ stw r0, 0(r31)
+ li r5, 0
+ b lbl_803B8FD8
+lbl_803B8FB8:
+ lbz r0, 8(r3)
+ cmplwi r0, 0xff
+ bne lbl_803B8FD0
+ lwz r4, 0(r31)
+ addi r0, r4, 1
+ stw r0, 0(r31)
+lbl_803B8FD0:
+ addi r3, r3, 0x40
+ addi r5, r5, 1
+lbl_803B8FD8:
+ clrlwi r0, r5, 0x10
+ cmplwi r0, 0x7f
+ blt lbl_803B8FB8
+lbl_803B8FE4:
+ lwz r30, 0x18(r1)
+ bl OSDisableInterrupts
+ lwz r0, 0(r30)
+ cmpwi r0, 0
+ beq lbl_803B9004
+ li r0, 0
+ stw r0, 4(r30)
+ b lbl_803B9018
+lbl_803B9004:
+ lwz r0, 4(r30)
+ cmpwi r0, -1
+ bne lbl_803B9018
+ li r0, 0
+ stw r0, 4(r30)
+lbl_803B9018:
+ bl OSRestoreInterrupts
+ li r3, 0
+lbl_803B9020:
+ lwz r0, 0x34(r1)
+ lwz r31, 0x2c(r1)
+ lwz r30, 0x28(r1)
+ lwz r29, 0x24(r1)
+ addi r1, r1, 0x30
+ mtlr r0
+ blr
+}
+/* clang-format on */
+#pragma pop
+#endif
+
+static BOOL OnReset(BOOL f) {
+ if (!f) {
+ if (CARDUnmount(0) == CARD_RESULT_BUSY || CARDUnmount(1) == CARD_RESULT_BUSY) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
diff --git a/src/Dolphin/card/CARDBlock.c b/src/Dolphin/card/CARDBlock.c
new file mode 100644
index 0000000..b6305f1
--- /dev/null
+++ b/src/Dolphin/card/CARDBlock.c
@@ -0,0 +1,159 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+u16* __CARDGetFatBlock(CARDControl* card) { return card->currentFat; }
+
+static void WriteCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+ u16* fat;
+ u16* fatBack;
+
+ card = &__CARDBlock[chan];
+
+ if (result >= 0) {
+ fat = (u16*)((u8*)card->workArea + 0x6000);
+ fatBack = (u16*)((u8*)card->workArea + 0x8000);
+
+ if (card->currentFat == fat) {
+ card->currentFat = fatBack;
+ memcpy(fatBack, fat, 0x2000);
+ } else {
+ card->currentFat = fat;
+ memcpy(fat, fatBack, 0x2000);
+ }
+ }
+
+ if (card->apiCallback == NULL) {
+ __CARDPutControlBlock(card, result);
+ }
+
+ callback = card->eraseCallback;
+ if (callback) {
+ card->eraseCallback = NULL;
+ callback(chan, result);
+ }
+}
+
+static void EraseCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+ u32 temp[2]; /* this compiler sucks */
+ u16* fat;
+ u32 addr;
+
+ card = &__CARDBlock[chan];
+ if (result < 0) {
+ goto error;
+ }
+
+ fat = __CARDGetFatBlock(card);
+ addr = ((u32)fat - (u32)card->workArea) / CARD_SYSTEM_BLOCK_SIZE * card->sectorSize;
+ result = __CARDWrite(chan, addr, CARD_SYSTEM_BLOCK_SIZE, fat, WriteCallback);
+ if (result < 0) {
+ goto error;
+ }
+
+ return;
+
+error:
+ if (card->apiCallback == NULL) {
+ __CARDPutControlBlock(card, result);
+ }
+ callback = card->eraseCallback;
+ if (callback) {
+ card->eraseCallback = NULL;
+ callback(chan, result);
+ }
+}
+
+s32 __CARDAllocBlock(s32 chan, u32 cBlock, CARDCallback callback) {
+ CARDControl* card;
+ u16* fat;
+ u16 iBlock;
+ u16 startBlock;
+ u16 prevBlock;
+ u16 count;
+
+ card = &__CARDBlock[chan];
+ if (!card->attached) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ fat = __CARDGetFatBlock(card);
+ if (fat[3] < cBlock) {
+ return CARD_RESULT_INSSPACE;
+ }
+
+ fat[3] -= cBlock;
+ startBlock = 0xFFFF;
+ iBlock = fat[4];
+ count = 0;
+ while (0 < cBlock) {
+ if (card->cBlock - 5 < ++count) {
+ return CARD_RESULT_BROKEN;
+ }
+
+ iBlock++;
+ if (!CARDIsValidBlockNo(card, iBlock)) {
+ iBlock = 5;
+ }
+
+ if (fat[iBlock] == 0x0000u) {
+ if (startBlock == 0xFFFF) {
+ startBlock = iBlock;
+ } else {
+ fat[prevBlock] = iBlock;
+ }
+ prevBlock = iBlock;
+ fat[iBlock] = 0xFFFF;
+ --cBlock;
+ }
+ }
+ fat[4] = iBlock;
+ card->startBlock = startBlock;
+
+ return __CARDUpdateFatBlock(chan, fat, callback);
+}
+
+s32 __CARDFreeBlock(s32 chan, u16 nBlock, CARDCallback callback) {
+ CARDControl* card;
+ u16* fat;
+ u16 nextBlock;
+
+ card = card = &__CARDBlock[chan];
+ if (!card->attached) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ fat = __CARDGetFatBlock(card);
+ while (nBlock != 0xFFFF) {
+ if (!CARDIsValidBlockNo(card, nBlock)) {
+ return CARD_RESULT_BROKEN;
+ }
+
+ nextBlock = fat[nBlock];
+ fat[nBlock] = 0;
+ nBlock = nextBlock;
+ ++fat[3];
+ }
+
+ return __CARDUpdateFatBlock(chan, fat, callback);
+}
+
+s32 __CARDUpdateFatBlock(s32 chan, u16* fat, CARDCallback callback) {
+ CARDControl* card;
+
+ card = &__CARDBlock[chan];
+ ++fat[2];
+ __CARDCheckSum(fat + 2, 0x1FFC, fat, fat + 1);
+ DCStoreRange(fat, 0x2000);
+ card->eraseCallback = callback;
+
+ return __CARDEraseSector(chan, (((u32)fat - (u32)card->workArea) / 8192u) * card->sectorSize,
+ EraseCallback);
+}
diff --git a/src/Dolphin/card/CARDCheck.c b/src/Dolphin/card/CARDCheck.c
new file mode 100644
index 0000000..2253d55
--- /dev/null
+++ b/src/Dolphin/card/CARDCheck.c
@@ -0,0 +1,688 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+#include <dolphin/OSRtcPriv.h>
+
+#include "string.h"
+
+#define __CARDGetDirCheck(dir) ((CARDDirCheck*)&(dir)[CARD_MAX_FILE])
+
+void __CARDCheckSum(void* ptr, int length, u16* checksum, u16* checksumInv) {
+ u16* p;
+ int i;
+
+ length /= sizeof(u16);
+ *checksum = *checksumInv = 0;
+ for (i = 0, p = ptr; i < length; i++, p++) {
+ *checksum += *p;
+ *checksumInv += ~*p;
+ }
+ if (*checksum == 0xffff) {
+ *checksum = 0;
+ }
+ if (*checksumInv == 0xffff) {
+ *checksumInv = 0;
+ }
+}
+
+static s32 VerifyID(CARDControl* card) {
+ CARDID* id;
+ u16 checksum;
+ u16 checksumInv;
+ OSSramEx* sramEx;
+ OSTime rand;
+ int i;
+
+ id = card->workArea;
+
+ if (id->deviceID != 0 || id->size != card->size) {
+ return CARD_RESULT_BROKEN;
+ }
+
+ __CARDCheckSum(id, sizeof(CARDID) - sizeof(u32), &checksum, &checksumInv);
+ if (id->checkSum != checksum || id->checkSumInv != checksumInv) {
+ return CARD_RESULT_BROKEN;
+ }
+
+ rand = *(OSTime*)&id->serial[12];
+ sramEx = __OSLockSramEx();
+
+ for (i = 0; i < 12; i++) {
+ rand = (rand * 1103515245 + 12345) >> 16;
+ if (id->serial[i] != (u8)(sramEx->flashID[card - __CARDBlock][i] + rand)) {
+ __OSUnlockSramEx(FALSE);
+ return CARD_RESULT_BROKEN;
+ }
+ rand = ((rand * 1103515245 + 12345) >> 16) & 0x7FFF;
+ }
+
+ __OSUnlockSramEx(FALSE);
+ if (id->encode != __CARDGetFontEncode()) {
+ return CARD_RESULT_ENCODING;
+ }
+
+ return CARD_RESULT_READY;
+}
+
+#ifdef FULL_FRANK
+static s32 VerifyDir(CARDControl* card, int* outCurrent) {
+ CARDDir* dir[2];
+ CARDDirCheck* check[2];
+ u16 checkSum;
+ u16 checkSumInv;
+ int i;
+ int errors;
+ int current;
+
+ current = errors = 0;
+ for (i = 0; i < 2; i++) {
+ dir[i] = (CARDDir*)((u8*)card->workArea + (1 + i) * CARD_SYSTEM_BLOCK_SIZE);
+ check[i] = __CARDGetDirCheck(dir[i]);
+ __CARDCheckSum(dir[i], CARD_SYSTEM_BLOCK_SIZE - sizeof(u32), &checkSum, &checkSumInv);
+ if (check[i]->checkSum != checkSum || check[i]->checkSumInv != checkSumInv) {
+ ++errors;
+ current = i;
+ card->currentDir = 0;
+ }
+ }
+
+ if (0 == errors) {
+ if (card->currentDir == 0) {
+ if ((check[0]->checkCode - check[1]->checkCode) < 0) {
+ current = 0;
+ } else {
+ current = 1;
+ }
+ card->currentDir = dir[current];
+ memcpy(dir[current], dir[current ^ 1], CARD_SYSTEM_BLOCK_SIZE);
+ } else {
+ current = (card->currentDir == dir[0]) ? 0 : 1;
+ }
+ }
+ if (outCurrent) {
+ *outCurrent = current;
+ }
+ return errors;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level
+#pragma optimizewithasm off
+static asm s32 VerifyDir(CARDControl* card, int* outCurrent) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x38(r1)
+ stw r31, 0x34(r1)
+ addi r7, r1, 0x1c
+ addi r8, r1, 0x14
+ stw r30, 0x30(r1)
+ li r31, 0
+ li r30, 0
+ stw r29, 0x2c(r1)
+ addi r29, r4, 0
+ li r4, 0
+lbl_803BB038:
+ addi r0, r4, 1
+ lwz r5, 0x80(r3)
+ slwi r0, r0, 0xd
+ add r0, r5, r0
+ stw r0, 0(r7)
+ li r6, 0x1ffc
+ srawi r6, r6, 1
+ lwz r5, 0(r7)
+ addze. r6, r6
+ li r11, 0
+ addi r0, r5, 0x1fc0
+ stw r0, 0(r8)
+ li r10, 0
+ lwz r5, 0(r7)
+ ble lbl_803BB12C
+ rlwinm. r0, r6, 0x1d, 3, 0x1f
+ mtctr r0
+ beq lbl_803BB110
+lbl_803BB080:
+ lhz r9, 0(r5)
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 2(r5)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 4(r5)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 6(r5)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 8(r5)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 0xa(r5)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 0xc(r5)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 0xe(r5)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ add r11, r11, r0
+ addi r5, r5, 0x10
+ bdnz lbl_803BB080
+ andi. r6, r6, 7
+ beq lbl_803BB12C
+lbl_803BB110:
+ mtctr r6
+lbl_803BB114:
+ lhz r9, 0(r5)
+ addi r5, r5, 2
+ nor r0, r9, r9
+ add r10, r10, r9
+ add r11, r11, r0
+ bdnz lbl_803BB114
+lbl_803BB12C:
+ clrlwi r0, r10, 0x10
+ cmplwi r0, 0xffff
+ bne lbl_803BB13C
+ li r10, 0
+lbl_803BB13C:
+ clrlwi r0, r11, 0x10
+ cmplwi r0, 0xffff
+ bne lbl_803BB14C
+ li r11, 0
+lbl_803BB14C:
+ lwz r6, 0(r8)
+ clrlwi r5, r10, 0x10
+ lhz r0, 0x3c(r6)
+ cmplw r5, r0
+ bne lbl_803BB170
+ lhz r0, 0x3e(r6)
+ clrlwi r5, r11, 0x10
+ cmplw r5, r0
+ beq lbl_803BB180
+lbl_803BB170:
+ li r0, 0
+ stw r0, 0x84(r3)
+ addi r30, r4, 0
+ addi r31, r31, 1
+lbl_803BB180:
+ addi r4, r4, 1
+ cmpwi r4, 2
+ addi r7, r7, 4
+ addi r8, r8, 4
+ blt lbl_803BB038
+ cmpwi r31, 0
+ bne lbl_803BB21C
+ lwz r4, 0x84(r3)
+ cmplwi r4, 0
+ bne lbl_803BB200
+ lwz r5, 0x18(r1)
+ lwz r4, 0x14(r1)
+ lha r5, 0x3a(r5)
+ lha r0, 0x3a(r4)
+ subf. r0, r5, r0
+ bge lbl_803BB1C8
+ li r30, 0
+ b lbl_803BB1CC
+lbl_803BB1C8:
+ li r30, 1
+lbl_803BB1CC:
+ slwi r0, r30, 2
+ addi r6, r1, 0x1c
+ add r6, r6, r0
+ lwz r4, 0(r6)
+ xori r0, r30, 1
+ slwi r0, r0, 2
+ stw r4, 0x84(r3)
+ addi r4, r1, 0x1c
+ li r5, 0x2000
+ lwz r3, 0(r6)
+ lwzx r4, r4, r0
+ bl memcpy
+ b lbl_803BB21C
+lbl_803BB200:
+ lwz r0, 0x1c(r1)
+ cmplw r4, r0
+ bne lbl_803BB214
+ li r0, 0
+ b lbl_803BB218
+lbl_803BB214:
+ li r0, 1
+lbl_803BB218:
+ mr r30, r0
+lbl_803BB21C:
+ cmplwi r29, 0
+ beq lbl_803BB228
+ stw r30, 0(r29)
+lbl_803BB228:
+ mr r3, r31
+ lwz r0, 0x3c(r1)
+ lwz r31, 0x34(r1)
+ lwz r30, 0x30(r1)
+ lwz r29, 0x2c(r1)
+ addi r1, r1, 0x38
+ mtlr r0
+ blr
+}
+/* clang-format on */
+#pragma pop
+#endif
+
+#ifdef FULL_FRANK
+static s32 VerifyFAT(CARDControl* card, int* outCurrent) {
+ u16* fat[2];
+ u16* fatp;
+ u16 nBlock;
+ u16 cFree;
+ int i;
+ u16 checkSum;
+ u16 checkSumInv;
+ int errors;
+ int current;
+
+ current = errors = 0;
+ for (i = 0; i < 2; i++) {
+ fatp = fat[i] = (u16*)((u8*)card->workArea + (3 + i) * CARD_SYSTEM_BLOCK_SIZE);
+
+ __CARDCheckSum(&fatp[CARD_FAT_CHECKCODE], CARD_SYSTEM_BLOCK_SIZE - sizeof(u32), &checkSum,
+ &checkSumInv);
+ if (fatp[CARD_FAT_CHECKSUM] != checkSum || fatp[CARD_FAT_CHECKSUMINV] != checkSumInv) {
+ ++errors;
+ current = i;
+ card->currentFat = 0;
+ continue;
+ }
+
+ cFree = 0;
+ for (nBlock = CARD_NUM_SYSTEM_BLOCK; nBlock < card->cBlock; nBlock++) {
+ if (fatp[nBlock] == CARD_FAT_AVAIL) {
+ cFree++;
+ }
+ }
+ if (cFree != fatp[CARD_FAT_FREEBLOCKS]) {
+ ++errors;
+ current = i;
+ card->currentFat = 0;
+ continue;
+ }
+ }
+
+ if (0 == errors) {
+ if (card->currentFat == 0) {
+ if (((s16)fat[0][CARD_FAT_CHECKCODE] - (s16)fat[1][CARD_FAT_CHECKCODE]) < 0) {
+ current = 0;
+ } else {
+ current = 1;
+ }
+ card->currentFat = fat[current];
+ memcpy(fat[current], fat[current ^ 1], CARD_SYSTEM_BLOCK_SIZE);
+ } else {
+ current = (card->currentFat == fat[0]) ? 0 : 1;
+ }
+ }
+ if (outCurrent) {
+ *outCurrent = current;
+ }
+ return errors;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level
+#pragma optimizewithasm off
+
+static asm s32 VerifyFAT(CARDControl* card, int* outCurrent) {
+ nofralloc
+ mflr r0
+ li r5, 0
+ stw r0, 4(r1)
+ stwu r1, -0x28(r1)
+ stw r31, 0x24(r1)
+ li r31, 0
+ stw r30, 0x20(r1)
+ li r30, 0
+ stw r29, 0x1c(r1)
+ addi r29, r4, 0
+ addi r4, r1, 0x10
+lbl_803BB274:
+ li r8, 0x1ffc
+ lwz r6, 0x80(r3)
+ addi r0, r5, 3
+ srawi r8, r8, 1
+ slwi r0, r0, 0xd
+ add r7, r6, r0
+ addze. r8, r8
+ stw r7, 0(r4)
+ addi r6, r7, 4
+ li r11, 0
+ li r10, 0
+ ble lbl_803BB35C
+ rlwinm. r0, r8, 0x1d, 3, 0x1f
+ mtctr r0
+ beq lbl_803BB340
+lbl_803BB2B0:
+ lhz r9, 0(r6)
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 2(r6)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 4(r6)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 6(r6)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 8(r6)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 0xa(r6)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 0xc(r6)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 0xe(r6)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ add r11, r11, r0
+ addi r6, r6, 0x10
+ bdnz lbl_803BB2B0
+ andi. r8, r8, 7
+ beq lbl_803BB35C
+lbl_803BB340:
+ mtctr r8
+lbl_803BB344:
+ lhz r9, 0(r6)
+ addi r6, r6, 2
+ nor r0, r9, r9
+ add r10, r10, r9
+ add r11, r11, r0
+ bdnz lbl_803BB344
+lbl_803BB35C:
+ clrlwi r0, r10, 0x10
+ cmplwi r0, 0xffff
+ bne lbl_803BB36C
+ li r10, 0
+lbl_803BB36C:
+ clrlwi r0, r11, 0x10
+ cmplwi r0, 0xffff
+ bne lbl_803BB37C
+ li r11, 0
+lbl_803BB37C:
+ lhz r6, 0(r7)
+ clrlwi r0, r10, 0x10
+ cmplw r6, r0
+ bne lbl_803BB39C
+ lhz r6, 2(r7)
+ clrlwi r0, r11, 0x10
+ cmplw r6, r0
+ beq lbl_803BB3B0
+lbl_803BB39C:
+ li r0, 0
+ stw r0, 0x88(r3)
+ addi r30, r5, 0
+ addi r31, r31, 1
+ b lbl_803BB408
+lbl_803BB3B0:
+ lhz r8, 0x10(r3)
+ addi r6, r7, 0xa
+ li r10, 0
+ li r9, 5
+ b lbl_803BB3DC
+lbl_803BB3C4:
+ lhz r0, 0(r6)
+ cmplwi r0, 0
+ bne lbl_803BB3D4
+ addi r10, r10, 1
+lbl_803BB3D4:
+ addi r6, r6, 2
+ addi r9, r9, 1
+lbl_803BB3DC:
+ clrlwi r0, r9, 0x10
+ cmplw r0, r8
+ blt lbl_803BB3C4
+ lhz r0, 6(r7)
+ clrlwi r6, r10, 0x10
+ cmplw r6, r0
+ beq lbl_803BB408
+ li r0, 0
+ stw r0, 0x88(r3)
+ addi r30, r5, 0
+ addi r31, r31, 1
+lbl_803BB408:
+ addi r5, r5, 1
+ cmpwi r5, 2
+ addi r4, r4, 4
+ blt lbl_803BB274
+ cmpwi r31, 0
+ bne lbl_803BB4A0
+ lwz r4, 0x88(r3)
+ cmplwi r4, 0
+ bne lbl_803BB484
+ lwz r5, 0x14(r1)
+ lwz r4, 0x10(r1)
+ lha r5, 4(r5)
+ lha r0, 4(r4)
+ subf. r0, r5, r0
+ bge lbl_803BB44C
+ li r30, 0
+ b lbl_803BB450
+lbl_803BB44C:
+ li r30, 1
+lbl_803BB450:
+ slwi r0, r30, 2
+ addi r6, r1, 0x10
+ add r6, r6, r0
+ lwz r4, 0(r6)
+ xori r0, r30, 1
+ slwi r0, r0, 2
+ stw r4, 0x88(r3)
+ addi r4, r1, 0x10
+ li r5, 0x2000
+ lwz r3, 0(r6)
+ lwzx r4, r4, r0
+ bl memcpy
+ b lbl_803BB4A0
+lbl_803BB484:
+ lwz r0, 0x10(r1)
+ cmplw r4, r0
+ bne lbl_803BB498
+ li r0, 0
+ b lbl_803BB49C
+lbl_803BB498:
+ li r0, 1
+lbl_803BB49C:
+ mr r30, r0
+lbl_803BB4A0:
+ cmplwi r29, 0
+ beq lbl_803BB4AC
+ stw r30, 0(r29)
+lbl_803BB4AC:
+ mr r3, r31
+ lwz r0, 0x2c(r1)
+ lwz r31, 0x24(r1)
+ lwz r30, 0x20(r1)
+ lwz r29, 0x1c(r1)
+ addi r1, r1, 0x28
+ mtlr r0
+ blr
+}
+
+/* clang-format on */
+#pragma pop
+#endif
+
+s32 __CARDVerify(CARDControl* card) {
+ s32 result;
+ int errors;
+
+ result = VerifyID(card);
+ if (result < 0) {
+ return result;
+ }
+
+ errors = VerifyDir(card, NULL);
+ errors += VerifyFAT(card, NULL);
+ switch (errors) {
+ case 0:
+ return CARD_RESULT_READY;
+ case 1:
+ return CARD_RESULT_BROKEN;
+ default:
+ return CARD_RESULT_BROKEN;
+ }
+}
+
+s32 CARDCheckExAsync(s32 chan, s32* xferBytes, CARDCallback callback) {
+ CARDControl* card;
+ CARDDir* dir[2];
+ u16* fat[2];
+ u16* map;
+ s32 result;
+ int errors;
+ int currentFat;
+ int currentDir;
+ s32 fileNo;
+ u16 iBlock;
+ u16 cBlock;
+ u16 cFree;
+ BOOL updateFat = FALSE;
+ BOOL updateDir = FALSE;
+ BOOL updateOrphan = FALSE;
+
+ if (xferBytes) {
+ *xferBytes = 0;
+ }
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ result = VerifyID(card);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+
+ errors = VerifyDir(card, &currentDir);
+ errors += VerifyFAT(card, &currentFat);
+ if (1 < errors) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+
+ dir[0] = (CARDDir*)((u8*)card->workArea + (1 + 0) * CARD_SYSTEM_BLOCK_SIZE);
+ dir[1] = (CARDDir*)((u8*)card->workArea + (1 + 1) * CARD_SYSTEM_BLOCK_SIZE);
+ fat[0] = (u16*)((u8*)card->workArea + (3 + 0) * CARD_SYSTEM_BLOCK_SIZE);
+ fat[1] = (u16*)((u8*)card->workArea + (3 + 1) * CARD_SYSTEM_BLOCK_SIZE);
+
+ switch (errors) {
+ case 0:
+ break;
+ case 1:
+ if (!card->currentDir) {
+ card->currentDir = dir[currentDir];
+ memcpy(dir[currentDir], dir[currentDir ^ 1], CARD_SYSTEM_BLOCK_SIZE);
+ updateDir = TRUE;
+ } else {
+ card->currentFat = fat[currentFat];
+ memcpy(fat[currentFat], fat[currentFat ^ 1], CARD_SYSTEM_BLOCK_SIZE);
+ updateFat = TRUE;
+ }
+ break;
+ }
+
+ map = fat[currentFat ^ 1];
+ memset(map, 0, CARD_SYSTEM_BLOCK_SIZE);
+
+ for (fileNo = 0; fileNo < CARD_MAX_FILE; fileNo++) {
+ CARDDir* ent;
+
+ ent = &card->currentDir[fileNo];
+ if (ent->gameName[0] == 0xff) {
+ continue;
+ }
+
+ for (iBlock = ent->startBlock, cBlock = 0; iBlock != 0xFFFF && cBlock < ent->length;
+ iBlock = card->currentFat[iBlock], ++cBlock) {
+ if (!CARDIsValidBlockNo(card, iBlock) || 1 < ++map[iBlock]) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+ }
+ if (cBlock != ent->length || iBlock != 0xFFFF) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+ }
+
+ cFree = 0;
+ for (iBlock = CARD_NUM_SYSTEM_BLOCK; iBlock < card->cBlock; iBlock++) {
+ u16 nextBlock;
+
+ nextBlock = card->currentFat[iBlock];
+ if (map[iBlock] == 0) {
+ if (nextBlock != CARD_FAT_AVAIL) {
+ card->currentFat[iBlock] = CARD_FAT_AVAIL;
+ updateOrphan = TRUE;
+ }
+ cFree++;
+ } else if (!CARDIsValidBlockNo(card, nextBlock) && nextBlock != 0xFFFF) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+ }
+ if (cFree != card->currentFat[CARD_FAT_FREEBLOCKS]) {
+ card->currentFat[CARD_FAT_FREEBLOCKS] = cFree;
+ updateOrphan = TRUE;
+ }
+ if (updateOrphan) {
+ __CARDCheckSum(&card->currentFat[CARD_FAT_CHECKCODE], CARD_SYSTEM_BLOCK_SIZE - sizeof(u32),
+ &card->currentFat[CARD_FAT_CHECKSUM], &card->currentFat[CARD_FAT_CHECKSUMINV]);
+ }
+
+ memcpy(fat[currentFat ^ 1], fat[currentFat], CARD_SYSTEM_BLOCK_SIZE);
+
+ if (updateDir) {
+ if (xferBytes) {
+ *xferBytes = CARD_SYSTEM_BLOCK_SIZE;
+ }
+ return __CARDUpdateDir(chan, callback);
+ }
+
+ if (updateFat | updateOrphan) {
+ if (xferBytes) {
+ *xferBytes = CARD_SYSTEM_BLOCK_SIZE;
+ }
+ return __CARDUpdateFatBlock(chan, card->currentFat, callback);
+ }
+
+ __CARDPutControlBlock(card, CARD_RESULT_READY);
+ if (callback) {
+ BOOL enabled = OSDisableInterrupts();
+ callback(chan, CARD_RESULT_READY);
+ OSRestoreInterrupts(enabled);
+ }
+ return CARD_RESULT_READY;
+}
+
+s32 CARDCheckAsync(s32 chan, CARDCallback callback) {
+ s32 xferBytes;
+
+ return CARDCheckExAsync(chan, &xferBytes, callback);
+}
diff --git a/src/Dolphin/card/CARDCreate.c b/src/Dolphin/card/CARDCreate.c
new file mode 100644
index 0000000..6802b91
--- /dev/null
+++ b/src/Dolphin/card/CARDCreate.c
@@ -0,0 +1,115 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+static void CreateCallbackFat(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ callback = card->apiCallback;
+ card->apiCallback = 0;
+ if (result < 0) {
+ goto error;
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[card->freeNo];
+ memcpy(ent->gameName, card->diskID->gameName, sizeof(ent->gameName));
+ memcpy(ent->company, card->diskID->company, sizeof(ent->company));
+ ent->permission = CARD_ATTR_PUBLIC;
+ ent->copyTimes = 0;
+ ent->startBlock = card->startBlock;
+
+ ent->bannerFormat = 0;
+ ent->iconAddr = 0xffffffff;
+ ent->iconFormat = 0;
+ ent->iconSpeed = 0;
+ ent->commentAddr = 0xffffffff;
+
+ CARDSetIconSpeed(ent, 0, CARD_STAT_SPEED_FAST);
+
+ card->fileInfo->offset = 0;
+ card->fileInfo->iBlock = ent->startBlock;
+
+ ent->time = (u32)OSTicksToSeconds(OSGetTime());
+ result = __CARDUpdateDir(chan, callback);
+ if (result < 0) {
+ goto error;
+ }
+ return;
+
+error:
+ __CARDPutControlBlock(card, result);
+ if (callback) {
+ callback(chan, result);
+ }
+}
+
+s32 CARDCreateAsync(s32 chan, const char* fileName, u32 size, CARDFileInfo* fileInfo,
+ CARDCallback callback) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+ u16 fileNo;
+ u16 freeNo;
+ u16* fat;
+
+ if (strlen(fileName) > (u32)CARD_FILENAME_MAX) {
+ return CARD_RESULT_NAMETOOLONG;
+ }
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ if (size <= 0 || (size % card->sectorSize) != 0) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+
+ freeNo = (u16)-1;
+ dir = __CARDGetDirBlock(card);
+ for (fileNo = 0; fileNo < CARD_MAX_FILE; fileNo++) {
+ ent = &dir[fileNo];
+ if (ent->gameName[0] == 0xff) {
+ if (freeNo == (u16)-1) {
+ freeNo = fileNo;
+ }
+ } else if (memcmp(ent->gameName, card->diskID->gameName, sizeof(ent->gameName)) == 0 &&
+ memcmp(ent->company, card->diskID->company, sizeof(ent->company)) == 0 &&
+ __CARDCompareFileName(ent, fileName)) {
+ return __CARDPutControlBlock(card, CARD_RESULT_EXIST);
+ }
+ }
+ if (freeNo == (u16)-1) {
+ return __CARDPutControlBlock(card, CARD_RESULT_NOENT);
+ }
+
+ fat = __CARDGetFatBlock(card);
+ if (card->sectorSize * fat[CARD_FAT_FREEBLOCKS] < size) {
+ return __CARDPutControlBlock(card, CARD_RESULT_INSSPACE);
+ }
+
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+ card->freeNo = freeNo;
+ ent = &dir[freeNo];
+ ent->length = (u16)(size / card->sectorSize);
+ strncpy(ent->fileName, fileName, CARD_FILENAME_MAX);
+
+ card->fileInfo = fileInfo;
+ fileInfo->chan = chan;
+ fileInfo->fileNo = freeNo;
+
+ result = __CARDAllocBlock(chan, size / card->sectorSize, CreateCallbackFat);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+ return result;
+}
diff --git a/src/Dolphin/card/CARDDelete.c b/src/Dolphin/card/CARDDelete.c
new file mode 100644
index 0000000..f715751
--- /dev/null
+++ b/src/Dolphin/card/CARDDelete.c
@@ -0,0 +1,97 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+static void DeleteCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ callback = card->apiCallback;
+ card->apiCallback = 0;
+
+ if (result < 0) {
+ goto error;
+ }
+
+ result = __CARDFreeBlock(chan, card->startBlock, callback);
+ if (result < 0) {
+ goto error;
+ }
+ return;
+
+error:
+ __CARDPutControlBlock(card, result);
+ if (callback) {
+ callback(chan, result);
+ }
+}
+
+s32 CARDFastDeleteAsync(s32 chan, s32 fileNo, CARDCallback callback) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+
+ if (fileNo < 0 || CARD_MAX_FILE <= fileNo) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileNo];
+ result = __CARDAccess(card, ent);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+ if (__CARDIsOpened(card, fileNo)) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BUSY);
+ }
+ card->startBlock = ent->startBlock;
+ memset(ent, 0xff, sizeof(CARDDir));
+
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+ result = __CARDUpdateDir(chan, DeleteCallback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ return result;
+}
+
+s32 CARDDeleteAsync(s32 chan, const char* fileName, CARDCallback callback) {
+ CARDControl* card;
+ s32 fileNo;
+ s32 result;
+ CARDDir* dir;
+ CARDDir* ent;
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+ result = __CARDGetFileNo(card, fileName, &fileNo);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+ if (__CARDIsOpened(card, fileNo)) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BUSY);
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileNo];
+ card->startBlock = ent->startBlock;
+ memset(ent, 0xff, sizeof(CARDDir));
+
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+ result = __CARDUpdateDir(chan, DeleteCallback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ return result;
+}
diff --git a/src/Dolphin/card/CARDDir.c b/src/Dolphin/card/CARDDir.c
new file mode 100644
index 0000000..1daecf8
--- /dev/null
+++ b/src/Dolphin/card/CARDDir.c
@@ -0,0 +1,92 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+CARDDir* __CARDGetDirBlock(CARDControl* card) { return card->currentDir; }
+
+static void WriteCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ if (0 <= result) {
+ CARDDir* dir0 = (CARDDir*)((u8*)card->workArea + 0x2000);
+ CARDDir* dir1 = (CARDDir*)((u8*)card->workArea + 0x4000);
+
+ if (card->currentDir == dir0) {
+ card->currentDir = dir1;
+ memcpy(dir1, dir0, 0x2000);
+ } else {
+ card->currentDir = dir0;
+ memcpy(dir0, dir1, 0x2000);
+ }
+ }
+
+error:
+ if (card->apiCallback == 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ callback = card->eraseCallback;
+ if (callback) {
+ card->eraseCallback = 0;
+ callback(chan, result);
+ }
+}
+
+static void EraseCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+ CARDDir* dir;
+ u32 tmp[2];
+ u32 addr;
+
+ card = &__CARDBlock[chan];
+ if (result < 0) {
+ goto error;
+ }
+
+ dir = __CARDGetDirBlock(card);
+ addr = ((u32)dir - (u32)card->workArea) / 0x2000 * card->sectorSize;
+ result = __CARDWrite(chan, addr, 0x2000, dir, WriteCallback);
+ if (result < 0) {
+ goto error;
+ }
+
+ return;
+
+error:
+ if (card->apiCallback == 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ callback = card->eraseCallback;
+ if (callback) {
+ card->eraseCallback = 0;
+ callback(chan, result);
+ }
+}
+
+s32 __CARDUpdateDir(s32 chan, CARDCallback callback) {
+ CARDControl* card;
+ CARDDirCheck* check;
+ u32 tmp[2];
+ u32 addr;
+ CARDDir* dir;
+
+ card = &__CARDBlock[chan];
+ if (!card->attached) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ dir = __CARDGetDirBlock(card);
+ check = __CARDGetDirCheck(dir);
+ ++check->checkCode;
+ __CARDCheckSum(dir, 0x2000 - sizeof(u32), &check->checkSum, &check->checkSumInv);
+ DCStoreRange(dir, 0x2000);
+
+ card->eraseCallback = callback;
+ addr = ((u32)dir - (u32)card->workArea) / 0x2000 * card->sectorSize;
+ return __CARDEraseSector(chan, addr, EraseCallback);
+}
diff --git a/src/Dolphin/card/CARDFormat.c b/src/Dolphin/card/CARDFormat.c
new file mode 100644
index 0000000..ebfff8b
--- /dev/null
+++ b/src/Dolphin/card/CARDFormat.c
@@ -0,0 +1,127 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+#include <dolphin/OSRtcPriv.h>
+#include <dolphin/vi.h>
+
+static void FormatCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ if (result < 0) {
+ goto error;
+ }
+
+ ++card->formatStep;
+ if (card->formatStep < CARD_NUM_SYSTEM_BLOCK) {
+ result = __CARDEraseSector(chan, (u32)card->sectorSize * card->formatStep, FormatCallback);
+ if (0 <= result) {
+ return;
+ }
+ } else if (card->formatStep < 2 * CARD_NUM_SYSTEM_BLOCK) {
+ int step = card->formatStep - CARD_NUM_SYSTEM_BLOCK;
+ result = __CARDWrite(chan, (u32)card->sectorSize * step, CARD_SYSTEM_BLOCK_SIZE,
+ (u8*)card->workArea + (CARD_SYSTEM_BLOCK_SIZE * step), FormatCallback);
+ if (result >= 0) {
+ return;
+ }
+ } else {
+ card->currentDir = (CARDDir*)((u8*)card->workArea + (1 + 0) * CARD_SYSTEM_BLOCK_SIZE);
+ memcpy(card->currentDir, (u8*)card->workArea + (1 + 1) * CARD_SYSTEM_BLOCK_SIZE,
+ CARD_SYSTEM_BLOCK_SIZE);
+ card->currentFat = (u16*)((u8*)card->workArea + (3 + 0) * CARD_SYSTEM_BLOCK_SIZE);
+ memcpy(card->currentFat, (u8*)card->workArea + (3 + 1) * CARD_SYSTEM_BLOCK_SIZE,
+ CARD_SYSTEM_BLOCK_SIZE);
+ }
+
+error:
+ callback = card->apiCallback;
+ card->apiCallback = 0;
+ __CARDPutControlBlock(card, result);
+ callback(chan, result);
+}
+
+s32 __CARDFormatRegionAsync(s32 chan, u16 encode, CARDCallback callback) {
+ CARDControl* card;
+ CARDID* id;
+ CARDDir* dir;
+ u16* fat;
+ s16 i;
+ s32 result;
+ OSSram* sram;
+ OSSramEx* sramEx;
+ u16 viDTVStatus;
+ OSTime time;
+ OSTime rand;
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ id = (CARDID*)card->workArea;
+ memset(id, 0xff, CARD_SYSTEM_BLOCK_SIZE);
+ viDTVStatus = __VIRegs[55];
+
+ id->encode = encode;
+
+ sram = __OSLockSram();
+ *(u32*)&id->serial[20] = sram->counterBias;
+ *(u32*)&id->serial[24] = sram->language;
+ __OSUnlockSram(FALSE);
+
+ rand = time = OSGetTime();
+
+ sramEx = __OSLockSramEx();
+ for (i = 0; i < 12; i++) {
+ rand = (rand * 1103515245 + 12345) >> 16;
+ id->serial[i] = (u8)(sramEx->flashID[chan][i] + rand);
+ rand = ((rand * 1103515245 + 12345) >> 16) & 0x7FFF;
+ }
+ __OSUnlockSramEx(FALSE);
+
+ *(u32*)&id->serial[28] = viDTVStatus;
+ *(OSTime*)&id->serial[12] = time;
+
+ id->deviceID = 0;
+ id->size = card->size;
+ __CARDCheckSum(id, sizeof(CARDID) - sizeof(u32), &id->checkSum, &id->checkSumInv);
+
+ for (i = 0; i < 2; i++) {
+ CARDDirCheck* check;
+
+ dir = (CARDDir*)((u8*)card->workArea + (1 + i) * CARD_SYSTEM_BLOCK_SIZE);
+ memset(dir, 0xff, CARD_SYSTEM_BLOCK_SIZE);
+ check = __CARDGetDirCheck(dir);
+ check->checkCode = i;
+ __CARDCheckSum(dir, CARD_SYSTEM_BLOCK_SIZE - sizeof(u32), &check->checkSum,
+ &check->checkSumInv);
+ }
+ for (i = 0; i < 2; i++) {
+ fat = (u16*)((u8*)card->workArea + (3 + i) * CARD_SYSTEM_BLOCK_SIZE);
+ memset(fat, 0x00, CARD_SYSTEM_BLOCK_SIZE);
+ fat[CARD_FAT_CHECKCODE] = (u16)i;
+ fat[CARD_FAT_FREEBLOCKS] = (u16)(card->cBlock - CARD_NUM_SYSTEM_BLOCK);
+ fat[CARD_FAT_LASTSLOT] = CARD_NUM_SYSTEM_BLOCK - 1;
+ __CARDCheckSum(&fat[CARD_FAT_CHECKCODE], CARD_SYSTEM_BLOCK_SIZE - sizeof(u32),
+ &fat[CARD_FAT_CHECKSUM], &fat[CARD_FAT_CHECKSUMINV]);
+ }
+
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+ DCStoreRange(card->workArea, CARD_WORKAREA_SIZE);
+
+ card->formatStep = 0;
+ result = __CARDEraseSector(chan, (u32)card->sectorSize * card->formatStep, FormatCallback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ return result;
+}
+
+s32 CARDFormatAsync(s32 chan, CARDCallback callback) {
+ return __CARDFormatRegionAsync(chan, __CARDGetFontEncode(), callback);
+}
diff --git a/src/Dolphin/card/CARDMount.c b/src/Dolphin/card/CARDMount.c
new file mode 100644
index 0000000..fd361e9
--- /dev/null
+++ b/src/Dolphin/card/CARDMount.c
@@ -0,0 +1,354 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+#include <dolphin/OSRtcPriv.h>
+
+u8 GameChoice : (OS_BASE_CACHED | 0x000030E3);
+
+static u32 SectorSizeTable[8] = {
+ 8 * 1024, 16 * 1024, 32 * 1024, 64 * 1024, 128 * 1024, 256 * 1024, 0, 0,
+};
+
+static u32 LatencyTable[8] = {
+ 4, 8, 16, 32, 64, 128, 256, 512,
+};
+
+void __CARDMountCallback(s32 chan, s32 result);
+static void DoUnmount(s32 chan, s32 result);
+
+static BOOL IsCard(u32 id) {
+ u32 size;
+ s32 sectorSize;
+ if (id & (0xFFFF0000) && (id != 0x80000004 || __CARDVendorID == 0xFFFF)) {
+ return FALSE;
+ }
+
+ if ((id & 3) != 0) {
+ return FALSE;
+ }
+
+ size = id & 0xfc;
+ switch (size) {
+ case 4:
+ case 8:
+ case 16:
+ case 32:
+ case 64:
+ case 128:
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+
+ sectorSize = SectorSizeTable[(id & 0x00003800) >> 11];
+ if (sectorSize == 0) {
+ return FALSE;
+ }
+
+ if ((size * 1024 * 1024 / 8) / sectorSize < 8) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+s32 CARDProbeEx(s32 chan, s32* memSize, s32* sectorSize) {
+ u32 id;
+ CARDControl* card;
+ BOOL enabled;
+ s32 result;
+ int probe;
+
+ if (chan < 0 || 2 <= chan) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+
+ if (GameChoice & 0x80) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ card = &__CARDBlock[chan];
+ enabled = OSDisableInterrupts();
+
+ probe = EXIProbeEx(chan);
+ if (probe == -1) {
+ result = CARD_RESULT_NOCARD;
+ } else if (probe == 0) {
+ result = CARD_RESULT_BUSY;
+ } else if (card->attached) {
+ if (card->mountStep < 1) {
+ result = CARD_RESULT_BUSY;
+ } else {
+ if (memSize) {
+ *memSize = card->size;
+ }
+ if (sectorSize) {
+ *sectorSize = card->sectorSize;
+ }
+ result = CARD_RESULT_READY;
+ }
+ } else if ((EXIGetState(chan) & 8)) {
+ result = CARD_RESULT_WRONGDEVICE;
+ } else if (!EXIGetID(chan, 0, &id)) {
+ result = CARD_RESULT_BUSY;
+ } else if (IsCard(id)) {
+ if (memSize) {
+ *memSize = (s32)(id & 0xfc);
+ }
+ if (sectorSize) {
+ *sectorSize = SectorSizeTable[(id & 0x00003800) >> 11];
+ }
+ result = CARD_RESULT_READY;
+ } else {
+ result = CARD_RESULT_WRONGDEVICE;
+ }
+
+ OSRestoreInterrupts(enabled);
+ return result;
+}
+
+static s32 DoMount(s32 chan) {
+ CARDControl* card;
+ u32 id;
+ u8 status;
+ s32 result;
+ OSSramEx* sram;
+ int i;
+ u8 checkSum;
+ int step;
+
+ card = &__CARDBlock[chan];
+
+ if (card->mountStep == 0) {
+ if (EXIGetID(chan, 0, &id) == 0) {
+ result = CARD_RESULT_NOCARD;
+ } else if (IsCard(id)) {
+ result = CARD_RESULT_READY;
+ } else {
+ result = CARD_RESULT_WRONGDEVICE;
+ }
+ if (result < 0) {
+ goto error;
+ }
+
+ card->cid = id;
+
+ card->size = (u16)(id & 0xFC);
+ card->sectorSize = SectorSizeTable[(id & 0x00003800) >> 11];
+ card->cBlock = (u16)((card->size * 1024 * 1024 / 8) / card->sectorSize);
+ card->latency = LatencyTable[(id & 0x00000700) >> 8];
+
+ result = __CARDClearStatus(chan);
+ if (result < 0) {
+ goto error;
+ }
+ result = __CARDReadStatus(chan, &status);
+ if (result < 0) {
+ goto error;
+ }
+
+ if (!EXIProbe(chan)) {
+ result = CARD_RESULT_NOCARD;
+ goto error;
+ }
+
+ if (!(status & 0x40)) {
+ result = __CARDUnlock(chan, card->id);
+ if (result < 0) {
+ goto error;
+ }
+
+ checkSum = 0;
+ sram = __OSLockSramEx();
+ for (i = 0; i < 12; i++) {
+ sram->flashID[chan][i] = card->id[i];
+ checkSum += card->id[i];
+ }
+ sram->flashIDCheckSum[chan] = (u8)~checkSum;
+ __OSUnlockSramEx(TRUE);
+
+ return result;
+ } else {
+ card->mountStep = 1;
+
+ checkSum = 0;
+ sram = __OSLockSramEx();
+ for (i = 0; i < 12; i++) {
+ checkSum += sram->flashID[chan][i];
+ }
+ __OSUnlockSramEx(FALSE);
+ if (sram->flashIDCheckSum[chan] != (u8)~checkSum) {
+ result = CARD_RESULT_IOERROR;
+ goto error;
+ }
+ }
+ }
+
+ if (card->mountStep == 1) {
+ if (card->cid == 0x80000004) {
+ u16 vendorID;
+
+ sram = __OSLockSramEx();
+ vendorID = *(u16*)sram->flashID[chan];
+ __OSUnlockSramEx(FALSE);
+
+ if (__CARDVendorID == 0xffff || vendorID != __CARDVendorID) {
+ result = CARD_RESULT_WRONGDEVICE;
+ goto error;
+ }
+ }
+
+ card->mountStep = 2;
+
+ result = __CARDEnableInterrupt(chan, TRUE);
+ if (result < 0) {
+ goto error;
+ }
+
+ EXISetExiCallback(chan, __CARDExiHandler);
+ EXIUnlock(chan);
+ DCInvalidateRange(card->workArea, CARD_WORKAREA_SIZE);
+ }
+
+ step = card->mountStep - 2;
+ result = __CARDRead(chan, (u32)card->sectorSize * step, CARD_SYSTEM_BLOCK_SIZE,
+ (u8*)card->workArea + (CARD_SYSTEM_BLOCK_SIZE * step), __CARDMountCallback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ return result;
+
+error:
+ EXIUnlock(chan);
+ DoUnmount(chan, result);
+ return result;
+}
+
+void __CARDMountCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+
+ switch (result) {
+ case CARD_RESULT_READY:
+ if (++card->mountStep < CARD_MAX_MOUNT_STEP) {
+ result = DoMount(chan);
+ if (0 <= result) {
+ return;
+ }
+ } else {
+ result = __CARDVerify(card);
+ }
+ break;
+ case CARD_RESULT_UNLOCKED:
+ card->unlockCallback = __CARDMountCallback;
+ if (!EXILock(chan, 0, __CARDUnlockedHandler)) {
+ return;
+ }
+ card->unlockCallback = 0;
+
+ result = DoMount(chan);
+ if (0 <= result) {
+ return;
+ }
+ break;
+ case CARD_RESULT_IOERROR:
+ case CARD_RESULT_NOCARD:
+ DoUnmount(chan, result);
+ break;
+ }
+
+ callback = card->apiCallback;
+ card->apiCallback = 0;
+ __CARDPutControlBlock(card, result);
+ callback(chan, result);
+}
+
+s32 CARDMountAsync(s32 chan, void* workArea, CARDCallback detachCallback,
+ CARDCallback attachCallback) {
+ CARDControl* card;
+ BOOL enabled;
+
+ if (chan < 0 || 2 <= chan) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+ if (GameChoice & 0x80) {
+ return CARD_RESULT_NOCARD;
+ }
+ card = &__CARDBlock[chan];
+
+ enabled = OSDisableInterrupts();
+ if (card->result == CARD_RESULT_BUSY) {
+ OSRestoreInterrupts(enabled);
+ return CARD_RESULT_BUSY;
+ }
+
+ if (!card->attached && (EXIGetState(chan) & 0x08)) {
+ OSRestoreInterrupts(enabled);
+ return CARD_RESULT_WRONGDEVICE;
+ }
+
+ card->result = CARD_RESULT_BUSY;
+ card->workArea = workArea;
+ card->extCallback = detachCallback;
+ card->apiCallback = attachCallback ? attachCallback : __CARDDefaultApiCallback;
+ card->exiCallback = 0;
+
+ if (!card->attached && !EXIAttach(chan, __CARDExtHandler)) {
+ card->result = CARD_RESULT_NOCARD;
+ OSRestoreInterrupts(enabled);
+ return CARD_RESULT_NOCARD;
+ }
+
+ card->mountStep = 0;
+ card->attached = TRUE;
+ EXISetExiCallback(chan, 0);
+ OSCancelAlarm(&card->alarm);
+
+ card->currentDir = 0;
+ card->currentFat = 0;
+
+ OSRestoreInterrupts(enabled);
+
+ card->unlockCallback = __CARDMountCallback;
+ if (!EXILock(chan, 0, __CARDUnlockedHandler)) {
+ return CARD_RESULT_READY;
+ }
+ card->unlockCallback = 0;
+
+ return DoMount(chan);
+}
+
+static void DoUnmount(s32 chan, s32 result) {
+ CARDControl* card;
+ BOOL enabled;
+
+ card = &__CARDBlock[chan];
+ enabled = OSDisableInterrupts();
+ if (card->attached) {
+ EXISetExiCallback(chan, 0);
+ EXIDetach(chan);
+ OSCancelAlarm(&card->alarm);
+ card->attached = FALSE;
+ card->result = result;
+ card->mountStep = 0;
+ }
+ OSRestoreInterrupts(enabled);
+}
+
+s32 CARDUnmount(s32 chan) {
+ CARDControl* card;
+ s32 result;
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+ DoUnmount(chan, CARD_RESULT_NOCARD);
+ return CARD_RESULT_READY;
+}
diff --git a/src/Dolphin/card/CARDNet.c b/src/Dolphin/card/CARDNet.c
new file mode 100644
index 0000000..828dbad
--- /dev/null
+++ b/src/Dolphin/card/CARDNet.c
@@ -0,0 +1,33 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+u16 __CARDVendorID = 0xffff;
+
+s32 CARDGetSerialNo(s32 chan, u64* serialNo) {
+ CARDControl* card;
+ CARDID* id;
+ int i;
+ u64 code;
+ s32 result;
+
+ if (!(0 <= chan && chan < 2)) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ id = (CARDID*)card->workArea;
+ for (code = 0, i = 0; i < sizeof(id->serial) / sizeof(u64); ++i) {
+ code ^= *(u64*)&id->serial[sizeof(u64) * i];
+ }
+ *serialNo = code;
+
+ return __CARDPutControlBlock(card, CARD_RESULT_READY);
+}
diff --git a/src/Dolphin/card/CARDOpen.c b/src/Dolphin/card/CARDOpen.c
new file mode 100644
index 0000000..60234bf
--- /dev/null
+++ b/src/Dolphin/card/CARDOpen.c
@@ -0,0 +1,123 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+BOOL __CARDCompareFileName(CARDDir* ent, const char* fileName) {
+ char* entName;
+ char c1;
+ char c2;
+ int n;
+
+ entName = (char*)ent->fileName;
+ n = CARD_FILENAME_MAX;
+ while (0 <= --n) {
+ if ((c1 = *entName++) != (c2 = *fileName++)) {
+ return FALSE;
+ } else if (c2 == '\0') {
+ return TRUE;
+ }
+ }
+
+ if (*fileName == '\0') {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+s32 __CARDAccess(CARDControl* card, CARDDir* ent) {
+ if (ent->gameName[0] == 0xFF) {
+ return CARD_RESULT_NOFILE;
+ }
+
+ if (card->diskID == &__CARDDiskNone || (memcmp(ent->gameName, card->diskID->gameName, 4) == 0 &&
+ memcmp(ent->company, card->diskID->company, 2) == 0)) {
+ return CARD_RESULT_READY;
+ }
+
+ return CARD_RESULT_NOPERM;
+}
+
+BOOL __CARDIsWritable(CARDDir* ent) {
+ if (ent->gameName[0] == 0xFF) {
+ return CARD_RESULT_NOFILE;
+ }
+
+ if ((ent->permission & CARD_ATTR_PUBLIC) != 0) {
+ return CARD_RESULT_READY;
+ }
+
+ return CARD_RESULT_NOPERM;
+}
+
+s32 __CARDGetFileNo(CARDControl* card, const char* fileName, s32* pfileNo) {
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 fileNo;
+ s32 result;
+
+ if (!card->attached) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ dir = __CARDGetDirBlock(card);
+ for (fileNo = 0; fileNo < CARD_MAX_FILE; fileNo++) {
+ ent = &dir[fileNo];
+ result = __CARDAccess(card, ent);
+ if (result < 0) {
+ continue;
+ }
+ if (__CARDCompareFileName(ent, fileName)) {
+ *pfileNo = fileNo;
+ return CARD_RESULT_READY;
+ }
+ }
+
+ return CARD_RESULT_NOFILE;
+}
+
+s32 CARDOpen(s32 chan, const char* fileName, CARDFileInfo* fileInfo) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+ s32 fileNo;
+
+ fileInfo->chan = -1;
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+ result = __CARDGetFileNo(card, fileName, &fileNo);
+ if (0 <= result) {
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileNo];
+ if (!CARDIsValidBlockNo(card, ent->startBlock)) {
+ result = CARD_RESULT_BROKEN;
+ } else {
+ fileInfo->chan = chan;
+ fileInfo->fileNo = fileNo;
+ fileInfo->offset = 0;
+ fileInfo->iBlock = ent->startBlock;
+ }
+ }
+ return __CARDPutControlBlock(card, result);
+}
+
+s32 CARDClose(CARDFileInfo* fileInfo) {
+ CARDControl* card;
+ s32 result;
+
+ result = __CARDGetControlBlock(fileInfo->chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ fileInfo->chan = -1;
+ return __CARDPutControlBlock(card, CARD_RESULT_READY);
+}
+
+BOOL __CARDIsOpened(CARDControl* card, s32 fileNo) { return FALSE; }
diff --git a/src/Dolphin/card/CARDRdwr.c b/src/Dolphin/card/CARDRdwr.c
new file mode 100644
index 0000000..2f41be7
--- /dev/null
+++ b/src/Dolphin/card/CARDRdwr.c
@@ -0,0 +1,104 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+static void BlockReadCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ if (result < 0) {
+ goto error;
+ }
+
+ card->xferred += CARD_SEG_SIZE;
+
+ card->addr += CARD_SEG_SIZE;
+ (u8*)card->buffer += CARD_SEG_SIZE;
+ if (--card->repeat <= 0) {
+ goto error;
+ }
+
+ result = __CARDReadSegment(chan, BlockReadCallback);
+ if (result < 0) {
+ goto error;
+ }
+ return;
+
+error:
+ if (card->apiCallback == 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ callback = card->xferCallback;
+ if (callback) {
+ card->xferCallback = 0;
+ callback(chan, result);
+ }
+}
+
+s32 __CARDRead(s32 chan, u32 addr, s32 length, void* dst, CARDCallback callback) {
+ CARDControl* card;
+ card = &__CARDBlock[chan];
+ if (!card->attached) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ card->xferCallback = callback;
+ card->repeat = (int)(length / CARD_SEG_SIZE);
+ card->addr = addr;
+ card->buffer = dst;
+
+ return __CARDReadSegment(chan, BlockReadCallback);
+}
+
+static void BlockWriteCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ if (result < 0) {
+ goto error;
+ }
+
+ card->xferred += CARD_PAGE_SIZE;
+
+ card->addr += CARD_PAGE_SIZE;
+ (u8*)card->buffer += CARD_PAGE_SIZE;
+ if (--card->repeat <= 0) {
+ goto error;
+ }
+
+ result = __CARDWritePage(chan, BlockWriteCallback);
+ if (result < 0) {
+ goto error;
+ }
+ return;
+
+error:
+ if (card->apiCallback == 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ callback = card->xferCallback;
+ if (callback) {
+ card->xferCallback = 0;
+ callback(chan, result);
+ }
+}
+
+s32 __CARDWrite(s32 chan, u32 addr, s32 length, void* dst, CARDCallback callback) {
+ CARDControl* card;
+ card = &__CARDBlock[chan];
+ if (!card->attached) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ card->xferCallback = callback;
+ card->repeat = (int)(length / CARD_PAGE_SIZE);
+ card->addr = addr;
+ card->buffer = dst;
+
+ return __CARDWritePage(chan, BlockWriteCallback);
+}
diff --git a/src/Dolphin/card/CARDRead.c b/src/Dolphin/card/CARDRead.c
new file mode 100644
index 0000000..ffffc5f
--- /dev/null
+++ b/src/Dolphin/card/CARDRead.c
@@ -0,0 +1,141 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+s32 __CARDSeek(CARDFileInfo* fileInfo, s32 length, s32 offset, CARDControl** pcard) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+ u16* fat;
+
+ result = __CARDGetControlBlock(fileInfo->chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ if (!CARDIsValidBlockNo(card, fileInfo->iBlock) ||
+ card->cBlock * card->sectorSize <= fileInfo->offset) {
+ return __CARDPutControlBlock(card, CARD_RESULT_FATAL_ERROR);
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileInfo->fileNo];
+ if (ent->length * card->sectorSize <= offset ||
+ ent->length * card->sectorSize < offset + length) {
+ return __CARDPutControlBlock(card, CARD_RESULT_LIMIT);
+ }
+
+ card->fileInfo = fileInfo;
+ fileInfo->length = length;
+ if (offset < fileInfo->offset) {
+ fileInfo->offset = 0;
+ fileInfo->iBlock = ent->startBlock;
+ if (!CARDIsValidBlockNo(card, fileInfo->iBlock)) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+ }
+ fat = __CARDGetFatBlock(card);
+ while (fileInfo->offset < TRUNC(offset, card->sectorSize)) {
+ fileInfo->offset += card->sectorSize;
+ fileInfo->iBlock = fat[fileInfo->iBlock];
+ if (!CARDIsValidBlockNo(card, fileInfo->iBlock)) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+ }
+
+ fileInfo->offset = offset;
+
+ *pcard = card;
+ return CARD_RESULT_READY;
+}
+
+static void ReadCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+ u16* fat;
+ CARDFileInfo* fileInfo;
+ s32 length;
+
+ card = &__CARDBlock[chan];
+ if (result < 0) {
+ goto error;
+ }
+
+ fileInfo = card->fileInfo;
+ if (fileInfo->length < 0) {
+ result = CARD_RESULT_CANCELED;
+ goto error;
+ }
+
+ length = (s32)TRUNC(fileInfo->offset + card->sectorSize, card->sectorSize) - fileInfo->offset;
+ fileInfo->length -= length;
+ if (fileInfo->length <= 0) {
+ goto error;
+ }
+
+ fat = __CARDGetFatBlock(card);
+ fileInfo->offset += length;
+ fileInfo->iBlock = fat[fileInfo->iBlock];
+ if (!CARDIsValidBlockNo(card, fileInfo->iBlock)) {
+ result = CARD_RESULT_BROKEN;
+ goto error;
+ }
+
+ result = __CARDRead(chan, card->sectorSize * (u32)fileInfo->iBlock,
+ (fileInfo->length < card->sectorSize) ? fileInfo->length : card->sectorSize,
+ card->buffer, ReadCallback);
+ if (result < 0) {
+ goto error;
+ }
+
+ return;
+
+error:
+ callback = card->apiCallback;
+ card->apiCallback = 0;
+ __CARDPutControlBlock(card, result);
+ callback(chan, result);
+}
+
+s32 CARDReadAsync(CARDFileInfo* fileInfo, void* buf, s32 length, s32 offset,
+ CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+ CARDDir* dir;
+ CARDDir* ent;
+
+ if (OFFSET(offset, CARD_SEG_SIZE) != 0 || OFFSET(length, CARD_SEG_SIZE) != 0) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+ result = __CARDSeek(fileInfo, length, offset, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileInfo->fileNo];
+ result = __CARDAccess(card, ent);
+ if (result == CARD_RESULT_NOPERM) {
+ result = __CARDIsWritable(ent);
+ }
+
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+
+ DCInvalidateRange(buf, (u32)length);
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+
+ offset = (s32)OFFSET(fileInfo->offset, card->sectorSize);
+ length = (length < card->sectorSize - offset) ? length : card->sectorSize - offset;
+ result = __CARDRead(fileInfo->chan, card->sectorSize * (u32)fileInfo->iBlock + offset, length,
+ buf, ReadCallback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ return result;
+}
diff --git a/src/Dolphin/card/CARDRename.c b/src/Dolphin/card/CARDRename.c
new file mode 100644
index 0000000..83d1c8a
--- /dev/null
+++ b/src/Dolphin/card/CARDRename.c
@@ -0,0 +1,70 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+s32 CARDRenameAsync(s32 chan, const char* old, const char* new, CARDCallback callback) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+ int fileNo;
+ int newNo;
+ int oldNo;
+
+ if (*old == 0xff || *new == 0xff || *old == 0x00 || *new == 0x00) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+ if (CARD_FILENAME_MAX < (u32)strlen(old) || CARD_FILENAME_MAX < (u32)strlen(new)) {
+ return CARD_RESULT_NAMETOOLONG;
+ }
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ newNo = oldNo = -1;
+ dir = __CARDGetDirBlock(card);
+ for (fileNo = 0; fileNo < CARD_MAX_FILE; fileNo++) {
+ ent = &dir[fileNo];
+ if (ent->gameName[0] == 0xff) {
+ continue;
+ }
+
+ if (memcmp(ent->gameName, card->diskID->gameName, sizeof(ent->gameName)) != 0 ||
+ memcmp(ent->company, card->diskID->company, sizeof(ent->company)) != 0) {
+ continue;
+ }
+
+ if (__CARDCompareFileName(ent, old)) {
+ oldNo = fileNo;
+ }
+ if (__CARDCompareFileName(ent, new)) {
+ newNo = fileNo;
+ }
+ }
+
+ if (oldNo == -1) {
+ return __CARDPutControlBlock(card, CARD_RESULT_NOFILE);
+ }
+ if (newNo != -1) {
+ return __CARDPutControlBlock(card, CARD_RESULT_EXIST);
+ }
+
+ ent = &dir[oldNo];
+ result = __CARDAccess(card, ent);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+
+ strncpy((char*)ent->fileName, new, CARD_FILENAME_MAX);
+
+ ent->time = (u32)OSTicksToSeconds(OSGetTime());
+ result = __CARDUpdateDir(chan, callback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ return result;
+}
diff --git a/src/Dolphin/card/CARDStat.c b/src/Dolphin/card/CARDStat.c
new file mode 100644
index 0000000..152d535
--- /dev/null
+++ b/src/Dolphin/card/CARDStat.c
@@ -0,0 +1,144 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+static void UpdateIconOffsets(CARDDir* ent, CARDStat* stat) {
+ u32 offset;
+ BOOL iconTlut;
+ int i;
+
+ offset = ent->iconAddr;
+ if (offset == 0xffffffff) {
+ stat->bannerFormat = 0;
+ stat->iconFormat = 0;
+ stat->iconSpeed = 0;
+ offset = 0;
+ }
+
+ iconTlut = FALSE;
+ switch (CARDGetBannerFormat(ent)) {
+ case CARD_STAT_BANNER_C8:
+ stat->offsetBanner = offset;
+ offset += CARD_BANNER_WIDTH * CARD_BANNER_HEIGHT;
+ stat->offsetBannerTlut = offset;
+ offset += 2 * 256;
+ break;
+ case CARD_STAT_BANNER_RGB5A3:
+ stat->offsetBanner = offset;
+ offset += 2 * CARD_BANNER_WIDTH * CARD_BANNER_HEIGHT;
+ stat->offsetBannerTlut = 0xffffffff;
+ break;
+ default:
+ stat->offsetBanner = 0xffffffff;
+ stat->offsetBannerTlut = 0xffffffff;
+ break;
+ }
+ for (i = 0; i < CARD_ICON_MAX; ++i) {
+ switch (CARDGetIconFormat(ent, i)) {
+ case CARD_STAT_ICON_C8:
+ stat->offsetIcon[i] = offset;
+ offset += CARD_ICON_WIDTH * CARD_ICON_HEIGHT;
+ iconTlut = TRUE;
+ break;
+ case CARD_STAT_ICON_RGB5A3:
+ stat->offsetIcon[i] = offset;
+ offset += 2 * CARD_ICON_WIDTH * CARD_ICON_HEIGHT;
+ break;
+ default:
+ stat->offsetIcon[i] = 0xffffffff;
+ break;
+ }
+ }
+ if (iconTlut) {
+ stat->offsetIconTlut = offset;
+ offset += 2 * 256;
+ } else {
+ stat->offsetIconTlut = 0xffffffff;
+ }
+ stat->offsetData = offset;
+}
+
+s32 CARDGetStatus(s32 chan, s32 fileNo, CARDStat* stat) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+
+ if (fileNo < 0 || CARD_MAX_FILE <= fileNo) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileNo];
+ result = __CARDAccess(card, ent);
+ if (result == CARD_RESULT_NOPERM) {
+ result = __CARDIsWritable(ent);
+ }
+
+ if (result >= 0) {
+ memcpy(stat->gameName, ent->gameName, sizeof(stat->gameName));
+ memcpy(stat->company, ent->company, sizeof(stat->company));
+ stat->length = (u32)ent->length * card->sectorSize;
+ memcpy(stat->fileName, ent->fileName, CARD_FILENAME_MAX);
+ stat->time = ent->time;
+
+ stat->bannerFormat = ent->bannerFormat;
+ stat->iconAddr = ent->iconAddr;
+ stat->iconFormat = ent->iconFormat;
+ stat->iconSpeed = ent->iconSpeed;
+ stat->commentAddr = ent->commentAddr;
+
+ UpdateIconOffsets(ent, stat);
+ }
+ return __CARDPutControlBlock(card, result);
+}
+
+s32 CARDSetStatusAsync(s32 chan, s32 fileNo, CARDStat* stat, CARDCallback callback) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+
+ if (fileNo < 0 || CARD_MAX_FILE <= fileNo ||
+ (stat->iconAddr != 0xffffffff && CARD_READ_SIZE <= stat->iconAddr) ||
+ (stat->commentAddr != 0xffffffff &&
+ CARD_SYSTEM_BLOCK_SIZE - CARD_COMMENT_SIZE < stat->commentAddr % CARD_SYSTEM_BLOCK_SIZE)) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileNo];
+ result = __CARDAccess(card, ent);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+
+ ent->bannerFormat = stat->bannerFormat;
+ ent->iconAddr = stat->iconAddr;
+ ent->iconFormat = stat->iconFormat;
+ ent->iconSpeed = stat->iconSpeed;
+ ent->commentAddr = stat->commentAddr;
+ UpdateIconOffsets(ent, stat);
+
+ if (ent->iconAddr == 0xffffffff) {
+ CARDSetIconSpeed(ent, 0, CARD_STAT_SPEED_FAST);
+ }
+
+ ent->time = (u32)OSTicksToSeconds(OSGetTime());
+ result = __CARDUpdateDir(chan, callback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ return result;
+}
diff --git a/src/Dolphin/card/CARDUnlock.c b/src/Dolphin/card/CARDUnlock.c
new file mode 100644
index 0000000..ebe8300
--- /dev/null
+++ b/src/Dolphin/card/CARDUnlock.c
@@ -0,0 +1,396 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+static void InitCallback(void* task);
+static void DoneCallback(void* task);
+
+static u8 CardData[] ATTRIBUTE_ALIGN(32) = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x02, 0xFF, 0x00, 0x21,
+ 0x13, 0x06, 0x12, 0x03, 0x12, 0x04, 0x13, 0x05, 0x00, 0x92, 0x00, 0xFF, 0x00, 0x88, 0xFF, 0xFF,
+ 0x00, 0x89, 0xFF, 0xFF, 0x00, 0x8A, 0xFF, 0xFF, 0x00, 0x8B, 0xFF, 0xFF, 0x8F, 0x00, 0x02, 0xBF,
+ 0x00, 0x88, 0x16, 0xFC, 0xDC, 0xD1, 0x16, 0xFD, 0x00, 0x00, 0x16, 0xFB, 0x00, 0x01, 0x02, 0xBF,
+ 0x00, 0x8E, 0x25, 0xFF, 0x03, 0x80, 0xFF, 0x00, 0x02, 0x94, 0x00, 0x27, 0x02, 0xBF, 0x00, 0x8E,
+ 0x1F, 0xDF, 0x24, 0xFF, 0x02, 0x40, 0x0F, 0xFF, 0x00, 0x98, 0x04, 0x00, 0x00, 0x9A, 0x00, 0x10,
+ 0x00, 0x99, 0x00, 0x00, 0x8E, 0x00, 0x02, 0xBF, 0x00, 0x94, 0x02, 0xBF, 0x86, 0x44, 0x02, 0xBF,
+ 0x00, 0x88, 0x16, 0xFC, 0xDC, 0xD1, 0x16, 0xFD, 0x00, 0x03, 0x16, 0xFB, 0x00, 0x01, 0x8F, 0x00,
+ 0x02, 0xBF, 0x00, 0x8E, 0x03, 0x80, 0xCD, 0xD1, 0x02, 0x94, 0x00, 0x48, 0x27, 0xFF, 0x03, 0x80,
+ 0x00, 0x01, 0x02, 0x95, 0x00, 0x5A, 0x03, 0x80, 0x00, 0x02, 0x02, 0x95, 0x80, 0x00, 0x02, 0x9F,
+ 0x00, 0x48, 0x00, 0x21, 0x8E, 0x00, 0x02, 0xBF, 0x00, 0x8E, 0x25, 0xFF, 0x02, 0xBF, 0x00, 0x8E,
+ 0x25, 0xFF, 0x02, 0xBF, 0x00, 0x8E, 0x25, 0xFF, 0x02, 0xBF, 0x00, 0x8E, 0x00, 0xC5, 0xFF, 0xFF,
+ 0x03, 0x40, 0x0F, 0xFF, 0x1C, 0x9F, 0x02, 0xBF, 0x00, 0x8E, 0x00, 0xC7, 0xFF, 0xFF, 0x02, 0xBF,
+ 0x00, 0x8E, 0x00, 0xC6, 0xFF, 0xFF, 0x02, 0xBF, 0x00, 0x8E, 0x00, 0xC0, 0xFF, 0xFF, 0x02, 0xBF,
+ 0x00, 0x8E, 0x20, 0xFF, 0x03, 0x40, 0x0F, 0xFF, 0x1F, 0x5F, 0x02, 0xBF, 0x00, 0x8E, 0x21, 0xFF,
+ 0x02, 0xBF, 0x00, 0x8E, 0x23, 0xFF, 0x12, 0x05, 0x12, 0x06, 0x02, 0x9F, 0x80, 0xB5, 0x00, 0x21,
+ 0x27, 0xFC, 0x03, 0xC0, 0x80, 0x00, 0x02, 0x9D, 0x00, 0x88, 0x02, 0xDF, 0x27, 0xFE, 0x03, 0xC0,
+ 0x80, 0x00, 0x02, 0x9C, 0x00, 0x8E, 0x02, 0xDF, 0x2E, 0xCE, 0x2C, 0xCF, 0x00, 0xF8, 0xFF, 0xCD,
+ 0x00, 0xF9, 0xFF, 0xC9, 0x00, 0xFA, 0xFF, 0xCB, 0x26, 0xC9, 0x02, 0xC0, 0x00, 0x04, 0x02, 0x9D,
+ 0x00, 0x9C, 0x02, 0xDF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+typedef struct DecodeParameters {
+ u8* inputAddr;
+ u32 inputLength;
+ u32 aramAddr;
+ u8* outputAddr;
+} DecodeParameters;
+
+static unsigned long int next = 1;
+
+static int CARDRand(void) {
+ next = next * 1103515245 + 12345;
+ return (int)((unsigned int)(next / 65536) % 32768);
+}
+
+static void CARDSrand(unsigned int seed) { next = seed; }
+
+static u32 GetInitVal(void) {
+ u32 tmp;
+ u32 tick;
+
+ tick = OSGetTick();
+ CARDSrand(tick);
+ tmp = 0x7fec8000;
+ tmp |= CARDRand();
+ tmp &= 0xfffff000;
+ return tmp;
+}
+
+static u32 exnor_1st(u32 data, u32 rshift) {
+ u32 wk;
+ u32 w;
+ u32 i;
+
+ w = data;
+ for (i = 0; i < rshift; i++) {
+ wk = ~(w ^ (w >> 7) ^ (w >> 15) ^ (w >> 23));
+ w = (w >> 1) | ((wk << 30) & 0x40000000);
+ }
+ return w;
+}
+
+static u32 exnor(u32 data, u32 lshift) {
+ u32 wk;
+ u32 w;
+ u32 i;
+
+ w = data;
+ for (i = 0; i < lshift; i++) {
+ // 1bit Left Shift
+ wk = ~(w ^ (w << 7) ^ (w << 15) ^ (w << 23));
+ w = (w << 1) | ((wk >> 30) & 0x00000002);
+ // printf("i=%d, w=%8x\n", i, w);
+ }
+ return w;
+}
+
+static u32 bitrev(u32 data) {
+ u32 wk;
+ u32 i;
+ u32 k = 0;
+ u32 j = 1;
+
+ wk = 0;
+ for (i = 0; i < 32; i++) {
+ if (i > 15) {
+ if (i == 31) {
+ wk |= (((data & (0x01 << 31)) >> 31) & 0x01);
+ } else {
+ wk |= ((data & (0x01 << i)) >> j);
+ j += 2;
+ }
+ } else {
+ wk |= ((data & (0x01 << i)) << (31 - i - k));
+ k++;
+ }
+ }
+ return wk;
+}
+
+#define SEC_AD1(x) ((u8)(((x) >> 29) & 0x03))
+#define SEC_AD2(x) ((u8)(((x) >> 21) & 0xff))
+#define SEC_AD3(x) ((u8)(((x) >> 19) & 0x03))
+#define SEC_BA(x) ((u8)(((x) >> 12) & 0x7f))
+
+static s32 ReadArrayUnlock(s32 chan, u32 data, void* rbuf, s32 rlen, s32 mode) {
+ CARDControl* card;
+ BOOL err;
+ u8 cmd[5];
+
+ card = &__CARDBlock[chan];
+ if (!EXISelect(chan, 0, 4)) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ data &= 0xfffff000;
+ memset(cmd, 0, 5);
+ cmd[0] = 0x52;
+ if (mode == 0) {
+ cmd[1] = SEC_AD1(data);
+ cmd[2] = SEC_AD2(data);
+ cmd[3] = SEC_AD3(data);
+ cmd[4] = SEC_BA(data);
+ } else {
+ cmd[1] = (u8)((data & 0xff000000) >> 24);
+ cmd[2] = (u8)((data & 0x00ff0000) >> 16);
+ }
+
+ err = FALSE;
+ err |= !EXIImmEx(chan, cmd, 5, 1);
+ err |= !EXIImmEx(chan, (u8*)card->workArea + (u32)sizeof(CARDID), card->latency, 1);
+ err |= !EXIImmEx(chan, rbuf, rlen, 0);
+ err |= !EXIDeselect(chan);
+
+ return err ? CARD_RESULT_NOCARD : CARD_RESULT_READY;
+}
+
+// Calculate Dummy Read Length, 4-32Bytes
+static s32 DummyLen(void) {
+ u32 tick;
+ u32 wk;
+ s32 tmp;
+ u32 max;
+
+ wk = 1;
+ max = 0;
+ tick = OSGetTick();
+ CARDSrand(tick);
+
+ tmp = CARDRand();
+ tmp &= 0x0000001f;
+ tmp += 1;
+ while ((tmp < 4) && (max < 10)) {
+ tick = OSGetTick();
+ tmp = (s32)(tick << wk);
+ wk++;
+ if (wk > 16) {
+ wk = 1;
+ }
+ CARDSrand((u32)tmp);
+ tmp = CARDRand();
+ tmp &= 0x0000001f;
+ tmp += 1;
+ max++;
+ }
+ if (tmp < 4) {
+ tmp = 4;
+ }
+
+ return tmp;
+}
+
+s32 __CARDUnlock(s32 chan, u8 flashID[12]) {
+ u32 init_val;
+ u32 data;
+
+ s32 dummy;
+ s32 rlen;
+ u32 rshift;
+
+ u8 fsts;
+ u32 wk, wk1;
+ u32 Ans1 = 0;
+ u32 Ans2 = 0;
+ u32* dp;
+ u8 rbuf[64];
+ u32 para1A = 0;
+ u32 para1B = 0;
+ u32 para2A = 0;
+ u32 para2B = 0;
+
+ CARDControl* card;
+ DSPTaskInfo* task;
+ DecodeParameters* param;
+ u8* input;
+ u8* output;
+
+ card = &__CARDBlock[chan];
+ task = &card->task;
+ param = (DecodeParameters*)card->workArea;
+ input = (u8*)((u8*)param + sizeof(DecodeParameters));
+ input = (u8*)OSRoundUp32B(input);
+ output = input + 32;
+
+ fsts = 0;
+ init_val = GetInitVal();
+
+ dummy = DummyLen();
+ rlen = dummy;
+ if (ReadArrayUnlock(chan, init_val, rbuf, rlen, 0) < 0) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ rshift = (u32)(dummy * 8 + 1);
+ wk = exnor_1st(init_val, rshift);
+ wk1 = ~(wk ^ (wk >> 7) ^ (wk >> 15) ^ (wk >> 23));
+ card->scramble = (wk | ((wk1 << 31) & 0x80000000));
+ card->scramble = bitrev(card->scramble);
+ dummy = DummyLen();
+ rlen = 20 + dummy;
+ data = 0;
+ if (ReadArrayUnlock(chan, data, rbuf, rlen, 1) < 0) {
+ return CARD_RESULT_NOCARD;
+ }
+ dp = (u32*)rbuf;
+ para1A = *dp++;
+ para1B = *dp++;
+ Ans1 = *dp++;
+ para2A = *dp++;
+ para2B = *dp++;
+ para1A = (para1A ^ card->scramble);
+ rshift = 32;
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+ para1B = (para1B ^ card->scramble);
+ rshift = 32;
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+ Ans1 ^= card->scramble;
+ rshift = 32;
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+ para2A = (para2A ^ card->scramble);
+ rshift = 32;
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+ para2B = (para2B ^ card->scramble);
+ rshift = (u32)(dummy * 8);
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+ rshift = 32 + 1;
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+
+ *(u32*)&input[0] = para2A;
+ *(u32*)&input[4] = para2B;
+
+ param->inputAddr = input;
+ param->inputLength = 8;
+ param->outputAddr = output;
+ param->aramAddr = 0;
+
+ DCFlushRange(input, 8);
+ DCInvalidateRange(output, 4);
+ DCFlushRange(param, sizeof(DecodeParameters));
+
+ task->priority = 255;
+ task->iram_mmem_addr = (u16*)OSPhysicalToCached(CardData);
+ task->iram_length = 0x160;
+ task->iram_addr = 0;
+ task->dsp_init_vector = 0x10;
+ task->init_cb = InitCallback;
+ task->res_cb = NULL;
+ task->done_cb = DoneCallback;
+ task->req_cb = NULL;
+ DSPAddTask(task);
+
+ dp = (u32*)flashID;
+ *dp++ = para1A;
+ *dp++ = para1B;
+ *dp = Ans1;
+
+ return CARD_RESULT_READY;
+}
+
+static void InitCallback(void* _task) {
+ s32 chan;
+ CARDControl* card;
+ DSPTaskInfo* task;
+ DecodeParameters* param;
+
+ task = _task;
+ for (chan = 0; chan < 2; ++chan) {
+ card = &__CARDBlock[chan];
+ if ((DSPTaskInfo*)&card->task == task) {
+ break;
+ }
+ }
+ param = (DecodeParameters*)card->workArea;
+
+ DSPSendMailToDSP(0xff000000);
+ while (DSPCheckMailToDSP())
+ ;
+
+ DSPSendMailToDSP((u32)param);
+ while (DSPCheckMailToDSP())
+ ;
+}
+
+static void DoneCallback(void* _task) {
+ u8 rbuf[64];
+ u32 data;
+ s32 dummy;
+ s32 rlen;
+ u32 rshift;
+
+ u8 unk;
+ u32 wk, wk1;
+ u32 Ans2;
+
+ s32 chan;
+ CARDControl* card;
+ s32 result;
+ DSPTaskInfo* task;
+ DecodeParameters* param;
+
+ u8* input;
+ u8* output;
+ task = _task;
+ for (chan = 0; chan < 2; ++chan) {
+ card = &__CARDBlock[chan];
+ if ((DSPTaskInfo*)&card->task == task) {
+ break;
+ }
+ }
+
+ param = (DecodeParameters*)card->workArea;
+ input = (u8*)((u8*)param + sizeof(DecodeParameters));
+ input = (u8*)OSRoundUp32B(input);
+ output = input + 32;
+
+ Ans2 = *(u32*)output;
+ dummy = DummyLen();
+ rlen = dummy;
+ data = ((Ans2 ^ card->scramble) & 0xffff0000);
+ if (ReadArrayUnlock(chan, data, rbuf, rlen, 1) < 0) {
+ EXIUnlock(chan);
+ __CARDMountCallback(chan, CARD_RESULT_NOCARD);
+ return;
+ }
+
+ rshift = (u32)((dummy + 4 + card->latency) * 8 + 1);
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+
+ dummy = DummyLen();
+ rlen = dummy;
+ data = (((Ans2 << 16) ^ card->scramble) & 0xffff0000);
+ if (ReadArrayUnlock(chan, data, rbuf, rlen, 1) < 0) {
+ EXIUnlock(chan);
+ __CARDMountCallback(chan, CARD_RESULT_NOCARD);
+ return;
+ }
+ result = __CARDReadStatus(chan, &unk);
+ if (!EXIProbe(chan)) {
+ EXIUnlock(chan);
+ __CARDMountCallback(chan, CARD_RESULT_NOCARD);
+ return;
+ }
+ if (result == CARD_RESULT_READY && !(unk & 0x40)) {
+ EXIUnlock(chan);
+ result = CARD_RESULT_IOERROR;
+ }
+ __CARDMountCallback(chan, result);
+}
diff --git a/src/Dolphin/card/CARDWrite.c b/src/Dolphin/card/CARDWrite.c
new file mode 100644
index 0000000..69a11b3
--- /dev/null
+++ b/src/Dolphin/card/CARDWrite.c
@@ -0,0 +1,117 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+static void EraseCallback(s32 chan, s32 result);
+
+static void WriteCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+ u16* fat;
+ CARDDir* dir;
+ CARDDir* ent;
+ CARDFileInfo* fileInfo;
+
+ card = &__CARDBlock[chan];
+ if (result < 0) {
+ goto error;
+ }
+
+ fileInfo = card->fileInfo;
+ if (fileInfo->length < 0) {
+ result = CARD_RESULT_CANCELED;
+ goto error;
+ }
+
+ fileInfo->length -= card->sectorSize;
+ if (fileInfo->length <= 0) {
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileInfo->fileNo];
+ ent->time = (u32)OSTicksToSeconds(OSGetTime());
+ callback = card->apiCallback;
+ card->apiCallback = 0;
+ result = __CARDUpdateDir(chan, callback);
+ } else {
+ fat = __CARDGetFatBlock(card);
+ fileInfo->offset += card->sectorSize;
+ fileInfo->iBlock = fat[fileInfo->iBlock];
+ if (!CARDIsValidBlockNo(card, fileInfo->iBlock)) {
+ result = CARD_RESULT_BROKEN;
+ goto error;
+ }
+ result = __CARDEraseSector(chan, card->sectorSize * (u32)fileInfo->iBlock, EraseCallback);
+ }
+
+ if (result < 0) {
+ goto error;
+ }
+ return;
+
+error:
+ callback = card->apiCallback;
+ card->apiCallback = 0;
+ __CARDPutControlBlock(card, result);
+ callback(chan, result);
+}
+
+static void EraseCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+ CARDFileInfo* fileInfo;
+
+ card = &__CARDBlock[chan];
+ if (result < 0) {
+ goto error;
+ }
+
+ fileInfo = card->fileInfo;
+ result = __CARDWrite(chan, card->sectorSize * (u32)fileInfo->iBlock, card->sectorSize,
+ card->buffer, WriteCallback);
+ if (result < 0) {
+ goto error;
+ }
+ return;
+
+error:
+ callback = card->apiCallback;
+ card->apiCallback = 0;
+ __CARDPutControlBlock(card, result);
+ callback(chan, result);
+}
+
+s32 CARDWriteAsync(CARDFileInfo* fileInfo, const void* buf, s32 length, s32 offset,
+ CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+ CARDDir* dir;
+ CARDDir* ent;
+
+ result = __CARDSeek(fileInfo, length, offset, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ if (OFFSET(offset, card->sectorSize) != 0 || OFFSET(length, card->sectorSize) != 0) {
+ return __CARDPutControlBlock(card, CARD_RESULT_FATAL_ERROR);
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileInfo->fileNo];
+ result = __CARDAccess(card, ent);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+
+ DCStoreRange((void*)buf, (u32)length);
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+ card->buffer = (void*)buf;
+ result =
+ __CARDEraseSector(fileInfo->chan, card->sectorSize * (u32)fileInfo->iBlock, EraseCallback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ return result;
+}