#include "global.h"
#include "event_flag.h"
#include "flash.h"
#include "friend_area.h"
#include "memory.h"
#include "pokemon.h"
#include "random.h"
#include "save.h"
#include "team_inventory.h"
#include "exclusive_pokemon.h"


EWRAM_DATA u32 gUnknown_203B17C;
EWRAM_DATA char *gUnknown_203B180;
EWRAM_DATA struct UnkStruct_203B184 *gUnknown_203B184;

struct unk_struct
{
    // size: 0x800
    u32 unk0;
    u8 unk4[0x10]; // has "POKE_DUNGEON__05
    u32 unk14;
    u32 unk18;
    u32 dungeonLocation;
    u32 unk20;
    u32 padding[503];
};

struct SavePakRead
{
    u32 state;
    u32 readStatus;
};

EWRAM_DATA struct SavePakRead *gSavePakRead;

struct SavePakWrite
{
    u32 state;
    s32 unk4;
    u32 saveStatus;
    struct OpenedFile *faceFile;
    u8 *faceData;
    u16 unk14;
    u16 unk16;
    u8 unk18;
    u8 unk19;
    u8 unk1A;
    u8 unk1B;
    u16 pokeID;
};

EWRAM_DATA struct SavePakWrite *gSavePakWrite;

struct QuickSaveRead
{
    s32 state;
    u8 *dest;
    u32 size;
    bool8 saveValid;
};
EWRAM_DATA struct QuickSaveRead *gQuickSaveRead;

struct QuickSaveWrite
{
    s32 state;
    u8 *unk4;
    u32 unk8;
    u8 unkC;
    u32 quickSaveStatus;
};
EWRAM_DATA struct QuickSaveWrite *gQuickSaveWrite;


extern s32 gUnknown_202DE28;
extern bool8 *gFriendAreas;
extern u32 gUnknown_203B46C;
extern u8 *gUnknown_203B480;
extern u8 *gUnknown_203B484;
extern u32 *gUnknown_203B488;
extern u32 *gUnknown_203B48C;
extern u32 gUnknown_203B490;
extern u32 gUnknown_203B494;

ALIGNED(4) const char PokeDungeon_Text[] = _("POKE_DUNGEON__05");

ALIGNED(4) const char fill_save0[] = _("pksdir0");
ALIGNED(4) const char fill_save1[] = _("pksdir0");
ALIGNED(4) const char fill_save2[] = _("pksdir0");
ALIGNED(4) const char fill_save3[] = _("pksdir0");

ALIGNED(4) const char gSaveCorrupted[] = _("{CENTER_ALIGN}The game data is corrupted.\n"
                                            "{CENTER_ALIGN}Your data will be erased.");

ALIGNED(4) const char fill_save4[] = _("pksdir0");

ALIGNED(4) const char gSavingAdventure[] = _("{CENTER_ALIGN}Saving your adventure...\n"
                                            "{CENTER_ALIGN}Please don{APOSTROPHE}t turn off the power.");

ALIGNED(4) const char gWriteGamePak[] = _("{CENTER_ALIGN}Writing to GBA Game Pak.\n"
                                      "{CENTER_ALIGN}Do not remove the GBA Game Pak.\n"
                                      "{CENTER_ALIGN}Please wait with the power on.");

ALIGNED(4) const char gSaveCompleted[] = _("{CENTER_ALIGN}Save completed!");

ALIGNED(4) const char gSaveNotWritten[] = _("{CENTER_ALIGN}The data could not be written.\n"
                                        "{CENTER_ALIGN}Please turn off the power and remove\n"
                                        "{CENTER_ALIGN}and reinsert the DS Card.");

ALIGNED(4) const char gSaveFailed[] = _("{CENTER_ALIGN}Save failed.");

ALIGNED(4) const char fill_save5[] = _("pksdir0");

