#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;
}