533 lines
14 KiB
C
533 lines
14 KiB
C
|
#include "global.h"
|
||
|
#include "malloc.h"
|
||
|
#include "decompress.h"
|
||
|
#include "ereader_helpers.h"
|
||
|
#include "link.h"
|
||
|
#include "main.h"
|
||
|
#include "mystery_gift_menu.h"
|
||
|
#include "save.h"
|
||
|
#include "sound.h"
|
||
|
#include "sprite.h"
|
||
|
#include "task.h"
|
||
|
#include "strings.h"
|
||
|
#include "util.h"
|
||
|
#include "constants/songs.h"
|
||
|
|
||
|
// Equivalent to MysteryGiftTaskData
|
||
|
struct EReaderTaskData
|
||
|
{
|
||
|
u16 timer;
|
||
|
u16 unused1;
|
||
|
u16 unused2;
|
||
|
u16 unused3;
|
||
|
u8 state;
|
||
|
u8 textState;
|
||
|
u8 unused4;
|
||
|
u8 unused5;
|
||
|
u8 unused6;
|
||
|
u8 unused7;
|
||
|
u8 status;
|
||
|
u8 *unusedBuffer;
|
||
|
};
|
||
|
|
||
|
struct EReaderData
|
||
|
{
|
||
|
u16 status;
|
||
|
u32 size;
|
||
|
u32 *data;
|
||
|
};
|
||
|
|
||
|
static void Task_EReader(u8);
|
||
|
|
||
|
struct EReaderData gEReaderData;
|
||
|
|
||
|
extern const u8 gEReaderLinkData_Start[];
|
||
|
extern const u8 gEReaderLinkData_End[];
|
||
|
|
||
|
static void EReader_Load(struct EReaderData *eReader, int size, u32 *data)
|
||
|
{
|
||
|
volatile u16 backupIME = REG_IME;
|
||
|
REG_IME = 0;
|
||
|
gIntrTable[1] = EReaderHelper_SerialCallback;
|
||
|
gIntrTable[2] = EReaderHelper_Timer3Callback;
|
||
|
EReaderHelper_SaveRegsState();
|
||
|
EReaderHelper_ClearSendRecvMgr();
|
||
|
REG_IE |= INTR_FLAG_VCOUNT;
|
||
|
REG_IME = backupIME;
|
||
|
eReader->status = 0;
|
||
|
eReader->size = size;
|
||
|
eReader->data = data;
|
||
|
}
|
||
|
|
||
|
static void EReader_Reset(struct EReaderData *eReader)
|
||
|
{
|
||
|
volatile u16 backupIME = REG_IME;
|
||
|
REG_IME = 0;
|
||
|
EReaderHelper_ClearSendRecvMgr();
|
||
|
EReaderHelper_RestoreRegsState();
|
||
|
RestoreSerialTimer3IntrHandlers();
|
||
|
REG_IME = backupIME;
|
||
|
}
|
||
|
|
||
|
// Return values for EReader_Transfer
|
||
|
enum {
|
||
|
TRANSFER_ACTIVE,
|
||
|
TRANSFER_SUCCESS,
|
||
|
TRANSFER_CANCELED,
|
||
|
TRANSFER_TIMEOUT,
|
||
|
};
|
||
|
|
||
|
static u8 EReader_Transfer(struct EReaderData *eReader)
|
||
|
{
|
||
|
u8 transferStatus = TRANSFER_ACTIVE;
|
||
|
eReader->status = EReaderHandleTransfer(TRUE, eReader->size, eReader->data, NULL);
|
||
|
|
||
|
if ((eReader->status & EREADER_XFER_MASK) == 0 && eReader->status & EREADER_CHECKSUM_OK_MASK)
|
||
|
transferStatus = TRANSFER_SUCCESS;
|
||
|
|
||
|
if (eReader->status & EREADER_CANCEL_KEY_MASK)
|
||
|
transferStatus = TRANSFER_CANCELED;
|
||
|
|
||
|
if (eReader->status & EREADER_CANCEL_TIMEOUT_MASK)
|
||
|
transferStatus = TRANSFER_TIMEOUT;
|
||
|
|
||
|
gShouldAdvanceLinkState = 0;
|
||
|
return transferStatus;
|
||
|
}
|
||
|
|
||
|
static void OpenEReaderLink(void)
|
||
|
{
|
||
|
memset(gDecompressionBuffer, 0, 0x2000);
|
||
|
gLinkType = LINKTYPE_EREADER;
|
||
|
OpenLink();
|
||
|
SetSuppressLinkErrorMessage(TRUE);
|
||
|
}
|
||
|
|
||
|
static bool32 ValidateEReaderConnection(void)
|
||
|
{
|
||
|
volatile u16 backupIME;
|
||
|
u16 handshakes[MAX_LINK_PLAYERS];
|
||
|
|
||
|
backupIME = REG_IME;
|
||
|
REG_IME = 0;
|
||
|
*(u64 *)handshakes = *(u64 *)gLink.handshakeBuffer;
|
||
|
REG_IME = backupIME;
|
||
|
|
||
|
// Validate that we are player 1, the EReader is player 2,
|
||
|
// and that players 3 and 4 are empty.
|
||
|
if (handshakes[0] == SLAVE_HANDSHAKE && handshakes[1] == EREADER_HANDSHAKE
|
||
|
&& handshakes[2] == 0xFFFF && handshakes[3] == 0xFFFF)
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
static bool32 IsChildConnected(void)
|
||
|
{
|
||
|
if (IsLinkMaster() && GetLinkPlayerCount_2() == 2)
|
||
|
return TRUE;
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// States for TryReceiveCard
|
||
|
enum {
|
||
|
RECV_STATE_INIT,
|
||
|
RECV_STATE_WAIT_START,
|
||
|
RECV_STATE_START,
|
||
|
RECV_STATE_EXCHANGE,
|
||
|
RECV_STATE_START_DISCONNECT,
|
||
|
RECV_STATE_WAIT_DISCONNECT,
|
||
|
};
|
||
|
|
||
|
// Return values for TryReceiveCard
|
||
|
enum {
|
||
|
RECV_ACTIVE,
|
||
|
RECV_CANCELED,
|
||
|
RECV_SUCCESS,
|
||
|
RECV_ERROR,
|
||
|
RECV_DISCONNECTED,
|
||
|
RECV_TIMEOUT,
|
||
|
};
|
||
|
|
||
|
static u32 TryReceiveCard(u8 *state, u16 *timer)
|
||
|
{
|
||
|
if (*state >= RECV_STATE_EXCHANGE
|
||
|
&& *state <= RECV_STATE_WAIT_DISCONNECT
|
||
|
&& HasLinkErrorOccurred())
|
||
|
{
|
||
|
// Return error status if an error occurs
|
||
|
// during the link exchange.
|
||
|
*state = 0;
|
||
|
return RECV_ERROR;
|
||
|
}
|
||
|
|
||
|
switch (*state)
|
||
|
{
|
||
|
case RECV_STATE_INIT:
|
||
|
if (IsLinkMaster() && GetLinkPlayerCount_2() > 1)
|
||
|
{
|
||
|
*state = RECV_STATE_WAIT_START;
|
||
|
}
|
||
|
else if (JOY_NEW(B_BUTTON))
|
||
|
{
|
||
|
*state = 0;
|
||
|
return RECV_CANCELED;
|
||
|
}
|
||
|
break;
|
||
|
case RECV_STATE_WAIT_START:
|
||
|
if (++(*timer) > 5)
|
||
|
{
|
||
|
*timer = 0;
|
||
|
*state = RECV_STATE_START;
|
||
|
}
|
||
|
break;
|
||
|
case RECV_STATE_START:
|
||
|
if (GetLinkPlayerCount_2() == 2)
|
||
|
{
|
||
|
PlaySE(SE_DING_DONG);
|
||
|
CheckShouldAdvanceLinkState();
|
||
|
*timer = 0;
|
||
|
*state = RECV_STATE_EXCHANGE;
|
||
|
}
|
||
|
else if (JOY_NEW(B_BUTTON))
|
||
|
{
|
||
|
*state = 0;
|
||
|
return RECV_CANCELED;
|
||
|
}
|
||
|
break;
|
||
|
case RECV_STATE_EXCHANGE:
|
||
|
if (++(*timer) > 30)
|
||
|
{
|
||
|
*state = 0;
|
||
|
return RECV_TIMEOUT;
|
||
|
}
|
||
|
|
||
|
if (IsLinkConnectionEstablished())
|
||
|
{
|
||
|
if (gReceivedRemoteLinkPlayers)
|
||
|
{
|
||
|
if (IsLinkPlayerDataExchangeComplete())
|
||
|
{
|
||
|
*state = 0;
|
||
|
return RECV_SUCCESS;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*state = RECV_STATE_START_DISCONNECT;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*state = RECV_STATE_EXCHANGE;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case RECV_STATE_START_DISCONNECT:
|
||
|
SetCloseLinkCallbackAndType(0);
|
||
|
*state = RECV_STATE_WAIT_DISCONNECT;
|
||
|
break;
|
||
|
case RECV_STATE_WAIT_DISCONNECT:
|
||
|
if (!gReceivedRemoteLinkPlayers)
|
||
|
{
|
||
|
*state = 0;
|
||
|
return RECV_DISCONNECTED;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
return RECV_ACTIVE;
|
||
|
}
|
||
|
|
||
|
return RECV_ACTIVE;
|
||
|
}
|
||
|
|
||
|
void CreateEReaderTask(void)
|
||
|
{
|
||
|
struct EReaderTaskData *data;
|
||
|
u8 taskId = CreateTask(Task_EReader, 0);
|
||
|
data = (struct EReaderTaskData *)gTasks[taskId].data;
|
||
|
data->state = 0;
|
||
|
data->textState = 0;
|
||
|
data->unused4 = 0;
|
||
|
data->unused5 = 0;
|
||
|
data->unused6 = 0;
|
||
|
data->unused7 = 0;
|
||
|
data->timer = 0;
|
||
|
data->unused1 = 0;
|
||
|
data->unused2 = 0;
|
||
|
data->unused3 = 0;
|
||
|
data->status = 0;
|
||
|
data->unusedBuffer = AllocZeroed(0x40);
|
||
|
}
|
||
|
|
||
|
static void ResetTimer(u16 *timer)
|
||
|
{
|
||
|
*timer = 0;
|
||
|
}
|
||
|
|
||
|
static bool32 UpdateTimer(u16 *timer, u16 time)
|
||
|
{
|
||
|
if (++(*timer) > time)
|
||
|
{
|
||
|
// Timer has finished
|
||
|
*timer = 0;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// States for Task_EReader
|
||
|
enum {
|
||
|
ER_STATE_START,
|
||
|
ER_STATE_INIT_LINK,
|
||
|
ER_STATE_INIT_LINK_WAIT,
|
||
|
ER_STATE_INIT_LINK_CHECK,
|
||
|
ER_STATE_MSG_SELECT_CONNECT,
|
||
|
ER_STATE_MSG_SELECT_CONNECT_WAIT,
|
||
|
ER_STATE_TRY_LINK,
|
||
|
ER_STATE_INCORRECT_LINK,
|
||
|
ER_STATE_CONNECTING,
|
||
|
ER_STATE_TRANSFER,
|
||
|
ER_STATE_TRANSFER_END,
|
||
|
ER_STATE_TRANSFER_SUCCESS,
|
||
|
ER_STATE_LOAD_CARD_START,
|
||
|
ER_STATE_LOAD_CARD,
|
||
|
ER_STATE_WAIT_RECV_CARD,
|
||
|
ER_STATE_VALIDATE_CARD,
|
||
|
ER_STATE_WAIT_DISCONNECT,
|
||
|
ER_STATE_SAVE,
|
||
|
ER_STATE_SUCCESS_MSG,
|
||
|
ER_STATE_SUCCESS_END,
|
||
|
ER_STATE_LINK_ERROR,
|
||
|
ER_STATE_LINK_ERROR_TRY_AGAIN,
|
||
|
ER_STATE_SAVE_FAILED,
|
||
|
ER_STATE_CANCELED_CARD_READ,
|
||
|
ER_STATE_UNUSED_1,
|
||
|
ER_STATE_UNUSED_2,
|
||
|
ER_STATE_END,
|
||
|
};
|
||
|
|
||
|
static void Task_EReader(u8 taskId)
|
||
|
{
|
||
|
struct EReaderTaskData *data = (struct EReaderTaskData *)gTasks[taskId].data;
|
||
|
switch (data->state)
|
||
|
{
|
||
|
case ER_STATE_START:
|
||
|
if (PrintMysteryGiftMenuMessage(&data->textState, gJPText_ReceiveMysteryGiftWithEReader))
|
||
|
data->state = ER_STATE_INIT_LINK;
|
||
|
break;
|
||
|
case ER_STATE_INIT_LINK:
|
||
|
OpenEReaderLink();
|
||
|
ResetTimer(&data->timer);
|
||
|
data->state = ER_STATE_INIT_LINK_WAIT;
|
||
|
break;
|
||
|
case ER_STATE_INIT_LINK_WAIT:
|
||
|
if (UpdateTimer(&data->timer, 10))
|
||
|
data->state = ER_STATE_INIT_LINK_CHECK;
|
||
|
break;
|
||
|
case ER_STATE_INIT_LINK_CHECK:
|
||
|
if (!IsChildConnected())
|
||
|
{
|
||
|
CloseLink();
|
||
|
data->state = ER_STATE_MSG_SELECT_CONNECT;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
data->state = ER_STATE_LOAD_CARD;
|
||
|
}
|
||
|
break;
|
||
|
case ER_STATE_MSG_SELECT_CONNECT:
|
||
|
if (PrintMysteryGiftMenuMessage(&data->textState, gJPText_SelectConnectFromEReaderMenu))
|
||
|
{
|
||
|
AddTextPrinterToWindow1(gJPText_SelectConnectWithGBA);
|
||
|
ResetTimer(&data->timer);
|
||
|
data->state = ER_STATE_MSG_SELECT_CONNECT_WAIT;
|
||
|
}
|
||
|
break;
|
||
|
case ER_STATE_MSG_SELECT_CONNECT_WAIT:
|
||
|
if (UpdateTimer(&data->timer, 90))
|
||
|
{
|
||
|
OpenEReaderLink();
|
||
|
data->state = ER_STATE_TRY_LINK;
|
||
|
}
|
||
|
else if (JOY_NEW(B_BUTTON))
|
||
|
{
|
||
|
ResetTimer(&data->timer);
|
||
|
PlaySE(SE_SELECT);
|
||
|
data->state = ER_STATE_CANCELED_CARD_READ;
|
||
|
}
|
||
|
break;
|
||
|
case ER_STATE_TRY_LINK:
|
||
|
if (JOY_NEW(B_BUTTON))
|
||
|
{
|
||
|
// Canceled
|
||
|
PlaySE(SE_SELECT);
|
||
|
CloseLink();
|
||
|
ResetTimer(&data->timer);
|
||
|
data->state = ER_STATE_CANCELED_CARD_READ;
|
||
|
}
|
||
|
else if (GetLinkPlayerCount_2() > 1)
|
||
|
{
|
||
|
ResetTimer(&data->timer);
|
||
|
CloseLink();
|
||
|
data->state = ER_STATE_INCORRECT_LINK;
|
||
|
}
|
||
|
else if (ValidateEReaderConnection())
|
||
|
{
|
||
|
// Successful connection
|
||
|
PlaySE(SE_SELECT);
|
||
|
CloseLink();
|
||
|
ResetTimer(&data->timer);
|
||
|
data->state = ER_STATE_CONNECTING;
|
||
|
}
|
||
|
else if (UpdateTimer(&data->timer, 10))
|
||
|
{
|
||
|
// Retry connection
|
||
|
CloseLink();
|
||
|
OpenEReaderLink();
|
||
|
ResetTimer(&data->timer);
|
||
|
}
|
||
|
break;
|
||
|
case ER_STATE_INCORRECT_LINK:
|
||
|
if (PrintMysteryGiftMenuMessage(&data->textState, gJPText_LinkIsIncorrect))
|
||
|
data->state = ER_STATE_MSG_SELECT_CONNECT;
|
||
|
break;
|
||
|
case ER_STATE_CONNECTING:
|
||
|
AddTextPrinterToWindow1(gJPText_Connecting);
|
||
|
// XXX: This (u32*) cast is discarding the const qualifier from gEReaderLinkData_Start
|
||
|
EReader_Load(&gEReaderData, gEReaderLinkData_End - gEReaderLinkData_Start, (u32*)gEReaderLinkData_Start);
|
||
|
data->state = ER_STATE_TRANSFER;
|
||
|
break;
|
||
|
case ER_STATE_TRANSFER:
|
||
|
data->status = EReader_Transfer(&gEReaderData);
|
||
|
if (data->status != TRANSFER_ACTIVE)
|
||
|
data->state = ER_STATE_TRANSFER_END;
|
||
|
break;
|
||
|
case ER_STATE_TRANSFER_END:
|
||
|
EReader_Reset(&gEReaderData);
|
||
|
if (data->status == TRANSFER_TIMEOUT)
|
||
|
{
|
||
|
data->state = ER_STATE_LINK_ERROR;
|
||
|
}
|
||
|
else if (data->status == TRANSFER_SUCCESS)
|
||
|
{
|
||
|
ResetTimer(&data->timer);
|
||
|
AddTextPrinterToWindow1(gJPText_PleaseWaitAMoment);
|
||
|
data->state = ER_STATE_TRANSFER_SUCCESS;
|
||
|
}
|
||
|
else // TRANSFER_CANCELED
|
||
|
{
|
||
|
data->state = ER_STATE_START;
|
||
|
}
|
||
|
break;
|
||
|
case ER_STATE_TRANSFER_SUCCESS:
|
||
|
if (UpdateTimer(&data->timer, 840))
|
||
|
data->state = ER_STATE_LOAD_CARD_START;
|
||
|
break;
|
||
|
case ER_STATE_LOAD_CARD_START:
|
||
|
OpenEReaderLink();
|
||
|
AddTextPrinterToWindow1(gJPText_AllowEReaderToLoadCard);
|
||
|
data->state = ER_STATE_LOAD_CARD;
|
||
|
break;
|
||
|
case ER_STATE_LOAD_CARD:
|
||
|
switch (TryReceiveCard(&data->textState, &data->timer))
|
||
|
{
|
||
|
case RECV_ACTIVE:
|
||
|
break;
|
||
|
case RECV_SUCCESS:
|
||
|
AddTextPrinterToWindow1(gJPText_Connecting);
|
||
|
data->state = ER_STATE_WAIT_RECV_CARD;
|
||
|
break;
|
||
|
case RECV_CANCELED:
|
||
|
PlaySE(SE_SELECT);
|
||
|
CloseLink();
|
||
|
data->state = ER_STATE_CANCELED_CARD_READ;
|
||
|
break;
|
||
|
case RECV_TIMEOUT:
|
||
|
CloseLink();
|
||
|
data->state = ER_STATE_LINK_ERROR_TRY_AGAIN;
|
||
|
break;
|
||
|
case RECV_ERROR:
|
||
|
case RECV_DISCONNECTED:
|
||
|
CloseLink();
|
||
|
data->state = ER_STATE_LINK_ERROR;
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case ER_STATE_WAIT_RECV_CARD:
|
||
|
if (HasLinkErrorOccurred())
|
||
|
{
|
||
|
CloseLink();
|
||
|
data->state = ER_STATE_LINK_ERROR;
|
||
|
}
|
||
|
else if (GetBlockReceivedStatus())
|
||
|
{
|
||
|
ResetBlockReceivedFlags();
|
||
|
data->state = ER_STATE_VALIDATE_CARD;
|
||
|
}
|
||
|
break;
|
||
|
case ER_STATE_VALIDATE_CARD:
|
||
|
data->status = ValidateTrainerHillData((struct EReaderTrainerHillSet *)gDecompressionBuffer);
|
||
|
SetCloseLinkCallbackAndType(data->status);
|
||
|
data->state = ER_STATE_WAIT_DISCONNECT;
|
||
|
break;
|
||
|
case ER_STATE_WAIT_DISCONNECT:
|
||
|
if (!gReceivedRemoteLinkPlayers)
|
||
|
{
|
||
|
if (data->status == TRUE) // Was data valid?
|
||
|
data->state = ER_STATE_SAVE;
|
||
|
else
|
||
|
data->state = ER_STATE_LINK_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
case ER_STATE_SAVE:
|
||
|
if (TryWriteTrainerHill((struct EReaderTrainerHillSet *)&gDecompressionBuffer))
|
||
|
{
|
||
|
AddTextPrinterToWindow1(gJPText_ConnectionComplete);
|
||
|
ResetTimer(&data->timer);
|
||
|
data->state = ER_STATE_SUCCESS_MSG;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
data->state = ER_STATE_SAVE_FAILED;
|
||
|
}
|
||
|
break;
|
||
|
case ER_STATE_SUCCESS_MSG:
|
||
|
if (UpdateTimer(&data->timer, 120))
|
||
|
{
|
||
|
AddTextPrinterToWindow1(gJPText_NewTrainerHasComeToHoenn);
|
||
|
PlayFanfare(MUS_OBTAIN_ITEM);
|
||
|
data->state = ER_STATE_SUCCESS_END;
|
||
|
}
|
||
|
break;
|
||
|
case ER_STATE_SUCCESS_END:
|
||
|
if (IsFanfareTaskInactive() && (JOY_NEW(A_BUTTON | B_BUTTON)))
|
||
|
data->state = ER_STATE_END;
|
||
|
break;
|
||
|
case ER_STATE_CANCELED_CARD_READ:
|
||
|
if (PrintMysteryGiftMenuMessage(&data->textState, gJPText_CardReadingHasBeenHalted))
|
||
|
data->state = ER_STATE_END;
|
||
|
break;
|
||
|
case ER_STATE_LINK_ERROR:
|
||
|
if (PrintMysteryGiftMenuMessage(&data->textState, gJPText_ConnectionErrorCheckLink))
|
||
|
data->state = ER_STATE_START;
|
||
|
break;
|
||
|
case ER_STATE_LINK_ERROR_TRY_AGAIN:
|
||
|
if (PrintMysteryGiftMenuMessage(&data->textState, gJPText_ConnectionErrorTryAgain))
|
||
|
data->state = ER_STATE_START;
|
||
|
break;
|
||
|
case ER_STATE_SAVE_FAILED:
|
||
|
if (PrintMysteryGiftMenuMessage(&data->textState, gJPText_WriteErrorUnableToSaveData))
|
||
|
data->state = ER_STATE_START;
|
||
|
break;
|
||
|
case ER_STATE_END:
|
||
|
Free(data->unusedBuffer);
|
||
|
DestroyTask(taskId);
|
||
|
SetMainCallback2(MainCB_FreeAllBuffersAndReturnToInitTitleScreen);
|
||
|
break;
|
||
|
}
|
||
|
}
|