ALIGNED(4) const char gUnknown_80D44C8[] = _("{CENTER_ALIGN}Your data was not properly saved{COMMA}\n"
                                             "{CENTER_ALIGN}so your game cannot be resumed\n"
                                             "{CENTER_ALIGN}from your last spot.{EXTRA_MSG}"
                                             "{CENTER_ALIGN}Your last outing is considered a defeat.\n"
                                             "{CENTER_ALIGN}Before shutting down{COMMA} save in your\n"
                                             "{CENTER_ALIGN}team base{COMMA} or quicksave in a dungeon.");

ALIGNED(4) const char fill_save6[] = _("pksdir0");

ALIGNED(4) const char gUnknown_80D45AC[] = _("{CENTER_ALIGN}Quicksaving your adventure...\n"
                                             "{CENTER_ALIGN}Please don{APOSTROPHE}t turn off the power.");

ALIGNED(4) const char gUnknown_80D45F4[] = _("{CENTER_ALIGN}Your adventure has been saved.\n"
                                             "{CENTER_ALIGN}When you are in a dungeon{COMMA} quicksave\n"
                                             "{CENTER_ALIGN}your progress before ending the game.");

ALIGNED(4) const char gUnknown_80D4668[] = _("{CENTER_ALIGN}Your adventure has been saved.\n"
                                             "{CENTER_ALIGN}When you resume play{COMMA} the quicksave\n"
                                             "{CENTER_ALIGN}data will be deleted.{EXTRA_MSG}"
                                             "{CENTER_ALIGN}When you are shutting down the game{COMMA}\n"
                                             "{CENTER_ALIGN}save your progress in your team base{COMMA}\n"
                                             "{CENTER_ALIGN}or quicksave in a dungeon.");

ALIGNED(4) const char gSaveNotWritten2[] = _("{CENTER_ALIGN}The data could not be written.\n"
                                             "{CENTER_ALIGN}Please turn off the power and remove\n"
                                             "{CENTER_ALIGN}and reinsert the DS Card.");

ALIGNED(4) const char gSaveFailed2[] = _("{CENTER_ALIGN}Save failed.");
ALIGNED(4) const char fill_save7[] = _("pksdir0");


extern bool8 sub_800DAB0(u16, u8*, s32);
extern bool8 sub_800DAB4(u16, u8*, s32);
extern bool8 sub_800DAB8(void);
extern void sub_8011830(void);
extern void sub_80140DC();
extern void sub_8014114();
extern void sub_80141B4(const char *r0, u32 r1, u8 *r2, u16 r3);
extern u32 sub_80144A4(u32 *a);
extern u32 SaveRecruitedPokemon(u8 *, u32);
extern u32 RestoreRecruitedPokemon(void* a, s32 b);
extern u32 SavePokemonStruct2(u8 *, u32);
extern u32 RestorePokemonStruct2(void* a, s32 b);
extern u32 sub_80921C4(u8 *, u32);
extern u8 *sub_8095100(void);
extern u32 *sub_8095108(void);
extern u32 *sub_8095110(void);
extern void sub_8095118(void);
extern u32 sub_80954CC(void* a, s32 b);
extern u32 sub_8095624(u8 *, u32);
extern u32 sub_8096FA0(void* a, s32 b);
extern u32 sub_80970D8(u8 *, u32);
extern void sub_80976A8();
extern u32 sub_8097D60(u8 *, u32);
extern u32 sub_8097D98(void* a, s32 b);
extern void sub_80993E4();
extern void sub_800135C(void);
extern u32 GetGameOptions(void);
extern void InitializeGameOptions(u8 r0);
extern u8 *sub_80950F8(void);
extern void sub_80958E4(u32 *a, u32 b);
extern u32 sub_80958F8(void);
extern void sub_8095900(void);
extern void sub_80972F4(void);
extern u32 sub_8097680(void);
extern u32 *GetDungeonLocationInfo(void);
extern void sub_80974E8(void);
extern void ResetNumAdventures(void);
extern void sub_80993D8(void);

extern void xxx_call_start_bg_music(void);


u32 sub_8011C1C(void)
{
    return gUnknown_203B17C;
}

