#include "dolphin/gba/GBAPriv.h" static volatile u8 D54[] = { 0x18, 0xFC, 0xC0, 0x80, 0x7f, 0x40, 0x3f, 0x01, 0x00, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x4b, 0x61, 0x77, 0x61, 0x73, 0x65, 0x64, 0x6f, 0x00, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0xAC, 0xC4, 0xF8, 0x08, 0x10, 0xBF, 0x18, }; static void F23(s32 chan, s32 ret); static void F25(s32 chan, s32 ret); static void F27(s32 chan, s32 ret); static void F29(s32 chan, s32 ret); static void F31(s32 chan, s32 ret); static void F33(s32 chan, s32 ret); static void F35(s32 chan, s32 ret); static void F37(s32 chan, s32 ret); static void F39(s32 chan, s32 ret); static u32 F72(u32 crc, u32 src, vu8* keyp) { int i; // r31 int poly = (keyp[0x13] << 8) + ((keyp[0x15] + (keyp[0x18] - (keyp[0x18] << 4))) - keyp[0x10]); // r30 for (i = keyp[1]; i > keyp[11]; --i) { if ((src ^ crc) & 1) { crc >>= 1; crc ^= poly; } else { crc >>= 1; } src >>= 1; } return crc; } static u32 F95(u32 src, vu8* keyp) { src = (src * ((keyp[3] << keyp[0x16]) | ((keyp[1] | (keyp[4] << keyp[0x11])) | (keyp[4] << keyp[0x18])))) - (keyp[7] - keyp[6]); return src; } static void F104(s32 chan, s32 ret) { GBABootInfo* bootInfo; GBACallback callback; bootInfo = &__GBA[chan].bootInfo; bootInfo->begin = 0; if (bootInfo->callback != NULL) { callback = bootInfo->callback; bootInfo->callback = NULL; callback(chan, ret); } } s32 GBAJoyBootAsync(s32 chan, s32 paletteColor, s32 paletteSpeed, u8* programp, s32 length, u8* status, GBACallback callback) { int ret; GBABootInfo* bootInfo = &__GBA[chan].bootInfo; u8 percent; if (chan & ~3) { return GBA_JOYBOOT_ERR_INVALID; } else if (length == 0 || GBA_JOYBOOT_PROGRAM_SIZE_MAX < length) { return GBA_JOYBOOT_ERR_INVALID; } else if (paletteSpeed < -4 || paletteSpeed > 4) { return GBA_JOYBOOT_ERR_INVALID; } else if (paletteColor < 0 || paletteColor > 6) { return GBA_JOYBOOT_ERR_INVALID; } else if (programp[0xac] * programp[0xad] * programp[0xae] * programp[0xaf] == 0) { return GBA_JOYBOOT_ERR_INVALID; } ret = GBAGetProcessStatus(chan, &percent); if (ret != GBA_READY) { return ret; } bootInfo->paletteColor = paletteColor; bootInfo->paletteSpeed = paletteSpeed; bootInfo->programp = programp; bootInfo->length = length; bootInfo->status = status; bootInfo->callback = callback; bootInfo->curOffset = D54[8]; ret = GBAGetStatusAsync(chan, bootInfo->status, F23); if (ret != GBA_READY) { bootInfo->callback = NULL; } return ret; } static void F23(s32 chan, s32 ret) { GBAControl* gba; GBABootInfo* bootInfo; gba = &__GBA[chan]; bootInfo = &__GBA[chan].bootInfo; if (ret != GBA_READY || (ret = GBAResetAsync(chan, bootInfo->status, F25)) != GBA_READY) { F104(chan, ret); } gba->ret = ret; } static void F25(s32 chan, s32 ret) { GBAControl* gba = &__GBA[chan]; GBABootInfo* bootInfo = &__GBA[chan].bootInfo; if (ret == GBA_READY && *bootInfo->status != D54[37]) { ret = GBA_JOYBOOT_UNKNOWN_STATE; } if (ret != GBA_READY || (ret = GBAGetStatusAsync(chan, bootInfo->status, F27))) { F104(chan, ret); } gba->ret = ret; } static void F27(s32 chan, s32 ret) { GBAControl* gba = &__GBA[chan]; GBABootInfo* bootInfo = &__GBA[chan].bootInfo; if (ret == GBA_READY && *bootInfo->status != D54[0]) { ret = GBA_JOYBOOT_UNKNOWN_STATE; } if (ret != GBA_READY || (ret = GBAReadAsync(chan, bootInfo->readbuf, bootInfo->status, F29))) { F104(chan, ret); } gba->ret = ret; } static void F29(s32 chan, s32 ret) { GBAControl* gba = &__GBA[chan]; GBABootInfo* bootInfo = &__GBA[chan].bootInfo; int val200; if (ret == GBA_READY) { bootInfo->keyA = (bootInfo->readbuf[3] ^ D54[28]) << 24 | (bootInfo->readbuf[2] ^ D54[14]) << 16 | (bootInfo->readbuf[1] ^ D54[15]) << 8 | (bootInfo->readbuf[0] ^ D54[25]); if ( bootInfo->readbuf[3] == 0 || bootInfo->readbuf[2] == 0 || (bootInfo->keyA & (D54[5] << 9)) == 0 || bootInfo->readbuf[1] == 0 || (bootInfo->keyA >> 15) == 0 || bootInfo->readbuf[0] == 0 ) { ret = GBA_JOYBOOT_UNKNOWN_STATE; } else { switch (bootInfo->paletteSpeed) { case -4: case -3: case -2: case -1: bootInfo->keyB = (bootInfo->paletteColor << 4 | (3 - bootInfo->paletteSpeed)*2) << 16; break; case 0: bootInfo->keyB = (bootInfo->paletteColor << 1 | 0x70) << 16; break; case 1: case 2: case 3: case 4: bootInfo->keyB = (bootInfo->paletteColor << 4 | (bootInfo->paletteSpeed - 1)*2) << 16; break; } bootInfo->i = ~D54[36] & bootInfo->length + D54[36]; val200 = D54[20] << D54[33]; if (bootInfo->i < val200) { bootInfo->i = val200; } bootInfo->realLength = bootInfo->i; bootInfo->i -= val200; bootInfo->i >>= D54[32]; bootInfo->keyB |= ((bootInfo->i & (D54[3] << D54[36])) << D54[31]) | ((bootInfo->i & (D54[3] | (D54[6] << D54[37]))) << D54[7]) | bootInfo->i & D54[4]; bootInfo->keyB |= ((bootInfo->keyB + (bootInfo->keyB >> 8) + (bootInfo->keyB >> 16))) << 24 | D54[3] << 24 | D54[5] << 17 | D54[17] << 10 | D54[3]; if ((bootInfo->keyB & val200) != 0) { bootInfo->writebuf[3] = (bootInfo->keyB >> 24) ^ D54[13]; bootInfo->writebuf[2] = (bootInfo->keyB >> 16) ^ D54[16]; bootInfo->writebuf[0] = bootInfo->keyB ^ D54[25]; bootInfo->writebuf[1] = (bootInfo->keyB >> 8) ^ D54[26]; } else { bootInfo->writebuf[0] = bootInfo->keyB ^ D54[21]; bootInfo->writebuf[1] = (bootInfo->keyB >> 8) ^ D54[22]; bootInfo->writebuf[3] = (bootInfo->keyB >> 24) ^ D54[22]; bootInfo->writebuf[2] = (bootInfo->keyB >> 16) ^ D54[23]; } bootInfo->crc = (D54[38] + 1) << D54[34]; bootInfo->curOffset = D54[8]; bootInfo->begin = OSGetTime(); bootInfo->firstXfer = TRUE; ret = GBAWriteAsync(chan, bootInfo->writebuf, bootInfo->status, F31); } } if (ret != GBA_READY) { F104(chan, ret); } gba->ret = ret; } static void F31(s32 chan, s32 ret) { GBAControl* gba; GBABootInfo* bootInfo; u32 writeWord; gba = &__GBA[chan]; bootInfo = &__GBA[chan].bootInfo; if (ret == GBA_READY) { if (bootInfo->firstXfer != FALSE) { bootInfo->firstXfer = FALSE; } else if ((*bootInfo->status & D54[20]) == 0 || (*bootInfo->status & D54[42]) >> D54[33] != (bootInfo->curOffset & D54[33]) >> D54[31]) { ret = GBA_JOYBOOT_UNKNOWN_STATE; goto exit; } else { bootInfo->curOffset -= D54[25] - D54[23]; } if (bootInfo->curOffset <= bootInfo->realLength) { if (bootInfo->curOffset < bootInfo->realLength) { writeWord = D54[29]; for (bootInfo->i = D54[29]; bootInfo->i < D54[33]; ++bootInfo->i) { if (bootInfo->length != 0) { writeWord |= *(bootInfo->programp++) << (bootInfo->i * D54[37]); --bootInfo->length; } } if (bootInfo->curOffset == D54[38]) { bootInfo->initialCode = writeWord; } else if (bootInfo->curOffset == D54[39]) { writeWord = chan << D54[37]; } if (bootInfo->curOffset >= D54[2]) { bootInfo->crc = F72(bootInfo->crc, writeWord, &D54[19]); } if (bootInfo->curOffset == D54[40] + 0x100) { bootInfo->dummyWord[0] = writeWord; } else if (bootInfo->curOffset == D54[1] + 0x100) { bootInfo->i = D54[7]; bootInfo->dummyWord[bootInfo->i] = writeWord; } } else { writeWord = bootInfo->crc | (bootInfo->curOffset << 16); } if (bootInfo->curOffset > D54[43]) { bootInfo->keyA = F95(bootInfo->keyA, &D54[20]); writeWord ^= bootInfo->keyA; writeWord ^= -(bootInfo->curOffset + D54[11] * 0x100000); writeWord ^= (D54[11] | (D54[11] << 24)) | (D54[19] << 16) | (D54[18] << 8); } bootInfo->writebuf[3] = (writeWord >> D54[0]); bootInfo->writebuf[0] = (writeWord >> D54[30]); bootInfo->writebuf[1] = (writeWord >> D54[41]); bootInfo->writebuf[2] = (writeWord >> D54[42]); if (bootInfo->curOffset == D54[1] + D54[1]) { bootInfo->dummyWord[2] = writeWord; } if (bootInfo->i < D54[33]) { bootInfo->dummyWord[3 - (1 - bootInfo->i)] = writeWord; bootInfo->dummyWord[5 - bootInfo->i] = bootInfo->dummyWord[(2 - (1 - bootInfo->i))] * bootInfo->dummyWord[4 - bootInfo->i]; bootInfo->dummyWord[5 - (1 - bootInfo->i)] = bootInfo->dummyWord[((1 - bootInfo->i))] * bootInfo->dummyWord[1 - (1 - bootInfo->i)]; bootInfo->dummyWord[7 - bootInfo->i] = bootInfo->dummyWord[(-(1 - bootInfo->i))] * bootInfo->dummyWord[4 - bootInfo->i]; } ret = GBAWriteAsync(chan, bootInfo->writebuf, bootInfo->status, F31); } else { bootInfo->start = OSGetTick(); ret = GBAReadAsync(chan, bootInfo->readbuf, bootInfo->status, F33); } } exit: if (ret != GBA_READY) { F104(chan, ret); } gba->ret = ret; } static void F33(s32 chan, s32 ret) { GBAControl* gba = &__GBA[chan]; GBABootInfo* bootInfo = &__GBA[chan].bootInfo; if (ret == GBA_READY) { for (bootInfo->i = 33; bootInfo->i < 36; ++bootInfo->i) { ret = (((bootInfo->readbuf[3] ^ (bootInfo->dummyWord[D54[bootInfo->i]] >> 24)) | (bootInfo->readbuf[2] ^ (bootInfo->dummyWord[D54[bootInfo->i]] >> 16)) | (bootInfo->readbuf[1] ^ (bootInfo->dummyWord[D54[bootInfo->i]] >> 8)) | (bootInfo->readbuf[0] ^ (bootInfo->dummyWord[D54[bootInfo->i]] >> 0))) << 24); if (ret == GBA_READY) { break; } } if (ret != GBA_READY) { ret = GBA_JOYBOOT_UNKNOWN_STATE; } else { bootInfo->start = OSGetTick(); ret = GBAGetStatusAsync(chan, bootInfo->status, F35); } } if (ret != GBA_READY) { F104(chan, ret); } gba->ret = ret; } static void F35(s32 chan, s32 ret) { GBAControl* gba = &__GBA[chan]; GBABootInfo* bootInfo = &__GBA[chan].bootInfo; if (ret == 0) { if (OSSecondsToTicks(10) <= OSGetTick() - bootInfo->start) { ret = GBA_JOYBOOT_UNKNOWN_STATE; } else if ((*bootInfo->status & 0x32) != 0) { ret = GBA_JOYBOOT_UNKNOWN_STATE; } else if (*bootInfo->status != 8) { ret = GBAGetStatusAsync(chan, bootInfo->status, F35); } else { __GBASetDelay(chan, OSMillisecondsToTicks(8)); ret = GBAReadAsync(chan, bootInfo->readbuf, bootInfo->status, F37); __GBASetDelay(chan, OSMicrosecondsToTicks(60)); } } if (ret != GBA_READY) { F104(chan, ret); } gba->ret = ret; } static void F37(s32 chan, s32 ret) { GBAControl* gba = &__GBA[chan]; GBABootInfo* bootInfo = &__GBA[chan].bootInfo; if (ret == GBA_READY) { if ((((bootInfo->readbuf[3] ^ (bootInfo->initialCode >> 24)) | (bootInfo->readbuf[2] ^ (bootInfo->initialCode >> 16)) | (bootInfo->readbuf[1] ^ (bootInfo->initialCode >> 8)) | (bootInfo->readbuf[0] ^ (bootInfo->initialCode >> 0))) << 24) != 0) { ret = GBA_JOYBOOT_UNKNOWN_STATE; } else { ret = GBAWriteAsync(chan, bootInfo->readbuf, bootInfo->status, F39); } } if (ret != GBA_READY) { F104(chan, ret); } gba->ret = ret; } static void F39(s32 chan, s32 ret) { GBAControl* gba = &__GBA[chan]; GBABootInfo* bootInfo = &__GBA[chan].bootInfo; if (ret == GBA_READY) { *bootInfo->status = 0; } F104(chan, ret); gba->ret = ret; } s32 GBAJoyBoot(s32 chan, s32 paletteColor, s32 paletteSpeed, u8* programp, s32 length, u8* status) { s32 ret = GBAJoyBootAsync(chan, paletteColor, paletteSpeed, programp, length, status, __GBASyncCallback); if (ret != GBA_READY) { return ret; } return __GBASync(chan); }