#include "global.h" #include "malloc.h" #include "berry_powder.h" #include "item.h" #include "load_save.h" #include "main.h" #include "overworld.h" #include "pokemon.h" #include "pokemon_storage_system.h" #include "random.h" #include "save_location.h" #include "trainer_hill.h" #include "gba/flash_internal.h" #include "decoration_inventory.h" #include "agb_flash.h" #include "event_data.h" #include "constants/event_objects.h" static void ApplyNewEncryptionKeyToAllEncryptedData(u32 encryptionKey); #define SAVEBLOCK_MOVE_RANGE 128 struct LoadedSaveData { /*0x0000*/ struct ItemSlot items[BAG_ITEMS_COUNT]; /*0x0078*/ struct ItemSlot keyItems[BAG_KEYITEMS_COUNT]; /*0x00F0*/ struct ItemSlot pokeBalls[BAG_POKEBALLS_COUNT]; /*0x0130*/ struct ItemSlot TMsHMs[BAG_TMHM_COUNT]; /*0x0230*/ struct ItemSlot berries[BAG_BERRIES_COUNT]; /*0x02E8*/ struct Mail mail[MAIL_COUNT]; }; // EWRAM DATA EWRAM_DATA struct SaveBlock3 gSaveblock3 = {}; EWRAM_DATA struct SaveBlock2ASLR gSaveblock2 = {0}; EWRAM_DATA struct SaveBlock1ASLR gSaveblock1 = {0}; EWRAM_DATA struct PokemonStorageASLR gPokemonStorage = {0}; EWRAM_DATA struct LoadedSaveData gLoadedSaveData = {0}; EWRAM_DATA u32 gLastEncryptionKey = 0; // IWRAM common COMMON_DATA bool32 gFlashMemoryPresent = 0; COMMON_DATA struct SaveBlock1 *gSaveBlock1Ptr = NULL; COMMON_DATA struct SaveBlock2 *gSaveBlock2Ptr = NULL; IWRAM_INIT struct SaveBlock3 *gSaveBlock3Ptr = &gSaveblock3; COMMON_DATA struct PokemonStorage *gPokemonStoragePtr = NULL; // code void CheckForFlashMemory(void) { if (!IdentifyFlash()) { gFlashMemoryPresent = TRUE; InitFlashTimer(); } else { gFlashMemoryPresent = FALSE; } } void ClearSav3(void) { CpuFill16(0, &gSaveblock3, sizeof(struct SaveBlock3)); } void ClearSav2(void) { CpuFill16(0, &gSaveblock2, sizeof(struct SaveBlock2ASLR)); } void ClearSav1(void) { CpuFill16(0, &gSaveblock1, sizeof(struct SaveBlock1ASLR)); } // Offset is the sum of the trainer id bytes void SetSaveBlocksPointers(u16 offset) { struct SaveBlock1** sav1_LocalVar = &gSaveBlock1Ptr; offset = (offset + Random()) & (SAVEBLOCK_MOVE_RANGE - 4); gSaveBlock2Ptr = (void *)(&gSaveblock2) + offset; *sav1_LocalVar = (void *)(&gSaveblock1) + offset; gPokemonStoragePtr = (void *)(&gPokemonStorage) + offset; SetBagItemsPointers(); SetDecorationInventoriesPointers(); } void MoveSaveBlocks_ResetHeap(void) { void *vblankCB, *hblankCB; u32 encryptionKey; struct SaveBlock2 *saveBlock2Copy; struct SaveBlock1 *saveBlock1Copy; struct PokemonStorage *pokemonStorageCopy; // save interrupt functions and turn them off vblankCB = gMain.vblankCallback; hblankCB = gMain.hblankCallback; gMain.vblankCallback = NULL; gMain.hblankCallback = NULL; gTrainerHillVBlankCounter = NULL; saveBlock2Copy = (struct SaveBlock2 *)(gHeap); saveBlock1Copy = (struct SaveBlock1 *)(gHeap + sizeof(struct SaveBlock2)); pokemonStorageCopy = (struct PokemonStorage *)(gHeap + sizeof(struct SaveBlock2) + sizeof(struct SaveBlock1)); // backup the saves. *saveBlock2Copy = *gSaveBlock2Ptr; *saveBlock1Copy = *gSaveBlock1Ptr; *pokemonStorageCopy = *gPokemonStoragePtr; // change saveblocks' pointers // argument is a sum of the individual trainerId bytes SetSaveBlocksPointers( saveBlock2Copy->playerTrainerId[0] + saveBlock2Copy->playerTrainerId[1] + saveBlock2Copy->playerTrainerId[2] + saveBlock2Copy->playerTrainerId[3]); // restore saveblock data since the pointers changed *gSaveBlock2Ptr = *saveBlock2Copy; *gSaveBlock1Ptr = *saveBlock1Copy; *gPokemonStoragePtr = *pokemonStorageCopy; // heap was destroyed in the copying process, so reset it InitHeap(gHeap, HEAP_SIZE); // restore interrupt functions gMain.hblankCallback = hblankCB; gMain.vblankCallback = vblankCB; // create a new encryption key encryptionKey = Random32(); ApplyNewEncryptionKeyToAllEncryptedData(encryptionKey); gSaveBlock2Ptr->encryptionKey = encryptionKey; } u32 UseContinueGameWarp(void) { return gSaveBlock2Ptr->specialSaveWarpFlags & CONTINUE_GAME_WARP; } void ClearContinueGameWarpStatus(void) { gSaveBlock2Ptr->specialSaveWarpFlags &= ~CONTINUE_GAME_WARP; } void SetContinueGameWarpStatus(void) { gSaveBlock2Ptr->specialSaveWarpFlags |= CONTINUE_GAME_WARP; } void SetContinueGameWarpStatusToDynamicWarp(void) { SetContinueGameWarpToDynamicWarp(0); gSaveBlock2Ptr->specialSaveWarpFlags |= CONTINUE_GAME_WARP; } void ClearContinueGameWarpStatus2(void) { gSaveBlock2Ptr->specialSaveWarpFlags &= ~CONTINUE_GAME_WARP; } void SavePlayerParty(void) { int i; gSaveBlock1Ptr->playerPartyCount = gPlayerPartyCount; for (i = 0; i < PARTY_SIZE; i++) gSaveBlock1Ptr->playerParty[i] = gPlayerParty[i]; } void LoadPlayerParty(void) { int i; gPlayerPartyCount = gSaveBlock1Ptr->playerPartyCount; for (i = 0; i < PARTY_SIZE; i++) { u32 data; gPlayerParty[i] = gSaveBlock1Ptr->playerParty[i]; // TODO: Turn this into a save migration once those are available. // At which point we can remove hp and status from Pokemon entirely. data = gPlayerParty[i].maxHP - gPlayerParty[i].hp; SetBoxMonData(&gPlayerParty[i].box, MON_DATA_HP_LOST, &data); data = gPlayerParty[i].status; SetBoxMonData(&gPlayerParty[i].box, MON_DATA_STATUS, &data); } } void SaveObjectEvents(void) { int i; u16 graphicsId; for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { gSaveBlock1Ptr->objectEvents[i] = gObjectEvents[i]; // Swap graphicsId bytes when saving and loading // This keeps compatibility with vanilla, // since the lower graphicsIds will be in the same place as vanilla graphicsId = gObjectEvents[i].graphicsId; gSaveBlock1Ptr->objectEvents[i].graphicsId = (graphicsId >> 8) | (graphicsId << 8); gSaveBlock1Ptr->objectEvents[i].spriteId = 127; // magic number // To avoid crash on vanilla, save follower as inactive if (gObjectEvents[i].localId == OBJ_EVENT_ID_FOLLOWER) gSaveBlock1Ptr->objectEvents[i].active = FALSE; } } void LoadObjectEvents(void) { int i; u16 graphicsId; for (i = 0; i < OBJECT_EVENTS_COUNT; i++) { gObjectEvents[i] = gSaveBlock1Ptr->objectEvents[i]; // Swap graphicsId bytes when saving and loading // This keeps compatibility with vanilla, // since the lower graphicsIds will be in the same place as vanilla graphicsId = gObjectEvents[i].graphicsId; gObjectEvents[i].graphicsId = (graphicsId >> 8) | (graphicsId << 8); if (gObjectEvents[i].spriteId != 127) gObjectEvents[i].graphicsId &= 0xFF; gObjectEvents[i].spriteId = 0; // Try to restore saved inactive follower if (gObjectEvents[i].localId == OBJ_EVENT_ID_FOLLOWER && !gObjectEvents[i].active && gObjectEvents[i].graphicsId >= OBJ_EVENT_GFX_MON_BASE) gObjectEvents[i].active = TRUE; } } void CopyPartyAndObjectsToSave(void) { SavePlayerParty(); SaveObjectEvents(); } void CopyPartyAndObjectsFromSave(void) { LoadPlayerParty(); LoadObjectEvents(); } void LoadPlayerBag(void) { int i; // load player items. for (i = 0; i < BAG_ITEMS_COUNT; i++) gLoadedSaveData.items[i] = gSaveBlock1Ptr->bagPocket_Items[i]; // load player key items. for (i = 0; i < BAG_KEYITEMS_COUNT; i++) gLoadedSaveData.keyItems[i] = gSaveBlock1Ptr->bagPocket_KeyItems[i]; // load player pokeballs. for (i = 0; i < BAG_POKEBALLS_COUNT; i++) gLoadedSaveData.pokeBalls[i] = gSaveBlock1Ptr->bagPocket_PokeBalls[i]; // load player TMs and HMs. for (i = 0; i < BAG_TMHM_COUNT; i++) gLoadedSaveData.TMsHMs[i] = gSaveBlock1Ptr->bagPocket_TMHM[i]; // load player berries. for (i = 0; i < BAG_BERRIES_COUNT; i++) gLoadedSaveData.berries[i] = gSaveBlock1Ptr->bagPocket_Berries[i]; // load mail. for (i = 0; i < MAIL_COUNT; i++) gLoadedSaveData.mail[i] = gSaveBlock1Ptr->mail[i]; gLastEncryptionKey = gSaveBlock2Ptr->encryptionKey; } void SavePlayerBag(void) { int i; u32 encryptionKeyBackup; // save player items. for (i = 0; i < BAG_ITEMS_COUNT; i++) gSaveBlock1Ptr->bagPocket_Items[i] = gLoadedSaveData.items[i]; // save player key items. for (i = 0; i < BAG_KEYITEMS_COUNT; i++) gSaveBlock1Ptr->bagPocket_KeyItems[i] = gLoadedSaveData.keyItems[i]; // save player pokeballs. for (i = 0; i < BAG_POKEBALLS_COUNT; i++) gSaveBlock1Ptr->bagPocket_PokeBalls[i] = gLoadedSaveData.pokeBalls[i]; // save player TMs and HMs. for (i = 0; i < BAG_TMHM_COUNT; i++) gSaveBlock1Ptr->bagPocket_TMHM[i] = gLoadedSaveData.TMsHMs[i]; // save player berries. for (i = 0; i < BAG_BERRIES_COUNT; i++) gSaveBlock1Ptr->bagPocket_Berries[i] = gLoadedSaveData.berries[i]; // save mail. for (i = 0; i < MAIL_COUNT; i++) gSaveBlock1Ptr->mail[i] = gLoadedSaveData.mail[i]; encryptionKeyBackup = gSaveBlock2Ptr->encryptionKey; gSaveBlock2Ptr->encryptionKey = gLastEncryptionKey; ApplyNewEncryptionKeyToBagItems(encryptionKeyBackup); gSaveBlock2Ptr->encryptionKey = encryptionKeyBackup; // updated twice? } void ApplyNewEncryptionKeyToHword(u16 *hWord, u32 newKey) { *hWord ^= gSaveBlock2Ptr->encryptionKey; *hWord ^= newKey; } void ApplyNewEncryptionKeyToWord(u32 *word, u32 newKey) { *word ^= gSaveBlock2Ptr->encryptionKey; *word ^= newKey; } static void ApplyNewEncryptionKeyToAllEncryptedData(u32 encryptionKey) { ApplyNewEncryptionKeyToGameStats(encryptionKey); ApplyNewEncryptionKeyToBagItems_(encryptionKey); ApplyNewEncryptionKeyToBerryPowder(encryptionKey); ApplyNewEncryptionKeyToWord(&gSaveBlock1Ptr->money, encryptionKey); ApplyNewEncryptionKeyToHword(&gSaveBlock1Ptr->coins, encryptionKey); }