void sub_8011C28(u32 in)
{
    gUnknown_203B17C = in;
}

s32 sub_8011C34(void)
{
    return gUnknown_202DE28;
}

void sub_8011C40(s32 in)
{
    gUnknown_202DE28 = in;
}

char *sub_8011C4C(void)
{
    return gUnknown_203B180; // returns POKE_DUNGEON__05
}

void CalculateChecksum(u8 *out, u32 size)
{
    u32 checksum = 0;
    s32 i = size / 4;
    if (i > 1)
    {
        u32 *ptr = (u32 *)&out[4];
        --i;
        do
            checksum += *ptr++;
        while (--i);
    }
    *(u32 *)out = checksum;
}

bool8 ValidateChecksum(u8 *in, u32 size)
{
    u32 checksum = 0;
    s32 i = size / 4;
    if (i > 1)
	{
        u32 *ptr = (u32 *)&in[4];
        --i;
        do
            checksum += *ptr++;
        while (--i);
	}
    if (*(u32 *)in != checksum)
        return TRUE;
    return FALSE;
}

void sub_8011CA8(u32 *out, s32 size)
{
    if (gUnknown_203B184 == NULL) {
        AGB_WARNING("A warning most likely went here.");
        *out += (size + 0xFFF) / 0x1000;
    } else {
        *out += (size + 0xFFF) / 0x1000;
    }
}

u32 WriteSaveSector(s32 *a, u8 *src, s32 size)
{
    u32 r1;
    s32 r6 = *a;
    sub_8011CA8(a, size);
    CalculateChecksum(src, size);
    if (gUnknown_203B184 == NULL) {
        r1 = WriteFlashData(r6, src, size);
    }
    else if (sub_800DAB8()) {
        if (!sub_800DAB4(r6, src, size)) {
            r1 = 3;
        }
        else {
            r1 = 0;
        }
    }
    else {
        return 2;
    }
    if (r1 == 4) {
        return 1;
    }
    if (r1) {
        return 2;
    }
    return 0;
}

u32 ReadSaveSector(s32 *a, u8 *dest, s32 size)
{
    u32 r1;
    s32 r6 = *a;
    sub_8011CA8(a, size);
    if (gUnknown_203B184 == NULL) {
        r1 = ReadFlashData(r6, dest, size);
    }
    else if (sub_800DAB8()) {
        if (!sub_800DAB0(r6, dest, size)) {
            r1 = 1;
        }
        else {
            r1 = 0;
        }
    }
    else {
        return READ_SAVE_FAILED;
    }
    if (r1) {
        return READ_SAVE_FAILED;
    }
    if (ValidateChecksum(dest, size)) {
        return READ_SAVE_CHECKSUM_ERROR;
    }
    return READ_SAVE_VALID;
}

// Unused
bool8 sub_8011DA8(void)
{
    return TRUE;
}

u32 ReadSaveFromPak(u32 *a)
{
    struct UnkStruct_sub_8011DAC *r5 = MemoryAlloc(sizeof(struct UnkStruct_sub_8011DAC), 5);
    u8 *r4 = (u8*)r5->unk448;
    u32 r7 = ReadSaveSector(a, (u8*)r5, sizeof(struct UnkStruct_sub_8011DAC));
    u32 r1;
    if (r7)
    {
        r7 = ReadSaveSector(a, (u8*)r5, sizeof(struct UnkStruct_sub_8011DAC));
    }
    else
    {
        sub_8011CA8(a, sizeof(struct UnkStruct_sub_8011DAC));
    }
    if (!r7)
    {
        if (r5->unk414 != 0x5071412) {
            r7 = 4;
        }
    }
    if (!r7)
    {
        if (gUnknown_203B184 == NULL) {
            sub_8011C28(r5->unk41C);
            sub_8011C40(r5->unk418);
            SetRngState(r5->RngState);
        }
        else {
            gUnknown_203B184->unk054 = r5->unk41C;
            gUnknown_203B184->unk050 = r5->unk418;
            gUnknown_203B184->RngState = r5->RngState;
        }
    }
    if (!r7)
    {
        if (gUnknown_203B184 == NULL) {
            if (!sub_8002718(r5->unk004)) {
                r7 = 4;
            }
        }
        else {
            MemoryCopy8(gUnknown_203B184->unk04C, r5->unk004, ARRAY_COUNT(r5->unk004));
        }
    }
    if (!r7)
    {
        r1 = RestoreRecruitedPokemon(r4, 0x4650);
        if (r1 != r5->savedRecruitedPokemon) {
            r7 = 3;
        }
        r4 += 0x4650;
        r1 = RestorePokemonStruct2(r4, 0x258);
        if (r1 != r5->unk428) {
            r7 = 3;
        }
        r4 += 0x258;
        r1 = RestoreTeamInventory(r4, 0x1D8);
        if (r1 != r5->unk430) {
            r7 = 3;
        }
        r4 += 0x1d8;
        r1 = ReadRescueTeamInfo(r4, 0x10);
        if (r1 != r5->savedRescueTeamInfo) {
            r7 = 3;
        }
        r4 += 0x10;
        r1 = ReadSavedFriendAreas(r4, 0x8);
        if (r1 != r5->savedFriendAreas) {
            r7 = 3;
        }
        r4 += 0x8;
        r1 = sub_8097D98(r4, 0x100);
        if (r1 != r5->unk43C) {
            r7 = 3;
        }
        r4 += 0x100;
        r1 = sub_80954CC(r4, 0x594);
        if (r1 != r5->unk440) {
            r7 = 3;
        }
        r4 += 0x594;
        r1 = sub_8096FA0(r4, 0x221);
        if (r1 != r5->unk444) {
            r7 = 3;
        }
    }
    MemoryFree(r5);
    return r7;
}

u32 sub_8011F9C(s32 *r0, u8 *dest, s32 size)
{
    return ReadSaveSector(r0, dest, size);
}

u32 sub_8011FA8(void)
{
    u32 temp2;
    u32 temp3;
    u32 temp;
    struct unk_struct *r5 = MemoryAlloc(sizeof(struct unk_struct), 5);
    temp = 0x1F;
    temp2 = ReadSaveSector(&temp, (u8 *)r5, sizeof(struct unk_struct));
    if( temp2 == READ_SAVE_VALID)
    {
        if(r5->unk14 != 0x5071412)
        {
            temp2 = 4;
        }
    }
    temp3 = 0xf1209;
    if(temp2 == READ_SAVE_VALID)
    {
        temp3 = r5->unk18;
    }
    MemoryFree(r5);
    return temp3;
}

bool8 IsSaveCorrupted(void)
{
    u32 temp;
    bool8 isCorrupted;
    struct unk_struct *r5 = MemoryAlloc(sizeof(struct unk_struct), 5);
    temp = 0x1F;
    isCorrupted = FALSE;
    if(ReadSaveSector(&temp, (u8 *)r5, sizeof(struct unk_struct)) == READ_SAVE_VALID)
    {
        if(r5->unk14 == 0x5071412)
        {
            isCorrupted = TRUE;
        }
    }
    MemoryFree(r5);
    return isCorrupted;
}

u32 WriteSavetoPak(s32 *param_1, u32 param_2)
{
  struct UnkStruct_sub_8011DAC *iVar1;
  char *__src;
  int r5;
  int r4;
  u8 *array_ptr;

  iVar1 = MemoryAlloc(sizeof(struct UnkStruct_sub_8011DAC),5);
  array_ptr = iVar1->unk448;
  if (gUnknown_203B184 == NULL) {
    iVar1->unk41C = param_2;
    iVar1->unk418 = sub_8011C34();
    iVar1->RngState = GetRngState();
  }
  else {
    iVar1->unk41C = gUnknown_203B184->unk054;
    iVar1->unk418 = gUnknown_203B184->unk050;
    iVar1->RngState = gUnknown_203B184->RngState;
  }
   iVar1->unk414 = 0x5071412;
  __src = sub_8011C4C();
  strncpy(iVar1->unk404,__src, ARRAY_COUNT(iVar1->unk404));
  if (gUnknown_203B184 == NULL) {
    sub_8002700(iVar1->unk004);
  }
  else {
    MemoryCopy8(iVar1->unk004,gUnknown_203B184->unk04C,ARRAY_COUNT(iVar1->unk004));
  }

  iVar1->savedRecruitedPokemon = SaveRecruitedPokemon(array_ptr,0x4650);
  array_ptr += 0x4650;
  iVar1->unk428 = SavePokemonStruct2(array_ptr,0x258);
  array_ptr += 0x258;
  iVar1->unk430 = SaveTeamInventory(array_ptr,0x1D8);
  array_ptr += 0x1D8;
  iVar1->savedRescueTeamInfo = SaveRescueTeamInfo(array_ptr,0x10);
  array_ptr += 0x10;
  iVar1->savedFriendAreas = SaveFriendAreas(array_ptr,8);
  array_ptr += 8;
  iVar1->unk43C = sub_8097D60(array_ptr,0x100);
  array_ptr += 0x100;
  iVar1->unk440 = sub_8095624(array_ptr,0x594);
  array_ptr += 0x594;
  iVar1->unk444 = sub_80970D8(array_ptr,0x221);

  r5 = WriteSaveSector(param_1,(u8 *)iVar1,sizeof(struct UnkStruct_sub_8011DAC));
  r4 = WriteSaveSector(param_1,(u8 *)iVar1,sizeof(struct UnkStruct_sub_8011DAC));
  MemoryFree(iVar1);

  if (r5 != SAVE_COMPLETED)
    return r5;
  if (r4 != SAVE_COMPLETED)
    return r4;

  return SAVE_COMPLETED;
}


s32 sub_80121D4(s32 *a, u8 *src, s32 size)
{
    return WriteSaveSector(a, src, size);
}

u32 sub_80121E0(u32 r0)
{
    u32 temp;
    char *string;
    u32 temp3;
    struct unk_struct *r4 = MemoryAlloc(sizeof(struct unk_struct), 5);
    temp = 0x1F;

    r4->unk18 = r0;
    r4->dungeonLocation = *GetDungeonLocationInfo();
    r4->unk14 = 0x5071412;

    string = sub_8011C4C();
    strncpy(r4->unk4, string, ARRAY_COUNT(r4->unk4));
    sub_80958E4(&r4->unk20, 0);
    temp3 = WriteSaveSector(&temp, (u8 *)r4, sizeof(struct unk_struct));
    MemoryFree(r4);
    return temp3;
}

u32 sub_8012240(void)
{
    s32 temp;
    struct unk_struct *r5 = MemoryAlloc(sizeof(struct unk_struct), 5);
    MemoryFill8((u8 *)r5, 0xFF, 0x4);
    sub_8011830();
    temp = WriteFlashData(0x1F, (u8 *)r5, sizeof(struct unk_struct));
    xxx_call_start_bg_music();
    MemoryFree(r5);
    if(temp != 0)
        return 2;
    else
        return 0;
}

void sub_8012284(void)
{
    sub_80122D0();
    sub_80122F4();
    InitializePlayerData();
}

void sub_8012298(void)
{
    sub_80976A8();
    sub_80122A8();
}

void sub_80122A8(void)
{
    sub_80122D0();
    sub_80122F4();
    InitializePlayerData();
    sub_8012240();
}

void nullsub_33(void)
{
}

void sub_80122C4(void)
{
    sub_80122A8();
}

void sub_80122D0(void)
{
    sub_8011C28(0);
    sub_8011C40(-1);
    ResetNumAdventures();
    ResetPlayTime(gPlayTimeRef);
}

void sub_80122F4(void)
{
    sub_800135C();
}

void InitializePlayerData(void)
{
    InitializeFriendAreas();
    InitializeRecruitedPokemon();
    InitializeMoneyItems();
    InitializeRescueTeamInfo();
    sub_80972F4();
    sub_8095118();
    sub_8095900();
    sub_80974E8();
    InitializeGameOptions(1);
    InitializeExclusivePokemon();
}


// Unused
void nullsub_200(u32 r0)
{
}

// Unused
void sub_8012334(struct UnkStruct_203B184 *r0)
{
    gUnknown_203B184 = r0;
    if(r0 != NULL)
    {
       gTeamInventory_203B460 = r0->MoneyItems;
       gRecruitedPokemonRef = r0->recruitedPokemon;
       gUnknown_203B480 = r0->unk8;
       gUnknown_203B484 = r0->unkC;
       gUnknown_203B488 = r0->unk10;
       gUnknown_203B48C = r0->unk14;
       gUnknown_203B490 = r0->unk18;
       gRescueTeamInfoRef = r0->RescueTeamInfo;
       gUnknown_203B494 = r0->unk20;
       gUnknown_203B498 = r0->ExclusivePokemon;
       gFriendAreas     = r0->BoughtFriendAreas;
       gUnknown_203B46C = r0->gameOptions;
       gPlayTimeRef     = r0->playTime;
       return;
    }
       gTeamInventory_203B460 = GetMoneyItemsInfo();
       gRecruitedPokemonRef = GetRecruitedPokemon();
       gUnknown_203B480 = sub_80950F8();
       gUnknown_203B484 = sub_8095100();
       gUnknown_203B488 = sub_8095108();
       gUnknown_203B48C = sub_8095110();
       gUnknown_203B490 = sub_80958F8();
       gRescueTeamInfoRef = GetRescueTeamInfo();
       gUnknown_203B494 = sub_8097680();
       gUnknown_203B498 = GetExclusivePokemon();
       gFriendAreas     = GetBoughtFriendAreas();
       gUnknown_203B46C = GetGameOptions();
       gPlayTimeRef     = GetPlayTime();

}

void PrepareSavePakRead(void)
{
    gSavePakRead = MemoryAlloc(sizeof(struct SavePakRead), 5);
    gSavePakRead->state = 1;

}

u8 ReadSavePak(void)
{
    u32 temp;
    u32 temp2;
    switch(gSavePakRead->state)
    {
        case 0:
            gSavePakRead->state = 1;
            break;
        case 1:
            temp = 0;
            gSavePakRead->readStatus = ReadSaveFromPak(&temp);
            gSavePakRead->state = 2;
            break;
        case 2:
            if(gSavePakRead->readStatus != READ_SAVE_VALID)
            {
                if(IsSaveCorrupted())
                {
                    sub_80141B4(gSaveCorrupted, 0, 0, 0x301);
                    gSavePakRead->state = 3;
                }
                else
                {
                    gSavePakRead->state = 4;
                }
            }
            else
            {
                gSavePakRead->state = 6;
            }
            break;
        case 3:
            if(sub_80144A4(&temp2) == 0)
                gSavePakRead->state = 4;
            break;
        case 4:
            sub_8012298();
            gSavePakRead->state = 6;
        case 5:
            break;
        case 6:
            return 0;
    }
    return 1;
}

void FinishReadSavePak(void)
{
    if(gSavePakRead != NULL)
    {
        MemoryFree(gSavePakRead);
        gSavePakRead = NULL;
    }
}

void PrepareSavePakWrite(s16 PokemonID)
{
  struct OpenedFile *file;
  s32 id_s32;
  u8 *preload_face;

  id_s32 = PokemonID; // had to cast for asr shift

  sub_80993D8();
  gSavePakWrite = MemoryAlloc(sizeof(struct SavePakWrite),5);
  gSavePakWrite->pokeID = id_s32;
  gSavePakWrite->faceFile = NULL;
  gSavePakWrite->faceData = NULL;
  if (PokemonID != SPECIES_NONE) {
    file = GetDialogueSpriteDataPtr(PokemonID);
    gSavePakWrite->faceFile = file;
    gSavePakWrite->faceData = file->data;
    gSavePakWrite->unk18 = 0;
    gSavePakWrite->unk19 = 0;
    gSavePakWrite->unk1A = 0;
    gSavePakWrite->unk14 = 2;
    gSavePakWrite->unk16 = 8;
  }
  if (gSavePakWrite->faceFile != 0) {
      preload_face = (u8 *)&gSavePakWrite->faceFile;
      sub_80141B4(gSavingAdventure,0,preload_face,0x20);
  }
  else {
      sub_80141B4(gSavingAdventure,0,0,0x20);
  }
  gSavePakWrite->state = 3;
}

bool8 WriteSavePak(void)
{
  struct OpenedFile **faceFile;
  u32 local_14;
  u32 other_stack;

  faceFile = NULL;
  if (gSavePakWrite->faceFile != 0) {
    faceFile = &gSavePakWrite->faceFile;
  }
  switch(gSavePakWrite->state)
  {
    case 0:
        gSavePakWrite->state = 7;
        break;
    case 1:
        gSavePakWrite->unk4++;
        if (gSavePakWrite->unk4 > 8) {
            sub_80141B4(gWriteGamePak, 0, 0, 0x20);
            gSavePakWrite->state = 3;
        }
        break;
    case 2:
        break;
    case 3:
        gSavePakWrite->state = 4;
        break;
    case 4:
        local_14 = 0;
        sub_80140DC();
        gSavePakWrite->saveStatus = WriteSavetoPak(&local_14, sub_8011C1C());
        switch(gSavePakWrite->saveStatus)
        {
            case SAVE_COMPLETED:
                if(gSavePakWrite->faceFile != NULL)
                    sub_80141B4(gSaveCompleted, 0, (u8 *)faceFile, 0x101);
                else
                    sub_80141B4(gSaveCompleted, 0, (u8 *)faceFile, 0x101);
                gSavePakWrite->state = 5;
                break;
            case SAVE_NOT_WRTTEN:
                sub_80141B4(gSaveNotWritten, 0, 0, 0);
                gSavePakWrite->state = 6;
                break;
            default:
                if(gSavePakWrite->faceFile != NULL)
                    sub_80141B4(gSaveFailed, 0, (u8 *)faceFile, 0x101);
                else
                    sub_80141B4(gSaveFailed, 0, (u8 *)faceFile, 0x101);
                gSavePakWrite->state = 5;
                break;
        }
        sub_8014114();
        break;
    case 5:
        if (sub_80144A4(&other_stack) == 0)
            gSavePakWrite->state = 7;
        break;
    case 6:
        break;
    case 7:
        return FALSE;
    }
    return TRUE;
}

u32 GetSavePakStatus(void)
{
    return gSavePakWrite->saveStatus;
}

void FinishWriteSavePak(void)
{
    if(gSavePakWrite != NULL)
    {
        if(gSavePakWrite->faceFile != NULL)
            CloseFile(gSavePakWrite->faceFile);
        MemoryFree(gSavePakWrite);
        gSavePakWrite = NULL;
    }
    sub_80993E4();
}

void PrepareQuickSaveRead(u8 *dest, u32 size)
{
    gQuickSaveRead = MemoryAlloc(sizeof(struct QuickSaveRead), 5);
    gQuickSaveRead->dest = dest;
    gQuickSaveRead->size = size;
    gQuickSaveRead->saveValid = FALSE;
    gQuickSaveRead->state = 1;
}

bool8 ReadQuickSave(void)
{
    u32 stack_1;
    u32 stack_2;

    switch(gQuickSaveRead->state)
    {
        case 0:
            gQuickSaveRead->state = 1;
            break;
        case 1:
            stack_1 = 16;
            if(sub_8011F9C(&stack_1, gQuickSaveRead->dest, gQuickSaveRead->size) == READ_SAVE_VALID)
            {
                gQuickSaveRead->saveValid = TRUE;
                gQuickSaveRead->state = 3;
            }
            else
            {
                // Data was not properly saved
                // so your game cannot be resumed
                // from your last spot. Your last outing is considered a defeat.
                // Before shutting down, save in your
                // team base or quicksave in a dungeon
                sub_80141B4(gUnknown_80D44C8, 0, 0, 0x301);
                gQuickSaveRead->state = 2;
            }
            break;
        case 2:
            if(sub_80144A4(&stack_2) == 0)
                gQuickSaveRead->state = 3;
            break;
        case 3:
            return FALSE;
        default:
            break;
    }
    return TRUE;
}

bool8 IsQuickSaveValid(void)
{
    return gQuickSaveRead->saveValid;
}

void FinishQuickSaveRead(void)
{
    if(gQuickSaveRead != NULL){
        MemoryFree(gQuickSaveRead);
        gQuickSaveRead = 0;
    }
}

void PrepareQuickSaveWrite(u8 *r0, u32 r1, u8 r2)
{
    gQuickSaveWrite = MemoryAlloc(sizeof(struct QuickSaveWrite), 5);
    gQuickSaveWrite->unk4 = r0;
    gQuickSaveWrite->unk8 = r1;
    gQuickSaveWrite->unkC = r2;
    gQuickSaveWrite->state = 0;
    if(gQuickSaveWrite->unkC != 0)
        // Quicksaving your adventure...
        // Please don't turn off the power.
        sub_80141B4(gUnknown_80D45AC, 0, 0, 0x20);
    else
        // Quicksaving your adventure...
        // Please don't turn off the power.
        sub_80141B4(gUnknown_80D45AC, 0, 0, 0x20);
}

u32 WriteQuickSave(void)
{
    u32 stack_1;
    u32 stack_2;
    switch(gQuickSaveWrite->state)
    {
        case 0:
            gQuickSaveWrite->state = 1;
            break;
        case 1:
            sub_80140DC();
            stack_1 = 16;
            gQuickSaveWrite->quickSaveStatus = sub_80121D4(&stack_1, gQuickSaveWrite->unk4, gQuickSaveWrite->unk8);
            gQuickSaveWrite->state = 2;
            return 1;
        case 2:
            stack_1 = 0;
            if(gQuickSaveWrite->quickSaveStatus == 0)
                gQuickSaveWrite->quickSaveStatus = WriteSavetoPak(&stack_1, 2);
            switch(gQuickSaveWrite->quickSaveStatus)
            {
                case SAVE_COMPLETED:
                    sub_80121E0(0xf1207);
                    if(gQuickSaveWrite->unkC != 0)
                        // Your adventure has been saved
                        // When you are in a dungeon, quicksave
                        // your progress before ending the game.
                        sub_80141B4(gUnknown_80D45F4, 0, 0, 0x301);
                    else
                        // Your adventure has been saved.
                        // When you resume play, the quicksave
                        // data will be deleted. When you are shutting down the game,
                        // save your progress in your team base,
                        // or quicksave in a dungeon.
                        sub_80141B4(gUnknown_80D4668, 0, 0, 0x301);
                    gQuickSaveWrite->state = 3;
                    break;
                case SAVE_NOT_WRTTEN:
                    // The data could not be written.
                    // Please turn off the power and remove
                    // and reinsert the DS Card
                    sub_80141B4(gSaveNotWritten2, 0, 0, 0);
                    gQuickSaveWrite->state = 4;
                    break;
                default:
                    sub_80121E0(0xf1209);
                    sub_80141B4(gSaveFailed2, 0, 0, 0x301);
                    gQuickSaveWrite->state = 3;
                    break;
            }
            sub_8014114();
            break;
        case 3:
            if(sub_80144A4(&stack_2) == 0)
                gQuickSaveWrite->state = 5;
            break;
        case 4:
            break;
        case 5:
            if(gQuickSaveWrite->quickSaveStatus == SAVE_COMPLETED)
                return 2;
            else
                return 3;
            break;
        default:
            break;
    }
    return 0;
}

void FinishQuickSaveWrite()
{
    if (gQuickSaveWrite) {
        MemoryFree(gQuickSaveWrite);
        gQuickSaveWrite = NULL;
    }
}

