mirror of
https://github.com/pret/berry-fix.git
synced 2026-03-21 17:54:20 -05:00
commit
3ef7972413
|
|
@ -18,7 +18,7 @@ _start: @ 0
|
|||
b _entry
|
||||
arm_func_end _start
|
||||
|
||||
.include "asm/berry_fix_header.inc"
|
||||
.include "asm/rom_header.inc"
|
||||
|
||||
@ C0
|
||||
.word 0
|
||||
|
|
@ -107,7 +107,7 @@ _1a0:
|
|||
bne _1a0
|
||||
mov r1, #0
|
||||
strh r1, [r0, 0xa] @ SIOMLT_SEND
|
||||
ldr r0, =_data_2f0
|
||||
ldr r0, =gPayload
|
||||
ldr r1, =gCode
|
||||
swi 0x11 << 16
|
||||
ldr lr, =gCode
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
.section .rodata
|
||||
|
||||
_data_2f0::
|
||||
gPayload::
|
||||
.incbin "data/payload.gba.lz"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ gHeldKeys
|
|||
gNewKeys
|
||||
gIntrVector
|
||||
gUpdateSuccessful
|
||||
gUnknown_3001194
|
||||
gUnknown_30011A0
|
||||
gUnusedVar
|
||||
gUnusedBuffer
|
||||
gMainCallbackState
|
||||
gGameVersion
|
||||
|
|
|
|||
8
payload/common_syms/save.txt
Normal file
8
payload/common_syms/save.txt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
gLastWrittenSector
|
||||
gLastSaveCounter
|
||||
gLastKnownGoodSector
|
||||
gDamagedSaveSectors
|
||||
gSaveCounter
|
||||
gReadWriteSector
|
||||
gIncrementalSectorId
|
||||
gFlashIdentIsValid
|
||||
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
17
payload/include/berry_fix_save.h
Normal file
17
payload/include/berry_fix_save.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef GUARD_BERRY_FIX_SAVE_H
|
||||
#define GUARD_BERRY_FIX_SAVE_H
|
||||
|
||||
#include "save.h"
|
||||
|
||||
enum
|
||||
{
|
||||
SAVE_NORMAL, // Save full save slot
|
||||
SAVE_SAVEBLOCKS, // Save just SaveBlock1 and SaveBlock2
|
||||
SAVE_SAVEBLOCK2, // Save just SaveBlock2
|
||||
};
|
||||
|
||||
bool32 BerryFix_IdentifyFlash(void);
|
||||
u8 BerryFix_TrySave(u8 mode);
|
||||
bool8 BerryFix_LoadSave(u32);
|
||||
|
||||
#endif //GUARD_BERRY_FIX_SAVE_H
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
#ifndef GUARD_CONSTANTS_GAME_STAT_H
|
||||
#define GUARD_CONSTANTS_GAME_STAT_H
|
||||
|
||||
#define GAME_STAT_SAVED_GAME 0
|
||||
#define GAME_STAT_FIRST_HOF_PLAY_TIME 1
|
||||
#define GAME_STAT_STARTED_TRENDS 2
|
||||
#define GAME_STAT_PLANTED_BERRIES 3
|
||||
#define GAME_STAT_TRADED_BIKES 4
|
||||
#define GAME_STAT_STEPS 5
|
||||
#define GAME_STAT_GOT_INTERVIEWED 6
|
||||
#define GAME_STAT_TOTAL_BATTLES 7
|
||||
#define GAME_STAT_WILD_BATTLES 8
|
||||
#define GAME_STAT_TRAINER_BATTLES 9
|
||||
#define GAME_STAT_ENTERED_HOF 10
|
||||
#define GAME_STAT_POKEMON_CAPTURES 11
|
||||
#define GAME_STAT_FISHING_CAPTURES 12
|
||||
#define GAME_STAT_HATCHED_EGGS 13
|
||||
#define GAME_STAT_EVOLVED_POKEMON 14
|
||||
#define GAME_STAT_USED_POKECENTER 15
|
||||
#define GAME_STAT_RESTED_AT_HOME 16
|
||||
#define GAME_STAT_ENTERED_SAFARI_ZONE 17
|
||||
#define GAME_STAT_USED_CUT 18
|
||||
#define GAME_STAT_USED_ROCK_SMASH 19
|
||||
#define GAME_STAT_MOVED_SECRET_BASE 20
|
||||
#define GAME_STAT_POKEMON_TRADES 21
|
||||
#define GAME_STAT_UNKNOWN_22 22
|
||||
#define GAME_STAT_LINK_BATTLE_WINS 23
|
||||
#define GAME_STAT_LINK_BATTLE_LOSSES 24
|
||||
#define GAME_STAT_LINK_BATTLE_DRAWS 25
|
||||
#define GAME_STAT_USED_SPLASH 26
|
||||
#define GAME_STAT_USED_STRUGGLE 27
|
||||
#define GAME_STAT_SLOT_JACKPOTS 28
|
||||
#define GAME_STAT_CONSECUTIVE_ROULETTE_WINS 29
|
||||
#define GAME_STAT_ENTERED_BATTLE_TOWER 30
|
||||
#define GAME_STAT_UNKNOWN_31 31
|
||||
#define GAME_STAT_BATTLE_TOWER_BEST_STREAK 32
|
||||
#define GAME_STAT_POKEBLOCKS 33
|
||||
#define GAME_STAT_POKEBLOCKS_WITH_FRIENDS 34
|
||||
#define GAME_STAT_WON_LINK_CONTEST 35
|
||||
#define GAME_STAT_ENTERED_CONTEST 36
|
||||
#define GAME_STAT_WON_CONTEST 37
|
||||
#define GAME_STAT_SHOPPED 38
|
||||
#define GAME_STAT_USED_ITEMFINDER 39
|
||||
#define GAME_STAT_GOT_RAINED_ON 40
|
||||
#define GAME_STAT_CHECKED_POKEDEX 41
|
||||
#define GAME_STAT_RECEIVED_RIBBONS 42
|
||||
#define GAME_STAT_JUMPED_DOWN_LEDGES 43
|
||||
#define GAME_STAT_WATCHED_TV 44
|
||||
#define GAME_STAT_CHECKED_CLOCK 45
|
||||
#define GAME_STAT_WON_POKEMON_LOTTERY 46
|
||||
#define GAME_STAT_USED_DAYCARE 47
|
||||
#define GAME_STAT_RODE_CABLE_CAR 48
|
||||
#define GAME_STAT_ENTERED_HOT_SPRINGS 49
|
||||
#define NUM_GAME_STATS 50
|
||||
|
||||
#endif // GUARD_CONSTANTS_GAME_STAT_H
|
||||
|
|
@ -1,196 +1,11 @@
|
|||
#ifndef GUARD_CONSTANTS_VARS_H
|
||||
#define GUARD_CONSTANTS_VARS_H
|
||||
|
||||
#define VAR_0x3F20 0x3F20
|
||||
#define VARS_START 0x4000
|
||||
#define SPECIAL_VARS_START 0x8000
|
||||
|
||||
#define VARS_START 0x4000
|
||||
#define VARS_COUNT 256
|
||||
|
||||
// temporary vars
|
||||
// The first 0x10 vars are are temporary--they are cleared every time a map is loaded.
|
||||
#define VAR_TEMP_0 0x4000
|
||||
#define VAR_TEMP_1 0x4001
|
||||
#define VAR_TEMP_2 0x4002
|
||||
#define VAR_TEMP_3 0x4003
|
||||
#define VAR_TEMP_4 0x4004
|
||||
#define VAR_TEMP_5 0x4005
|
||||
#define VAR_TEMP_6 0x4006
|
||||
#define VAR_TEMP_7 0x4007
|
||||
#define VAR_TEMP_8 0x4008
|
||||
#define VAR_TEMP_9 0x4009
|
||||
#define VAR_TEMP_A 0x400A
|
||||
#define VAR_TEMP_B 0x400B
|
||||
#define VAR_TEMP_C 0x400C
|
||||
#define VAR_TEMP_D 0x400D
|
||||
#define VAR_TEMP_E 0x400E
|
||||
#define VAR_TEMP_F 0x400F
|
||||
|
||||
// object gfx id vars
|
||||
// These 0x10 vars are used to dynamically control a event object's sprite.
|
||||
// For example, the rival's sprite id is dynamically set based on the player's gender.
|
||||
// See VarGetEventObjectGraphicsId().
|
||||
#define VAR_OBJ_GFX_ID_0 0x4010
|
||||
#define VAR_OBJ_GFX_ID_1 0x4011
|
||||
#define VAR_OBJ_GFX_ID_2 0x4012
|
||||
#define VAR_OBJ_GFX_ID_3 0x4013
|
||||
#define VAR_OBJ_GFX_ID_4 0x4014
|
||||
#define VAR_OBJ_GFX_ID_5 0x4015
|
||||
#define VAR_OBJ_GFX_ID_6 0x4016
|
||||
#define VAR_OBJ_GFX_ID_7 0x4017
|
||||
#define VAR_OBJ_GFX_ID_8 0x4018
|
||||
#define VAR_OBJ_GFX_ID_9 0x4019
|
||||
#define VAR_OBJ_GFX_ID_A 0x401A
|
||||
#define VAR_OBJ_GFX_ID_B 0x401B
|
||||
#define VAR_OBJ_GFX_ID_C 0x401C
|
||||
#define VAR_OBJ_GFX_ID_D 0x401D
|
||||
#define VAR_OBJ_GFX_ID_E 0x401E
|
||||
#define VAR_OBJ_GFX_ID_F 0x401F
|
||||
|
||||
// general purpose vars
|
||||
#define VAR_RECYCLE_GOODS 0x4020
|
||||
#define VAR_REPEL_STEP_COUNT 0x4021
|
||||
#define VAR_ICE_STEP_COUNT 0x4022
|
||||
#define VAR_STARTER_MON 0x4023 // 0=Treecko, 1=Torchic, 2=Mudkip
|
||||
#define VAR_MIRAGE_RND_H 0x4024
|
||||
#define VAR_MIRAGE_RND_L 0x4025
|
||||
#define VAR_SECRET_BASE_MAP 0x4026
|
||||
#define VAR_CYCLING_ROAD_RECORD_COLLISIONS 0x4027
|
||||
#define VAR_CYCLING_ROAD_RECORD_TIME_L 0x4028
|
||||
#define VAR_CYCLING_ROAD_RECORD_TIME_H 0x4029
|
||||
#define VAR_HAPPINESS_STEP_COUNTER 0x402A
|
||||
#define VAR_POISON_STEP_COUNTER 0x402B
|
||||
#define VAR_RESET_RTC_ENABLE 0x402C
|
||||
#define VAR_ENIGMA_BERRY_AVAILABLE 0x402D
|
||||
|
||||
#define VAR_DAYS 0x4040
|
||||
#define VAR_FANCLUB_UNKNOWN_1 0x4041 // TODO: document these two fanclub vars
|
||||
#define VAR_FANCLUB_UNKNOWN_2 0x4042
|
||||
#define VAR_DEPT_STORE_FLOOR 0x4043
|
||||
#define VAR_TRICK_HOUSE_ROOMS_COMPLETED 0x4044
|
||||
#define VAR_LOTTERY_PRIZE 0x4045
|
||||
#define VAR_NATIONAL_DEX 0x4046
|
||||
#define VAR_SHROOMISH_SIZE_RECORD 0x4047
|
||||
#define VAR_ASH_GATHER_COUNT 0x4048
|
||||
#define VAR_BIRCH_STATE 0x4049
|
||||
#define VAR_CRUISE_STEP_COUNT 0x404A
|
||||
#define VAR_LOTTERY_RND_L 0x404B
|
||||
#define VAR_LOTTERY_RND_H 0x404C
|
||||
|
||||
#define VAR_BARBOACH_SIZE_RECORD 0x404F
|
||||
#define VAR_LITTLEROOT_STATE 0x4050
|
||||
#define VAR_ROUTE102_ACCESSIBLE 0x4051
|
||||
|
||||
#define VAR_LAVARIDGE_RIVAL_STATE 0x4053
|
||||
#define VAR_CURRENT_SECRET_BASE 0x4054
|
||||
|
||||
#define VAR_PETALBURG_STATE 0x4057
|
||||
#define VAR_SLATEPORT_STATE 0x4058
|
||||
|
||||
#define VAR_RUSTBORO_STATE 0x405A
|
||||
|
||||
#define VAR_SOOTOPOLIS_STATE 0x405E
|
||||
|
||||
#define VAR_ROUTE101_STATE 0x4060
|
||||
|
||||
#define VAR_ROUTE103_STATE 0x4062
|
||||
|
||||
#define VAR_ROUTE110_STATE 0x4069
|
||||
|
||||
#define VAR_ROUTE116_STATE 0x406F
|
||||
|
||||
#define VAR_ROUTE118_STATE 0x4071
|
||||
#define VAR_ROUTE119_STATE 0x4072
|
||||
|
||||
#define VAR_ROUTE121_STATE 0x4074
|
||||
#define VAR_ROUTE128_STATE 0x407B
|
||||
|
||||
#define VAR_LITTLEROOT_HOUSES_STATE 0x4082 // TODO: needs more investigation
|
||||
|
||||
#define VAR_BIRCH_LAB_STATE 0x4084
|
||||
#define VAR_PETALBURG_GYM_STATE 0x4085
|
||||
#define VAR_LINK_CONTEST_ROOM_STATE 0x4086
|
||||
#define VAR_CABLE_CLUB_STATE 0x4087
|
||||
#define VAR_CONTEST_LOCATION 0x4088
|
||||
#define VAR_MAP_SCENE_SIX_ISLAND_POKEMON_CENTER_1F 0x4089 // TODO: related to decorations
|
||||
#define VAR_CONTEST_PRIZE_PICKUP 0x408A
|
||||
|
||||
#define VAR_LITTLEROOT_HOUSES_STATE_2 0x408C // TODO: needs more investigation
|
||||
#define VAR_LITTLEROOT_RIVAL_STATE 0x408D
|
||||
#define VAR_BOARD_BRINEY_BOAT_ROUTE104_STATE 0x408E
|
||||
#define VAR_DEVON_CORP_3F_STATE 0x408F
|
||||
#define VAR_BRINEY_HOUSE_STATE 0x4090
|
||||
|
||||
#define VAR_LITTLEROOT_INTRO_STATE 0x4092
|
||||
#define VAR_MAUVILLE_GYM_STATE 0x4093
|
||||
#define VAR_LILYCOVE_MUSEUM_2F_STATE 0x4094
|
||||
#define VAR_LILYCOVE_FAN_CLUB_STATE 0x4095
|
||||
#define VAR_BRINEY_LOCATION 0x4096
|
||||
#define VAR_0x4097 0x4097 // TODO: related to creating new secret base
|
||||
#define VAR_PETALBURG_WOODS_STATE 0x4098
|
||||
#define VAR_LILYCOVE_CONTEST_LOBBY_STATE 0x4099
|
||||
#define VAR_RUSTURF_TUNNEL_STATE 0x409a
|
||||
#define VAR_CAVE_OF_ORIGIN_B4F_STATE 0x409B
|
||||
#define VAR_ELITE_4_STATE 0x409C
|
||||
|
||||
#define VAR_SLATEPORT_HARBOR_STATE 0x40A0
|
||||
|
||||
#define VAR_SEAFLOOR_CAVERN_STATE 0x40A2
|
||||
#define VAR_CABLE_CAR_STATION_STATE 0x40A3
|
||||
#define VAR_SAFARI_ZONE_STATE 0x40A4
|
||||
#define VAR_TRICK_HOUSE_ENTRANCE_STATE 0x40A5
|
||||
#define VAR_TRICK_HOUSE_ENTRANCE_STATE_2 0x40A6
|
||||
#define VAR_TRICK_HOUSE_ENTRANCE_STATE_3 0x40A7
|
||||
|
||||
#define VAR_CYCLING_CHALLENGE_STATE 0x40A9
|
||||
#define VAR_SLATEPORT_MUSEUM_1F_STATE 0x40AA
|
||||
#define VAR_TRICK_HOUSE_PUZZLE_1_STATE 0x40AB
|
||||
#define VAR_TRICK_HOUSE_PUZZLE_2_STATE 0x40AC
|
||||
#define VAR_TRICK_HOUSE_PUZZLE_3_STATE 0x40AD
|
||||
#define VAR_TRICK_HOUSE_PUZZLE_4_STATE 0x40AE
|
||||
#define VAR_TRICK_HOUSE_PUZZLE_5_STATE 0x40AF
|
||||
#define VAR_TRICK_HOUSE_PUZZLE_6_STATE 0x40B0
|
||||
#define VAR_TRICK_HOUSE_PUZZLE_7_STATE 0x40B1
|
||||
#define VAR_TRICK_HOUSE_PUZZLE_8_STATE 0x40B2
|
||||
#define VAR_WEATHER_INSTITUTE_STATE 0x40B3
|
||||
#define VAR_PORTHOLE_STATE 0x40B4
|
||||
#define VAR_TRICK_HOUSE_STATE 0x40B5 // TODO: needs some further investigation
|
||||
#define VAR_TRICK_HOUSE_PUZZLE_7_STATE_2 0x40B6
|
||||
#define VAR_SLATEPORT_FAN_CLUB_STATE 0x40B7
|
||||
|
||||
#define VAR_MT_PYRE_STATE 0x40B9
|
||||
#define VAR_NEW_MAUVILLE_STATE 0x40BA
|
||||
|
||||
#define VAR_BRAVO_TRAINER_BATTLE_TOWER_ON 0x40BC
|
||||
#define VAR_JAGGED_PASS_VOLCANIC_ASH_WEATHER 0x40BD
|
||||
#define VAR_GLASS_WORKSHOP_STATE 0x40BE
|
||||
#define VAR_METEOR_FALLS_STATE 0x40BF
|
||||
#define VAR_GAME_CORNER_STATE 0x40C0
|
||||
#define VAR_TRICK_HOUSE_PRIZE_PICKUP 0x40C1
|
||||
#define VAR_PACIFIDLOG_TM_RECEIVED_DAY 0x40C2
|
||||
#define VAR_VICTORY_ROAD_1F_STATE 0x40C3
|
||||
#define VAR_FOSSIL_RESURRECTION_STATE 0x40C4
|
||||
#define VAR_WHICH_FOSSIL_REVIVED 0x40C5
|
||||
#define VAR_STEVENS_HOUSE_STATE 0x40C6
|
||||
#define VAR_OLDALE_STATE 0x40C7
|
||||
|
||||
// special vars
|
||||
// They are commonly used as parameters to commands, or return values from commands.
|
||||
#define VAR_SPECIAL_0 0x8000
|
||||
#define VAR_SPECIAL_1 0x8001
|
||||
#define VAR_SPECIAL_2 0x8002
|
||||
#define VAR_SPECIAL_3 0x8003
|
||||
#define VAR_SPECIAL_4 0x8004
|
||||
#define VAR_SPECIAL_5 0x8005
|
||||
#define VAR_SPECIAL_6 0x8006
|
||||
#define VAR_SPECIAL_7 0x8007
|
||||
#define VAR_SPECIAL_8 0x8008
|
||||
#define VAR_SPECIAL_9 0x8009
|
||||
#define VAR_SPECIAL_A 0x800A
|
||||
#define VAR_SPECIAL_B 0x800B
|
||||
#define FACING 0x800C
|
||||
#define RESULT 0x800D
|
||||
#define ITEM_ID 0x800E
|
||||
#define LAST_TALKED 0x800F
|
||||
#define CONTEST_RANK 0x8010
|
||||
#define CONTEST_CATEGORY 0x8011
|
||||
#define VAR_PACIFIDLOG_TM_RECEIVED_DAY 0x40C2
|
||||
|
||||
#endif // GUARD_CONSTANTS_VARS_H
|
||||
|
|
|
|||
7
payload/include/event_data.h
Normal file
7
payload/include/event_data.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef GUARD_EVENT_DATA_H
|
||||
#define GUARD_EVENT_DATA_H
|
||||
|
||||
bool32 BerryFix_IsPacifidlogTMCorrect(void);
|
||||
bool32 BerryFix_ResetPacifidlogTM(void);
|
||||
|
||||
#endif //GUARD_EVENT_DATA_H
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
#ifndef GUARD_FLASH_H
|
||||
#define GUARD_FLASH_H
|
||||
|
||||
#include "gba/gba.h"
|
||||
|
||||
enum
|
||||
{
|
||||
SECTOR_DAMAGED,
|
||||
SECTOR_OK,
|
||||
SECTOR_CHECK, // unused
|
||||
};
|
||||
|
||||
enum MsgBoxUpdateMessage
|
||||
{
|
||||
MSGBOX_WILL_NOW_UPDATE = 0,
|
||||
MSGBOX_HAS_BEEN_UPDATED,
|
||||
MSGBOX_UNABLE_TO_UPDATE,
|
||||
MSGBOX_NO_NEED_TO_UPDATE,
|
||||
MSGBOX_UPDATING
|
||||
};
|
||||
|
||||
struct SaveSector
|
||||
{
|
||||
u8 data[0xFF4];
|
||||
u16 id;
|
||||
u16 checksum;
|
||||
u32 signature;
|
||||
u32 counter;
|
||||
}; // size is 0x1000
|
||||
|
||||
// headless save section?
|
||||
struct UnkSaveSection
|
||||
{
|
||||
u8 data[0xFF4];
|
||||
u32 signature;
|
||||
}; // size is 0xFF8
|
||||
|
||||
#define eSaveSection ((struct SaveSector *)0x2020000)
|
||||
|
||||
#define NUM_SECTORS_PER_SAVE_SLOT 14 // Number of sectors occupied by a save slot
|
||||
#define FILE_SIGNATURE 0x08012025
|
||||
|
||||
#define SAVE_STATUS_EMPTY 0
|
||||
#define SAVE_STATUS_OK 1
|
||||
#define SAVE_STATUS_NO_FLASH 4
|
||||
#define SAVE_STATUS_ERROR 0xFF
|
||||
|
||||
bool32 flash_maincb_ident_is_valid(void);
|
||||
bool8 flash_maincb_read_save(u32);
|
||||
void msg_load_gfx(void);
|
||||
void msg_display(enum MsgBoxUpdateMessage);
|
||||
bool32 flash_maincb_check_need_reset_pacifidlog_tm(void);
|
||||
bool32 flash_maincb_reset_pacifidlog_tm(void);
|
||||
|
||||
#endif //GUARD_FLASH_H
|
||||
|
|
@ -7,6 +7,6 @@
|
|||
#include "gba/multiboot.h"
|
||||
#include "gba/syscall.h"
|
||||
#include "gba/macro.h"
|
||||
#include "gba/isagbprint.h"
|
||||
#include "gba/flash_internal.h"
|
||||
|
||||
#endif // GUARD_GBA_GBA_H
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
#ifndef GUARD_GBA_ISAGBPRINT_H
|
||||
#define GUARD_GBA_ISAGBPRINT_H
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define AGBPrintInit()
|
||||
#define AGBPutc(cChr)
|
||||
#define AGBPrint(pBuf)
|
||||
#define AGBPrintf(pBuf, ...)
|
||||
#define AGBPrintFlush1Block()
|
||||
#define AGBPrintFlush()
|
||||
#define AGBAssert(pFile, nLine, pExpression, nStopProgram)
|
||||
#else
|
||||
void AGBPrintInit(void);
|
||||
void AGBPutc(const char cChr);
|
||||
void AGBPrint(const char *pBuf);
|
||||
void AGBPrintf(const char *pBuf, ...);
|
||||
void AGBPrintFlush1Block(void);
|
||||
void AGBPrintFlush(void);
|
||||
void AGBAssert(const char *pFile, int nLine, const char *pExpression, int nStopProgram);
|
||||
#endif
|
||||
|
||||
#undef AGB_ASSERT
|
||||
#ifdef NDEBUG
|
||||
#define AGB_ASSERT(exp)
|
||||
#else
|
||||
#define AGB_ASSERT(exp) (exp) ? ((void*)0) : AGBAssert(__FILE__, __LINE__, #exp, 1);
|
||||
#endif
|
||||
|
||||
#undef AGB_WARNING
|
||||
#ifdef NDEBUG
|
||||
#define AGB_WARNING(exp)
|
||||
#else
|
||||
#define AGB_WARNING(exp) (exp) ? ((void*)0) : AGBAssert(__FILE__, __LINE__, #exp, 0);
|
||||
#endif
|
||||
|
||||
// for matching purposes
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define AGB_ASSERT_EX(exp, file, line)
|
||||
#else
|
||||
#define AGB_ASSERT_EX(exp, file, line) (exp) ? ((void*)0) : AGBAssert(file, line, #exp, 1);
|
||||
#endif
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define AGB_WARNING_EX(exp, file, line)
|
||||
#else
|
||||
#define AGB_WARNING_EX(exp, file, line) (exp) ? ((void*)0) : AGBAssert(file, line, #exp, 0);
|
||||
#endif
|
||||
|
||||
#endif // GUARD_GBA_ISAGBPRINT_H
|
||||
|
|
@ -1,467 +0,0 @@
|
|||
#ifndef GUARD_GBA_M4A_INTERNAL_H
|
||||
#define GUARD_GBA_M4A_INTERNAL_H
|
||||
|
||||
#include "gba/gba.h"
|
||||
|
||||
// ASCII encoding of 'Smsh' in reverse
|
||||
// This is presumably short for SMASH, the developer of MKS4AGB.
|
||||
#define ID_NUMBER 0x68736D53
|
||||
|
||||
#define C_V 0x40 // center value for PAN, BEND, and TUNE
|
||||
|
||||
#define SOUND_MODE_REVERB_VAL 0x0000007F
|
||||
#define SOUND_MODE_REVERB_SET 0x00000080
|
||||
#define SOUND_MODE_MAXCHN 0x00000F00
|
||||
#define SOUND_MODE_MAXCHN_SHIFT 8
|
||||
#define SOUND_MODE_MASVOL 0x0000F000
|
||||
#define SOUND_MODE_MASVOL_SHIFT 12
|
||||
#define SOUND_MODE_FREQ_05734 0x00010000
|
||||
#define SOUND_MODE_FREQ_07884 0x00020000
|
||||
#define SOUND_MODE_FREQ_10512 0x00030000
|
||||
#define SOUND_MODE_FREQ_13379 0x00040000
|
||||
#define SOUND_MODE_FREQ_15768 0x00050000
|
||||
#define SOUND_MODE_FREQ_18157 0x00060000
|
||||
#define SOUND_MODE_FREQ_21024 0x00070000
|
||||
#define SOUND_MODE_FREQ_26758 0x00080000
|
||||
#define SOUND_MODE_FREQ_31536 0x00090000
|
||||
#define SOUND_MODE_FREQ_36314 0x000A0000
|
||||
#define SOUND_MODE_FREQ_40137 0x000B0000
|
||||
#define SOUND_MODE_FREQ_42048 0x000C0000
|
||||
#define SOUND_MODE_FREQ 0x000F0000
|
||||
#define SOUND_MODE_FREQ_SHIFT 16
|
||||
#define SOUND_MODE_DA_BIT_9 0x00800000
|
||||
#define SOUND_MODE_DA_BIT_8 0x00900000
|
||||
#define SOUND_MODE_DA_BIT_7 0x00A00000
|
||||
#define SOUND_MODE_DA_BIT_6 0x00B00000
|
||||
#define SOUND_MODE_DA_BIT 0x00B00000
|
||||
#define SOUND_MODE_DA_BIT_SHIFT 20
|
||||
|
||||
struct WaveData
|
||||
{
|
||||
u16 type;
|
||||
u16 status;
|
||||
u32 freq;
|
||||
u32 loopStart;
|
||||
u32 size; // number of samples
|
||||
s8 data[1]; // samples
|
||||
};
|
||||
|
||||
#define TONEDATA_TYPE_CGB 0x07
|
||||
#define TONEDATA_TYPE_FIX 0x08
|
||||
#define TONEDATA_TYPE_SPL 0x40 // key split
|
||||
#define TONEDATA_TYPE_RHY 0x80 // rhythm
|
||||
|
||||
#define TONEDATA_P_S_PAN 0xc0
|
||||
#define TONEDATA_P_S_PAM TONEDATA_P_S_PAN
|
||||
|
||||
struct ToneData
|
||||
{
|
||||
u8 type;
|
||||
u8 key;
|
||||
u8 length; // sound length (compatible sound)
|
||||
u8 pan_sweep; // pan or sweep (compatible sound ch. 1)
|
||||
struct WaveData *wav;
|
||||
u8 attack;
|
||||
u8 decay;
|
||||
u8 sustain;
|
||||
u8 release;
|
||||
};
|
||||
|
||||
struct CgbChannel
|
||||
{
|
||||
u8 sf;
|
||||
u8 ty;
|
||||
u8 rightVolume;
|
||||
u8 leftVolume;
|
||||
u8 at;
|
||||
u8 de;
|
||||
u8 su;
|
||||
u8 re;
|
||||
u8 ky;
|
||||
u8 ev;
|
||||
u8 eg;
|
||||
u8 ec;
|
||||
u8 echoVolume;
|
||||
u8 echoLength;
|
||||
u8 d1;
|
||||
u8 d2;
|
||||
u8 gt;
|
||||
u8 mk;
|
||||
u8 ve;
|
||||
u8 pr;
|
||||
u8 rp;
|
||||
u8 d3[3];
|
||||
u8 d5;
|
||||
u8 sg;
|
||||
u8 n4;
|
||||
u8 pan;
|
||||
u8 panMask;
|
||||
u8 mo;
|
||||
u8 le;
|
||||
u8 sw;
|
||||
u32 fr;
|
||||
u32 wp;
|
||||
u32 cp;
|
||||
u32 tp;
|
||||
u32 pp;
|
||||
u32 np;
|
||||
u8 d4[8];
|
||||
};
|
||||
|
||||
struct MusicPlayerTrack;
|
||||
|
||||
struct SoundChannel
|
||||
{
|
||||
u8 status;
|
||||
u8 type;
|
||||
u8 rightVolume;
|
||||
u8 leftVolume;
|
||||
u8 attack;
|
||||
u8 decay;
|
||||
u8 sustain;
|
||||
u8 release;
|
||||
u8 ky;
|
||||
u8 ev;
|
||||
u8 er;
|
||||
u8 el;
|
||||
u8 echoVolume;
|
||||
u8 echoLength;
|
||||
u8 d1;
|
||||
u8 d2;
|
||||
u8 gt;
|
||||
u8 mk;
|
||||
u8 ve;
|
||||
u8 pr;
|
||||
u8 rp;
|
||||
u8 d3[3];
|
||||
u32 ct;
|
||||
u32 fw;
|
||||
u32 freq;
|
||||
struct WaveData *wav;
|
||||
u32 cp;
|
||||
struct MusicPlayerTrack *track;
|
||||
u32 pp;
|
||||
u32 np;
|
||||
u32 d4;
|
||||
u16 xpi;
|
||||
u16 xpc;
|
||||
};
|
||||
|
||||
#define MAX_DIRECTSOUND_CHANNELS 12
|
||||
|
||||
#define PCM_DMA_BUF_SIZE 1584 // size of Direct Sound buffer
|
||||
|
||||
struct SoundInfo
|
||||
{
|
||||
// This field is normally equal to ID_NUMBER but it is set to other
|
||||
// values during sensitive operations for locking purposes.
|
||||
// This field should be volatile but isn't. This could potentially cause
|
||||
// race conditions.
|
||||
u32 ident;
|
||||
|
||||
vu8 pcmDmaCounter;
|
||||
|
||||
// Direct Sound
|
||||
u8 reverb;
|
||||
u8 maxChans;
|
||||
u8 masterVolume;
|
||||
u8 freq;
|
||||
|
||||
u8 mode;
|
||||
u8 c15;
|
||||
u8 pcmDmaPeriod; // number of V-blanks per PCM DMA
|
||||
u8 maxLines;
|
||||
u8 gap[3];
|
||||
s32 pcmSamplesPerVBlank;
|
||||
s32 pcmFreq;
|
||||
s32 divFreq;
|
||||
struct CgbChannel *cgbChans;
|
||||
u32 func;
|
||||
u32 intp;
|
||||
void (*CgbSound)(void);
|
||||
void (*CgbOscOff)(u8);
|
||||
u32 (*MidiKeyToCgbFreq)(u8, u8, u8);
|
||||
u32 MPlayJumpTable;
|
||||
u32 plynote;
|
||||
u32 ExtVolPit;
|
||||
u8 gap2[16];
|
||||
struct SoundChannel chans[MAX_DIRECTSOUND_CHANNELS];
|
||||
s8 pcmBuffer[PCM_DMA_BUF_SIZE * 2];
|
||||
};
|
||||
|
||||
struct SongHeader
|
||||
{
|
||||
u8 trackCount;
|
||||
u8 blockCount;
|
||||
u8 priority;
|
||||
u8 reverb;
|
||||
struct ToneData *tone;
|
||||
u8 *part[1];
|
||||
};
|
||||
|
||||
struct PokemonCrySong
|
||||
{
|
||||
u8 trackCount;
|
||||
u8 blockCount;
|
||||
u8 priority;
|
||||
u8 reverb;
|
||||
struct ToneData *tone;
|
||||
u8 *part[2];
|
||||
u8 gap;
|
||||
u8 part0; // 0x11
|
||||
u8 tuneValue; // 0x12
|
||||
u8 gotoCmd; // 0x13
|
||||
u32 gotoTarget; // 0x14
|
||||
u8 part1; // 0x18
|
||||
u8 tuneValue2; // 0x19
|
||||
u8 cont[2]; // 0x1A
|
||||
u8 volCmd; // 0x1C
|
||||
u8 volumeValue; // 0x1D
|
||||
u8 unkCmd0D[2]; // 0x1E
|
||||
u32 unkCmd0DParam; // 0x20
|
||||
u8 xreleCmd[2]; // 0x24
|
||||
u8 releaseValue; // 0x26
|
||||
u8 panCmd;
|
||||
u8 panValue; // 0x28
|
||||
u8 tieCmd; // 0x29
|
||||
u8 tieKeyValue; // 0x2A
|
||||
u8 tieVelocityValue; // 0x2B
|
||||
u8 unkCmd0C[2]; // 0x2C
|
||||
u16 unkCmd0CParam; // 0x2E
|
||||
u8 end[2]; // 0x30
|
||||
};
|
||||
|
||||
#define MPT_FLG_VOLSET 0x01
|
||||
#define MPT_FLG_VOLCHG 0x03
|
||||
#define MPT_FLG_PITSET 0x04
|
||||
#define MPT_FLG_PITCHG 0x0C
|
||||
#define MPT_FLG_START 0x40
|
||||
#define MPT_FLG_EXIST 0x80
|
||||
|
||||
struct MusicPlayerTrack
|
||||
{
|
||||
u8 flags;
|
||||
u8 wait;
|
||||
u8 patternLevel;
|
||||
u8 repN;
|
||||
u8 gateTime;
|
||||
u8 key;
|
||||
u8 velocity;
|
||||
u8 runningStatus;
|
||||
u8 keyM;
|
||||
u8 pitM;
|
||||
s8 keyShift;
|
||||
s8 keyShiftX;
|
||||
s8 tune;
|
||||
u8 pitX;
|
||||
s8 bend;
|
||||
u8 bendRange;
|
||||
u8 volMR;
|
||||
u8 volML;
|
||||
u8 vol;
|
||||
u8 volX;
|
||||
s8 pan;
|
||||
s8 panX;
|
||||
s8 modM;
|
||||
u8 mod;
|
||||
u8 modT;
|
||||
u8 lfoSpeed;
|
||||
u8 lfoSpeedC;
|
||||
u8 lfoDelay;
|
||||
u8 lfoDelayC;
|
||||
u8 priority;
|
||||
u8 echoVolume;
|
||||
u8 echoLength;
|
||||
struct SoundChannel *chan;
|
||||
struct ToneData tone;
|
||||
u8 gap[10];
|
||||
u16 unk_3A;
|
||||
u32 unk_3C;
|
||||
u8 *cmdPtr;
|
||||
u8 *patternStack[3];
|
||||
};
|
||||
|
||||
#define MUSICPLAYER_STATUS_TRACK 0x0000ffff
|
||||
#define MUSICPLAYER_STATUS_PAUSE 0x80000000
|
||||
|
||||
#define MAX_MUSICPLAYER_TRACKS 16
|
||||
|
||||
#define TEMPORARY_FADE 0x0001
|
||||
#define FADE_IN 0x0002
|
||||
#define FADE_VOL_MAX 64
|
||||
#define FADE_VOL_SHIFT 2
|
||||
|
||||
struct MusicPlayerInfo
|
||||
{
|
||||
struct SongHeader *songHeader;
|
||||
u32 status;
|
||||
u8 trackCount;
|
||||
u8 priority;
|
||||
u8 cmd;
|
||||
u8 unk_B;
|
||||
u32 clock;
|
||||
u8 gap[8];
|
||||
u8 *memAccArea;
|
||||
u16 tempoD;
|
||||
u16 tempoU;
|
||||
u16 tempoI;
|
||||
u16 tempoC;
|
||||
u16 fadeOI;
|
||||
u16 fadeOC;
|
||||
u16 fadeOV;
|
||||
struct MusicPlayerTrack *tracks;
|
||||
struct ToneData *tone;
|
||||
u32 ident;
|
||||
u32 func;
|
||||
u32 intp;
|
||||
};
|
||||
|
||||
struct MusicPlayer
|
||||
{
|
||||
struct MusicPlayerInfo *info;
|
||||
struct MusicPlayerTrack *track;
|
||||
u8 unk_8;
|
||||
u16 unk_A;
|
||||
};
|
||||
|
||||
struct Song
|
||||
{
|
||||
struct SongHeader *header;
|
||||
u16 ms;
|
||||
u16 me;
|
||||
};
|
||||
|
||||
extern const struct MusicPlayer gMPlayTable[];
|
||||
extern const struct Song gSongTable[];
|
||||
|
||||
|
||||
|
||||
extern u8 gMPlayMemAccArea[];
|
||||
|
||||
//u8 gPokemonCrySong[52];
|
||||
//u8 gPokemonCrySongs[52 * MAX_POKEMON_CRIES];
|
||||
|
||||
#define MAX_POKEMON_CRIES 2
|
||||
|
||||
extern struct PokemonCrySong gPokemonCrySong;
|
||||
extern struct PokemonCrySong gPokemonCrySongs[];
|
||||
|
||||
extern struct MusicPlayerInfo gPokemonCryMusicPlayers[];
|
||||
extern struct MusicPlayerTrack gPokemonCryTracks[];
|
||||
|
||||
extern char SoundMainRAM[];
|
||||
|
||||
extern void *gMPlayJumpTable[];
|
||||
|
||||
typedef void (*XcmdFunc)(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
extern const XcmdFunc gXcmdTable[];
|
||||
|
||||
extern struct CgbChannel gCgbChans[];
|
||||
|
||||
extern const u8 gScaleTable[];
|
||||
extern const u32 gFreqTable[];
|
||||
extern const u16 gPcmSamplesPerVBlankTable[];
|
||||
|
||||
extern const u8 gCgbScaleTable[];
|
||||
extern const s16 gCgbFreqTable[];
|
||||
extern const u8 gNoiseTable[];
|
||||
|
||||
extern const struct PokemonCrySong gPokemonCrySongTemplate;
|
||||
|
||||
extern const struct ToneData voicegroup000;
|
||||
|
||||
extern char gNumMusicPlayers[];
|
||||
extern char gMaxLines[];
|
||||
|
||||
#define NUM_MUSIC_PLAYERS ((u16)gNumMusicPlayers)
|
||||
#define MAX_LINES ((u32)gMaxLines)
|
||||
|
||||
u32 umul3232H32(u32 multiplier, u32 multiplicand);
|
||||
void SoundMain(void);
|
||||
void SoundMainBTM(void);
|
||||
void TrackStop(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track);
|
||||
void MPlayMain(void);
|
||||
void RealClearChain(void *x);
|
||||
|
||||
void MPlayContinue(struct MusicPlayerInfo *mplayInfo);
|
||||
void MPlayStart(struct MusicPlayerInfo *mplayInfo, struct SongHeader *songHeader);
|
||||
void m4aMPlayStop(struct MusicPlayerInfo *mplayInfo);
|
||||
void FadeOutBody(struct MusicPlayerInfo *mplayInfo);
|
||||
void TrkVolPitSet(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track);
|
||||
void MPlayFadeOut(struct MusicPlayerInfo *mplayInfo, u16 speed);
|
||||
void ClearChain(void *x);
|
||||
void Clear64byte(void *addr);
|
||||
void SoundInit(struct SoundInfo *soundInfo);
|
||||
void MPlayExtender(struct CgbChannel *cgbChans);
|
||||
void m4aSoundMode(u32 mode);
|
||||
void MPlayOpen(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track, u8 a3);
|
||||
void CgbSound(void);
|
||||
void CgbOscOff(u8);
|
||||
u32 MidiKeyToCgbFreq(u8, u8, u8);
|
||||
void DummyFunc(void);
|
||||
void MPlayJumpTableCopy(void **mplayJumpTable);
|
||||
void SampleFreqSet(u32 freq);
|
||||
void m4aSoundVSyncOn(void);
|
||||
void m4aSoundVSyncOff(void);
|
||||
|
||||
void m4aMPlayTempoControl(struct MusicPlayerInfo *mplayInfo, u16 tempo);
|
||||
void m4aMPlayVolumeControl(struct MusicPlayerInfo *mplayInfo, u16 trackBits, u16 volume);
|
||||
void m4aMPlayPitchControl(struct MusicPlayerInfo *mplayInfo, u16 trackBits, s16 pitch);
|
||||
void m4aMPlayPanpotControl(struct MusicPlayerInfo *mplayInfo, u16 trackBits, s8 pan);
|
||||
void ClearModM(struct MusicPlayerTrack *track);
|
||||
void m4aMPlayModDepthSet(struct MusicPlayerInfo *mplayInfo, u16 trackBits, u8 modDepth);
|
||||
void m4aMPlayLFOSpeedSet(struct MusicPlayerInfo *mplayInfo, u16 trackBits, u8 lfoSpeed);
|
||||
|
||||
struct MusicPlayerInfo *SetPokemonCryTone(struct ToneData *tone);
|
||||
void SetPokemonCryVolume(u8 val);
|
||||
void SetPokemonCryPanpot(s8 val);
|
||||
void SetPokemonCryPitch(s16 val);
|
||||
void SetPokemonCryLength(u16 val);
|
||||
void SetPokemonCryRelease(u8 val);
|
||||
void SetPokemonCryProgress(u32 val);
|
||||
bool32 IsPokemonCryPlaying(struct MusicPlayerInfo *mplayInfo);
|
||||
void SetPokemonCryChorus(s8 val);
|
||||
void SetPokemonCryStereo(u32 val);
|
||||
void SetPokemonCryPriority(u8 val);
|
||||
|
||||
// sound command handler functions
|
||||
void ply_fine(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_goto(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_patt(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_pend(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_rept(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_memacc(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_prio(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_tempo(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_keysh(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_voice(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_vol(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_pan(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_bend(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_bendr(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_lfos(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_lfodl(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_mod(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_modt(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_tune(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_port(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_xcmd(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_endtie(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_note(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
|
||||
// extended sound command handler functions
|
||||
void ply_xxx(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_xwave(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_xtype(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_xatta(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_xdeca(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_xsust(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_xrele(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_xiecv(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_xiecl(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_xleng(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_xswee(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_xcmd_0C(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
void ply_xcmd_0D(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
|
||||
|
||||
#endif // GUARD_GBA_M4A_INTERNAL_H
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#ifndef GUARD_GLOBAL_BERRY_H
|
||||
#define GUARD_GLOBAL_BERRY_H
|
||||
|
||||
struct Berry
|
||||
{
|
||||
/*0x00*/ u8 name[7];
|
||||
/*0x07*/ u8 firmness;
|
||||
/*0x08*/ u16 size;
|
||||
/*0x0A*/ u8 maxYield;
|
||||
/*0x0B*/ u8 minYield;
|
||||
/*0x0C*/ const u8 *description1;
|
||||
/*0x10*/ const u8 *description2;
|
||||
/*0x14*/ u8 stageDuration;
|
||||
/*0x15*/ u8 spicy;
|
||||
/*0x16*/ u8 dry;
|
||||
/*0x17*/ u8 sweet;
|
||||
/*0x18*/ u8 bitter;
|
||||
/*0x19*/ u8 sour;
|
||||
/*0x1A*/ u8 smoothness;
|
||||
};
|
||||
|
||||
struct EnigmaBerry
|
||||
{
|
||||
/*0x000*/ struct Berry berry;
|
||||
/*0x01B*/ u8 pic[(6 * 6) * TILE_SIZE_4BPP];
|
||||
/*0x49C*/ u16 palette[16];
|
||||
/*0x4BC*/ u8 description1[45];
|
||||
/*0x4E9*/ u8 description2[45];
|
||||
/*0x516*/ u8 itemEffect[18];
|
||||
/*0x528*/ u8 holdEffect;
|
||||
/*0x529*/ u8 holdEffectParam;
|
||||
/*0x52C*/ u32 checksum;
|
||||
};
|
||||
|
||||
struct BattleEnigmaBerry
|
||||
{
|
||||
/*0x00*/ u8 name[7];
|
||||
/*0x07*/ u8 holdEffect;
|
||||
/*0x08*/ u8 itemEffect[18];
|
||||
/*0x1A*/ u8 holdEffectParam;
|
||||
};
|
||||
|
||||
struct BerryTree
|
||||
{
|
||||
/*0x00*/ u8 berry;
|
||||
/*0x01*/ u8 stage:7;
|
||||
/*
|
||||
A berry sparkle is a state that a berry tree
|
||||
can be in after growing within the player's
|
||||
viewport.
|
||||
*/
|
||||
/*0x01*/ bool8 growthSparkle:1;
|
||||
/*0x02*/ u16 minutesUntilNextStage;
|
||||
/*0x04*/ u8 berryYield;
|
||||
/*0x05*/ u8 regrowthCount:4;
|
||||
/*0x05*/ u8 watered1:1;
|
||||
/*0x05*/ u8 watered2:1;
|
||||
/*0x05*/ u8 watered3:1;
|
||||
/*0x05*/ u8 watered4:1;
|
||||
};
|
||||
|
||||
#endif // GUARD_GLOBAL_BERRY_H
|
||||
|
|
@ -1,310 +0,0 @@
|
|||
#ifndef GUARD_GLOBAL_FIELDMAP_H
|
||||
#define GUARD_GLOBAL_FIELDMAP_H
|
||||
|
||||
enum
|
||||
{
|
||||
CONNECTION_SOUTH = 1,
|
||||
CONNECTION_NORTH,
|
||||
CONNECTION_WEST,
|
||||
CONNECTION_EAST,
|
||||
CONNECTION_DIVE,
|
||||
CONNECTION_EMERGE
|
||||
};
|
||||
|
||||
typedef void (*TilesetCB)(void);
|
||||
|
||||
struct Tileset
|
||||
{
|
||||
/*0x00*/ bool8 isCompressed;
|
||||
/*0x01*/ bool8 isSecondary;
|
||||
/*0x04*/ void *tiles;
|
||||
/*0x08*/ void *palettes;
|
||||
/*0x0c*/ void *metatiles;
|
||||
/*0x10*/ void *metatileAttributes;
|
||||
/*0x14*/ TilesetCB callback;
|
||||
};
|
||||
|
||||
struct MapLayout
|
||||
{
|
||||
/*0x00*/ s32 width;
|
||||
/*0x04*/ s32 height;
|
||||
/*0x08*/ u16 *border;
|
||||
/*0x0c*/ u16 *map;
|
||||
/*0x10*/ struct Tileset *primaryTileset;
|
||||
/*0x14*/ struct Tileset *secondaryTileset;
|
||||
};
|
||||
|
||||
struct BackupMapLayout
|
||||
{
|
||||
s32 width;
|
||||
s32 height;
|
||||
u16 *map;
|
||||
};
|
||||
|
||||
struct EventObjectTemplate
|
||||
{
|
||||
/*0x00*/ u8 localId;
|
||||
/*0x01*/ u8 graphicsId;
|
||||
/*0x02*/ u8 unk2;
|
||||
/*0x04*/ s16 x;
|
||||
/*0x06*/ s16 y;
|
||||
/*0x08*/ u8 elevation;
|
||||
/*0x09*/ u8 movementType;
|
||||
/*0x0A*/ u8 movementRangeX:4;
|
||||
u8 movementRangeY:4;
|
||||
/*0x0C*/ u16 trainerType;
|
||||
/*0x0E*/ u16 trainerRange_berryTreeId;
|
||||
/*0x10*/ u8 *script;
|
||||
/*0x14*/ u16 flagId;
|
||||
};
|
||||
|
||||
struct WarpEvent
|
||||
{
|
||||
s16 x, y;
|
||||
u8 elevation;
|
||||
u8 warpId;
|
||||
u8 mapNum;
|
||||
u8 mapGroup;
|
||||
};
|
||||
|
||||
struct CoordEvent
|
||||
{
|
||||
s16 x, y;
|
||||
u8 elevation;
|
||||
u16 trigger;
|
||||
u16 index;
|
||||
u8 filler_A[0x2];
|
||||
u8 *script;
|
||||
};
|
||||
|
||||
struct BgEvent
|
||||
{
|
||||
u16 x, y;
|
||||
u8 elevation;
|
||||
u8 kind; // The "kind" field determines how to access bgUnion union below.
|
||||
union {
|
||||
u8 *script;
|
||||
struct {
|
||||
u16 item;
|
||||
u16 hiddenItemId;
|
||||
} hiddenItem;
|
||||
u32 secretBaseId;
|
||||
} bgUnion;
|
||||
};
|
||||
|
||||
struct MapEvents
|
||||
{
|
||||
u8 eventObjectCount;
|
||||
u8 warpCount;
|
||||
u8 coordEventCount;
|
||||
u8 bgEventCount;
|
||||
|
||||
struct EventObjectTemplate *eventObjects;
|
||||
struct WarpEvent *warps;
|
||||
struct CoordEvent *coordEvents;
|
||||
struct BgEvent *bgEvents;
|
||||
};
|
||||
|
||||
struct MapConnection
|
||||
{
|
||||
/*0x00*/ u8 direction;
|
||||
/*0x01*/ u32 offset;
|
||||
/*0x05*/ u8 mapGroup;
|
||||
/*0x06*/ u8 mapNum;
|
||||
};
|
||||
|
||||
struct MapConnections
|
||||
{
|
||||
s32 count;
|
||||
struct MapConnection *connections;
|
||||
};
|
||||
|
||||
struct MapHeader
|
||||
{
|
||||
/* 0x00 */ struct MapLayout *mapLayout;
|
||||
/* 0x04 */ struct MapEvents *events;
|
||||
/* 0x08 */ u8 *mapScripts;
|
||||
/* 0x0C */ struct MapConnections *connections;
|
||||
/* 0x10 */ u16 music;
|
||||
/* 0x12 */ u16 mapLayoutId;
|
||||
/* 0x14 */ u8 regionMapSectionId;
|
||||
/* 0x15 */ u8 cave;
|
||||
/* 0x16 */ u8 weather;
|
||||
/* 0x17 */ u8 mapType;
|
||||
/* 0x18 */ u8 filler_18;
|
||||
/* 0x19 */ u8 escapeRope;
|
||||
/* 0x1A */ u8 flags;
|
||||
/* 0x1B */ u8 battleType;
|
||||
};
|
||||
|
||||
struct EventObject
|
||||
{
|
||||
/*0x00*/ u32 active:1;
|
||||
u32 singleMovementActive:1;
|
||||
u32 triggerGroundEffectsOnMove:1;
|
||||
u32 triggerGroundEffectsOnStop:1;
|
||||
u32 disableCoveringGroundEffects:1; // disables ground effects that cover parts of the object's sprite
|
||||
u32 landingJump:1;
|
||||
u32 heldMovementActive:1;
|
||||
u32 heldMovementFinished:1;
|
||||
/*0x01*/ u32 frozen:1;
|
||||
u32 facingDirectionLocked:1;
|
||||
u32 disableAnim:1; // used to disable forced movement sliding animations (like on ice)
|
||||
u32 enableAnim:1;
|
||||
u32 inanimate:1;
|
||||
u32 invisible:1;
|
||||
u32 offScreen:1;
|
||||
u32 trackedByCamera:1; // only set for the player object
|
||||
/*0x02*/ u32 isPlayer:1;
|
||||
u32 hasReflection:1;
|
||||
u32 inShortGrass:1;
|
||||
u32 inShallowFlowingWater:1;
|
||||
u32 inSandPile:1;
|
||||
u32 inHotSprings:1;
|
||||
u32 hasShadow:1;
|
||||
u32 spriteAnimPausedBackup:1;
|
||||
/*0x03*/ u32 spriteAffineAnimPausedBackup:1;
|
||||
u32 disableJumpLandingGroundEffect:1;
|
||||
u32 fixedPriority:1;
|
||||
/*0x04*/ u8 spriteId;
|
||||
/*0x05*/ u8 graphicsId;
|
||||
/*0x06*/ u8 movementType;
|
||||
/*0x07*/ u8 trainerType;
|
||||
/*0x08*/ u8 localId;
|
||||
/*0x09*/ u8 mapNum;
|
||||
/*0x0A*/ u8 mapGroup;
|
||||
/*0x0B*/ u8 currentElevation:4;
|
||||
u8 previousElevation:4;
|
||||
/*0x0C*/ struct Coords16 initialCoords;
|
||||
/*0x10*/ struct Coords16 currentCoords;
|
||||
/*0x14*/ struct Coords16 previousCoords;
|
||||
/*0x18*/ u8 facingDirection:4;
|
||||
/*0x18*/ u8 movementDirection:4;
|
||||
/*0x19*/ union __attribute__((packed)) {
|
||||
u8 as_byte;
|
||||
struct __attribute__((packed)) {
|
||||
u16 x:4;
|
||||
u16 y:4;
|
||||
} as_nybbles;
|
||||
} range;
|
||||
/*0x1A*/ u8 fieldEffectSpriteId;
|
||||
/*0x1B*/ u8 warpArrowSpriteId;
|
||||
/*0x1C*/ u8 movementActionId;
|
||||
/*0x1D*/ u8 trainerRange_berryTreeId;
|
||||
/*0x1E*/ u8 currentMetatileBehavior;
|
||||
/*0x1F*/ u8 previousMetatileBehavior;
|
||||
/*0x20*/ u8 previousMovementDirection;
|
||||
/*0x21*/ u8 directionSequenceIndex;
|
||||
/*0x22*/ u8 playerCopyableMovement; // used as an index to gCopyPlayerMovementFuncs for the "copy player" movement types
|
||||
/*size = 0x24*/
|
||||
};
|
||||
|
||||
struct EventObjectGraphicsInfo
|
||||
{
|
||||
/*0x00*/ u16 tileTag;
|
||||
/*0x02*/ u16 paletteTag;
|
||||
/*0x04*/ u16 bridgeReflectionPaletteTag;
|
||||
/*0x06*/ u16 size;
|
||||
/*0x08*/ s16 width;
|
||||
/*0x0A*/ s16 height;
|
||||
/*0x0C*/ u8 paletteSlot:4;
|
||||
u8 shadowSize:2;
|
||||
u8 inanimate:1;
|
||||
u8 disableReflectionPaletteLoad:1;
|
||||
/*0x0D*/ u8 tracks;
|
||||
/*0x10*/ const struct OamData *oam;
|
||||
/*0x14*/ const struct SubspriteTable *subspriteTables;
|
||||
/*0x18*/ const union AnimCmd *const *anims;
|
||||
/*0x1C*/ const struct SpriteFrameImage *images;
|
||||
/*0x20*/ const union AffineAnimCmd *const *affineAnims;
|
||||
};
|
||||
|
||||
#define PLAYER_AVATAR_FLAG_ON_FOOT (1 << 0)
|
||||
#define PLAYER_AVATAR_FLAG_MACH_BIKE (1 << 1)
|
||||
#define PLAYER_AVATAR_FLAG_ACRO_BIKE (1 << 2)
|
||||
#define PLAYER_AVATAR_FLAG_SURFING (1 << 3)
|
||||
#define PLAYER_AVATAR_FLAG_UNDERWATER (1 << 4)
|
||||
#define PLAYER_AVATAR_FLAG_CONTROLLABLE (1 << 5)
|
||||
#define PLAYER_AVATAR_FLAG_FORCED_MOVE (1 << 6)
|
||||
#define PLAYER_AVATAR_FLAG_DASH (1 << 7)
|
||||
|
||||
enum
|
||||
{
|
||||
ACRO_BIKE_NORMAL,
|
||||
ACRO_BIKE_TURNING,
|
||||
ACRO_BIKE_WHEELIE_STANDING,
|
||||
ACRO_BIKE_BUNNY_HOP,
|
||||
ACRO_BIKE_WHEELIE_MOVING,
|
||||
ACRO_BIKE_STATE5,
|
||||
ACRO_BIKE_STATE6,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
DIR_NONE,
|
||||
DIR_SOUTH,
|
||||
DIR_NORTH,
|
||||
DIR_WEST,
|
||||
DIR_EAST,
|
||||
DIR_SOUTHWEST,
|
||||
DIR_SOUTHEAST,
|
||||
DIR_NORTHWEST,
|
||||
DIR_NORTHEAST,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
COLLISION_LEDGE_JUMP = 6
|
||||
};
|
||||
|
||||
// player running states
|
||||
enum
|
||||
{
|
||||
NOT_MOVING,
|
||||
TURN_DIRECTION, // not the same as turning! turns your avatar without moving. also known as a turn frame in some circles
|
||||
MOVING,
|
||||
};
|
||||
|
||||
// player tile transition states
|
||||
enum
|
||||
{
|
||||
T_NOT_MOVING,
|
||||
T_TILE_TRANSITION,
|
||||
T_TILE_CENTER, // player is on a frame in which they are centered on a tile during which the player either stops or keeps their momentum and keeps going, changing direction if necessary.
|
||||
};
|
||||
|
||||
struct PlayerAvatar /* 0x202E858 */
|
||||
{
|
||||
/*0x00*/ u8 flags;
|
||||
/*0x01*/ u8 unk1; // used to be named bike, but its definitely not that. seems to be some transition flags
|
||||
/*0x02*/ u8 runningState; // this is a static running state. 00 is not moving, 01 is turn direction, 02 is moving.
|
||||
/*0x03*/ u8 tileTransitionState; // this is a transition running state: 00 is not moving, 01 is transition between tiles, 02 means you are on the frame in which you have centered on a tile but are about to keep moving, even if changing directions. 2 is also used for a ledge hop, since you are transitioning.
|
||||
/*0x04*/ u8 spriteId;
|
||||
/*0x05*/ u8 eventObjectId;
|
||||
/*0x06*/ bool8 preventStep;
|
||||
/*0x07*/ u8 gender;
|
||||
/*0x08*/ u8 acroBikeState; // 00 is normal, 01 is turning, 02 is standing wheelie, 03 is hopping wheelie
|
||||
/*0x09*/ u8 newDirBackup; // during bike movement, the new direction as opposed to player's direction is backed up here.
|
||||
/*0x0A*/ u8 bikeFrameCounter; // on the mach bike, when this value is 1, the bike is moving but not accelerating yet for 1 tile. on the acro bike, this acts as a timer for acro bike.
|
||||
/*0x0B*/ u8 bikeSpeed;
|
||||
// acro bike only
|
||||
/*0x0C*/ u32 directionHistory; // up/down/left/right history is stored in each nybble, but using the field directions and not the io inputs.
|
||||
/*0x10*/ u32 abStartSelectHistory; // same as above but for A + B + start + select only
|
||||
// these two are timer history arrays which [0] is the active timer for acro bike. every element is backed up to the next element upon update.
|
||||
/*0x14*/ u8 dirTimerHistory[8];
|
||||
/*0x1C*/ u8 abStartSelectTimerHistory[8];
|
||||
};
|
||||
|
||||
struct Camera
|
||||
{
|
||||
bool8 active:1;
|
||||
s32 x;
|
||||
s32 y;
|
||||
};
|
||||
|
||||
extern struct EventObject gMapObjects[];
|
||||
extern u8 gSelectedEventObject;
|
||||
extern struct MapHeader gMapHeader;
|
||||
extern struct PlayerAvatar gPlayerAvatar;
|
||||
|
||||
#endif // GUARD_GLOBAL_FIELDMAP_H
|
||||
|
|
@ -2,8 +2,7 @@
|
|||
#define GUARD_GLOBAL_H
|
||||
|
||||
#include "gba/gba.h"
|
||||
|
||||
// global.h from pokemon ruby
|
||||
#include "constants/vars.h"
|
||||
|
||||
// IDE support
|
||||
#if defined(__APPLE__) || defined(__CYGWIN__) || defined(__INTELLISENSE__)
|
||||
|
|
@ -19,62 +18,17 @@
|
|||
#define INCBIN_S32 INCBIN
|
||||
#endif // IDE support
|
||||
|
||||
// Prevent cross-jump optimization.
|
||||
#define BLOCK_CROSS_JUMP asm("");
|
||||
|
||||
// to help in decompiling
|
||||
#define asm_comment(x) asm volatile("@ -- " x " -- ")
|
||||
|
||||
#define asm_unified(x) asm(".syntax unified\n" x "\n.syntax divided\n")
|
||||
|
||||
#define ARRAY_COUNT(array) (sizeof(array) / sizeof((array)[0]))
|
||||
|
||||
|
||||
#define POKEMON_SLOTS_NUMBER 412
|
||||
#define POKEMON_NAME_LENGTH 10
|
||||
#define OT_NAME_LENGTH 7
|
||||
#define RomHeaderGameTitle ((const char *)0x080000A0)
|
||||
#define RomHeaderGameCode ((const char *)0x080000AC)
|
||||
#define RomHeaderMakerCode ((const char *)0x080000B0)
|
||||
#define RomHeaderMagic ((const u8 *) 0x080000B2)
|
||||
#define RomHeaderSoftwareVersion ((const u8 *) 0x080000BC)
|
||||
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define max(a, b) ((a) >= (b) ? (a) : (b))
|
||||
|
||||
// why does GF hate 2d arrays
|
||||
#define MULTI_DIM_ARR(x, dim, y) ((x) * dim + (y))
|
||||
|
||||
// dim access enums
|
||||
enum
|
||||
{
|
||||
B_8 = 1,
|
||||
B_16 = 2,
|
||||
B_32 = 4
|
||||
};
|
||||
|
||||
// There are many quirks in the source code which have overarching behavioral differences from
|
||||
// a number of other files. For example, diploma.c seems to declare rodata before each use while
|
||||
// other files declare out of order and must be at the beginning. There are also a number of
|
||||
// macros which differ from one file to the next due to the method of obtaining the result, such
|
||||
// as these below. Because of this, there is a theory (Two Team Theory) that states that these
|
||||
// programming projects had more than 1 "programming team" which utilized different macros for
|
||||
// each of the files that were worked on.
|
||||
#define T1_READ_8(ptr) ((ptr)[0])
|
||||
#define T1_READ_16(ptr) ((ptr)[0] | ((ptr)[1] << 8))
|
||||
#define T1_READ_32(ptr) ((ptr)[0] | ((ptr)[1] << 8) | ((ptr)[2] << 16) | ((ptr)[3] << 24))
|
||||
#define T1_READ_PTR(ptr) (u8*) T1_READ_32(ptr)
|
||||
|
||||
// T2_READ_8 is a duplicate to remain consistent with each group.
|
||||
#define T2_READ_8(ptr) ((ptr)[0])
|
||||
#define T2_READ_16(ptr) ((ptr)[0] + ((ptr)[1] << 8))
|
||||
#define T2_READ_32(ptr) ((ptr)[0] + ((ptr)[1] << 8) + ((ptr)[2] << 16) + ((ptr)[3] << 24))
|
||||
#define T2_READ_PTR(ptr) (void*) T2_READ_32(ptr)
|
||||
|
||||
// Credits to Made (dolphin emoji)
|
||||
#define S16TOPOSFLOAT(val) \
|
||||
({ \
|
||||
s16 v = (val); \
|
||||
float f = (float)v; \
|
||||
if(v < 0) f += 65536.0f; \
|
||||
f; \
|
||||
})
|
||||
|
||||
enum
|
||||
{
|
||||
VERSION_SAPPHIRE = 1,
|
||||
|
|
@ -89,649 +43,6 @@ enum LanguageId
|
|||
LANGUAGE_GERMAN = 5,
|
||||
};
|
||||
|
||||
// capacities of various saveblock objects
|
||||
#define DAYCARE_MON_COUNT 2
|
||||
#define POKEBLOCKS_COUNT 40
|
||||
#define PARTY_SIZE 6
|
||||
#define EVENT_OBJECTS_COUNT 16
|
||||
#define BERRY_TREES_COUNT 128
|
||||
#define FLAGS_COUNT 288
|
||||
#define VARS_COUNT 256
|
||||
#define MAIL_COUNT 16
|
||||
#define SECRET_BASES_COUNT 20
|
||||
#define TV_SHOWS_COUNT 25
|
||||
#define POKE_NEWS_COUNT 16
|
||||
#define PC_ITEMS_COUNT 50
|
||||
#define BAG_ITEMS_COUNT 20
|
||||
#define BAG_KEYITEMS_COUNT 20
|
||||
#define BAG_POKEBALLS_COUNT 16
|
||||
#define BAG_TMHM_COUNT 64
|
||||
#define BAG_BERRIES_COUNT 46
|
||||
|
||||
enum
|
||||
{
|
||||
MALE,
|
||||
FEMALE
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
OPTIONS_BUTTON_MODE_NORMAL,
|
||||
OPTIONS_BUTTON_MODE_LR,
|
||||
OPTIONS_BUTTON_MODE_L_EQUALS_A
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
OPTIONS_TEXT_SPEED_SLOW,
|
||||
OPTIONS_TEXT_SPEED_MID,
|
||||
OPTIONS_TEXT_SPEED_FAST
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
OPTIONS_SOUND_MONO,
|
||||
OPTIONS_SOUND_STEREO
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
OPTIONS_BATTLE_STYLE_SHIFT,
|
||||
OPTIONS_BATTLE_STYLE_SET
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
BAG_ITEMS = 1,
|
||||
BAG_POKEBALLS,
|
||||
BAG_TMsHMs,
|
||||
BAG_BERRIES,
|
||||
BAG_KEYITEMS
|
||||
};
|
||||
|
||||
struct Coords16
|
||||
{
|
||||
s16 x;
|
||||
s16 y;
|
||||
};
|
||||
|
||||
struct UCoords16
|
||||
{
|
||||
u16 x;
|
||||
u16 y;
|
||||
};
|
||||
|
||||
struct SecretBaseRecord
|
||||
{
|
||||
/*0x1A08*/ u8 secretBaseId;
|
||||
/*0x1A09*/ u8 sbr_field_1_0:4;
|
||||
/*0x1A09*/ u8 gender:1;
|
||||
/*0x1A09*/ u8 sbr_field_1_5:1;
|
||||
/*0x1A09*/ u8 sbr_field_1_6:2;
|
||||
/*0x1A0A*/ u8 playerName[OT_NAME_LENGTH];
|
||||
/*0x1A11*/ u8 trainerId[4]; // byte 0 is used for determining trainer class
|
||||
/*0x1A16*/ u16 sbr_field_e;
|
||||
/*0x1A18*/ u8 sbr_field_10;
|
||||
/*0x1A19*/ u8 sbr_field_11;
|
||||
/*0x1A1A*/ u8 decorations[16];
|
||||
/*0x1A2A*/ u8 decorationPos[16];
|
||||
/*0x1A3C*/ u32 partyPersonality[6];
|
||||
/*0x1A54*/ u16 partyMoves[6 * 4];
|
||||
/*0x1A84*/ u16 partySpecies[6];
|
||||
/*0x1A90*/ u16 partyHeldItems[6];
|
||||
/*0x1A9C*/ u8 partyLevels[6];
|
||||
/*0x1AA2*/ u8 partyEVs[6];
|
||||
};
|
||||
|
||||
#include "constants/game_stat.h"
|
||||
#include "global.fieldmap.h"
|
||||
#include "global.berry.h"
|
||||
#include "pokemon.h"
|
||||
|
||||
struct WarpData
|
||||
{
|
||||
s8 mapGroup;
|
||||
s8 mapNum;
|
||||
s8 warpId;
|
||||
s16 x, y;
|
||||
};
|
||||
|
||||
struct ItemSlot
|
||||
{
|
||||
u16 itemId;
|
||||
u16 quantity;
|
||||
};
|
||||
|
||||
struct Pokeblock
|
||||
{
|
||||
u8 color;
|
||||
u8 spicy;
|
||||
u8 dry;
|
||||
u8 sweet;
|
||||
u8 bitter;
|
||||
u8 sour;
|
||||
u8 feel;
|
||||
};
|
||||
|
||||
struct Roamer
|
||||
{
|
||||
/*0x00*/ u32 ivs;
|
||||
/*0x04*/ u32 personality;
|
||||
/*0x08*/ u16 species;
|
||||
/*0x0A*/ u16 hp;
|
||||
/*0x0C*/ u8 level;
|
||||
/*0x0D*/ u8 status;
|
||||
/*0x0E*/ u8 cool;
|
||||
/*0x0F*/ u8 beauty;
|
||||
/*0x10*/ u8 cute;
|
||||
/*0x11*/ u8 smart;
|
||||
/*0x12*/ u8 tough;
|
||||
/*0x13*/ bool8 active;
|
||||
/*0x14*/ u8 filler[0x8];
|
||||
};
|
||||
|
||||
struct RamScriptData
|
||||
{
|
||||
u8 magic;
|
||||
u8 mapGroup;
|
||||
u8 mapNum;
|
||||
u8 objectId;
|
||||
u8 script[995];
|
||||
};
|
||||
|
||||
struct RamScript
|
||||
{
|
||||
u32 checksum;
|
||||
struct RamScriptData data;
|
||||
};
|
||||
|
||||
struct EasyChatPair
|
||||
{
|
||||
u16 unk0_0:7;
|
||||
u16 unk0_7:7;
|
||||
u16 unk1_6:1;
|
||||
u16 unk2;
|
||||
u16 words[2];
|
||||
}; /*size = 0x8*/
|
||||
|
||||
struct TVShowCommon
|
||||
{
|
||||
/*0x00*/ u8 kind;
|
||||
/*0x01*/ bool8 active;
|
||||
/*0x02*/ u8 pad02[20];
|
||||
/*0x16*/ u16 var16[3];
|
||||
/*0x1C*/ u8 srcTrainerId3Lo;
|
||||
/*0x1D*/ u8 srcTrainerId3Hi;
|
||||
/*0x1E*/ u8 srcTrainerId2Lo;
|
||||
/*0x1F*/ u8 srcTrainerId2Hi;
|
||||
/*0x20*/ u8 srcTrainerIdLo;
|
||||
/*0x21*/ u8 srcTrainerIdHi;
|
||||
/*0x22*/ u8 trainerIdLo;
|
||||
/*0x23*/ u8 trainerIdHi;
|
||||
};
|
||||
|
||||
struct TVShowFanClubLetter
|
||||
{
|
||||
/*0x00*/ u8 kind;
|
||||
/*0x01*/ bool8 active;
|
||||
/*0x02*/ u16 species;
|
||||
/*0x04*/ u16 pad04[6];
|
||||
/*0x10*/ u8 playerName[8];
|
||||
/*0x18*/ u8 language;
|
||||
};
|
||||
|
||||
struct TVShowRecentHappenings
|
||||
{
|
||||
/*0x00*/ u8 kind;
|
||||
/*0x01*/ bool8 active;
|
||||
/*0x02*/ u16 var02;
|
||||
/*0x04*/ u16 var04[6];
|
||||
/*0x10*/ u8 playerName[8];
|
||||
/*0x18*/ u8 language;
|
||||
/*0x19*/ u8 pad19[10];
|
||||
};
|
||||
|
||||
struct TVShowFanclubOpinions
|
||||
{
|
||||
/*0x00*/ u8 kind;
|
||||
/*0x01*/ bool8 active;
|
||||
/*0x02*/ u16 var02;
|
||||
/*0x04*/ u8 var04A:4;
|
||||
/*0x04*/ u8 var04B:4;
|
||||
/*0x05*/ u8 playerName[8];
|
||||
/*0x0D*/ u8 language;
|
||||
/*0x0E*/ u8 var0E;
|
||||
/*0x0F*/ u8 var0F;
|
||||
/*0x10*/ u8 var10[8];
|
||||
/*0x18*/ u16 var18[2];
|
||||
/*0x1C*/ u16 var1C[4];
|
||||
};
|
||||
|
||||
struct TVShowUnknownType04
|
||||
{
|
||||
/*0x00*/ u8 kind;
|
||||
/*0x01*/ bool8 active;
|
||||
/*0x02*/ u8 pad02[4];
|
||||
/*0x06*/ u16 var06;
|
||||
};
|
||||
|
||||
struct TVShowNameRaterShow
|
||||
{
|
||||
/*0x00*/ u8 kind;
|
||||
/*0x01*/ bool8 active;
|
||||
/*0x02*/ u16 species;
|
||||
/*0x04*/ u8 pokemonName[11];
|
||||
/*0x0F*/ u8 trainerName[11];
|
||||
/*0x1A*/ u8 random;
|
||||
/*0x1B*/ u8 random2;
|
||||
/*0x1C*/ u16 var1C;
|
||||
/*0x1E*/ u8 language;
|
||||
/*0x1F*/ u8 pokemonNameLanguage;
|
||||
};
|
||||
|
||||
struct TVShowBravoTrainerPokemonProfiles
|
||||
{
|
||||
/*0x00*/ u8 kind;
|
||||
/*0x01*/ bool8 active;
|
||||
/*0x02*/ u16 species;
|
||||
/*0x04*/ u16 var04[2];
|
||||
/*0x08*/ u8 pokemonNickname[11];
|
||||
/*0x13*/ u8 contestCategory:3;
|
||||
/*0x13*/ u8 contestRank:2;
|
||||
/*0x13*/ u8 contestResult:2;
|
||||
/*0x13*/ u8 var13_7:1;
|
||||
/*0x14*/ u16 var14;
|
||||
/*0x16*/ u8 playerName[8];
|
||||
/*0x1E*/ u8 language;
|
||||
/*0x1F*/ u8 var1f;
|
||||
};
|
||||
|
||||
struct TVShowBravoTrainerBattleTowerSpotlight
|
||||
{
|
||||
/*0x00*/ u8 kind;
|
||||
/*0x01*/ bool8 active;
|
||||
/*0x02*/ u8 trainerName[8];
|
||||
/*0x0A*/ u16 species;
|
||||
/*0x0C*/ u8 enemyTrainerName[8];
|
||||
/*0x14*/ u16 defeatedSpecies;
|
||||
/*0x16*/ u16 var16;
|
||||
/*0x18*/ u16 var18[1];
|
||||
/*0x1A*/ u8 btLevel;
|
||||
/*0x1B*/ u8 var1b;
|
||||
/*0x1C*/ u8 var1c;
|
||||
/*0x1D*/ u8 language;
|
||||
};
|
||||
|
||||
struct TVShowPokemonToday
|
||||
{
|
||||
/*0x00*/ u8 kind;
|
||||
/*0x01*/ bool8 active;
|
||||
/*0x02*/ u8 language;
|
||||
/*0x03*/ u8 language2;
|
||||
/*0x04*/ u8 nickname[11];
|
||||
/*0x0F*/ u8 ball;
|
||||
/*0x10*/ u16 species;
|
||||
/*0x12*/ u8 var12;
|
||||
/*0x13*/ u8 playerName[8];
|
||||
};
|
||||
|
||||
struct TVShowSmartShopper
|
||||
{
|
||||
/*0x00*/ u8 kind;
|
||||
/*0x01*/ bool8 active;
|
||||
/*0x02*/ u8 priceReduced;
|
||||
/*0x03*/ u8 language;
|
||||
/*0x04*/ u8 pad04[2];
|
||||
/*0x06*/ u16 itemIds[3];
|
||||
/*0x0C*/ u16 itemAmounts[3];
|
||||
/*0x12*/ u8 shopLocation;
|
||||
/*0x13*/ u8 playerName[8];
|
||||
};
|
||||
|
||||
struct TVShowPokemonTodayFailed
|
||||
{
|
||||
/*0x00*/ u8 kind;
|
||||
/*0x01*/ bool8 active;
|
||||
/*0x02*/ u8 language;
|
||||
/*0x03*/ u8 pad03[9];
|
||||
/*0x0c*/ u16 species;
|
||||
/*0x0e*/ u16 species2;
|
||||
/*0x10*/ u8 var10;
|
||||
/*0x11*/ u8 var11;
|
||||
/*0x12*/ u8 var12;
|
||||
/*0x13*/ u8 playerName[8];
|
||||
};
|
||||
|
||||
struct TVShowPokemonAngler
|
||||
{
|
||||
/*0x00*/ u8 kind;
|
||||
/*0x01*/ bool8 active;
|
||||
/*0x02*/ u8 var02;
|
||||
/*0x03*/ u8 var03;
|
||||
/*0x04*/ u16 var04;
|
||||
/*0x06*/ u8 language;
|
||||
u8 pad07[12];
|
||||
/*0x13*/ u8 playerName[8];
|
||||
};
|
||||
|
||||
struct TVShowWorldOfMasters
|
||||
{
|
||||
/*0x00*/ u8 kind;
|
||||
/*0x01*/ bool8 active;
|
||||
/*0x02*/ u16 var02;
|
||||
/*0x04*/ u16 var04;
|
||||
/*0x06*/ u16 var06;
|
||||
/*0x08*/ u16 var08;
|
||||
/*0x0a*/ u8 var0a;
|
||||
/*0x0b*/ u8 language;
|
||||
u8 pad0c[7];
|
||||
/*0x13*/ u8 playerName[8];
|
||||
};
|
||||
|
||||
struct TVShowMassOutbreak
|
||||
{
|
||||
/*0x00*/ u8 kind;
|
||||
/*0x01*/ bool8 active;
|
||||
/*0x02*/ u8 var02;
|
||||
/*0x03*/ u8 var03;
|
||||
/*0x04*/ u16 moves[4];
|
||||
/*0x0C*/ u16 species;
|
||||
/*0x0E*/ u16 var0E;
|
||||
/*0x10*/ u8 locationMapNum;
|
||||
/*0x11*/ u8 locationMapGroup;
|
||||
/*0x12*/ u8 var12;
|
||||
/*0x13*/ u8 probability;
|
||||
/*0x14*/ u8 level;
|
||||
/*0x15*/ u8 var15;
|
||||
/*0x16*/ u16 daysLeft;
|
||||
/*0x18*/ u8 language;
|
||||
u8 pad19[11];
|
||||
};
|
||||
|
||||
typedef union TVShow
|
||||
{
|
||||
struct TVShowCommon common;
|
||||
struct TVShowFanClubLetter fanclubLetter;
|
||||
struct TVShowRecentHappenings recentHappenings;
|
||||
struct TVShowFanclubOpinions fanclubOpinions;
|
||||
struct TVShowUnknownType04 unkShow04;
|
||||
struct TVShowNameRaterShow nameRaterShow;
|
||||
struct TVShowBravoTrainerPokemonProfiles bravoTrainer;
|
||||
struct TVShowBravoTrainerBattleTowerSpotlight bravoTrainerTower;
|
||||
struct TVShowPokemonToday pokemonToday;
|
||||
struct TVShowSmartShopper smartshopperShow;
|
||||
struct TVShowPokemonTodayFailed pokemonTodayFailed;
|
||||
struct TVShowPokemonAngler pokemonAngler;
|
||||
struct TVShowWorldOfMasters worldOfMasters;
|
||||
struct TVShowMassOutbreak massOutbreak;
|
||||
} TVShow;
|
||||
|
||||
struct MailStruct
|
||||
{
|
||||
/*0x00*/ u16 words[9];
|
||||
/*0x12*/ u8 playerName[8];
|
||||
/*0x1A*/ u8 trainerId[4];
|
||||
/*0x1E*/ u16 species;
|
||||
/*0x20*/ u16 itemId;
|
||||
};
|
||||
|
||||
|
||||
// Mauville Pokemon Center men
|
||||
|
||||
struct MauvilleManCommon
|
||||
{
|
||||
u8 id;
|
||||
};
|
||||
|
||||
struct MauvilleManBard
|
||||
{
|
||||
/*0x00*/ u8 id;
|
||||
/*0x02*/ u16 songLyrics[6];
|
||||
/*0x0E*/ u16 temporaryLyrics[6];
|
||||
/*0x1A*/ u8 playerName[8];
|
||||
/*0x22*/ u8 filler_2DB6[0x3];
|
||||
/*0x25*/ u8 playerTrainerId[4];
|
||||
/*0x29*/ bool8 hasChangedSong;
|
||||
}; /*size = 0x2C*/
|
||||
|
||||
struct MauvilleManHipster
|
||||
{
|
||||
u8 id;
|
||||
bool8 alreadySpoken;
|
||||
};
|
||||
|
||||
struct MauvilleManTrader
|
||||
{
|
||||
u8 id;
|
||||
u8 unk1[4];
|
||||
u8 unk5[4][11];
|
||||
bool8 alreadyTraded;
|
||||
};
|
||||
|
||||
struct MauvilleManStoryteller
|
||||
{
|
||||
u8 id;
|
||||
bool8 alreadyRecorded;
|
||||
u8 filler2[2];
|
||||
u8 gameStatIDs[4];
|
||||
u8 trainerNames[4][7];
|
||||
u8 statValues[4][4];
|
||||
};
|
||||
|
||||
struct MauvilleManGiddy
|
||||
{
|
||||
/*0x00*/ u8 id;
|
||||
/*0x01*/ u8 taleCounter;
|
||||
/*0x02*/ u8 questionNum;
|
||||
/*0x04*/ u16 randomWords[10];
|
||||
/*0x18*/ u8 questionList[12];
|
||||
}; /*size = 0x2C*/
|
||||
|
||||
|
||||
union MauvilleMan
|
||||
{
|
||||
struct MauvilleManCommon common;
|
||||
struct MauvilleManBard bard;
|
||||
struct MauvilleManHipster hipster;
|
||||
struct MauvilleManTrader trader;
|
||||
struct MauvilleManStoryteller storyteller;
|
||||
struct MauvilleManGiddy giddy;
|
||||
u8 filler[0x40]; // needed to pad out the struct
|
||||
};
|
||||
|
||||
struct PokeNews
|
||||
{
|
||||
u8 kind;
|
||||
u8 state;
|
||||
u16 days;
|
||||
};
|
||||
|
||||
struct GabbyAndTyData
|
||||
{
|
||||
/*2b10*/ u16 mon1;
|
||||
/*2b12*/ u16 mon2;
|
||||
/*2b14*/ u16 lastMove;
|
||||
/*2b16*/ u16 quote;
|
||||
/*2b18*/ u8 mapnum;
|
||||
/*2b19*/ u8 battleNum;
|
||||
/*2b1a*/ u8 valA_0:1;
|
||||
/*2b1a*/ u8 valA_1:1;
|
||||
/*2b1a*/ u8 valA_2:1;
|
||||
/*2b1a*/ u8 valA_3:1;
|
||||
/*2b1a*/ u8 valA_4:1;
|
||||
/*2b1a*/ u8 valA_5:3;
|
||||
/*2b1b*/ u8 valB_0:1;
|
||||
/*2b1b*/ u8 valB_1:1;
|
||||
/*2b1b*/ u8 valB_2:1;
|
||||
/*2b1b*/ u8 valB_3:1;
|
||||
/*2b1b*/ u8 valB_4:1;
|
||||
/*2b1b*/ u8 valB_5:3;
|
||||
};
|
||||
|
||||
struct DayCareMail
|
||||
{
|
||||
/*0x00*/ struct MailStruct message;
|
||||
/*0x24*/ u8 names[19];
|
||||
};
|
||||
|
||||
struct DayCareStepCountersEtc {
|
||||
u32 steps[DAYCARE_MON_COUNT];
|
||||
u16 pendingEggPersonality;
|
||||
u8 eggCycleStepsRemaining;
|
||||
};
|
||||
|
||||
struct RecordMixingDayCareMail
|
||||
{
|
||||
struct DayCareMail mail[DAYCARE_MON_COUNT];
|
||||
u32 numDaycareMons;
|
||||
u16 itemsHeld[DAYCARE_MON_COUNT]; // marks whether or not each daycare mon is currently holding an item.
|
||||
};
|
||||
|
||||
struct DayCareMisc
|
||||
{
|
||||
struct DayCareMail mail[DAYCARE_MON_COUNT];
|
||||
struct DayCareStepCountersEtc countersEtc;
|
||||
};
|
||||
|
||||
struct DayCare {
|
||||
struct BoxPokemon mons[DAYCARE_MON_COUNT];
|
||||
struct DayCareMisc misc;
|
||||
};
|
||||
|
||||
struct LinkBattleRecord
|
||||
{
|
||||
u8 name[8];
|
||||
u16 trainerId;
|
||||
u16 wins;
|
||||
u16 losses;
|
||||
u16 draws;
|
||||
};
|
||||
|
||||
struct RecordMixingGiftData
|
||||
{
|
||||
u8 unk0;
|
||||
u8 quantity;
|
||||
u16 itemId;
|
||||
u8 filler4[8];
|
||||
};
|
||||
|
||||
struct RecordMixingGift
|
||||
{
|
||||
int checksum;
|
||||
struct RecordMixingGiftData data;
|
||||
};
|
||||
|
||||
struct ContestWinner
|
||||
{
|
||||
/*0x00*/ u32 personality; // personality
|
||||
/*0x04*/ u32 otId; // otId
|
||||
/*0x08*/ u16 species; // species
|
||||
/*0x0A*/ u8 contestCategory;
|
||||
/*0x0B*/ u8 nickname[11];
|
||||
/*0x16*/ u8 trainerName[8];
|
||||
};
|
||||
|
||||
// there should be enough flags for all 412 slots
|
||||
// each slot takes up 8 flags
|
||||
// if the value is not divisible by 8, we need to account for the reminder as well
|
||||
#define DEX_FLAGS_NO ((POKEMON_SLOTS_NUMBER / 8) + ((POKEMON_SLOTS_NUMBER % 8) ? 1 : 0))
|
||||
|
||||
struct SaveBlock1 /* 0x02025734 */
|
||||
{
|
||||
/*0x00*/ struct Coords16 pos;
|
||||
/*0x04*/ struct WarpData location;
|
||||
/*0x0C*/ struct WarpData warp1;
|
||||
/*0x14*/ struct WarpData warp2;
|
||||
/*0x1C*/ struct WarpData lastHealLocation;
|
||||
/*0x24*/ struct WarpData warp4;
|
||||
/*0x2C*/ u16 savedMusic;
|
||||
/*0x2E*/ u8 weather;
|
||||
/*0x2F*/ u8 weatherCycleStage;
|
||||
/*0x30*/ u8 flashLevel; // flash level on current map, 0 being normal and 4 being the darkest
|
||||
/*0x32*/ u16 mapLayoutId;
|
||||
/*0x34*/ u16 mapView[0x100];
|
||||
/*0x234*/ u8 playerPartyCount;
|
||||
/*0x238*/ struct Pokemon playerParty[6];
|
||||
/*0x490*/ u32 money;
|
||||
/*0x494*/ u16 coins;
|
||||
/*0x496*/ u16 registeredItem; // registered for use with SELECT button
|
||||
/*0x498*/ struct ItemSlot pcItems[PC_ITEMS_COUNT];
|
||||
/*0x560*/ struct ItemSlot bagPocket_Items[BAG_ITEMS_COUNT];
|
||||
/*0x5B0*/ struct ItemSlot bagPocket_KeyItems[BAG_KEYITEMS_COUNT];
|
||||
/*0x600*/ struct ItemSlot bagPocket_PokeBalls[BAG_POKEBALLS_COUNT];
|
||||
/*0x640*/ struct ItemSlot bagPocket_TMHM[BAG_TMHM_COUNT];
|
||||
/*0x740*/ struct ItemSlot bagPocket_Berries[BAG_BERRIES_COUNT];
|
||||
/*0x7F8*/ struct Pokeblock pokeblocks[POKEBLOCKS_COUNT];
|
||||
/*0x938*/ u8 dexSeen2[DEX_FLAGS_NO];
|
||||
/*0x96C*/ u16 berryBlenderRecords[3];
|
||||
/*0x972*/ u8 filler_972[0x6];
|
||||
/*0x978*/ u16 trainerRematchStepCounter;
|
||||
/*0x97A*/ u8 trainerRematches[100];
|
||||
/*0x9E0*/ struct EventObject eventObjects[EVENT_OBJECTS_COUNT];
|
||||
/*0xC20*/ struct EventObjectTemplate eventObjectTemplates[64];
|
||||
/*0x1220*/ u8 flags[FLAGS_COUNT];
|
||||
/*0x1340*/ u16 vars[VARS_COUNT];
|
||||
/*0x1540*/ u32 gameStats[NUM_GAME_STATS];
|
||||
/*0x1608*/ struct BerryTree berryTrees[BERRY_TREES_COUNT];
|
||||
/*0x1A08*/ struct SecretBaseRecord secretBases[SECRET_BASES_COUNT];
|
||||
/*0x2688*/ u8 playerRoomDecor[12];
|
||||
/*0x2694*/ u8 playerRoomDecorPos[12];
|
||||
/*0x26A0*/ u8 decorDesk[10];
|
||||
/*0x26AA*/ u8 decorChair[10];
|
||||
/*0x26B4*/ u8 decorPlant[10];
|
||||
/*0x26BE*/ u8 decorOrnament[30];
|
||||
/*0x26DC*/ u8 decorMat[30];
|
||||
/*0x26FA*/ u8 decorPoster[10];
|
||||
/*0x2704*/ u8 decorDoll[40];
|
||||
/*0x272C*/ u8 decorCushion[10];
|
||||
/*0x2736*/ u8 padding_2736[2];
|
||||
/*0x2738*/ TVShow tvShows[TV_SHOWS_COUNT];
|
||||
/*0x2ABC*/ struct PokeNews pokeNews[POKE_NEWS_COUNT];
|
||||
/*0x2AFC*/ u16 outbreakPokemonSpecies;
|
||||
/*0x2AFE*/ u8 outbreakLocationMapNum;
|
||||
/*0x2AFF*/ u8 outbreakLocationMapGroup;
|
||||
/*0x2B00*/ u8 outbreakPokemonLevel;
|
||||
/*0x2B01*/ u8 outbreakUnk1;
|
||||
/*0x2B02*/ u16 outbreakUnk2;
|
||||
/*0x2B04*/ u16 outbreakPokemonMoves[4];
|
||||
/*0x2B0C*/ u8 outbreakUnk4;
|
||||
/*0x2B0D*/ u8 outbreakPokemonProbability;
|
||||
/*0x2B0E*/ u16 outbreakUnk5;
|
||||
/*0x2B10*/ struct GabbyAndTyData gabbyAndTyData;
|
||||
/*0x2B1C*/ struct {
|
||||
/*0x2B1C*/ u16 unk2B1C[6];
|
||||
/*0x2B28*/ u16 unk2B28[6];
|
||||
/*0x2B34*/ u16 unk2B34[6];
|
||||
/*0x2B40*/ u16 unk2B40[6];
|
||||
} easyChats;
|
||||
/*0x2B4C*/ struct MailStruct mail[MAIL_COUNT];
|
||||
/*0x2D8C*/ u8 unk2D8C[4]; // What is this? Apparently it's supposed to be 64 bytes in size.
|
||||
/*0x2D90*/ u8 filler_2D90[0x4];
|
||||
/*0x2D94*/ union MauvilleMan mauvilleMan;
|
||||
/*0x2DD4*/ struct EasyChatPair easyChatPairs[5]; //Dewford trend [0] and some other stuff
|
||||
/*0x2DFC*/ struct ContestWinner contestWinners[8];
|
||||
/*0x2EFC*/ struct ContestWinner museumPortraits[5];
|
||||
/*0x2F9C*/ struct DayCare daycare;
|
||||
/*0x30B8*/ struct LinkBattleRecord linkBattleRecords[5];
|
||||
struct {
|
||||
/*0x3108*/ u8 unknown1[8];
|
||||
/*0x3110*/ u8 giftRibbons[11];
|
||||
/*0x311B*/ u8 unknown2[8];
|
||||
/*0x3123*/ u32 currentPokeCoupons;
|
||||
/*0x3127*/ u32 totalEarnedPokeCoupons;
|
||||
/*0x312B*/ u8 unknown3[6];
|
||||
/*0x3131*/ u8 receivedWishmakerJirachi;
|
||||
/*0x3132*/ u8 unknown4[18];
|
||||
} __attribute__((packed)) externalReservedData;
|
||||
/*0x3144*/ struct Roamer roamer;
|
||||
/*0x3160*/ struct EnigmaBerry enigmaBerry;
|
||||
/*0x3690*/ struct RamScript ramScript;
|
||||
/*0x3A7C*/ struct RecordMixingGift recordMixingGift;
|
||||
/*0x3A8C*/ u8 dexSeen3[DEX_FLAGS_NO];
|
||||
};
|
||||
|
||||
extern struct SaveBlock1 gSaveBlock1;
|
||||
|
||||
struct Time
|
||||
{
|
||||
/*0x00*/ s16 days;
|
||||
|
|
@ -740,137 +51,30 @@ struct Time
|
|||
/*0x04*/ s8 seconds;
|
||||
};
|
||||
|
||||
struct Pokedex
|
||||
{
|
||||
/*0x00*/ u8 order;
|
||||
/*0x01*/ u8 unknown1;
|
||||
/*0x02*/ u8 nationalMagic; // must equal 0xDA in order to have National mode
|
||||
/*0x03*/ u8 unknown2;
|
||||
/*0x04*/ u32 unownPersonality; // set when you first see Unown
|
||||
/*0x08*/ u32 spindaPersonality; // set when you first see Spinda
|
||||
/*0x0C*/ u32 unknown3;
|
||||
/*0x10*/ u8 owned[DEX_FLAGS_NO];
|
||||
/*0x44*/ u8 seen[DEX_FLAGS_NO];
|
||||
};
|
||||
// Dummy Ruby/Sapphire save structs.
|
||||
// Only vars, localTimeOffset, and lastBerryTreeUpdate are needed.
|
||||
|
||||
struct BattleTowerTrainer
|
||||
struct SaveBlock1
|
||||
{
|
||||
/*0x00*/ u8 trainerClass;
|
||||
/*0x01*/ u8 name[8];
|
||||
/*0x09*/ u8 teamFlags;
|
||||
u8 filler0A[2];
|
||||
/*0x0C*/ u16 greeting[6];
|
||||
u8 dummy_0[0x1340];
|
||||
u16 vars[VARS_COUNT];
|
||||
u8 dummy_1[0x2580];
|
||||
};
|
||||
extern struct SaveBlock1 gSaveBlock1;
|
||||
|
||||
struct BattleTowerRecord // record mixing
|
||||
struct SaveBlock2
|
||||
{
|
||||
/*0x00*/ u8 battleTowerLevelType; // 0 = level 50, 1 = level 100
|
||||
/*0x01*/ u8 trainerClass;
|
||||
/*0x02*/ u16 winStreak;
|
||||
/*0x04*/ u8 name[8];
|
||||
/*0x0C*/ u8 trainerId[4];
|
||||
/*0x10*/ u16 greeting[6];
|
||||
/*0x1C*/ struct BattleTowerPokemon party[3];
|
||||
/*0xA0*/ u32 checksum;
|
||||
u8 dummy_0[0x98];
|
||||
struct Time localTimeOffset;
|
||||
struct Time lastBerryTreeUpdate;
|
||||
u8 dummy_1[0x7E8];
|
||||
};
|
||||
|
||||
struct BattleTowerEReaderTrainer
|
||||
{
|
||||
/*0x00*/ u8 unk0;
|
||||
/*0x01*/ u8 trainerClass;
|
||||
/*0x02*/ u16 winStreak;
|
||||
/*0x04*/ u8 name[8];
|
||||
/*0x0C*/ u8 trainerId[4];
|
||||
/*0x10*/ u16 greeting[6];
|
||||
/*0x1C*/ u16 farewellPlayerLost[6];
|
||||
/*0x28*/ u16 farewellPlayerWon[6];
|
||||
/*0x34*/ struct BattleTowerPokemon party[3];
|
||||
/*0xB8*/ u32 checksum;
|
||||
};
|
||||
|
||||
struct BattleTowerData
|
||||
{
|
||||
/*0x0000, 0x00A8*/ struct BattleTowerRecord playerRecord;
|
||||
/*0x00A4, 0x014C*/ struct BattleTowerRecord records[5]; // from record mixing
|
||||
/*0x03D8, 0x0480*/ u16 firstMonSpecies; // species of the first pokemon in the player's battle tower party
|
||||
/*0x03DA, 0x0482*/ u16 defeatedBySpecies; // species of the pokemon that defated the player
|
||||
/*0x03DC, 0x0484*/ u8 defeatedByTrainerName[8];
|
||||
/*0x03E4, 0x048C*/ u8 firstMonNickname[POKEMON_NAME_LENGTH]; // nickname of the first pokemon in the player's battle tower party
|
||||
/*0x03F0, 0x0498*/ struct BattleTowerEReaderTrainer ereaderTrainer;
|
||||
/*0x04AC, 0x0554*/ u8 battleTowerLevelType:1; // 0 = level 50; 1 = level 100
|
||||
/*0x04AC, 0x0554*/ u8 unk_554:1;
|
||||
/*0x04AD, 0x0555*/ u8 battleOutcome;
|
||||
/*0x04AE, 0x0556*/ u8 var_4AE[2];
|
||||
/*0x04B0, 0x0558*/ u16 curChallengeBattleNum[2]; // 1-based index of battle in the current challenge. (challenges consist of 7 battles)
|
||||
/*0x04B4, 0x055C*/ u16 curStreakChallengesNum[2]; // 1-based index of the current challenge in the current streak.
|
||||
/*0x04B8, 0x0560*/ u16 recordWinStreaks[2];
|
||||
/*0x04BC, 0x0564*/ u8 battleTowerTrainerId; // index for gBattleTowerTrainers table
|
||||
/*0x04BD, 0x0565*/ u8 selectedPartyMons[0x3]; // indices of the 3 selected player party mons.
|
||||
/*0x04C0, 0x0568*/ u16 prizeItem;
|
||||
/*0x04C2, 0x056A*/ u8 battledTrainerIds[6];
|
||||
/*0x04C8, 0x0570*/ u16 totalBattleTowerWins;
|
||||
/*0x04CA, 0x0572*/ u16 bestBattleTowerWinStreak;
|
||||
/*0x04CC, 0x0574*/ u16 currentWinStreaks[2];
|
||||
/*0x04D0, 0x0578*/ u8 lastStreakLevelType; // 0 = level 50, 1 = level 100. level type of the last streak. Used by tv to report the level mode.
|
||||
/*0x04D1, 0x0579*/ u8 filler_4D1[0x317];
|
||||
};
|
||||
|
||||
struct SaveBlock2 /* 0x02024EA4 */
|
||||
{
|
||||
/*0x00*/ u8 playerName[8];
|
||||
/*0x08*/ u8 playerGender; // MALE, FEMALE
|
||||
/*0x09*/ u8 specialSaveWarp;
|
||||
/*0x0A*/ u8 playerTrainerId[4];
|
||||
/*0x0E*/ u16 playTimeHours;
|
||||
/*0x10*/ u8 playTimeMinutes;
|
||||
/*0x11*/ u8 playTimeSeconds;
|
||||
/*0x12*/ u8 playTimeVBlanks;
|
||||
/*0x13*/ u8 optionsButtonMode; // OPTIONS_BUTTON_MODE_[NORMAL/LR/L_EQUALS_A]
|
||||
/*0x14*/ u16 optionsTextSpeed:3; // OPTIONS_TEXT_SPEED_[SLOW/MID/FAST]
|
||||
u16 optionsWindowFrameType:5; // Specifies one of the 20 decorative borders for text boxes
|
||||
u16 optionsSound:1; // OPTIONS_SOUND_[MONO/STEREO]
|
||||
u16 optionsBattleStyle:1; // OPTIONS_BATTLE_STYLE_[SHIFT/SET]
|
||||
u16 optionsBattleSceneOff:1; // whether battle animations are disabled
|
||||
u16 regionMapZoom:1; // whether the map is zoomed in
|
||||
/*0x18*/ struct Pokedex pokedex;
|
||||
/*0x90*/ u8 filler_90[0x8];
|
||||
/*0x98*/ struct Time localTimeOffset;
|
||||
/*0xA0*/ struct Time lastBerryTreeUpdate;
|
||||
/*0xA8*/ struct BattleTowerData battleTower;
|
||||
};
|
||||
|
||||
struct MapPosition
|
||||
{
|
||||
s16 x;
|
||||
s16 y;
|
||||
s8 height;
|
||||
};
|
||||
|
||||
struct UnkStruct_8054FF8
|
||||
{
|
||||
u8 a;
|
||||
u8 b;
|
||||
u8 c;
|
||||
u8 d;
|
||||
struct MapPosition sub;
|
||||
u16 field_C;
|
||||
};
|
||||
|
||||
// wasnt defined so I had to define it
|
||||
struct HallOfFame
|
||||
{
|
||||
u8 filler[0x1F00];
|
||||
};
|
||||
|
||||
extern struct SaveBlock2 gSaveBlock2;
|
||||
|
||||
#define RomHeaderGameTitle ((const char *)0x080000A0)
|
||||
#define RomHeaderGameCode ((const char *)0x080000AC)
|
||||
#define RomHeaderMakerCode ((const char *)0x080000B0)
|
||||
#define RomHeaderMagic ((const u8 *) 0x080000B2)
|
||||
#define RomHeaderSoftwareVersion ((const u8 *) 0x080000BC)
|
||||
|
||||
#define LocalTimeOffset ((struct Time *)0x02028098)
|
||||
#define LastBerryTreeUpdate ((struct Time *)0x020280A0)
|
||||
struct PokemonStorage
|
||||
{
|
||||
u8 dummy[0x83D0];
|
||||
};
|
||||
extern struct PokemonStorage gPokemonStorage;
|
||||
|
||||
#endif //GUARD_GLOBAL_H
|
||||
|
|
|
|||
|
|
@ -1,45 +1,7 @@
|
|||
#ifndef GUARD_MAIN_H
|
||||
#define GUARD_MAIN_H
|
||||
|
||||
#include "gba/gba.h"
|
||||
|
||||
enum RomHeaderValidationResult
|
||||
{
|
||||
SAPPHIRE_UPDATABLE = 2,
|
||||
RUBY_UPDATABLE,
|
||||
SAPPHIRE_NONEED,
|
||||
RUBY_NONEED,
|
||||
INVALID
|
||||
};
|
||||
|
||||
enum MainCallbackState
|
||||
{
|
||||
MAINCB_INIT = 0,
|
||||
MAINCB_CHECK_RTC,
|
||||
MAINCB_CHECK_FLASH,
|
||||
MAINCB_READ_SAVE,
|
||||
MAINCB_CHECK_TIME,
|
||||
MAINCB_FIX_DATE,
|
||||
MAINCB_NO_NEED_TO_FIX,
|
||||
MAINCB_YEAR_MAKES_NO_SENSE,
|
||||
MAINCB_FINISHED,
|
||||
MAINCB_CHECK_PACIFIDLOG_TM,
|
||||
MAINCB_FIX_PACIFIDLOG_TM,
|
||||
MAINCB_ERROR
|
||||
};
|
||||
|
||||
extern IntrFunc gIntrTable[];
|
||||
extern u16 gHeldKeys;
|
||||
extern u16 gNewKeys;
|
||||
extern u8 gIntrVector[];
|
||||
extern u32 gUpdateSuccessful;
|
||||
extern u32 gUnknown_3001194;
|
||||
extern u32 gUnknown_30011A0[];
|
||||
extern u32 gMainCallbackState;
|
||||
extern u32 gGameVersion;
|
||||
|
||||
extern u8 gSharedMem[0x8000];
|
||||
|
||||
extern const IntrFunc gIntrFuncPointers[];
|
||||
|
||||
#endif //GUARD_MAIN_H
|
||||
|
|
|
|||
16
payload/include/message_box.h
Normal file
16
payload/include/message_box.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef GUARD_MESSAGE_BOX_H
|
||||
#define GUARD_MESSAGE_BOX_H
|
||||
|
||||
enum Message
|
||||
{
|
||||
MSG_WILL_NOW_UPDATE = 0,
|
||||
MSG_HAS_BEEN_UPDATED,
|
||||
MSG_UNABLE_TO_UPDATE,
|
||||
MSG_NO_NEED_TO_UPDATE,
|
||||
MSG_UPDATING
|
||||
};
|
||||
|
||||
void MessageBox_Load(void);
|
||||
void MessageBox_Display(enum Message);
|
||||
|
||||
#endif //GUARD_MESSAGE_BOX_H
|
||||
|
|
@ -1,154 +0,0 @@
|
|||
#ifndef GUARD_POKEMON_H
|
||||
#define GUARD_POKEMON_H
|
||||
|
||||
struct PokemonSubstruct0
|
||||
{
|
||||
u16 species;
|
||||
u16 heldItem;
|
||||
u32 experience;
|
||||
u8 ppBonuses;
|
||||
u8 friendship;
|
||||
};
|
||||
|
||||
struct PokemonSubstruct1
|
||||
{
|
||||
u16 moves[4];
|
||||
u8 pp[4];
|
||||
};
|
||||
|
||||
struct PokemonSubstruct2
|
||||
{
|
||||
u8 hpEV;
|
||||
u8 attackEV;
|
||||
u8 defenseEV;
|
||||
u8 speedEV;
|
||||
u8 spAttackEV;
|
||||
u8 spDefenseEV;
|
||||
u8 cool;
|
||||
u8 beauty;
|
||||
u8 cute;
|
||||
u8 smart;
|
||||
u8 tough;
|
||||
u8 sheen;
|
||||
};
|
||||
|
||||
struct PokemonSubstruct3
|
||||
{
|
||||
/*0x00*/ u8 pokerus;
|
||||
/*0x01*/ u8 metLocation;
|
||||
|
||||
/*0x02*/ u16 metLevel:7;
|
||||
/*0x02*/ u16 metGame:4;
|
||||
/*0x03*/ u16 pokeball:4;
|
||||
/*0x03*/ u16 otGender:1;
|
||||
|
||||
/*0x04*/ u32 hpIV:5;
|
||||
/*0x04*/ u32 attackIV:5;
|
||||
/*0x05*/ u32 defenseIV:5;
|
||||
/*0x05*/ u32 speedIV:5;
|
||||
/*0x05*/ u32 spAttackIV:5;
|
||||
/*0x06*/ u32 spDefenseIV:5;
|
||||
/*0x07*/ u32 isEgg:1;
|
||||
/*0x07*/ u32 altAbility:1;
|
||||
|
||||
/*0x08*/ u32 coolRibbon:3;
|
||||
/*0x08*/ u32 beautyRibbon:3;
|
||||
/*0x08*/ u32 cuteRibbon:3;
|
||||
/*0x09*/ u32 smartRibbon:3;
|
||||
/*0x09*/ u32 toughRibbon:3;
|
||||
/*0x09*/ u32 championRibbon:1;
|
||||
/*0x0A*/ u32 winningRibbon:1;
|
||||
/*0x0A*/ u32 victoryRibbon:1;
|
||||
/*0x0A*/ u32 artistRibbon:1;
|
||||
/*0x0A*/ u32 effortRibbon:1;
|
||||
/*0x0A*/ u32 giftRibbon1:1;
|
||||
/*0x0A*/ u32 giftRibbon2:1;
|
||||
/*0x0A*/ u32 giftRibbon3:1;
|
||||
/*0x0A*/ u32 giftRibbon4:1;
|
||||
/*0x0B*/ u32 giftRibbon5:1;
|
||||
/*0x0B*/ u32 giftRibbon6:1;
|
||||
/*0x0B*/ u32 giftRibbon7:1;
|
||||
/*0x0B*/ u32 fatefulEncounter:5; // unused in Ruby/Sapphire, but the high bit must be set for Mew/Deoxys to obey in FR/LG/Emerald
|
||||
};
|
||||
|
||||
union PokemonSubstruct
|
||||
{
|
||||
struct PokemonSubstruct0 type0;
|
||||
struct PokemonSubstruct1 type1;
|
||||
struct PokemonSubstruct2 type2;
|
||||
struct PokemonSubstruct3 type3;
|
||||
u16 raw[6];
|
||||
};
|
||||
|
||||
struct BoxPokemon
|
||||
{
|
||||
/*0x00*/ u32 personality;
|
||||
/*0x04*/ u32 otId;
|
||||
/*0x08*/ u8 nickname[POKEMON_NAME_LENGTH];
|
||||
/*0x12*/ u8 language;
|
||||
/*0x13*/ u8 isBadEgg:1;
|
||||
u8 hasSpecies:1;
|
||||
u8 isEgg:1;
|
||||
/*0x14*/ u8 otName[OT_NAME_LENGTH];
|
||||
/*0x1B*/ u8 markings;
|
||||
/*0x1C*/ u16 checksum;
|
||||
/*0x1E*/ u16 unknown;
|
||||
|
||||
union
|
||||
{
|
||||
u32 raw[12];
|
||||
union PokemonSubstruct substructs[4];
|
||||
} secure;
|
||||
}; /*size = 0x50*/
|
||||
|
||||
struct Pokemon
|
||||
{
|
||||
/*0x00*/ struct BoxPokemon box;
|
||||
/*0x50*/ u32 status;
|
||||
/*0x54*/ u8 level;
|
||||
/*0x55*/ u8 mail;
|
||||
/*0x56*/ u16 hp;
|
||||
/*0x58*/ u16 maxHP;
|
||||
/*0x5A*/ u16 attack;
|
||||
/*0x5C*/ u16 defense;
|
||||
/*0x5E*/ u16 speed;
|
||||
/*0x60*/ u16 spAttack;
|
||||
/*0x62*/ u16 spDefense;
|
||||
};
|
||||
|
||||
struct BattleTowerPokemon
|
||||
{
|
||||
/*0x00*/u16 species;
|
||||
/*0x02*/u16 heldItem;
|
||||
/*0x04*/u16 moves[4];
|
||||
/*0x0C*/u8 level;
|
||||
/*0x0D*/u8 ppBonuses;
|
||||
/*0x0E*/u8 hpEV;
|
||||
/*0x0F*/u8 attackEV;
|
||||
/*0x10*/u8 defenseEV;
|
||||
/*0x11*/u8 speedEV;
|
||||
/*0x12*/u8 spAttackEV;
|
||||
/*0x13*/u8 spDefenseEV;
|
||||
/*0x14*/u32 otId;
|
||||
/*0x18*/u32 hpIV:5;
|
||||
/*0x18*/u32 attackIV:5;
|
||||
/*0x19*/u32 defenseIV:5;
|
||||
/*0x19*/u32 speedIV:5;
|
||||
/*0x1A*/u32 spAttackIV:5;
|
||||
/*0x1A*/u32 spDefenseIV:5;
|
||||
/*0x1B*/u32 gap:1;
|
||||
/*0x1B*/u32 altAbility:1;
|
||||
/*0x1C*/u32 personality;
|
||||
/*0x20*/u8 nickname[POKEMON_NAME_LENGTH + 1];
|
||||
/*0x2B*/u8 friendship;
|
||||
};
|
||||
|
||||
struct PokemonStorage
|
||||
{
|
||||
/*0x0000*/ u8 currentBox;
|
||||
/*0x0004*/ struct BoxPokemon boxes[14][30];
|
||||
/*0x8344*/ u8 boxNames[14][9];
|
||||
/*0x83c2*/ u8 wallpaper[14];
|
||||
};
|
||||
|
||||
#endif // GUARD_POKEMON_H
|
||||
|
|
@ -1,15 +1,25 @@
|
|||
#ifndef GUARD_RTC_H
|
||||
#define GUARD_RTC_H
|
||||
|
||||
#include "gba/gba.h"
|
||||
#include "siirtc.h"
|
||||
#include "global.h"
|
||||
#define RTC_INIT_ERROR 0x0001
|
||||
#define RTC_INIT_WARNING 0x0002
|
||||
|
||||
#define RTC_ERR_12HOUR_CLOCK 0x0010
|
||||
#define RTC_ERR_POWER_FAILURE 0x0020
|
||||
#define RTC_ERR_INVALID_YEAR 0x0040
|
||||
#define RTC_ERR_INVALID_MONTH 0x0080
|
||||
#define RTC_ERR_INVALID_DAY 0x0100
|
||||
#define RTC_ERR_INVALID_HOUR 0x0200
|
||||
#define RTC_ERR_INVALID_MINUTE 0x0400
|
||||
#define RTC_ERR_INVALID_SECOND 0x0800
|
||||
|
||||
#define RTC_ERR_FLAG_MASK 0x0FF0
|
||||
|
||||
extern struct Time gTimeSinceBerryUpdate;
|
||||
extern struct Time gRtcUTCTime;
|
||||
|
||||
bool32 rtc_maincb_is_rtc_working(void);
|
||||
bool32 rtc_maincb_is_time_since_last_berry_update_positive(u8 *);
|
||||
void rtc_maincb_fix_date(void);
|
||||
bool32 BerryFix_TryInitRtc(void);
|
||||
void BerryFix_TryFixDate(void);
|
||||
bool32 BerryFix_CalcTimeDifference(u8 *);
|
||||
|
||||
#endif //GUARD_RTC_H
|
||||
|
|
|
|||
63
payload/include/save.h
Normal file
63
payload/include/save.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#ifndef GUARD_SAVE_H
|
||||
#define GUARD_SAVE_H
|
||||
|
||||
// Each 4 KiB flash sector contains 3968 bytes of actual data followed by a 128 byte footer.
|
||||
// Only 12 bytes of the footer are used.
|
||||
#define SECTOR_DATA_SIZE 3968
|
||||
#define SECTOR_FOOTER_SIZE 128
|
||||
#define SECTOR_SIZE (SECTOR_DATA_SIZE + SECTOR_FOOTER_SIZE)
|
||||
|
||||
#define NUM_SAVE_SLOTS 2
|
||||
|
||||
// If the sector's security field is not this value then the sector is either invalid or empty.
|
||||
#define SECTOR_SECURITY_NUM 0x8012025
|
||||
|
||||
#define SECTOR_ID_SAVEBLOCK2 0
|
||||
#define SECTOR_ID_SAVEBLOCK1_START 1
|
||||
#define SECTOR_ID_SAVEBLOCK1_END 4
|
||||
#define NUM_SECTORS_PER_SLOT 14
|
||||
#define SECTORS_COUNT 32
|
||||
|
||||
#define SAVE_STATUS_EMPTY 0
|
||||
#define SAVE_STATUS_OK 1
|
||||
#define SAVE_STATUS_CORRUPT 2
|
||||
#define SAVE_STATUS_NO_FLASH 4
|
||||
#define SAVE_STATUS_ERROR 0xFF
|
||||
|
||||
// Special sector id value for certain save functions to
|
||||
// indicate that no specific sector should be used.
|
||||
#define FULL_SAVE_SLOT 0xFFFF
|
||||
|
||||
enum
|
||||
{
|
||||
SECTOR_DAMAGED,
|
||||
SECTOR_OK,
|
||||
SECTOR_CHECK, // unused
|
||||
};
|
||||
|
||||
struct SaveBlockChunk
|
||||
{
|
||||
u8 * data;
|
||||
u16 size;
|
||||
};
|
||||
|
||||
struct SaveSector
|
||||
{
|
||||
u8 data[SECTOR_DATA_SIZE];
|
||||
u8 unused[SECTOR_FOOTER_SIZE - 12]; // Unused portion of the footer
|
||||
u16 id;
|
||||
u16 checksum;
|
||||
u32 security;
|
||||
u32 counter;
|
||||
}; // size is SECTOR_SIZE (0x1000)
|
||||
|
||||
#define SECTOR_SECURITY_OFFSET offsetof(struct SaveSector, security)
|
||||
|
||||
extern u32 gDamagedSaveSectors;
|
||||
extern bool32 gFlashIdentIsValid;
|
||||
extern const struct SaveBlockChunk gSaveBlockChunks[];
|
||||
|
||||
u8 WriteSaveSectorOrSlot(u16 sectorId, const struct SaveBlockChunk *chunks);
|
||||
u8 TryLoadSaveSlot(u16 sectorId, const struct SaveBlockChunk *chunks);
|
||||
|
||||
#endif //GUARD_SAVE_H
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef GUARD_RTC_H
|
||||
#define GUARD_RTC_H
|
||||
#ifndef GUARD_SIIRTC_H
|
||||
#define GUARD_SIIRTC_H
|
||||
|
||||
#include "gba/gba.h"
|
||||
|
||||
|
|
@ -51,4 +51,4 @@ bool8 SiiRtcGetTime(struct SiiRtcInfo *rtc);
|
|||
bool8 SiiRtcSetTime(struct SiiRtcInfo *rtc);
|
||||
bool8 SiiRtcSetAlarm(struct SiiRtcInfo *rtc);
|
||||
|
||||
#endif // GUARD_RTC_H
|
||||
#endif // GUARD_SIIRTC_H
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ SECTIONS {
|
|||
asm/crt0.o(.text);
|
||||
src/main.o(.text);
|
||||
src/rtc.o(.text);
|
||||
src/flash.o(.text);
|
||||
src/berry_fix_save.o(.text);
|
||||
src/message_box.o(.text);
|
||||
src/save.o(.text);
|
||||
src/event_data.o(.text);
|
||||
} =0
|
||||
|
||||
lib_text :
|
||||
|
|
@ -31,7 +34,8 @@ SECTIONS {
|
|||
{
|
||||
src/main.o(.rodata);
|
||||
src/rtc.o(.rodata);
|
||||
src/flash.o(.rodata);
|
||||
src/save.o(.rodata);
|
||||
src/message_box.o(.rodata);
|
||||
} =0
|
||||
|
||||
lib_rodata :
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#include "gba/gba.h"
|
||||
#include "gba/flash_internal.h"
|
||||
|
||||
static u8 sTimerNum;
|
||||
static u16 sTimerCount;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#include "gba/gba.h"
|
||||
#include "gba/flash_internal.h"
|
||||
|
||||
static const char AgbLibFlashVersion[] = "FLASH1M_V103";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#include "gba/gba.h"
|
||||
#include "gba/flash_internal.h"
|
||||
|
||||
const u16 leMaxTime[] =
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#include "gba/gba.h"
|
||||
#include "gba/flash_internal.h"
|
||||
|
||||
const u16 mxMaxTime[] =
|
||||
{
|
||||
|
|
|
|||
70
payload/src/berry_fix_save.c
Normal file
70
payload/src/berry_fix_save.c
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#include "global.h"
|
||||
#include "main.h"
|
||||
#include "berry_fix_save.h"
|
||||
|
||||
bool32 BerryFix_IdentifyFlash(void)
|
||||
{
|
||||
gFlashIdentIsValid = TRUE;
|
||||
if (!IdentifyFlash())
|
||||
{
|
||||
SetFlashTimerIntr(0, &((IntrFunc *)gIntrFuncPointers)[9]);
|
||||
return TRUE;
|
||||
}
|
||||
gFlashIdentIsValid = FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Unused
|
||||
static void BerryFix_ReadFlash(u16 sectorNum, ptrdiff_t offset, void * dest, size_t size)
|
||||
{
|
||||
ReadFlash(sectorNum, offset, dest, size);
|
||||
}
|
||||
|
||||
static u8 BerryFix_WriteSaveSectorOrSlot(u16 sectorId, const struct SaveBlockChunk * chunks)
|
||||
{
|
||||
return WriteSaveSectorOrSlot(sectorId, chunks);
|
||||
}
|
||||
|
||||
static u8 BerryFix_TryLoadSaveSlot(u16 sectorId, const struct SaveBlockChunk * chunks)
|
||||
{
|
||||
return TryLoadSaveSlot(sectorId, chunks);
|
||||
}
|
||||
|
||||
static u32 * BerryFix_GetDamagedSaveSectors(void)
|
||||
{
|
||||
return &gDamagedSaveSectors;
|
||||
}
|
||||
|
||||
static s32 BerryFix_Save(u8 mode)
|
||||
{
|
||||
u8 i;
|
||||
switch (mode)
|
||||
{
|
||||
case SAVE_NORMAL:
|
||||
default:
|
||||
BerryFix_WriteSaveSectorOrSlot(FULL_SAVE_SLOT, gSaveBlockChunks);
|
||||
break;
|
||||
case SAVE_SAVEBLOCKS:
|
||||
for (i = SECTOR_ID_SAVEBLOCK2; i <= SECTOR_ID_SAVEBLOCK1_END; i++)
|
||||
BerryFix_WriteSaveSectorOrSlot(i, gSaveBlockChunks);
|
||||
break;
|
||||
case SAVE_SAVEBLOCK2:
|
||||
BerryFix_WriteSaveSectorOrSlot(SECTOR_ID_SAVEBLOCK2, gSaveBlockChunks);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 BerryFix_TrySave(u8 mode)
|
||||
{
|
||||
BerryFix_Save(mode);
|
||||
if (*BerryFix_GetDamagedSaveSectors() == 0)
|
||||
return SAVE_STATUS_OK;
|
||||
return SAVE_STATUS_ERROR;
|
||||
}
|
||||
|
||||
u8 BerryFix_LoadSave(u32 unused)
|
||||
{
|
||||
return BerryFix_TryLoadSaveSlot(FULL_SAVE_SLOT, gSaveBlockChunks);
|
||||
}
|
||||
53
payload/src/event_data.c
Normal file
53
payload/src/event_data.c
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#include "global.h"
|
||||
#include "rtc.h"
|
||||
#include "berry_fix_save.h"
|
||||
#include "event_data.h"
|
||||
|
||||
// Unused
|
||||
static void Dummy1()
|
||||
{
|
||||
}
|
||||
|
||||
// Unused
|
||||
static void Dummy2()
|
||||
{
|
||||
}
|
||||
|
||||
// Unused
|
||||
static void Dummy3()
|
||||
{
|
||||
}
|
||||
|
||||
static u16 * GetVarPointer(u16 id)
|
||||
{
|
||||
if (id < VARS_START)
|
||||
return NULL;
|
||||
if (id < SPECIAL_VARS_START)
|
||||
return &gSaveBlock1.vars[id - VARS_START];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool32 BerryFix_IsPacifidlogTMCorrect(void)
|
||||
{
|
||||
u8 year;
|
||||
u16 * var = GetVarPointer(VAR_PACIFIDLOG_TM_RECEIVED_DAY);
|
||||
BerryFix_CalcTimeDifference(&year);
|
||||
if (*var <= gRtcUTCTime.days)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 BerryFix_ResetPacifidlogTM(void)
|
||||
{
|
||||
u8 year;
|
||||
if (BerryFix_IsPacifidlogTMCorrect() == TRUE)
|
||||
return TRUE;
|
||||
BerryFix_CalcTimeDifference(&year);
|
||||
if (gRtcUTCTime.days < 0)
|
||||
return FALSE;
|
||||
*GetVarPointer(VAR_PACIFIDLOG_TM_RECEIVED_DAY) = 1;
|
||||
if (BerryFix_TrySave(SAVE_NORMAL) != SAVE_STATUS_OK)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
|
@ -1,752 +0,0 @@
|
|||
#include "gba/gba.h"
|
||||
#include "gba/flash_internal.h"
|
||||
#include "constants/vars.h"
|
||||
#include "global.h"
|
||||
#include "main.h"
|
||||
#include "flash.h"
|
||||
#include "rtc.h"
|
||||
|
||||
struct SaveBlockChunk
|
||||
{
|
||||
u8 * data;
|
||||
u16 size;
|
||||
};
|
||||
|
||||
u8 WriteSaveBlockChunks(u16 a0, const struct SaveBlockChunk * a1);
|
||||
u8 WriteSingleChunk(u16 a0, const struct SaveBlockChunk * a1);
|
||||
u8 TryWriteSector(u8, u8 *);
|
||||
u8 EraseCurrentChunk(u16 a0, const struct SaveBlockChunk * a1);
|
||||
u8 TryReadAllSaveSectorsCurrentSlot(u16 a0, const struct SaveBlockChunk * a1);
|
||||
u8 ReadAllSaveSectorsCurrentSlot(u16 a0, const struct SaveBlockChunk * a1);
|
||||
u8 GetSaveValidStatus(const struct SaveBlockChunk * a1);
|
||||
u32 DoReadFlashWholeSection(u8 a0, struct SaveSector * a1);
|
||||
u16 CalculateChecksum(const void *, u16);
|
||||
|
||||
u16 gFirstSaveSector;
|
||||
u32 gPrevSaveCounter;
|
||||
u16 gLastKnownGoodSector;
|
||||
u32 gDamagedSaveSectors;
|
||||
u32 gSaveCounter;
|
||||
struct SaveSector * gFastSaveSection;
|
||||
u16 gCurSaveChunk;
|
||||
bool32 gFlashIdentIsValid;
|
||||
|
||||
EWRAM_DATA struct SaveBlock2 gSaveBlock2 = {};
|
||||
EWRAM_DATA struct SaveBlock1 gSaveBlock1 = {};
|
||||
EWRAM_DATA struct PokemonStorage gPokemonStorage = {};
|
||||
|
||||
// Each 4 KiB flash sector contains 3968 bytes of actual data followed by a 128 byte footer
|
||||
#define SECTOR_DATA_SIZE 3968
|
||||
#define SECTOR_FOOTER_SIZE 128
|
||||
|
||||
#define SAVEBLOCK_CHUNK(structure, chunkNum) \
|
||||
{ \
|
||||
(u8 *)&structure + chunkNum * SECTOR_DATA_SIZE, \
|
||||
min(sizeof(structure) - chunkNum * SECTOR_DATA_SIZE, SECTOR_DATA_SIZE) \
|
||||
} \
|
||||
|
||||
static const struct SaveBlockChunk sSaveBlockChunks[] =
|
||||
{
|
||||
SAVEBLOCK_CHUNK(gSaveBlock2, 0),
|
||||
|
||||
SAVEBLOCK_CHUNK(gSaveBlock1, 0),
|
||||
SAVEBLOCK_CHUNK(gSaveBlock1, 1),
|
||||
SAVEBLOCK_CHUNK(gSaveBlock1, 2),
|
||||
SAVEBLOCK_CHUNK(gSaveBlock1, 3),
|
||||
|
||||
SAVEBLOCK_CHUNK(gPokemonStorage, 0),
|
||||
SAVEBLOCK_CHUNK(gPokemonStorage, 1),
|
||||
SAVEBLOCK_CHUNK(gPokemonStorage, 2),
|
||||
SAVEBLOCK_CHUNK(gPokemonStorage, 3),
|
||||
SAVEBLOCK_CHUNK(gPokemonStorage, 4),
|
||||
SAVEBLOCK_CHUNK(gPokemonStorage, 5),
|
||||
SAVEBLOCK_CHUNK(gPokemonStorage, 6),
|
||||
SAVEBLOCK_CHUNK(gPokemonStorage, 7),
|
||||
SAVEBLOCK_CHUNK(gPokemonStorage, 8),
|
||||
};
|
||||
|
||||
const u16 gInfoMessagesPal[] = INCBIN_U16("graphics/msg_box.gbapal");
|
||||
const u8 gInfoMessagesTilemap[] = INCBIN_U8("graphics/msg_box.tilemap.lz");
|
||||
const u8 gInfoMessagesGfx[] = INCBIN_U8("graphics/msg_box.4bpp.lz");
|
||||
|
||||
bool32 flash_maincb_ident_is_valid(void)
|
||||
{
|
||||
gFlashIdentIsValid = TRUE;
|
||||
if (!IdentifyFlash())
|
||||
{
|
||||
SetFlashTimerIntr(0, &((IntrFunc *)gIntrFuncPointers)[9]);
|
||||
return TRUE;
|
||||
}
|
||||
gFlashIdentIsValid = FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void Call_ReadFlash(u16 sectorNum, ptrdiff_t offset, void * dest, size_t size)
|
||||
{
|
||||
ReadFlash(sectorNum, offset, dest, size);
|
||||
}
|
||||
|
||||
u8 Call_WriteSaveBlockChunks(u16 a0, const struct SaveBlockChunk * a1)
|
||||
{
|
||||
return WriteSaveBlockChunks(a0, a1);
|
||||
}
|
||||
|
||||
u8 Call_TryReadAllSaveSectorsCurrentSlot(u16 a0, const struct SaveBlockChunk * a1)
|
||||
{
|
||||
return TryReadAllSaveSectorsCurrentSlot(a0, a1);
|
||||
}
|
||||
|
||||
u32 * GetDamagedSaveSectorsPtr(void)
|
||||
{
|
||||
return &gDamagedSaveSectors;
|
||||
}
|
||||
|
||||
s32 flash_write_save_block_chunks(u8 a0)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
switch (a0)
|
||||
{
|
||||
case 0:
|
||||
default:
|
||||
Call_WriteSaveBlockChunks(0xFFFF, sSaveBlockChunks);
|
||||
break;
|
||||
case 1:
|
||||
for (i = 0; i < 5; i++)
|
||||
{
|
||||
Call_WriteSaveBlockChunks(i, sSaveBlockChunks);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
Call_WriteSaveBlockChunks(0, sSaveBlockChunks);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 flash_write_save_block_chunks_check_damage(u8 a0)
|
||||
{
|
||||
flash_write_save_block_chunks(a0);
|
||||
if (*GetDamagedSaveSectorsPtr() == 0)
|
||||
return 1;
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
u8 flash_maincb_read_save(u32 unused)
|
||||
{
|
||||
return Call_TryReadAllSaveSectorsCurrentSlot(0xFFFF, sSaveBlockChunks);
|
||||
}
|
||||
|
||||
void msg_load_gfx(void)
|
||||
{
|
||||
REG_DISPCNT = 0;
|
||||
REG_BG0HOFS = 0;
|
||||
REG_BG0VOFS = 0;
|
||||
REG_BLDCNT = 0;
|
||||
LZ77UnCompVram(gInfoMessagesGfx, (void *)BG_VRAM);
|
||||
LZ77UnCompVram(gInfoMessagesTilemap, (void *)BG_SCREEN_ADDR(28));
|
||||
CpuCopy16(gInfoMessagesPal, (void *)BG_PLTT, 0x200);
|
||||
REG_BG0CNT = BGCNT_SCREENBASE(28) | BGCNT_TXT512x512;
|
||||
REG_DISPCNT = DISPCNT_BG0_ON;
|
||||
}
|
||||
|
||||
void msg_display(enum MsgBoxUpdateMessage a0)
|
||||
{
|
||||
switch (a0)
|
||||
{
|
||||
case MSGBOX_WILL_NOW_UPDATE:
|
||||
REG_BG0HOFS = 0;
|
||||
REG_BG0VOFS = 0;
|
||||
break;
|
||||
case MSGBOX_HAS_BEEN_UPDATED:
|
||||
REG_BG0HOFS = 0x100;
|
||||
REG_BG0VOFS = 0;
|
||||
break;
|
||||
case MSGBOX_UNABLE_TO_UPDATE:
|
||||
REG_BG0HOFS = 0x100;
|
||||
REG_BG0VOFS = 0xB0;
|
||||
break;
|
||||
case MSGBOX_NO_NEED_TO_UPDATE:
|
||||
REG_BG0HOFS = 0;
|
||||
REG_BG0VOFS = 0xB0;
|
||||
break;
|
||||
case MSGBOX_UPDATING:
|
||||
REG_BG0HOFS = 0;
|
||||
REG_BG0VOFS = 0x160;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Save_EraseAllData(void)
|
||||
{
|
||||
u16 i;
|
||||
for (i = 0; i < 32; i++)
|
||||
EraseFlashSector(i);
|
||||
}
|
||||
|
||||
void Save_ResetSaveCounters(void)
|
||||
{
|
||||
gSaveCounter = 0;
|
||||
gFirstSaveSector = 0;
|
||||
gDamagedSaveSectors = 0;
|
||||
}
|
||||
|
||||
bool32 SetSectorDamagedStatus(u8 op, u8 sectorNum)
|
||||
{
|
||||
bool32 retVal = FALSE;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case SECTOR_DAMAGED:
|
||||
gDamagedSaveSectors |= (1 << sectorNum);
|
||||
break;
|
||||
case SECTOR_OK:
|
||||
gDamagedSaveSectors &= ~(1 << sectorNum);
|
||||
break;
|
||||
case SECTOR_CHECK: // unused
|
||||
if (gDamagedSaveSectors & (1 << sectorNum))
|
||||
retVal = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
u8 WriteSaveBlockChunks(u16 chunkId, const struct SaveBlockChunk *chunks)
|
||||
{
|
||||
u32 retVal;
|
||||
u16 i;
|
||||
|
||||
gFastSaveSection = eSaveSection;
|
||||
|
||||
if (chunkId != 0xFFFF) // write single chunk
|
||||
{
|
||||
retVal = WriteSingleChunk(chunkId, chunks);
|
||||
}
|
||||
else // write all chunks
|
||||
{
|
||||
gLastKnownGoodSector = gFirstSaveSector;
|
||||
gPrevSaveCounter = gSaveCounter;
|
||||
gFirstSaveSector++;
|
||||
gFirstSaveSector %= NUM_SECTORS_PER_SAVE_SLOT;
|
||||
gSaveCounter++;
|
||||
retVal = SAVE_STATUS_OK;
|
||||
|
||||
for (i = 0; i < NUM_SECTORS_PER_SAVE_SLOT; i++)
|
||||
WriteSingleChunk(i, chunks);
|
||||
|
||||
// Check for any bad sectors
|
||||
if (gDamagedSaveSectors != 0) // skip the damaged sector.
|
||||
{
|
||||
retVal = SAVE_STATUS_ERROR;
|
||||
gFirstSaveSector = gLastKnownGoodSector;
|
||||
gSaveCounter = gPrevSaveCounter;
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
u8 WriteSingleChunk(u16 chunkId, const struct SaveBlockChunk * chunks)
|
||||
{
|
||||
u16 i;
|
||||
u16 sectorNum;
|
||||
u8 *chunkData;
|
||||
u16 chunkSize;
|
||||
|
||||
// select sector number
|
||||
sectorNum = chunkId + gFirstSaveSector;
|
||||
sectorNum %= NUM_SECTORS_PER_SAVE_SLOT;
|
||||
// select save slot
|
||||
sectorNum += NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2);
|
||||
|
||||
chunkData = chunks[chunkId].data;
|
||||
chunkSize = chunks[chunkId].size;
|
||||
|
||||
// clear save section.
|
||||
for (i = 0; i < sizeof(struct SaveSector); i++)
|
||||
((u8 *)gFastSaveSection)[i] = 0;
|
||||
|
||||
gFastSaveSection->id = chunkId;
|
||||
gFastSaveSection->signature = FILE_SIGNATURE;
|
||||
gFastSaveSection->counter = gSaveCounter;
|
||||
for (i = 0; i < chunkSize; i++)
|
||||
gFastSaveSection->data[i] = chunkData[i];
|
||||
gFastSaveSection->checksum = CalculateChecksum(chunkData, chunkSize);
|
||||
|
||||
return TryWriteSector(sectorNum, gFastSaveSection->data);
|
||||
}
|
||||
|
||||
u8 HandleWriteSectorNBytes(u8 sectorNum, u8 *data, u16 size)
|
||||
{
|
||||
u16 i;
|
||||
struct SaveSector *section = eSaveSection;
|
||||
|
||||
for (i = 0; i < sizeof(struct SaveSector); i++)
|
||||
((char *)section)[i] = 0;
|
||||
|
||||
section->signature = FILE_SIGNATURE;
|
||||
for (i = 0; i < size; i++)
|
||||
section->data[i] = data[i];
|
||||
section->id = CalculateChecksum(data, size); // though this appears to be incorrect, it might be some sector checksum instead of a whole save checksum and only appears to be relevent to HOF data, if used.
|
||||
|
||||
return TryWriteSector(sectorNum, section->data);
|
||||
}
|
||||
|
||||
u8 TryWriteSector(u8 sectorNum, u8 *data)
|
||||
{
|
||||
if (ProgramFlashSectorAndVerify(sectorNum, data) != 0) // is damaged?
|
||||
{
|
||||
SetSectorDamagedStatus(SECTOR_DAMAGED, sectorNum); // set damaged sector bits.
|
||||
return SAVE_STATUS_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetSectorDamagedStatus(SECTOR_OK, sectorNum); // unset damaged sector bits. it's safe now.
|
||||
return SAVE_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
u32 RestoreSaveBackupVarsAndIncrement(const struct SaveBlockChunk *chunk) // chunk is unused
|
||||
{
|
||||
gFastSaveSection = eSaveSection;
|
||||
gLastKnownGoodSector = gFirstSaveSector;
|
||||
gPrevSaveCounter = gSaveCounter;
|
||||
gFirstSaveSector++;
|
||||
gFirstSaveSector %= NUM_SECTORS_PER_SAVE_SLOT;
|
||||
gSaveCounter++;
|
||||
gCurSaveChunk = 0;
|
||||
gDamagedSaveSectors = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 RestoreSaveBackupVars(const struct SaveBlockChunk *chunk)
|
||||
{
|
||||
gFastSaveSection = eSaveSection;
|
||||
gLastKnownGoodSector = gFirstSaveSector;
|
||||
gPrevSaveCounter = gSaveCounter;
|
||||
gCurSaveChunk = 0;
|
||||
gDamagedSaveSectors = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 WriteSingleChunkAndIncrement(u16 a1, const struct SaveBlockChunk * chunk)
|
||||
{
|
||||
u8 retVal;
|
||||
|
||||
if (gCurSaveChunk < a1 - 1)
|
||||
{
|
||||
retVal = SAVE_STATUS_OK;
|
||||
WriteSingleChunk(gCurSaveChunk, chunk);
|
||||
gCurSaveChunk++;
|
||||
if (gDamagedSaveSectors)
|
||||
{
|
||||
retVal = SAVE_STATUS_ERROR;
|
||||
gFirstSaveSector = gLastKnownGoodSector;
|
||||
gSaveCounter = gPrevSaveCounter;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
retVal = SAVE_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
u8 ErasePreviousChunk(u16 a1, const struct SaveBlockChunk *chunk)
|
||||
{
|
||||
u8 retVal = SAVE_STATUS_OK;
|
||||
|
||||
EraseCurrentChunk(a1 - 1, chunk);
|
||||
|
||||
if (gDamagedSaveSectors)
|
||||
{
|
||||
retVal = SAVE_STATUS_ERROR;
|
||||
gFirstSaveSector = gLastKnownGoodSector;
|
||||
gSaveCounter = gPrevSaveCounter;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
u8 EraseCurrentChunk(u16 chunkId, const struct SaveBlockChunk *chunks)
|
||||
{
|
||||
u16 i;
|
||||
u16 sector;
|
||||
u8 *data;
|
||||
u16 size;
|
||||
u8 status;
|
||||
|
||||
// select sector number
|
||||
sector = chunkId + gFirstSaveSector;
|
||||
sector %= NUM_SECTORS_PER_SAVE_SLOT;
|
||||
// select save slot
|
||||
sector += NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2);
|
||||
|
||||
data = chunks[chunkId].data;
|
||||
size = chunks[chunkId].size;
|
||||
|
||||
// clear temp save section.
|
||||
for (i = 0; i < sizeof(struct SaveSector); i++)
|
||||
((char *)gFastSaveSection)[i] = 0;
|
||||
|
||||
gFastSaveSection->id = chunkId;
|
||||
gFastSaveSection->signature = FILE_SIGNATURE;
|
||||
gFastSaveSection->counter = gSaveCounter;
|
||||
|
||||
// set temp section's data.
|
||||
for (i = 0; i < size; i++)
|
||||
gFastSaveSection->data[i] = data[i];
|
||||
|
||||
// calculate checksum.
|
||||
gFastSaveSection->checksum = CalculateChecksum(data, size);
|
||||
|
||||
EraseFlashSector(sector);
|
||||
|
||||
status = SAVE_STATUS_OK;
|
||||
|
||||
for (i = 0; i < sizeof(struct UnkSaveSection); i++)
|
||||
{
|
||||
if (ProgramFlashByte(sector, i, gFastSaveSection->data[i]))
|
||||
{
|
||||
status = SAVE_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (status == SAVE_STATUS_ERROR)
|
||||
{
|
||||
SetSectorDamagedStatus(SECTOR_DAMAGED, sector);
|
||||
return SAVE_STATUS_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = SAVE_STATUS_OK;
|
||||
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
if (ProgramFlashByte(sector, 0xFF9 + i, ((u8 *)gFastSaveSection)[0xFF9 + i]))
|
||||
{
|
||||
status = SAVE_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (status == SAVE_STATUS_ERROR)
|
||||
{
|
||||
SetSectorDamagedStatus(SECTOR_DAMAGED, sector);
|
||||
return SAVE_STATUS_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetSectorDamagedStatus(SECTOR_OK, sector);
|
||||
return SAVE_STATUS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u8 WriteSomeFlashByteToPrevSector(u16 a1, const struct SaveBlockChunk *chunk)
|
||||
{
|
||||
u16 sector;
|
||||
|
||||
// select sector number
|
||||
sector = a1 + gFirstSaveSector - 1;
|
||||
sector %= NUM_SECTORS_PER_SAVE_SLOT;
|
||||
// select save slot
|
||||
sector += NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2);
|
||||
|
||||
if (ProgramFlashByte(sector, sizeof(struct UnkSaveSection), ((u8 *)gFastSaveSection)[sizeof(struct UnkSaveSection)]))
|
||||
{
|
||||
// sector is damaged, so enable the bit in gDamagedSaveSectors and restore the last written sector and save counter.
|
||||
SetSectorDamagedStatus(SECTOR_DAMAGED, sector);
|
||||
gFirstSaveSector = gLastKnownGoodSector;
|
||||
gSaveCounter = gPrevSaveCounter;
|
||||
return SAVE_STATUS_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetSectorDamagedStatus(SECTOR_OK, sector);
|
||||
return SAVE_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
u8 WriteSomeFlashByte0x25ToPrevSector(u16 a1, const struct SaveBlockChunk *chunk)
|
||||
{
|
||||
u16 sector;
|
||||
|
||||
sector = a1 + gFirstSaveSector - 1;
|
||||
sector %= NUM_SECTORS_PER_SAVE_SLOT;
|
||||
sector += NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2);
|
||||
|
||||
if (ProgramFlashByte(sector, sizeof(struct UnkSaveSection), 0x25))
|
||||
{
|
||||
// sector is damaged, so enable the bit in gDamagedSaveSectors and restore the last written sector and save counter.
|
||||
SetSectorDamagedStatus(SECTOR_DAMAGED, sector);
|
||||
gFirstSaveSector = gLastKnownGoodSector;
|
||||
gSaveCounter = gPrevSaveCounter;
|
||||
return SAVE_STATUS_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetSectorDamagedStatus(SECTOR_OK, sector);
|
||||
return SAVE_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
u8 TryReadAllSaveSectorsCurrentSlot(u16 a1, const struct SaveBlockChunk *chunk)
|
||||
{
|
||||
u8 retVal;
|
||||
gFastSaveSection = eSaveSection;
|
||||
if (a1 != 0xFFFF)
|
||||
{
|
||||
retVal = SAVE_STATUS_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
retVal = GetSaveValidStatus(chunk);
|
||||
ReadAllSaveSectorsCurrentSlot(0xFFFF, chunk);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
u8 ReadAllSaveSectorsCurrentSlot(u16 a1, const struct SaveBlockChunk *chunks)
|
||||
{
|
||||
u16 i;
|
||||
u16 checksum;
|
||||
u16 sector = NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2);
|
||||
u16 id;
|
||||
|
||||
for (i = 0; i < NUM_SECTORS_PER_SAVE_SLOT; i++)
|
||||
{
|
||||
DoReadFlashWholeSection(i + sector, gFastSaveSection);
|
||||
id = gFastSaveSection->id;
|
||||
if (id == 0)
|
||||
gFirstSaveSector = i;
|
||||
checksum = CalculateChecksum(gFastSaveSection->data, chunks[id].size);
|
||||
if (gFastSaveSection->signature == FILE_SIGNATURE
|
||||
&& gFastSaveSection->checksum == checksum)
|
||||
{
|
||||
u16 j;
|
||||
for (j = 0; j < chunks[id].size; j++)
|
||||
chunks[id].data[j] = gFastSaveSection->data[j];
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
u8 GetSaveValidStatus(const struct SaveBlockChunk *chunks)
|
||||
{
|
||||
u16 sector;
|
||||
bool8 signatureValid;
|
||||
u16 checksum;
|
||||
u32 slot1saveCounter = 0;
|
||||
u32 slot2saveCounter = 0;
|
||||
u8 slot1Status;
|
||||
u8 slot2Status;
|
||||
u32 validSectors;
|
||||
const u32 ALL_SECTORS = (1 << NUM_SECTORS_PER_SAVE_SLOT) - 1; // bitmask of all saveblock sectors
|
||||
|
||||
// check save slot 1.
|
||||
validSectors = 0;
|
||||
signatureValid = FALSE;
|
||||
for (sector = 0; sector < NUM_SECTORS_PER_SAVE_SLOT; sector++)
|
||||
{
|
||||
DoReadFlashWholeSection(sector, gFastSaveSection);
|
||||
if (gFastSaveSection->signature == FILE_SIGNATURE)
|
||||
{
|
||||
signatureValid = TRUE;
|
||||
checksum = CalculateChecksum(gFastSaveSection->data, chunks[gFastSaveSection->id].size);
|
||||
if (gFastSaveSection->checksum == checksum)
|
||||
{
|
||||
slot1saveCounter = gFastSaveSection->counter;
|
||||
validSectors |= 1 << gFastSaveSection->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (signatureValid)
|
||||
{
|
||||
if (validSectors == ALL_SECTORS)
|
||||
slot1Status = SAVE_STATUS_OK;
|
||||
else
|
||||
slot1Status = SAVE_STATUS_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
slot1Status = SAVE_STATUS_EMPTY;
|
||||
}
|
||||
|
||||
// check save slot 2.
|
||||
validSectors = 0;
|
||||
signatureValid = FALSE;
|
||||
for (sector = 0; sector < NUM_SECTORS_PER_SAVE_SLOT; sector++)
|
||||
{
|
||||
DoReadFlashWholeSection(NUM_SECTORS_PER_SAVE_SLOT + sector, gFastSaveSection);
|
||||
if (gFastSaveSection->signature == FILE_SIGNATURE)
|
||||
{
|
||||
signatureValid = TRUE;
|
||||
checksum = CalculateChecksum(gFastSaveSection->data, chunks[gFastSaveSection->id].size);
|
||||
if (gFastSaveSection->checksum == checksum)
|
||||
{
|
||||
slot2saveCounter = gFastSaveSection->counter;
|
||||
validSectors |= 1 << gFastSaveSection->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (signatureValid)
|
||||
{
|
||||
if (validSectors == ALL_SECTORS)
|
||||
slot2Status = SAVE_STATUS_OK;
|
||||
else
|
||||
slot2Status = SAVE_STATUS_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
slot2Status = SAVE_STATUS_EMPTY;
|
||||
}
|
||||
|
||||
if (slot1Status == SAVE_STATUS_OK && slot2Status == SAVE_STATUS_OK)
|
||||
{
|
||||
// Choose counter of the most recent save file
|
||||
if ((slot1saveCounter == -1 && slot2saveCounter == 0) || (slot1saveCounter == 0 && slot2saveCounter == -1))
|
||||
{
|
||||
if ((unsigned)(slot1saveCounter + 1) < (unsigned)(slot2saveCounter + 1))
|
||||
gSaveCounter = slot2saveCounter;
|
||||
else
|
||||
gSaveCounter = slot1saveCounter;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (slot1saveCounter < slot2saveCounter)
|
||||
gSaveCounter = slot2saveCounter;
|
||||
else
|
||||
gSaveCounter = slot1saveCounter;
|
||||
}
|
||||
return SAVE_STATUS_OK;
|
||||
}
|
||||
|
||||
if (slot1Status == SAVE_STATUS_OK)
|
||||
{
|
||||
gSaveCounter = slot1saveCounter;
|
||||
if (slot2Status == SAVE_STATUS_ERROR)
|
||||
return SAVE_STATUS_ERROR;
|
||||
else
|
||||
return SAVE_STATUS_OK;
|
||||
}
|
||||
|
||||
if (slot2Status == SAVE_STATUS_OK)
|
||||
{
|
||||
gSaveCounter = slot2saveCounter;
|
||||
if (slot1Status == SAVE_STATUS_ERROR)
|
||||
return SAVE_STATUS_ERROR;
|
||||
else
|
||||
return SAVE_STATUS_OK;
|
||||
}
|
||||
|
||||
if (slot1Status == SAVE_STATUS_EMPTY && slot2Status == SAVE_STATUS_EMPTY)
|
||||
{
|
||||
gSaveCounter = 0;
|
||||
gFirstSaveSector = 0;
|
||||
return SAVE_STATUS_EMPTY;
|
||||
}
|
||||
|
||||
gSaveCounter = 0;
|
||||
gFirstSaveSector = 0;
|
||||
return 2;
|
||||
}
|
||||
|
||||
u8 ReadSomeUnknownSectorAndVerify(u8 sector, u8 *data, u16 size)
|
||||
{
|
||||
u16 i;
|
||||
struct SaveSector *section = eSaveSection;
|
||||
|
||||
DoReadFlashWholeSection(sector, section);
|
||||
if (section->signature == FILE_SIGNATURE)
|
||||
{
|
||||
u16 checksum = CalculateChecksum(section->data, size);
|
||||
if (section->id == checksum)
|
||||
{
|
||||
for (i = 0; i < size; i++)
|
||||
data[i] = section->data[i];
|
||||
return SAVE_STATUS_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return SAVE_STATUS_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
u32 DoReadFlashWholeSection(u8 sector, struct SaveSector *section)
|
||||
{
|
||||
ReadFlash(sector, 0, section->data, sizeof(struct SaveSector));
|
||||
return 1;
|
||||
}
|
||||
|
||||
u16 CalculateChecksum(const void *data, u16 size)
|
||||
{
|
||||
u16 i;
|
||||
u32 checksum = 0;
|
||||
|
||||
for (i = 0; i < (size / 4); i++)
|
||||
{
|
||||
checksum += *((u32 *)data);
|
||||
data += sizeof(u32);
|
||||
}
|
||||
|
||||
return ((checksum >> 16) + checksum);
|
||||
}
|
||||
|
||||
void nullsub_0201182C()
|
||||
{
|
||||
}
|
||||
|
||||
void nullsub_02011830()
|
||||
{
|
||||
}
|
||||
|
||||
void nullsub_02011834()
|
||||
{
|
||||
}
|
||||
|
||||
u16 * get_var_addr(u16 a0)
|
||||
{
|
||||
if (a0 < VARS_START)
|
||||
return NULL;
|
||||
if (a0 < VAR_SPECIAL_0)
|
||||
return &gSaveBlock1.vars[a0 - VARS_START];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool32 flash_maincb_check_need_reset_pacifidlog_tm(void)
|
||||
{
|
||||
u8 sp0;
|
||||
u16 * data = get_var_addr(VAR_PACIFIDLOG_TM_RECEIVED_DAY);
|
||||
rtc_maincb_is_time_since_last_berry_update_positive(&sp0);
|
||||
if (*data <= gRtcUTCTime.days)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 flash_maincb_reset_pacifidlog_tm(void)
|
||||
{
|
||||
u8 sp0;
|
||||
if (flash_maincb_check_need_reset_pacifidlog_tm() == TRUE)
|
||||
return TRUE;
|
||||
rtc_maincb_is_time_since_last_berry_update_positive(&sp0);
|
||||
if (gRtcUTCTime.days < 0)
|
||||
return FALSE;
|
||||
*get_var_addr(VAR_PACIFIDLOG_TM_RECEIVED_DAY) = 1;
|
||||
if (flash_write_save_block_chunks_check_damage(0) != TRUE)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
|
@ -1,46 +1,79 @@
|
|||
#include "gba/gba.h"
|
||||
#include "global.h"
|
||||
#include "main.h"
|
||||
#include "rtc.h"
|
||||
#include "flash.h"
|
||||
#include "berry_fix_save.h"
|
||||
#include "event_data.h"
|
||||
#include "message_box.h"
|
||||
|
||||
static s32 gInitialWaitTimer;
|
||||
#define ROM_HEADER_MAGIC 0x96
|
||||
#define ROM_GAME_TITLE_LEN 15
|
||||
|
||||
// States for the main BerryFix function
|
||||
enum {
|
||||
STATE_INIT,
|
||||
STATE_CHECK_RTC,
|
||||
STATE_CHECK_FLASH,
|
||||
STATE_READ_SAVE,
|
||||
STATE_CHECK_TIME,
|
||||
STATE_FIX_DATE,
|
||||
STATE_NO_NEED_TO_FIX,
|
||||
STATE_ERROR_YEAR,
|
||||
STATE_FINISHED,
|
||||
STATE_CHECK_PACIFIDLOG_TM,
|
||||
STATE_FIX_PACIFIDLOG_TM,
|
||||
STATE_ERROR
|
||||
};
|
||||
|
||||
// Return values for ValidateRomHeader
|
||||
enum
|
||||
{
|
||||
UPDATE_SAPPHIRE = 2,
|
||||
UPDATE_RUBY,
|
||||
NO_UPDATE_SAPPHIRE,
|
||||
NO_UPDATE_RUBY,
|
||||
INVALID
|
||||
};
|
||||
|
||||
static s32 sInitialWaitTimer;
|
||||
IntrFunc gIntrTable[16];
|
||||
u16 gHeldKeys;
|
||||
u16 gNewKeys;
|
||||
u8 gIntrVector[0x100];
|
||||
u32 gUpdateSuccessful;
|
||||
u32 gUnknown_3001194;
|
||||
u32 gUnknown_30011A0[0x19];
|
||||
u32 gUnusedVar;
|
||||
u32 gUnusedBuffer[25];
|
||||
u32 gMainCallbackState;
|
||||
u32 gGameVersion;
|
||||
|
||||
EWRAM_DATA u8 gSharedMem[0x8000] = {};
|
||||
static EWRAM_DATA u8 sSharedMem[0x8000] = {};
|
||||
|
||||
void IntrMain(void);
|
||||
void ReadKeys(void);
|
||||
void dummy_intr_0(void);
|
||||
void dummy_intr_1(void);
|
||||
void main_callback(u32 *, void *, void *);
|
||||
extern void IntrMain(void);
|
||||
|
||||
static void ReadKeys(void);
|
||||
static void IntrDummy(void);
|
||||
static void SerialIntr(void);
|
||||
static void BerryFix(u32 *, void *, void *);
|
||||
|
||||
static const char sBerryFixGameCode[] = "AGBJ";
|
||||
|
||||
const char gBerryFixGameCode[] = "AGBJ";
|
||||
const IntrFunc gIntrFuncPointers[] = {
|
||||
dummy_intr_0,
|
||||
dummy_intr_1,
|
||||
dummy_intr_0,
|
||||
dummy_intr_0,
|
||||
dummy_intr_0,
|
||||
dummy_intr_0,
|
||||
dummy_intr_0,
|
||||
dummy_intr_0,
|
||||
dummy_intr_0,
|
||||
dummy_intr_0,
|
||||
IntrDummy,
|
||||
SerialIntr,
|
||||
IntrDummy,
|
||||
IntrDummy,
|
||||
IntrDummy,
|
||||
IntrDummy,
|
||||
IntrDummy,
|
||||
IntrDummy,
|
||||
IntrDummy,
|
||||
IntrDummy,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
const char gVersionData[][2] = {
|
||||
|
||||
// Language character code, followed by version number
|
||||
static const char sVersionData[][2] = {
|
||||
{'J', 1},
|
||||
{'E', 2},
|
||||
{'D', 1},
|
||||
|
|
@ -48,230 +81,255 @@ const char gVersionData[][2] = {
|
|||
{'I', 1},
|
||||
{'S', 1}
|
||||
};
|
||||
const char gRubyTitleAndCode[] = "POKEMON RUBYAXV";
|
||||
const char gSapphireTitleAndCode[] = "POKEMON SAPPAXP";
|
||||
const u16 sDebugPals[20] = {
|
||||
RGB(00, 00, 00),
|
||||
RGB(31, 00, 00),
|
||||
RGB(00, 31, 00),
|
||||
RGB(00, 00, 31)
|
||||
static const char sRubyTitleAndCode[ROM_GAME_TITLE_LEN] = "POKEMON RUBYAXV";
|
||||
static const char sSapphireTitleAndCode[ROM_GAME_TITLE_LEN] = "POKEMON SAPPAXP";
|
||||
static const u16 sDebugPals[20] = {
|
||||
RGB_BLACK,
|
||||
RGB_RED,
|
||||
RGB_GREEN,
|
||||
RGB_BLUE
|
||||
};
|
||||
const u16 sDebugDigitsGfx[] = INCBIN_U16("graphics/debug_digits.4bpp");
|
||||
static const u16 sDebugDigitsGfx[] = INCBIN_U16("graphics/debug_digits.4bpp");
|
||||
|
||||
void AgbMain(void)
|
||||
{
|
||||
RegisterRamReset(0x1E);
|
||||
DmaCopy32(3, gIntrFuncPointers, gIntrTable, sizeof gIntrFuncPointers);
|
||||
RegisterRamReset(RESET_IWRAM | RESET_PALETTE | RESET_VRAM | RESET_OAM);
|
||||
DmaCopy32(3, gIntrFuncPointers, gIntrTable, sizeof(gIntrFuncPointers));
|
||||
DmaCopy32(3, IntrMain, gIntrVector, sizeof(gIntrVector));
|
||||
INTR_VECTOR = gIntrVector;
|
||||
REG_IE = INTR_FLAG_VBLANK;
|
||||
if (*RomHeaderMagic == 0x96 && *(u32 *)RomHeaderGameCode == *(u32 *)gBerryFixGameCode)
|
||||
if (*RomHeaderMagic == ROM_HEADER_MAGIC && *(u32 *)RomHeaderGameCode == *(u32 *)sBerryFixGameCode)
|
||||
REG_IE |= INTR_FLAG_GAMEPAK;
|
||||
REG_DISPSTAT = DISPSTAT_VBLANK_INTR;
|
||||
REG_IME = INTR_FLAG_VBLANK;
|
||||
msg_load_gfx();
|
||||
gMainCallbackState = MAINCB_INIT;
|
||||
gUnknown_3001194 = 0;
|
||||
MessageBox_Load();
|
||||
gMainCallbackState = STATE_INIT;
|
||||
gUnusedVar = 0;
|
||||
for (;;)
|
||||
{
|
||||
VBlankIntrWait();
|
||||
ReadKeys();
|
||||
main_callback(&gMainCallbackState, gUnknown_30011A0, gSharedMem);
|
||||
BerryFix(&gMainCallbackState, gUnusedBuffer, sSharedMem);
|
||||
}
|
||||
}
|
||||
|
||||
void dummy_intr_1(void)
|
||||
static void SerialIntr(void)
|
||||
{}
|
||||
|
||||
void dummy_intr_0(void)
|
||||
static void IntrDummy(void)
|
||||
{}
|
||||
|
||||
void ReadKeys(void)
|
||||
static void ReadKeys(void)
|
||||
{
|
||||
u16 keyInput = REG_KEYINPUT ^ KEYS_MASK;
|
||||
gNewKeys = keyInput & ~gHeldKeys;
|
||||
gHeldKeys = keyInput;
|
||||
}
|
||||
|
||||
void fill_palette(const u8 * src, u16 * dest, u8 value)
|
||||
// Unused
|
||||
static void fill_palette(const u8 * src, u16 * dest, u8 value)
|
||||
{
|
||||
s32 i;
|
||||
for (i = 0; src[i] != 0; i++)
|
||||
dest[i] = src[i] | value << 12;
|
||||
}
|
||||
|
||||
bool32 berry_fix_memcmp(const char * src1, const char * src2, size_t size)
|
||||
static bool32 CheckSameString(const char * a, const char * b, size_t size)
|
||||
{
|
||||
s32 i;
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
if (src1[i] != src2[i])
|
||||
if (a[i] != b[i])
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
s32 validate_rom_header_internal(void)
|
||||
static s32 ValidateGameVersion(void)
|
||||
{
|
||||
char languageCode = *(RomHeaderGameCode + 3);
|
||||
char languageCode = RomHeaderGameCode[3];
|
||||
s32 softwareVersion = *RomHeaderSoftwareVersion;
|
||||
s32 shouldUpdate = -1;
|
||||
s32 i;
|
||||
for (i = 0; i < ARRAY_COUNT(gVersionData); i++)
|
||||
|
||||
// Check rom header data to see if games of this
|
||||
// language and revision need the berry fix.
|
||||
for (i = 0; i < ARRAY_COUNT(sVersionData); i++)
|
||||
{
|
||||
if (languageCode == gVersionData[i][0])
|
||||
if (languageCode == sVersionData[i][0])
|
||||
{
|
||||
if (softwareVersion >= gVersionData[i][1])
|
||||
{
|
||||
shouldUpdate = 0;
|
||||
}
|
||||
if (softwareVersion >= sVersionData[i][1])
|
||||
shouldUpdate = FALSE;
|
||||
else
|
||||
{
|
||||
shouldUpdate = 1;
|
||||
}
|
||||
shouldUpdate = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (shouldUpdate != -1)
|
||||
{
|
||||
if (berry_fix_memcmp(RomHeaderGameTitle, gRubyTitleAndCode, 15) == TRUE)
|
||||
// A valid language/revision was found, check game title
|
||||
// and code to see if it's Ruby or Sapphire
|
||||
|
||||
if (CheckSameString(RomHeaderGameTitle, sRubyTitleAndCode, ROM_GAME_TITLE_LEN) == TRUE)
|
||||
{
|
||||
if (shouldUpdate == 0)
|
||||
return RUBY_NONEED;
|
||||
if (shouldUpdate == FALSE)
|
||||
{
|
||||
return NO_UPDATE_RUBY;
|
||||
}
|
||||
else
|
||||
{
|
||||
gGameVersion = VERSION_RUBY;
|
||||
return RUBY_UPDATABLE;
|
||||
return UPDATE_RUBY;
|
||||
}
|
||||
}
|
||||
else if (berry_fix_memcmp(RomHeaderGameTitle, gSapphireTitleAndCode, 15) == TRUE)
|
||||
else if (CheckSameString(RomHeaderGameTitle, sSapphireTitleAndCode, ROM_GAME_TITLE_LEN) == TRUE)
|
||||
{
|
||||
if (shouldUpdate == 0)
|
||||
return SAPPHIRE_NONEED;
|
||||
if (shouldUpdate == FALSE)
|
||||
{
|
||||
return NO_UPDATE_SAPPHIRE;
|
||||
}
|
||||
else
|
||||
{
|
||||
gGameVersion = VERSION_SAPPHIRE;
|
||||
return SAPPHIRE_UPDATABLE;
|
||||
return UPDATE_SAPPHIRE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return INVALID;
|
||||
}
|
||||
|
||||
s32 validate_rom_header(void)
|
||||
static s32 ValidateRomHeader(void)
|
||||
{
|
||||
if (*RomHeaderMakerCode == '0' && *(RomHeaderMakerCode + 1) == '1' && *RomHeaderMagic == 0x96)
|
||||
return validate_rom_header_internal();
|
||||
if (RomHeaderMakerCode[0] == '0' && RomHeaderMakerCode[1] == '1' && *RomHeaderMagic == ROM_HEADER_MAGIC)
|
||||
return ValidateGameVersion();
|
||||
else
|
||||
return INVALID;
|
||||
}
|
||||
|
||||
void main_callback(u32 * state, void * unused1, void * unused2)
|
||||
static void BerryFix(u32 * state, void * unused1, void * unused2)
|
||||
{
|
||||
u8 year;
|
||||
switch (*state)
|
||||
{
|
||||
case MAINCB_INIT:
|
||||
msg_display(MSGBOX_WILL_NOW_UPDATE);
|
||||
if (++gInitialWaitTimer >= 180)
|
||||
case STATE_INIT:
|
||||
// "The Berry Program Update will now begin..."
|
||||
MessageBox_Display(MSG_WILL_NOW_UPDATE);
|
||||
if (++sInitialWaitTimer >= 180)
|
||||
{
|
||||
sInitialWaitTimer = 0;
|
||||
gUpdateSuccessful = 0;
|
||||
switch (ValidateRomHeader())
|
||||
{
|
||||
gInitialWaitTimer = 0;
|
||||
gUpdateSuccessful = 0;
|
||||
switch (validate_rom_header())
|
||||
{
|
||||
case SAPPHIRE_UPDATABLE:
|
||||
case RUBY_UPDATABLE: // Should Update Ruby
|
||||
++(*state); // MAINCB_CHECK_RTC
|
||||
break;
|
||||
case INVALID: // Invalid header
|
||||
*state = MAINCB_ERROR;
|
||||
break;
|
||||
case SAPPHIRE_NONEED: // Should not update Sapphire
|
||||
case RUBY_NONEED: // Should not update Ruby
|
||||
*state = MAINCB_NO_NEED_TO_FIX;
|
||||
break;
|
||||
}
|
||||
case UPDATE_SAPPHIRE:
|
||||
case UPDATE_RUBY:
|
||||
++(*state); // STATE_CHECK_RTC
|
||||
break;
|
||||
case INVALID: // Invalid header
|
||||
*state = STATE_ERROR;
|
||||
break;
|
||||
case NO_UPDATE_SAPPHIRE:
|
||||
case NO_UPDATE_RUBY:
|
||||
*state = STATE_NO_NEED_TO_FIX;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case MAINCB_CHECK_RTC:
|
||||
if (!rtc_maincb_is_rtc_working())
|
||||
*state = MAINCB_ERROR;
|
||||
}
|
||||
break;
|
||||
case STATE_CHECK_RTC:
|
||||
if (!BerryFix_TryInitRtc())
|
||||
*state = STATE_ERROR;
|
||||
else
|
||||
++(*state); // STATE_CHECK_FLASH
|
||||
break;
|
||||
case STATE_CHECK_FLASH:
|
||||
if (BerryFix_IdentifyFlash() == TRUE)
|
||||
++(*state); // STATE_READ_SAVE
|
||||
else
|
||||
*state = STATE_ERROR;
|
||||
break;
|
||||
case STATE_READ_SAVE:
|
||||
if (BerryFix_LoadSave(0) == SAVE_STATUS_OK)
|
||||
++(*state); // STATE_CHECK_TIME
|
||||
else
|
||||
*state = STATE_ERROR;
|
||||
break;
|
||||
case STATE_CHECK_TIME:
|
||||
if (BerryFix_CalcTimeDifference(&year) == TRUE)
|
||||
{
|
||||
// Time difference is okay, only fix the date if
|
||||
// the Berry Glitch hasn't happened yet (if year is 2000)
|
||||
if (year == 0)
|
||||
++(*state); // STATE_FIX_DATE
|
||||
else
|
||||
++(*state); // MAINCB_CHECK_FLASH
|
||||
break;
|
||||
case MAINCB_CHECK_FLASH:
|
||||
if (flash_maincb_ident_is_valid() == TRUE)
|
||||
++(*state); // MAINCB_READ_SAVE
|
||||
*state = STATE_CHECK_PACIFIDLOG_TM;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Time difference is incorrect, if the year is 2001
|
||||
// then the Berry Glitch is occurring. If the year is
|
||||
// not 2001 then some error has occurred.
|
||||
if (year != 1)
|
||||
*state = STATE_ERROR_YEAR;
|
||||
else
|
||||
*state = MAINCB_ERROR;
|
||||
break;
|
||||
case MAINCB_READ_SAVE:
|
||||
if (flash_maincb_read_save(0) == SAVE_STATUS_OK)
|
||||
++(*state); // MAINCB_CHECK_TIME
|
||||
else
|
||||
*state = MAINCB_ERROR;
|
||||
break;
|
||||
case MAINCB_CHECK_TIME:
|
||||
if (rtc_maincb_is_time_since_last_berry_update_positive(&year) == TRUE)
|
||||
{
|
||||
if (year == 0)
|
||||
++(*state); // MAINCB_FIX_DATE
|
||||
else
|
||||
*state = MAINCB_CHECK_PACIFIDLOG_TM;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (year != 1)
|
||||
*state = MAINCB_YEAR_MAKES_NO_SENSE;
|
||||
else
|
||||
++(*state); // MAINCB_FIX_DATE
|
||||
}
|
||||
break;
|
||||
case MAINCB_FIX_DATE:
|
||||
rtc_maincb_fix_date();
|
||||
++(*state); // STATE_FIX_DATE
|
||||
}
|
||||
break;
|
||||
case STATE_FIX_DATE:
|
||||
// Set the clock forward to fix the Berry Glitch
|
||||
// If the date is late enough that it is no
|
||||
// longer affected then this does nothing.
|
||||
BerryFix_TryFixDate();
|
||||
gUpdateSuccessful |= 1;
|
||||
*state = STATE_CHECK_PACIFIDLOG_TM;
|
||||
break;
|
||||
case STATE_CHECK_PACIFIDLOG_TM:
|
||||
if (BerryFix_IsPacifidlogTMCorrect() == TRUE)
|
||||
*state = STATE_FINISHED;
|
||||
else
|
||||
*state = STATE_FIX_PACIFIDLOG_TM;
|
||||
break;
|
||||
case STATE_FIX_PACIFIDLOG_TM:
|
||||
// "Updating. the Berry Program. Please wait..."
|
||||
MessageBox_Display(MSG_UPDATING);
|
||||
if (BerryFix_ResetPacifidlogTM() == TRUE)
|
||||
{
|
||||
gUpdateSuccessful |= 1;
|
||||
*state = MAINCB_CHECK_PACIFIDLOG_TM;
|
||||
break;
|
||||
case MAINCB_CHECK_PACIFIDLOG_TM:
|
||||
if (flash_maincb_check_need_reset_pacifidlog_tm() == TRUE)
|
||||
*state = MAINCB_FINISHED;
|
||||
else
|
||||
*state = MAINCB_FIX_PACIFIDLOG_TM;
|
||||
break;
|
||||
case MAINCB_FIX_PACIFIDLOG_TM:
|
||||
msg_display(MSGBOX_UPDATING);
|
||||
if (flash_maincb_reset_pacifidlog_tm() == TRUE)
|
||||
{
|
||||
gUpdateSuccessful |= 1;
|
||||
*state = MAINCB_FINISHED;
|
||||
}
|
||||
else
|
||||
*state = MAINCB_ERROR;
|
||||
break;
|
||||
case MAINCB_FINISHED:
|
||||
if (gUpdateSuccessful == 0)
|
||||
*state = MAINCB_NO_NEED_TO_FIX;
|
||||
else
|
||||
msg_display(MSGBOX_HAS_BEEN_UPDATED);
|
||||
break;
|
||||
case MAINCB_NO_NEED_TO_FIX:
|
||||
msg_display(MSGBOX_NO_NEED_TO_UPDATE);
|
||||
break;
|
||||
case MAINCB_YEAR_MAKES_NO_SENSE:
|
||||
msg_display(MSGBOX_UNABLE_TO_UPDATE);
|
||||
break;
|
||||
case MAINCB_ERROR:
|
||||
msg_display(MSGBOX_UNABLE_TO_UPDATE);
|
||||
break;
|
||||
*state = STATE_FINISHED;
|
||||
}
|
||||
else
|
||||
{
|
||||
*state = STATE_ERROR;
|
||||
}
|
||||
break;
|
||||
// The below 4 cases are all the possible end states
|
||||
// The Berry Fix Program will remain in these states until
|
||||
// the player turns off the GBA.
|
||||
case STATE_FINISHED:
|
||||
if (gUpdateSuccessful == 0)
|
||||
*state = STATE_NO_NEED_TO_FIX;
|
||||
else // "Your Berry Program has been updated"
|
||||
MessageBox_Display(MSG_HAS_BEEN_UPDATED);
|
||||
break;
|
||||
case STATE_NO_NEED_TO_FIX:
|
||||
// "There is no need to update your Berry Program"
|
||||
MessageBox_Display(MSG_NO_NEED_TO_UPDATE);
|
||||
break;
|
||||
case STATE_ERROR_YEAR:
|
||||
// "Unable to update the Berry Program"
|
||||
MessageBox_Display(MSG_UNABLE_TO_UPDATE);
|
||||
break;
|
||||
case STATE_ERROR:
|
||||
// "Unable to update the Berry Program"
|
||||
MessageBox_Display(MSG_UNABLE_TO_UPDATE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DBG_LoadDigitsPal(void)
|
||||
static void Debug_LoadDigitsPal(void)
|
||||
{
|
||||
s32 i;
|
||||
const u16 * src;
|
||||
vu16 * dest = (vu16 *)BG_PLTT + 1;
|
||||
DmaFill16(3, RGB(31, 31, 31), (vu16 *)BG_PLTT, BG_PLTT_SIZE);
|
||||
DmaFill16(3, RGB_WHITE, (vu16 *)BG_PLTT, BG_PLTT_SIZE);
|
||||
src = sDebugPals;
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
|
|
@ -281,9 +339,10 @@ void DBG_LoadDigitsPal(void)
|
|||
}
|
||||
}
|
||||
|
||||
void DBG_LoadDigits(void)
|
||||
// Unused
|
||||
static void Debug_LoadDigits(void)
|
||||
{
|
||||
DmaFill16(3, 0x1111, (void *)VRAM + 0x8420, 0x1800);
|
||||
DmaCopy32(3, sDebugDigitsGfx, (void *)VRAM + 0x8600, 0x200);
|
||||
DBG_LoadDigitsPal();
|
||||
Debug_LoadDigitsPal();
|
||||
}
|
||||
|
|
|
|||
46
payload/src/message_box.c
Normal file
46
payload/src/message_box.c
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#include "global.h"
|
||||
#include "message_box.h"
|
||||
|
||||
static const u16 sMessages_Pal[] = INCBIN_U16("graphics/messages.gbapal");
|
||||
static const u8 sMessages_Tilemap[] = INCBIN_U8("graphics/messages.bin.lz");
|
||||
static const u8 sMessages_Gfx[] = INCBIN_U8("graphics/messages.4bpp.lz");
|
||||
|
||||
void MessageBox_Load(void)
|
||||
{
|
||||
REG_DISPCNT = 0;
|
||||
REG_BG0HOFS = 0;
|
||||
REG_BG0VOFS = 0;
|
||||
REG_BLDCNT = 0;
|
||||
LZ77UnCompVram(sMessages_Gfx, (void *)BG_VRAM);
|
||||
LZ77UnCompVram(sMessages_Tilemap, (void *)BG_SCREEN_ADDR(28));
|
||||
CpuCopy16(sMessages_Pal, (void *)BG_PLTT, BG_PLTT_SIZE);
|
||||
REG_BG0CNT = BGCNT_SCREENBASE(28) | BGCNT_TXT512x512;
|
||||
REG_DISPCNT = DISPCNT_BG0_ON;
|
||||
}
|
||||
|
||||
void MessageBox_Display(enum Message msg)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
case MSG_WILL_NOW_UPDATE:
|
||||
REG_BG0HOFS = 0;
|
||||
REG_BG0VOFS = 0;
|
||||
break;
|
||||
case MSG_HAS_BEEN_UPDATED:
|
||||
REG_BG0HOFS = 0x100;
|
||||
REG_BG0VOFS = 0;
|
||||
break;
|
||||
case MSG_UNABLE_TO_UPDATE:
|
||||
REG_BG0HOFS = 0x100;
|
||||
REG_BG0VOFS = 0xB0;
|
||||
break;
|
||||
case MSG_NO_NEED_TO_UPDATE:
|
||||
REG_BG0HOFS = 0;
|
||||
REG_BG0VOFS = 0xB0;
|
||||
break;
|
||||
case MSG_UPDATING:
|
||||
REG_BG0HOFS = 0;
|
||||
REG_BG0VOFS = 0x160;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,346 +1,386 @@
|
|||
#include "gba/gba.h"
|
||||
#include "siirtc.h"
|
||||
#include "global.h"
|
||||
#include "siirtc.h"
|
||||
#include "main.h"
|
||||
#include "rtc.h"
|
||||
|
||||
struct Time gTimeSinceBerryUpdate;
|
||||
struct Time gRtcUTCTime;
|
||||
|
||||
static u16 sRtcProbeStatus;
|
||||
static u16 sErrorStatus;
|
||||
static struct SiiRtcInfo sRtcInfoBuffer;
|
||||
static u8 sRtcProbeCode;
|
||||
static u16 sImeBak;
|
||||
static u8 sProbeResult;
|
||||
static u16 sSavedIme;
|
||||
static struct SiiRtcInfo sRtcInfoWork;
|
||||
|
||||
const struct SiiRtcInfo sDefaultRTC = {
|
||||
.year = 0, // 2000
|
||||
.month = 1, // January
|
||||
.day = 1, // 01
|
||||
.dayOfWeek = 0,
|
||||
.hour = 0,
|
||||
.minute = 0,
|
||||
.second = 0,
|
||||
.status = 0,
|
||||
.alarmHour = 0,
|
||||
.alarmMinute = 0
|
||||
};
|
||||
const s32 sDaysPerMonth[] = {
|
||||
31,
|
||||
28,
|
||||
31,
|
||||
30,
|
||||
31,
|
||||
30,
|
||||
31,
|
||||
31,
|
||||
30,
|
||||
31,
|
||||
30,
|
||||
31
|
||||
static const struct SiiRtcInfo sDefaultRTC = {0, MONTH_JAN, 1}; // 2000 Jan 1
|
||||
|
||||
static const s32 sDaysPerMonth[] = {
|
||||
[MONTH_JAN - 1] = 31,
|
||||
[MONTH_FEB - 1] = 28,
|
||||
[MONTH_MAR - 1] = 31,
|
||||
[MONTH_APR - 1] = 30,
|
||||
[MONTH_MAY - 1] = 31,
|
||||
[MONTH_JUN - 1] = 30,
|
||||
[MONTH_JUL - 1] = 31,
|
||||
[MONTH_AUG - 1] = 31,
|
||||
[MONTH_SEP - 1] = 30,
|
||||
[MONTH_OCT - 1] = 31,
|
||||
[MONTH_NOV - 1] = 30,
|
||||
[MONTH_DEC - 1] = 31
|
||||
};
|
||||
|
||||
void rtc_get_status_and_datetime(struct SiiRtcInfo *);
|
||||
u16 rtc_validate_datetime(struct SiiRtcInfo *);
|
||||
static void RtcGetRawInfo(struct SiiRtcInfo *);
|
||||
static u16 RtcCheckInfo(struct SiiRtcInfo *);
|
||||
|
||||
|
||||
void rtc_intr_disable(void)
|
||||
static void RtcDisableInterrupts(void)
|
||||
{
|
||||
sImeBak = REG_IME;
|
||||
sSavedIme = REG_IME;
|
||||
REG_IME = 0;
|
||||
}
|
||||
|
||||
void rtc_intr_enable(void)
|
||||
static void RtcRestoreInterrupts(void)
|
||||
{
|
||||
REG_IME = sImeBak;
|
||||
REG_IME = sSavedIme;
|
||||
}
|
||||
|
||||
s32 bcd_to_hex(u8 a0)
|
||||
static s32 ConvertBcdToBinary(u8 bcd)
|
||||
{
|
||||
if (a0 >= 0xa0 || (a0 & 0xF) >= 10)
|
||||
if (bcd >= 0xa0 || (bcd & 0xF) >= 10)
|
||||
return 0xFF;
|
||||
return ((a0 >> 4) & 0xF) * 10 + (a0 & 0xF);
|
||||
return ((bcd >> 4) & 0xF) * 10 + (bcd & 0xF);
|
||||
}
|
||||
|
||||
bool8 is_leap_year(u8 year)
|
||||
static bool8 IsLeapYear(u8 year)
|
||||
{
|
||||
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
u16 rtc_count_days_parameterized(u8 year, u8 month, u8 day)
|
||||
static u16 ConvertDateToDayCount(u8 year, u8 month, u8 day)
|
||||
{
|
||||
u16 numDays = 0;
|
||||
s32 i;
|
||||
u16 dayCount = 0;
|
||||
|
||||
for (i = year - 1; i > 0; i--)
|
||||
{
|
||||
numDays += 365;
|
||||
if (is_leap_year(i) == TRUE)
|
||||
numDays++;
|
||||
dayCount += 365;
|
||||
|
||||
if (IsLeapYear(i) == TRUE)
|
||||
dayCount++;
|
||||
}
|
||||
|
||||
for (i = 0; i < month - 1; i++)
|
||||
numDays += sDaysPerMonth[i];
|
||||
if (month > MONTH_FEB && is_leap_year(year) == TRUE)
|
||||
numDays++;
|
||||
numDays += day;
|
||||
return numDays;
|
||||
dayCount += sDaysPerMonth[i];
|
||||
|
||||
if (month > MONTH_FEB && IsLeapYear(year) == TRUE)
|
||||
dayCount++;
|
||||
|
||||
dayCount += day;
|
||||
|
||||
return dayCount;
|
||||
}
|
||||
|
||||
u16 rtc_count_days_from_info(struct SiiRtcInfo *info)
|
||||
u16 RtcGetDayCount(struct SiiRtcInfo *rtc)
|
||||
{
|
||||
return rtc_count_days_parameterized(bcd_to_hex(info->year), bcd_to_hex(info->month), bcd_to_hex(info->day));
|
||||
}
|
||||
u8 year = ConvertBcdToBinary(rtc->year);
|
||||
u8 month = ConvertBcdToBinary(rtc->month);
|
||||
u8 day = ConvertBcdToBinary(rtc->day);
|
||||
return ConvertDateToDayCount(year, month, day);}
|
||||
|
||||
static void rtc_probe_status(void)
|
||||
static void RtcInit(void)
|
||||
{
|
||||
sRtcProbeStatus = 0;
|
||||
rtc_intr_disable();
|
||||
sErrorStatus = 0;
|
||||
|
||||
RtcDisableInterrupts();
|
||||
SiiRtcUnprotect();
|
||||
sRtcProbeCode = SiiRtcProbe();
|
||||
rtc_intr_enable();
|
||||
if ((sRtcProbeCode & 0xF) != 1)
|
||||
sRtcProbeStatus = 1;
|
||||
else
|
||||
sProbeResult = SiiRtcProbe();
|
||||
RtcRestoreInterrupts();
|
||||
|
||||
if ((sProbeResult & 0xF) != 1)
|
||||
{
|
||||
if (sRtcProbeCode & 0xF0)
|
||||
sRtcProbeStatus = 2;
|
||||
else
|
||||
sRtcProbeStatus = 0;
|
||||
rtc_get_status_and_datetime(&sRtcInfoBuffer);
|
||||
sRtcProbeStatus = rtc_validate_datetime(&sRtcInfoBuffer);
|
||||
sErrorStatus = RTC_INIT_ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
u16 rtc_get_probe_status(void)
|
||||
{
|
||||
return sRtcProbeStatus;
|
||||
}
|
||||
|
||||
void sub_020106EC(struct SiiRtcInfo * info)
|
||||
{
|
||||
if (sRtcProbeStatus & 0xFF0)
|
||||
*info = sDefaultRTC;
|
||||
if (sProbeResult & 0xF0)
|
||||
sErrorStatus = RTC_INIT_WARNING;
|
||||
else
|
||||
rtc_get_status_and_datetime(info);
|
||||
sErrorStatus = 0;
|
||||
|
||||
RtcGetRawInfo(&sRtcInfoBuffer);
|
||||
sErrorStatus = RtcCheckInfo(&sRtcInfoBuffer);
|
||||
}
|
||||
|
||||
void rtc_get_datetime(struct SiiRtcInfo * info)
|
||||
static u16 RtcGetErrorStatus(void)
|
||||
{
|
||||
rtc_intr_disable();
|
||||
SiiRtcGetDateTime(info);
|
||||
rtc_intr_enable();
|
||||
return sErrorStatus;
|
||||
}
|
||||
|
||||
void rtc_get_status(struct SiiRtcInfo * info)
|
||||
// Unused
|
||||
static void RtcGetInfo(struct SiiRtcInfo * rtc)
|
||||
{
|
||||
rtc_intr_disable();
|
||||
SiiRtcGetStatus(info);
|
||||
rtc_intr_enable();
|
||||
if (sErrorStatus & RTC_ERR_FLAG_MASK)
|
||||
*rtc = sDefaultRTC;
|
||||
else
|
||||
RtcGetRawInfo(rtc);
|
||||
}
|
||||
|
||||
void rtc_get_status_and_datetime(struct SiiRtcInfo * info)
|
||||
static void RtcGetDateTime(struct SiiRtcInfo * rtc)
|
||||
{
|
||||
rtc_get_status(info);
|
||||
rtc_get_datetime(info);
|
||||
RtcDisableInterrupts();
|
||||
SiiRtcGetDateTime(rtc);
|
||||
RtcRestoreInterrupts();
|
||||
}
|
||||
|
||||
u16 rtc_validate_datetime(struct SiiRtcInfo * info)
|
||||
static void RtcGetStatus(struct SiiRtcInfo * rtc)
|
||||
{
|
||||
s32 year, month, day;
|
||||
u16 r4 = (info->status & SIIRTCINFO_POWER) ? 0x20 : 0;
|
||||
if (!(info->status & SIIRTCINFO_24HOUR))
|
||||
r4 |= 0x10;
|
||||
year = bcd_to_hex(info->year);
|
||||
RtcDisableInterrupts();
|
||||
SiiRtcGetStatus(rtc);
|
||||
RtcRestoreInterrupts();
|
||||
}
|
||||
|
||||
static void RtcGetRawInfo(struct SiiRtcInfo * rtc)
|
||||
{
|
||||
RtcGetStatus(rtc);
|
||||
RtcGetDateTime(rtc);
|
||||
}
|
||||
|
||||
static u16 RtcCheckInfo(struct SiiRtcInfo * rtc)
|
||||
{
|
||||
u16 errorFlags = 0;
|
||||
s32 year;
|
||||
s32 month;
|
||||
s32 value;
|
||||
|
||||
if (rtc->status & SIIRTCINFO_POWER)
|
||||
errorFlags |= RTC_ERR_POWER_FAILURE;
|
||||
|
||||
if (!(rtc->status & SIIRTCINFO_24HOUR))
|
||||
errorFlags |= RTC_ERR_12HOUR_CLOCK;
|
||||
|
||||
year = ConvertBcdToBinary(rtc->year);
|
||||
if (year == 0xFF)
|
||||
r4 |= 0x40;
|
||||
month = bcd_to_hex(info->month);
|
||||
errorFlags |= RTC_ERR_INVALID_YEAR;
|
||||
|
||||
month = ConvertBcdToBinary(rtc->month);
|
||||
if (month == 0xFF || month == 0 || month > 12)
|
||||
r4 |= 0x80;
|
||||
day = bcd_to_hex(info->day);
|
||||
if (day == 0xFF)
|
||||
r4 |= 0x100;
|
||||
errorFlags |= RTC_ERR_INVALID_MONTH;
|
||||
|
||||
value = ConvertBcdToBinary(rtc->day);
|
||||
if (value == 0xFF)
|
||||
errorFlags |= RTC_ERR_INVALID_DAY;
|
||||
|
||||
if (month == MONTH_FEB)
|
||||
{
|
||||
if (day > is_leap_year(year) + sDaysPerMonth[1])
|
||||
r4 |= 0x100;
|
||||
if (value > IsLeapYear(year) + sDaysPerMonth[1])
|
||||
errorFlags |= RTC_ERR_INVALID_DAY;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (day > sDaysPerMonth[month - 1])
|
||||
r4 |= 0x100;
|
||||
if (value > sDaysPerMonth[month - 1])
|
||||
errorFlags |= RTC_ERR_INVALID_DAY;
|
||||
}
|
||||
day = bcd_to_hex(info->hour);
|
||||
if (day > 24)
|
||||
r4 |= 0x200;
|
||||
day = bcd_to_hex(info->minute);
|
||||
if (day > 60)
|
||||
r4 |= 0x400;
|
||||
day = bcd_to_hex(info->second);
|
||||
if (day > 60)
|
||||
r4 |= 0x800;
|
||||
return r4;
|
||||
|
||||
value = ConvertBcdToBinary(rtc->hour);
|
||||
if (value > 24)
|
||||
errorFlags |= RTC_ERR_INVALID_HOUR;
|
||||
|
||||
value = ConvertBcdToBinary(rtc->minute);
|
||||
if (value > 60)
|
||||
errorFlags |= RTC_ERR_INVALID_MINUTE;
|
||||
|
||||
value = ConvertBcdToBinary(rtc->second);
|
||||
if (value > 60)
|
||||
errorFlags |= RTC_ERR_INVALID_SECOND;
|
||||
|
||||
return errorFlags;
|
||||
}
|
||||
|
||||
void rtc_reset(void)
|
||||
// Unused
|
||||
static void RtcReset(void)
|
||||
{
|
||||
rtc_intr_disable();
|
||||
RtcDisableInterrupts();
|
||||
SiiRtcReset();
|
||||
rtc_intr_enable();
|
||||
RtcRestoreInterrupts();
|
||||
}
|
||||
|
||||
void rtc_sub_time_from_datetime(struct SiiRtcInfo * datetime, struct Time * dest, struct Time * timediff)
|
||||
static void RtcCalcTimeDifference(struct SiiRtcInfo * rtc, struct Time * result, struct Time * t)
|
||||
{
|
||||
u16 r4 = rtc_count_days_from_info(datetime);
|
||||
dest->seconds = bcd_to_hex(datetime->second) - timediff->seconds;
|
||||
dest->minutes = bcd_to_hex(datetime->minute) - timediff->minutes;
|
||||
dest->hours = bcd_to_hex(datetime->hour) - timediff->hours;
|
||||
dest->days = r4 - timediff->days;
|
||||
if (dest->seconds < 0)
|
||||
u16 days = RtcGetDayCount(rtc);
|
||||
result->seconds = ConvertBcdToBinary(rtc->second) - t->seconds;
|
||||
result->minutes = ConvertBcdToBinary(rtc->minute) - t->minutes;
|
||||
result->hours = ConvertBcdToBinary(rtc->hour) - t->hours;
|
||||
result->days = days - t->days;
|
||||
|
||||
if (result->seconds < 0)
|
||||
{
|
||||
dest->seconds += 60;
|
||||
dest->minutes--;
|
||||
result->seconds += 60;
|
||||
result->minutes--;
|
||||
}
|
||||
if (dest->minutes < 0)
|
||||
|
||||
if (result->minutes < 0)
|
||||
{
|
||||
dest->minutes += 60;
|
||||
dest->hours--;
|
||||
result->minutes += 60;
|
||||
result->hours--;
|
||||
}
|
||||
if (dest->hours < 0)
|
||||
|
||||
if (result->hours < 0)
|
||||
{
|
||||
dest->hours += 24;
|
||||
dest->days--;
|
||||
result->hours += 24;
|
||||
result->days--;
|
||||
}
|
||||
}
|
||||
|
||||
void rtc_sub_time_from_time(struct Time * dest, struct Time * diff, struct Time * src)
|
||||
static void CalcTimeDifference(struct Time * result, struct Time * t1, struct Time * t2)
|
||||
{
|
||||
dest->seconds = src->seconds - diff->seconds;
|
||||
dest->minutes = src->minutes - diff->minutes;
|
||||
dest->hours = src->hours - diff->hours;
|
||||
dest->days = src->days - diff->days;
|
||||
if (dest->seconds < 0)
|
||||
result->seconds = t2->seconds - t1->seconds;
|
||||
result->minutes = t2->minutes - t1->minutes;
|
||||
result->hours = t2->hours - t1->hours;
|
||||
result->days = t2->days - t1->days;
|
||||
|
||||
if (result->seconds < 0)
|
||||
{
|
||||
dest->seconds += 60;
|
||||
dest->minutes--;
|
||||
result->seconds += 60;
|
||||
result->minutes--;
|
||||
}
|
||||
if (dest->minutes < 0)
|
||||
|
||||
if (result->minutes < 0)
|
||||
{
|
||||
dest->minutes += 60;
|
||||
dest->hours--;
|
||||
result->minutes += 60;
|
||||
result->hours--;
|
||||
}
|
||||
if (dest->hours < 0)
|
||||
|
||||
if (result->hours < 0)
|
||||
{
|
||||
dest->hours += 24;
|
||||
dest->days--;
|
||||
result->hours += 24;
|
||||
result->days--;
|
||||
}
|
||||
}
|
||||
|
||||
bool32 rtc_maincb_is_rtc_working(void)
|
||||
// New code for Berry Fix Program starts here
|
||||
|
||||
bool32 BerryFix_TryInitRtc(void)
|
||||
{
|
||||
rtc_probe_status();
|
||||
if (rtc_get_probe_status() & 0xFF0)
|
||||
RtcInit();
|
||||
if (RtcGetErrorStatus() & RTC_ERR_FLAG_MASK)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void rtc_set_datetime(struct SiiRtcInfo * info)
|
||||
static void RtcSetDateTime(struct SiiRtcInfo * rtc)
|
||||
{
|
||||
vu16 imeBak = REG_IME;
|
||||
REG_IME = 0;
|
||||
SiiRtcSetDateTime(info);
|
||||
SiiRtcSetDateTime(rtc);
|
||||
REG_IME = imeBak;
|
||||
}
|
||||
|
||||
bool32 rtc_maincb_is_time_since_last_berry_update_positive(u8 * a0)
|
||||
// The below are equivalent to &gSaveBlock2.localTimeOffset and &gSaveBlock2.lastBerryTreeUpdate.
|
||||
// Replacing them both below doesn't match
|
||||
#define SaveBlock2Addr (EWRAM_START + 0x28000)
|
||||
#define LocalTimeOffset ((struct Time *)(SaveBlock2Addr + offsetof(struct SaveBlock2, localTimeOffset)))
|
||||
#define LastBerryTreeUpdate ((struct Time *)(SaveBlock2Addr + offsetof(struct SaveBlock2, lastBerryTreeUpdate)))
|
||||
|
||||
bool32 BerryFix_CalcTimeDifference(u8 * year)
|
||||
{
|
||||
rtc_get_status_and_datetime(&sRtcInfoWork);
|
||||
*a0 = bcd_to_hex(sRtcInfoWork.year);
|
||||
rtc_sub_time_from_datetime(&sRtcInfoWork, &gRtcUTCTime, LocalTimeOffset);
|
||||
rtc_sub_time_from_time(&gTimeSinceBerryUpdate, LastBerryTreeUpdate, &gRtcUTCTime);
|
||||
if (gTimeSinceBerryUpdate.days * 1440 + gTimeSinceBerryUpdate.hours * 60 + gTimeSinceBerryUpdate.minutes >= 0)
|
||||
RtcGetRawInfo(&sRtcInfoWork);
|
||||
*year = ConvertBcdToBinary(sRtcInfoWork.year);
|
||||
RtcCalcTimeDifference(&sRtcInfoWork, &gRtcUTCTime, LocalTimeOffset);
|
||||
CalcTimeDifference(&gTimeSinceBerryUpdate, LastBerryTreeUpdate, &gRtcUTCTime);
|
||||
if (gTimeSinceBerryUpdate.days * 24 * 60 + gTimeSinceBerryUpdate.hours * 60 + gTimeSinceBerryUpdate.minutes >= 0)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
u32 hex_to_bcd(u8 a0)
|
||||
static u32 ConvertBinaryToBcd(u8 binary)
|
||||
{
|
||||
u32 r4;
|
||||
if (a0 > 99)
|
||||
u32 bcd;
|
||||
if (binary > 99)
|
||||
return 0xFF;
|
||||
r4 = Div(a0, 10) << 4;
|
||||
r4 |= Mod(a0, 10);
|
||||
return r4;
|
||||
bcd = Div(binary, 10) << 4;
|
||||
bcd |= Mod(binary, 10);
|
||||
return bcd;
|
||||
}
|
||||
|
||||
void sii_rtc_inc(u8 * a0)
|
||||
static void RtcIncrement(u8 * val)
|
||||
{
|
||||
*a0 = hex_to_bcd(bcd_to_hex(*a0) + 1);
|
||||
*val = ConvertBinaryToBcd(ConvertBcdToBinary(*val) + 1);
|
||||
}
|
||||
|
||||
void sii_rtc_inc_month(struct SiiRtcInfo * a0)
|
||||
static void RtcIncrementMonth(struct SiiRtcInfo * rtc)
|
||||
{
|
||||
sii_rtc_inc(&a0->month);
|
||||
if (bcd_to_hex(a0->month) > 12)
|
||||
RtcIncrement(&rtc->month);
|
||||
if (ConvertBcdToBinary(rtc->month) > 12)
|
||||
{
|
||||
sii_rtc_inc(&a0->year);
|
||||
a0->month = MONTH_JAN;
|
||||
RtcIncrement(&rtc->year);
|
||||
rtc->month = MONTH_JAN;
|
||||
}
|
||||
}
|
||||
|
||||
void sii_rtc_inc_day(struct SiiRtcInfo * a0)
|
||||
static void RtcIncrementDay(struct SiiRtcInfo * rtc)
|
||||
{
|
||||
sii_rtc_inc(&a0->day);
|
||||
if (bcd_to_hex(a0->day) > sDaysPerMonth[bcd_to_hex(a0->month) - 1])
|
||||
RtcIncrement(&rtc->day);
|
||||
if (ConvertBcdToBinary(rtc->day) > sDaysPerMonth[ConvertBcdToBinary(rtc->month) - 1])
|
||||
{
|
||||
if (!is_leap_year(bcd_to_hex(a0->year)) || bcd_to_hex(a0->month) != MONTH_FEB || bcd_to_hex(a0->day) != 29)
|
||||
if (!IsLeapYear(ConvertBcdToBinary(rtc->year)) || ConvertBcdToBinary(rtc->month) != MONTH_FEB || ConvertBcdToBinary(rtc->day) != 29)
|
||||
{
|
||||
a0->day = 1;
|
||||
sii_rtc_inc_month(a0);
|
||||
rtc->day = 1;
|
||||
RtcIncrementMonth(rtc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool32 rtc_is_past_feb_28_2000(struct SiiRtcInfo * a0)
|
||||
// When fixing the RTC, consider if the leap day on Feb 29, 2000 was reached
|
||||
static bool32 RtcNeedsLeapDayIncrement(struct SiiRtcInfo * rtc)
|
||||
{
|
||||
if (bcd_to_hex(a0->year) == 0)
|
||||
if (ConvertBcdToBinary(rtc->year) == 0)
|
||||
{
|
||||
if (bcd_to_hex(a0->month) == MONTH_JAN)
|
||||
if (ConvertBcdToBinary(rtc->month) == MONTH_JAN)
|
||||
return FALSE;
|
||||
if (bcd_to_hex(a0->month) > MONTH_FEB)
|
||||
if (ConvertBcdToBinary(rtc->month) > MONTH_FEB)
|
||||
return TRUE;
|
||||
if (bcd_to_hex(a0->day) == 29)
|
||||
if (ConvertBcdToBinary(rtc->day) == 29)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
if (bcd_to_hex(a0->year) == 1)
|
||||
if (ConvertBcdToBinary(rtc->year) == 1)
|
||||
return TRUE;
|
||||
|
||||
// After 2001 (shouldn't occur)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void rtc_maincb_fix_date(void)
|
||||
void BerryFix_TryFixDate(void)
|
||||
{
|
||||
rtc_get_status_and_datetime(&sRtcInfoWork);
|
||||
if (bcd_to_hex(sRtcInfoWork.year) == 0 || bcd_to_hex(sRtcInfoWork.year) == 1)
|
||||
RtcGetRawInfo(&sRtcInfoWork);
|
||||
|
||||
// If the year is anything but 2000 or 2001 then the Berry Glitch has already passed
|
||||
if (ConvertBcdToBinary(sRtcInfoWork.year) == 0 || ConvertBcdToBinary(sRtcInfoWork.year) == 1)
|
||||
{
|
||||
if (bcd_to_hex(sRtcInfoWork.year) == 1)
|
||||
if (ConvertBcdToBinary(sRtcInfoWork.year) == 1)
|
||||
{
|
||||
// Year is 2001, the Berry Glitch is occurring
|
||||
// Set date forward to January 2, 2002
|
||||
sRtcInfoWork.year = 2;
|
||||
sRtcInfoWork.month = MONTH_JAN;
|
||||
sRtcInfoWork.day = 2;
|
||||
rtc_set_datetime(&sRtcInfoWork);
|
||||
RtcSetDateTime(&sRtcInfoWork);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rtc_is_past_feb_28_2000(&sRtcInfoWork) == TRUE)
|
||||
// Year is 2000, the Berry Glitch hasn't begun yet
|
||||
// Set the date forward 365/366 days to avoid the glitch
|
||||
if (RtcNeedsLeapDayIncrement(&sRtcInfoWork) == TRUE)
|
||||
{
|
||||
sii_rtc_inc_day(&sRtcInfoWork);
|
||||
sii_rtc_inc(&sRtcInfoWork.year);
|
||||
RtcIncrementDay(&sRtcInfoWork);
|
||||
RtcIncrement(&sRtcInfoWork.year);
|
||||
}
|
||||
else
|
||||
{
|
||||
sii_rtc_inc(&sRtcInfoWork.year);
|
||||
RtcIncrement(&sRtcInfoWork.year);
|
||||
}
|
||||
rtc_set_datetime(&sRtcInfoWork);
|
||||
RtcSetDateTime(&sRtcInfoWork);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
629
payload/src/save.c
Normal file
629
payload/src/save.c
Normal file
|
|
@ -0,0 +1,629 @@
|
|||
#include "global.h"
|
||||
#include "save.h"
|
||||
|
||||
/*
|
||||
The Berry Fix Program contains a copy of most of Ruby/Sapphire's save code.
|
||||
Much of it lies unused.
|
||||
*/
|
||||
|
||||
static u8 HandleWriteSector(u16, const struct SaveBlockChunk *);
|
||||
static u8 TryWriteSector(u8, u8 *);
|
||||
static u8 HandleReplaceSector(u16, const struct SaveBlockChunk *);
|
||||
static u8 CopySaveSlotData(u16, const struct SaveBlockChunk *);
|
||||
static u8 GetSaveValidStatus(const struct SaveBlockChunk *);
|
||||
static u32 ReadFlashSector(u8, struct SaveSector *);
|
||||
static u16 CalculateChecksum(const void *, u16);
|
||||
|
||||
u16 gLastWrittenSector;
|
||||
u32 gLastSaveCounter;
|
||||
u16 gLastKnownGoodSector;
|
||||
u32 gDamagedSaveSectors;
|
||||
u32 gSaveCounter;
|
||||
struct SaveSector * gReadWriteSector;
|
||||
u16 gIncrementalSectorId;
|
||||
bool32 gFlashIdentIsValid;
|
||||
|
||||
#define gSaveDataBuffer ((struct SaveSector *)(EWRAM_START + 0x20000))
|
||||
|
||||
EWRAM_DATA struct SaveBlock2 gSaveBlock2 = {};
|
||||
EWRAM_DATA struct SaveBlock1 gSaveBlock1 = {};
|
||||
EWRAM_DATA struct PokemonStorage gPokemonStorage = {};
|
||||
|
||||
#define SAVEBLOCK_CHUNK(structure, chunkNum) \
|
||||
{ \
|
||||
(u8 *)&structure + chunkNum * SECTOR_DATA_SIZE, \
|
||||
min(sizeof(structure) - chunkNum * SECTOR_DATA_SIZE, SECTOR_DATA_SIZE) \
|
||||
} \
|
||||
|
||||
const struct SaveBlockChunk gSaveBlockChunks[] =
|
||||
{
|
||||
SAVEBLOCK_CHUNK(gSaveBlock2, 0),
|
||||
|
||||
SAVEBLOCK_CHUNK(gSaveBlock1, 0),
|
||||
SAVEBLOCK_CHUNK(gSaveBlock1, 1),
|
||||
SAVEBLOCK_CHUNK(gSaveBlock1, 2),
|
||||
SAVEBLOCK_CHUNK(gSaveBlock1, 3),
|
||||
|
||||
SAVEBLOCK_CHUNK(gPokemonStorage, 0),
|
||||
SAVEBLOCK_CHUNK(gPokemonStorage, 1),
|
||||
SAVEBLOCK_CHUNK(gPokemonStorage, 2),
|
||||
SAVEBLOCK_CHUNK(gPokemonStorage, 3),
|
||||
SAVEBLOCK_CHUNK(gPokemonStorage, 4),
|
||||
SAVEBLOCK_CHUNK(gPokemonStorage, 5),
|
||||
SAVEBLOCK_CHUNK(gPokemonStorage, 6),
|
||||
SAVEBLOCK_CHUNK(gPokemonStorage, 7),
|
||||
SAVEBLOCK_CHUNK(gPokemonStorage, 8),
|
||||
};
|
||||
|
||||
// Unused
|
||||
static void ClearSaveData(void)
|
||||
{
|
||||
u16 i;
|
||||
for (i = 0; i < SECTORS_COUNT; i++)
|
||||
EraseFlashSector(i);
|
||||
}
|
||||
|
||||
// Unused
|
||||
static void Save_ResetSaveCounters(void)
|
||||
{
|
||||
gSaveCounter = 0;
|
||||
gLastWrittenSector = 0;
|
||||
gDamagedSaveSectors = 0;
|
||||
}
|
||||
|
||||
static bool32 SetDamagedSectorBits(u8 op, u8 sectorId)
|
||||
{
|
||||
bool32 retVal = FALSE;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case SECTOR_DAMAGED:
|
||||
gDamagedSaveSectors |= (1 << sectorId);
|
||||
break;
|
||||
case SECTOR_OK:
|
||||
gDamagedSaveSectors &= ~(1 << sectorId);
|
||||
break;
|
||||
case SECTOR_CHECK: // unused
|
||||
if (gDamagedSaveSectors & (1 << sectorId))
|
||||
retVal = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
u8 WriteSaveSectorOrSlot(u16 sectorId, const struct SaveBlockChunk *chunks)
|
||||
{
|
||||
u32 status;
|
||||
u16 i;
|
||||
|
||||
gReadWriteSector = gSaveDataBuffer;
|
||||
|
||||
if (sectorId != FULL_SAVE_SLOT)
|
||||
{
|
||||
// A sector was specified, just write that sector.
|
||||
status = HandleWriteSector(sectorId, chunks);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No sector was specified, write full save slot.
|
||||
gLastKnownGoodSector = gLastWrittenSector;
|
||||
gLastSaveCounter = gSaveCounter;
|
||||
gLastWrittenSector++;
|
||||
gLastWrittenSector %= NUM_SECTORS_PER_SLOT;
|
||||
gSaveCounter++;
|
||||
status = SAVE_STATUS_OK;
|
||||
|
||||
for (i = 0; i < NUM_SECTORS_PER_SLOT; i++)
|
||||
HandleWriteSector(i, chunks);
|
||||
|
||||
if (gDamagedSaveSectors)
|
||||
{
|
||||
// At least one sector save failed
|
||||
status = SAVE_STATUS_ERROR;
|
||||
gLastWrittenSector = gLastKnownGoodSector;
|
||||
gSaveCounter = gLastSaveCounter;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static u8 HandleWriteSector(u16 sectorId, const struct SaveBlockChunk * chunks)
|
||||
{
|
||||
u16 i;
|
||||
u16 sectorNum;
|
||||
u8 *data;
|
||||
u16 size;
|
||||
|
||||
// Adjust sector id for current save slot
|
||||
sectorNum = sectorId + gLastWrittenSector;
|
||||
sectorNum %= NUM_SECTORS_PER_SLOT;
|
||||
sectorNum += NUM_SECTORS_PER_SLOT * (gSaveCounter % NUM_SAVE_SLOTS);
|
||||
|
||||
// Get current save data
|
||||
data = chunks[sectorId].data;
|
||||
size = chunks[sectorId].size;
|
||||
|
||||
// Clear temp save sector
|
||||
for (i = 0; i < SECTOR_SIZE; i++)
|
||||
((u8 *)gReadWriteSector)[i] = 0;
|
||||
|
||||
// Set footer data
|
||||
gReadWriteSector->id = sectorId;
|
||||
gReadWriteSector->security = SECTOR_SECURITY_NUM;
|
||||
gReadWriteSector->counter = gSaveCounter;
|
||||
|
||||
// Copy current data to temp buffer for writing
|
||||
for (i = 0; i < size; i++)
|
||||
gReadWriteSector->data[i] = data[i];
|
||||
|
||||
gReadWriteSector->checksum = CalculateChecksum(data, size);
|
||||
|
||||
return TryWriteSector(sectorNum, gReadWriteSector->data);
|
||||
}
|
||||
|
||||
// Unused
|
||||
static u8 HandleWriteSectorNBytes(u8 sectorId, u8 *data, u16 size)
|
||||
{
|
||||
u16 i;
|
||||
struct SaveSector *sector = gSaveDataBuffer;
|
||||
|
||||
// Clear temp save sector
|
||||
for (i = 0; i < SECTOR_SIZE; i++)
|
||||
((u8 *)sector)[i] = 0;
|
||||
|
||||
sector->security = SECTOR_SECURITY_NUM;
|
||||
|
||||
// Copy data to temp buffer for writing
|
||||
for (i = 0; i < size; i++)
|
||||
sector->data[i] = data[i];
|
||||
|
||||
sector->id = CalculateChecksum(data, size); // though this appears to be incorrect, it might be some sector checksum instead of a whole save checksum and only appears to be relevent to HOF data, if used.
|
||||
return TryWriteSector(sectorId, sector->data);
|
||||
}
|
||||
|
||||
static u8 TryWriteSector(u8 sectorNum, u8 *data)
|
||||
{
|
||||
if (ProgramFlashSectorAndVerify(sectorNum, data) != 0) // is damaged?
|
||||
{
|
||||
// Failed
|
||||
SetDamagedSectorBits(SECTOR_DAMAGED, sectorNum);
|
||||
return SAVE_STATUS_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Succeeded
|
||||
SetDamagedSectorBits(SECTOR_OK, sectorNum);
|
||||
return SAVE_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Unused
|
||||
static u32 RestoreSaveBackupVarsAndIncrement(const struct SaveBlockChunk *chunks)
|
||||
{
|
||||
gReadWriteSector = gSaveDataBuffer;
|
||||
gLastKnownGoodSector = gLastWrittenSector;
|
||||
gLastSaveCounter = gSaveCounter;
|
||||
gLastWrittenSector++;
|
||||
gLastWrittenSector %= NUM_SECTORS_PER_SLOT;
|
||||
gSaveCounter++;
|
||||
gIncrementalSectorId = 0;
|
||||
gDamagedSaveSectors = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Unused
|
||||
static u32 RestoreSaveBackupVars(const struct SaveBlockChunk *chunks)
|
||||
{
|
||||
gReadWriteSector = gSaveDataBuffer;
|
||||
gLastKnownGoodSector = gLastWrittenSector;
|
||||
gLastSaveCounter = gSaveCounter;
|
||||
gIncrementalSectorId = 0;
|
||||
gDamagedSaveSectors = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Unused
|
||||
static u8 HandleWriteIncrementalSector(u16 numSectors, const struct SaveBlockChunk * chunks)
|
||||
{
|
||||
u8 status;
|
||||
|
||||
if (gIncrementalSectorId < numSectors - 1)
|
||||
{
|
||||
status = SAVE_STATUS_OK;
|
||||
HandleWriteSector(gIncrementalSectorId, chunks);
|
||||
gIncrementalSectorId++;
|
||||
if (gDamagedSaveSectors)
|
||||
{
|
||||
status = SAVE_STATUS_ERROR;
|
||||
gLastWrittenSector = gLastKnownGoodSector;
|
||||
gSaveCounter = gLastSaveCounter;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Exceeded max sector, finished
|
||||
status = SAVE_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// Unused
|
||||
static u8 HandleReplaceSectorAndVerify(u16 sectorId, const struct SaveBlockChunk *chunks)
|
||||
{
|
||||
u8 status = SAVE_STATUS_OK;
|
||||
|
||||
HandleReplaceSector(sectorId - 1, chunks);
|
||||
|
||||
if (gDamagedSaveSectors)
|
||||
{
|
||||
status = SAVE_STATUS_ERROR;
|
||||
gLastWrittenSector = gLastKnownGoodSector;
|
||||
gSaveCounter = gLastSaveCounter;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
// Unused
|
||||
// Similar to HandleWriteSector, but fully erases the sector first, and skips writing the first security byte
|
||||
static u8 HandleReplaceSector(u16 sectorId, const struct SaveBlockChunk *chunks)
|
||||
{
|
||||
u16 i;
|
||||
u16 sector;
|
||||
u8 *data;
|
||||
u16 size;
|
||||
u8 status;
|
||||
|
||||
// Adjust sector id for current save slot
|
||||
sector = sectorId + gLastWrittenSector;
|
||||
sector %= NUM_SECTORS_PER_SLOT;
|
||||
sector += NUM_SECTORS_PER_SLOT * (gSaveCounter % NUM_SAVE_SLOTS);
|
||||
|
||||
// Get current save data
|
||||
data = chunks[sectorId].data;
|
||||
size = chunks[sectorId].size;
|
||||
|
||||
// Clear temp save sector.
|
||||
for (i = 0; i < SECTOR_SIZE; i++)
|
||||
((u8 *)gReadWriteSector)[i] = 0;
|
||||
|
||||
gReadWriteSector->id = sectorId;
|
||||
gReadWriteSector->security = SECTOR_SECURITY_NUM;
|
||||
gReadWriteSector->counter = gSaveCounter;
|
||||
|
||||
// set temp section's data.
|
||||
for (i = 0; i < size; i++)
|
||||
gReadWriteSector->data[i] = data[i];
|
||||
|
||||
// calculate checksum.
|
||||
gReadWriteSector->checksum = CalculateChecksum(data, size);
|
||||
|
||||
EraseFlashSector(sector);
|
||||
|
||||
status = SAVE_STATUS_OK;
|
||||
|
||||
// Write new save data up to security field
|
||||
for (i = 0; i < SECTOR_SECURITY_OFFSET; i++)
|
||||
{
|
||||
if (ProgramFlashByte(sector, i, gReadWriteSector->data[i]))
|
||||
{
|
||||
status = SAVE_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (status == SAVE_STATUS_ERROR)
|
||||
{
|
||||
// Writing save data failed
|
||||
SetDamagedSectorBits(SECTOR_DAMAGED, sector);
|
||||
return SAVE_STATUS_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Writing save data succeeded, write security and counter
|
||||
status = SAVE_STATUS_OK;
|
||||
|
||||
// Write security (skipping the first byte) and counter fields.
|
||||
// The byte of security that is skipped is instead written by WriteSectorSecurityByte or WriteSectorSecurityByte_NoOffset
|
||||
for (i = 0; i < SECTOR_SIZE - (SECTOR_SECURITY_OFFSET + 1); i++)
|
||||
{
|
||||
if (ProgramFlashByte(sector, SECTOR_SECURITY_OFFSET + 1 + i, ((u8 *)gReadWriteSector)[SECTOR_SECURITY_OFFSET + 1 + i]))
|
||||
{
|
||||
status = SAVE_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (status == SAVE_STATUS_ERROR)
|
||||
{
|
||||
// Writing security/counter failed
|
||||
SetDamagedSectorBits(SECTOR_DAMAGED, sector);
|
||||
return SAVE_STATUS_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Succeeded
|
||||
SetDamagedSectorBits(SECTOR_OK, sector);
|
||||
return SAVE_STATUS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unused
|
||||
static u8 CopySectorSecurityByte(u16 sectorId, const struct SaveBlockChunk *chunks)
|
||||
{
|
||||
// Adjust sector id for current save slot
|
||||
u16 sector = sectorId + gLastWrittenSector - 1;
|
||||
sector %= NUM_SECTORS_PER_SLOT;
|
||||
sector += NUM_SECTORS_PER_SLOT * (gSaveCounter % NUM_SAVE_SLOTS);
|
||||
|
||||
// Copy just the first byte of the security field from the read/write buffer
|
||||
if (ProgramFlashByte(sector, SECTOR_SECURITY_OFFSET, ((u8 *)gReadWriteSector)[SECTOR_SECURITY_OFFSET]))
|
||||
{
|
||||
// Sector is damaged, so enable the bit in gDamagedSaveSectors and restore the last written sector and save counter.
|
||||
SetDamagedSectorBits(SECTOR_DAMAGED, sector);
|
||||
gLastWrittenSector = gLastKnownGoodSector;
|
||||
gSaveCounter = gLastSaveCounter;
|
||||
return SAVE_STATUS_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetDamagedSectorBits(SECTOR_OK, sector);
|
||||
return SAVE_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Unused
|
||||
static u8 WriteSectorSecurityByte(u16 sectorId, const struct SaveBlockChunk *chunks)
|
||||
{
|
||||
// Adjust sector id for current save slot
|
||||
u16 sector = sectorId + gLastWrittenSector - 1;
|
||||
sector %= NUM_SECTORS_PER_SLOT;
|
||||
sector += NUM_SECTORS_PER_SLOT * (gSaveCounter % 2);
|
||||
|
||||
// Write just the first byte of the security field, which was skipped by HandleReplaceSector
|
||||
if (ProgramFlashByte(sector, SECTOR_SECURITY_OFFSET, SECTOR_SECURITY_NUM & 0xFF))
|
||||
{
|
||||
// Sector is damaged, so enable the bit in gDamagedSaveSectors and restore the last written sector and save counter.
|
||||
SetDamagedSectorBits(SECTOR_DAMAGED, sector);
|
||||
gLastWrittenSector = gLastKnownGoodSector;
|
||||
gSaveCounter = gLastSaveCounter;
|
||||
return SAVE_STATUS_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Succeeded
|
||||
SetDamagedSectorBits(SECTOR_OK, sector);
|
||||
return SAVE_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
u8 TryLoadSaveSlot(u16 sectorId, const struct SaveBlockChunk *chunks)
|
||||
{
|
||||
u8 status;
|
||||
gReadWriteSector = gSaveDataBuffer;
|
||||
if (sectorId != FULL_SAVE_SLOT)
|
||||
{
|
||||
// This function may not be used with a specific sector id
|
||||
status = SAVE_STATUS_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = GetSaveValidStatus(chunks);
|
||||
CopySaveSlotData(FULL_SAVE_SLOT, chunks);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// sectorId arg is ignored, this always reads the full save slot
|
||||
static u8 CopySaveSlotData(u16 sectorId, const struct SaveBlockChunk *chunks)
|
||||
{
|
||||
u16 i;
|
||||
u16 checksum;
|
||||
u16 slotOffset = NUM_SECTORS_PER_SLOT * (gSaveCounter % NUM_SAVE_SLOTS);
|
||||
u16 id;
|
||||
|
||||
for (i = 0; i < NUM_SECTORS_PER_SLOT; i++)
|
||||
{
|
||||
ReadFlashSector(i + slotOffset, gReadWriteSector);
|
||||
|
||||
id = gReadWriteSector->id;
|
||||
if (id == 0)
|
||||
gLastWrittenSector = i;
|
||||
|
||||
checksum = CalculateChecksum(gReadWriteSector->data, chunks[id].size);
|
||||
|
||||
// Only copy data for sectors whose security and checksum fields are correct
|
||||
if (gReadWriteSector->security == SECTOR_SECURITY_NUM && gReadWriteSector->checksum == checksum)
|
||||
{
|
||||
u16 j;
|
||||
for (j = 0; j < chunks[id].size; j++)
|
||||
chunks[id].data[j] = gReadWriteSector->data[j];
|
||||
}
|
||||
}
|
||||
|
||||
return SAVE_STATUS_OK;
|
||||
}
|
||||
|
||||
static u8 GetSaveValidStatus(const struct SaveBlockChunk *chunks)
|
||||
{
|
||||
u16 i;
|
||||
bool8 securityPassed;
|
||||
u16 checksum;
|
||||
u32 slot1saveCounter = 0;
|
||||
u32 slot2saveCounter = 0;
|
||||
u8 slot1Status;
|
||||
u8 slot2Status;
|
||||
u32 validSectors;
|
||||
const u32 ALL_SECTORS = (1 << NUM_SECTORS_PER_SLOT) - 1; // bitmask of all saveblock sectors
|
||||
|
||||
// Check save slot 1
|
||||
validSectors = 0;
|
||||
securityPassed = FALSE;
|
||||
for (i = 0; i < NUM_SECTORS_PER_SLOT; i++)
|
||||
{
|
||||
ReadFlashSector(i, gReadWriteSector);
|
||||
if (gReadWriteSector->security == SECTOR_SECURITY_NUM)
|
||||
{
|
||||
securityPassed = TRUE;
|
||||
checksum = CalculateChecksum(gReadWriteSector->data, chunks[gReadWriteSector->id].size);
|
||||
if (gReadWriteSector->checksum == checksum)
|
||||
{
|
||||
slot1saveCounter = gReadWriteSector->counter;
|
||||
validSectors |= 1 << gReadWriteSector->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (securityPassed)
|
||||
{
|
||||
if (validSectors == ALL_SECTORS)
|
||||
slot1Status = SAVE_STATUS_OK;
|
||||
else
|
||||
slot1Status = SAVE_STATUS_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No sectors in slot 1 have the security number, treat it as empty
|
||||
slot1Status = SAVE_STATUS_EMPTY;
|
||||
}
|
||||
|
||||
// Check save slot 2
|
||||
validSectors = 0;
|
||||
securityPassed = FALSE;
|
||||
for (i = 0; i < NUM_SECTORS_PER_SLOT; i++)
|
||||
{
|
||||
ReadFlashSector(NUM_SECTORS_PER_SLOT + i, gReadWriteSector);
|
||||
if (gReadWriteSector->security == SECTOR_SECURITY_NUM)
|
||||
{
|
||||
securityPassed = TRUE;
|
||||
checksum = CalculateChecksum(gReadWriteSector->data, chunks[gReadWriteSector->id].size);
|
||||
if (gReadWriteSector->checksum == checksum)
|
||||
{
|
||||
slot2saveCounter = gReadWriteSector->counter;
|
||||
validSectors |= 1 << gReadWriteSector->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (securityPassed)
|
||||
{
|
||||
if (validSectors == ALL_SECTORS)
|
||||
slot2Status = SAVE_STATUS_OK;
|
||||
else
|
||||
slot2Status = SAVE_STATUS_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No sectors in slot 2 have the security number, treat it as empty.
|
||||
slot2Status = SAVE_STATUS_EMPTY;
|
||||
}
|
||||
|
||||
if (slot1Status == SAVE_STATUS_OK && slot2Status == SAVE_STATUS_OK)
|
||||
{
|
||||
// Choose counter of the most recent save file
|
||||
if ((slot1saveCounter == -1 && slot2saveCounter == 0)
|
||||
|| (slot1saveCounter == 0 && slot2saveCounter == -1))
|
||||
{
|
||||
if ((unsigned)(slot1saveCounter + 1) < (unsigned)(slot2saveCounter + 1))
|
||||
gSaveCounter = slot2saveCounter;
|
||||
else
|
||||
gSaveCounter = slot1saveCounter;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (slot1saveCounter < slot2saveCounter)
|
||||
gSaveCounter = slot2saveCounter;
|
||||
else
|
||||
gSaveCounter = slot1saveCounter;
|
||||
}
|
||||
return SAVE_STATUS_OK;
|
||||
}
|
||||
|
||||
// One or both save slots are not OK
|
||||
|
||||
if (slot1Status == SAVE_STATUS_OK)
|
||||
{
|
||||
gSaveCounter = slot1saveCounter;
|
||||
if (slot2Status == SAVE_STATUS_ERROR)
|
||||
return SAVE_STATUS_ERROR; // Slot 2 errored
|
||||
else
|
||||
return SAVE_STATUS_OK; // Slot 1 is OK, slot 2 is empty
|
||||
}
|
||||
|
||||
if (slot2Status == SAVE_STATUS_OK)
|
||||
{
|
||||
gSaveCounter = slot2saveCounter;
|
||||
if (slot1Status == SAVE_STATUS_ERROR)
|
||||
return SAVE_STATUS_ERROR; // Slot 1 errored
|
||||
else
|
||||
return SAVE_STATUS_OK; // Slot 2 is OK, slot 1 is empty
|
||||
}
|
||||
|
||||
// Neither slot is OK, check if both are empty
|
||||
if (slot1Status == SAVE_STATUS_EMPTY
|
||||
&& slot2Status == SAVE_STATUS_EMPTY)
|
||||
{
|
||||
gSaveCounter = 0;
|
||||
gLastWrittenSector = 0;
|
||||
return SAVE_STATUS_EMPTY;
|
||||
}
|
||||
|
||||
// Both slots errored
|
||||
gSaveCounter = 0;
|
||||
gLastWrittenSector = 0;
|
||||
return SAVE_STATUS_CORRUPT;
|
||||
}
|
||||
|
||||
// Unused
|
||||
static u8 TryLoadSaveSector(u8 sectorId, u8 *data, u16 size)
|
||||
{
|
||||
u16 i;
|
||||
struct SaveSector *sector = gSaveDataBuffer;
|
||||
ReadFlashSector(sectorId, sector);
|
||||
if (sector->security == SECTOR_SECURITY_NUM)
|
||||
{
|
||||
u16 checksum = CalculateChecksum(sector->data, size);
|
||||
if (sector->id == checksum)
|
||||
{
|
||||
// Security and checksum are correct, copy data
|
||||
for (i = 0; i < size; i++)
|
||||
data[i] = sector->data[i];
|
||||
return SAVE_STATUS_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Incorrect checksum
|
||||
return SAVE_STATUS_CORRUPT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Incorrect security value
|
||||
return SAVE_STATUS_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
// Return value always ignored
|
||||
static u32 ReadFlashSector(u8 sectorId, struct SaveSector *sector)
|
||||
{
|
||||
ReadFlash(sectorId, 0, sector->data, SECTOR_SIZE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static u16 CalculateChecksum(const void *data, u16 size)
|
||||
{
|
||||
u16 i;
|
||||
u32 checksum = 0;
|
||||
|
||||
for (i = 0; i < (size / 4); i++)
|
||||
{
|
||||
checksum += *((u32 *)data);
|
||||
data += sizeof(u32);
|
||||
}
|
||||
|
||||
return ((checksum >> 16) + checksum);
|
||||
}
|
||||
|
|
@ -46,6 +46,19 @@
|
|||
#define CMD_TIME CMD(3)
|
||||
#define CMD_ALARM CMD(4)
|
||||
|
||||
#define SCK_HI 1
|
||||
#define SIO_HI 2
|
||||
#define CS_HI 4
|
||||
|
||||
#define DIR_0_IN 0
|
||||
#define DIR_0_OUT 1
|
||||
#define DIR_1_IN 0
|
||||
#define DIR_1_OUT 2
|
||||
#define DIR_2_IN 0
|
||||
#define DIR_2_OUT 4
|
||||
#define DIR_ALL_IN (DIR_0_IN | DIR_1_IN | DIR_2_IN)
|
||||
#define DIR_ALL_OUT (DIR_0_OUT | DIR_1_OUT | DIR_2_OUT)
|
||||
|
||||
#define GPIO_PORT_DATA (*(vu16 *)0x80000C4)
|
||||
#define GPIO_PORT_DIRECTION (*(vu16 *)0x80000C6)
|
||||
#define GPIO_PORT_READ_ENABLE (*(vu16 *)0x80000C8)
|
||||
|
|
@ -58,24 +71,25 @@ static bool8 sLocked;
|
|||
static int WriteCommand(u8 value);
|
||||
static int WriteData(u8 value);
|
||||
static u8 ReadData();
|
||||
|
||||
static void EnableGpioPortRead();
|
||||
static void DisableGpioPortRead();
|
||||
|
||||
static const char AgbLibRtcVersion[] = "SIIRTC_V001";
|
||||
|
||||
void SiiRtcUnprotect()
|
||||
void SiiRtcUnprotect(void)
|
||||
{
|
||||
EnableGpioPortRead();
|
||||
sLocked = FALSE;
|
||||
}
|
||||
|
||||
void SiiRtcProtect()
|
||||
void SiiRtcProtect(void)
|
||||
{
|
||||
DisableGpioPortRead();
|
||||
sLocked = TRUE;
|
||||
}
|
||||
|
||||
u8 SiiRtcProbe()
|
||||
u8 SiiRtcProbe(void)
|
||||
{
|
||||
u8 errorCode;
|
||||
struct SiiRtcInfo rtc;
|
||||
|
|
@ -116,9 +130,9 @@ u8 SiiRtcProbe()
|
|||
return (errorCode << 4) | 1;
|
||||
}
|
||||
|
||||
bool8 SiiRtcReset()
|
||||
bool8 SiiRtcReset(void)
|
||||
{
|
||||
u8 result;
|
||||
bool8 result;
|
||||
struct SiiRtcInfo rtc;
|
||||
|
||||
if (sLocked == TRUE)
|
||||
|
|
@ -126,15 +140,15 @@ bool8 SiiRtcReset()
|
|||
|
||||
sLocked = TRUE;
|
||||
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = 5;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
GPIO_PORT_DATA = SCK_HI | CS_HI;
|
||||
|
||||
GPIO_PORT_DIRECTION = 7;
|
||||
GPIO_PORT_DIRECTION = DIR_ALL_OUT;
|
||||
|
||||
WriteCommand(CMD_RESET | WR);
|
||||
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
|
||||
sLocked = FALSE;
|
||||
|
||||
|
|
@ -154,14 +168,14 @@ bool8 SiiRtcGetStatus(struct SiiRtcInfo *rtc)
|
|||
|
||||
sLocked = TRUE;
|
||||
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = 5;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
GPIO_PORT_DATA = SCK_HI | CS_HI;
|
||||
|
||||
GPIO_PORT_DIRECTION = 7;
|
||||
GPIO_PORT_DIRECTION = DIR_ALL_OUT;
|
||||
|
||||
WriteCommand(CMD_STATUS | RD);
|
||||
|
||||
GPIO_PORT_DIRECTION = 5;
|
||||
GPIO_PORT_DIRECTION = DIR_0_OUT | DIR_1_IN | DIR_2_OUT;
|
||||
|
||||
statusData = ReadData();
|
||||
|
||||
|
|
@ -170,8 +184,8 @@ bool8 SiiRtcGetStatus(struct SiiRtcInfo *rtc)
|
|||
| ((statusData & STATUS_INTME) >> 2)
|
||||
| ((statusData & STATUS_INTFE) >> 1);
|
||||
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
|
||||
sLocked = FALSE;
|
||||
|
||||
|
|
@ -187,22 +201,22 @@ bool8 SiiRtcSetStatus(struct SiiRtcInfo *rtc)
|
|||
|
||||
sLocked = TRUE;
|
||||
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = 5;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
GPIO_PORT_DATA = SCK_HI | CS_HI;
|
||||
|
||||
statusData = STATUS_24HOUR
|
||||
| ((rtc->status & SIIRTCINFO_INTAE) << 3)
|
||||
| ((rtc->status & SIIRTCINFO_INTME) << 2)
|
||||
| ((rtc->status & SIIRTCINFO_INTFE) << 1);
|
||||
|
||||
GPIO_PORT_DIRECTION = 7;
|
||||
GPIO_PORT_DIRECTION = DIR_ALL_OUT;
|
||||
|
||||
WriteCommand(CMD_STATUS | WR);
|
||||
|
||||
WriteData(statusData);
|
||||
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
|
||||
sLocked = FALSE;
|
||||
|
||||
|
|
@ -218,22 +232,22 @@ bool8 SiiRtcGetDateTime(struct SiiRtcInfo *rtc)
|
|||
|
||||
sLocked = TRUE;
|
||||
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = 5;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
GPIO_PORT_DATA = SCK_HI | CS_HI;
|
||||
|
||||
GPIO_PORT_DIRECTION = 7;
|
||||
GPIO_PORT_DIRECTION = DIR_ALL_OUT;
|
||||
|
||||
WriteCommand(CMD_DATETIME | RD);
|
||||
|
||||
GPIO_PORT_DIRECTION = 5;
|
||||
GPIO_PORT_DIRECTION = DIR_0_OUT | DIR_1_IN | DIR_2_OUT;
|
||||
|
||||
for (i = 0; i < DATETIME_BUF_LEN; i++)
|
||||
DATETIME_BUF(rtc, i) = ReadData();
|
||||
|
||||
INFO_BUF(rtc, OFFSET_HOUR) &= 0x7F;
|
||||
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
|
||||
sLocked = FALSE;
|
||||
|
||||
|
|
@ -249,18 +263,18 @@ bool8 SiiRtcSetDateTime(struct SiiRtcInfo *rtc)
|
|||
|
||||
sLocked = TRUE;
|
||||
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = 5;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
GPIO_PORT_DATA = SCK_HI | CS_HI;
|
||||
|
||||
GPIO_PORT_DIRECTION = 7;
|
||||
GPIO_PORT_DIRECTION = DIR_ALL_OUT;
|
||||
|
||||
WriteCommand(CMD_DATETIME | WR);
|
||||
|
||||
for (i = 0; i < DATETIME_BUF_LEN; i++)
|
||||
WriteData(DATETIME_BUF(rtc, i));
|
||||
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
|
||||
sLocked = FALSE;
|
||||
|
||||
|
|
@ -276,22 +290,22 @@ bool8 SiiRtcGetTime(struct SiiRtcInfo *rtc)
|
|||
|
||||
sLocked = TRUE;
|
||||
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = 5;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
GPIO_PORT_DATA = SCK_HI | CS_HI;
|
||||
|
||||
GPIO_PORT_DIRECTION = 7;
|
||||
GPIO_PORT_DIRECTION = DIR_ALL_OUT;
|
||||
|
||||
WriteCommand(CMD_TIME | RD);
|
||||
|
||||
GPIO_PORT_DIRECTION = 5;
|
||||
GPIO_PORT_DIRECTION = DIR_0_OUT | DIR_1_IN | DIR_2_OUT;
|
||||
|
||||
for (i = 0; i < TIME_BUF_LEN; i++)
|
||||
TIME_BUF(rtc, i) = ReadData();
|
||||
|
||||
INFO_BUF(rtc, OFFSET_HOUR) &= 0x7F;
|
||||
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
|
||||
sLocked = FALSE;
|
||||
|
||||
|
|
@ -307,18 +321,18 @@ bool8 SiiRtcSetTime(struct SiiRtcInfo *rtc)
|
|||
|
||||
sLocked = TRUE;
|
||||
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = 5;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
GPIO_PORT_DATA = SCK_HI | CS_HI;
|
||||
|
||||
GPIO_PORT_DIRECTION = 7;
|
||||
GPIO_PORT_DIRECTION = DIR_ALL_OUT;
|
||||
|
||||
WriteCommand(CMD_TIME | WR);
|
||||
|
||||
for (i = 0; i < TIME_BUF_LEN; i++)
|
||||
WriteData(TIME_BUF(rtc, i));
|
||||
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
|
||||
sLocked = FALSE;
|
||||
|
||||
|
|
@ -347,18 +361,18 @@ bool8 SiiRtcSetAlarm(struct SiiRtcInfo *rtc)
|
|||
|
||||
alarmData[1] = rtc->alarmMinute;
|
||||
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = 5;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
GPIO_PORT_DATA = SCK_HI | CS_HI;
|
||||
|
||||
GPIOPortDirection = 7; // Why is this the only instance that uses a symbol?
|
||||
GPIOPortDirection = DIR_ALL_OUT; // Why is this the only instance that uses a symbol?
|
||||
|
||||
WriteCommand(CMD_ALARM | WR);
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
WriteData(alarmData[i]);
|
||||
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = 1;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
GPIO_PORT_DATA = SCK_HI;
|
||||
|
||||
sLocked = FALSE;
|
||||
|
||||
|
|
@ -373,13 +387,17 @@ static int WriteCommand(u8 value)
|
|||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
temp = ((value >> (7 - i)) & 1);
|
||||
GPIO_PORT_DATA = (temp << 1) | 4;
|
||||
GPIO_PORT_DATA = (temp << 1) | 4;
|
||||
GPIO_PORT_DATA = (temp << 1) | 4;
|
||||
GPIO_PORT_DATA = (temp << 1) | 5;
|
||||
GPIO_PORT_DATA = (temp << 1) | CS_HI;
|
||||
GPIO_PORT_DATA = (temp << 1) | CS_HI;
|
||||
GPIO_PORT_DATA = (temp << 1) | CS_HI;
|
||||
GPIO_PORT_DATA = (temp << 1) | SCK_HI | CS_HI;
|
||||
}
|
||||
|
||||
// control reaches end of non-void function
|
||||
// Nothing uses the returned value from this function,
|
||||
// so the undefined behavior is harmless in the vanilla game.
|
||||
#ifdef UBFIX
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int WriteData(u8 value)
|
||||
|
|
@ -390,13 +408,17 @@ static int WriteData(u8 value)
|
|||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
temp = ((value >> i) & 1);
|
||||
GPIO_PORT_DATA = (temp << 1) | 4;
|
||||
GPIO_PORT_DATA = (temp << 1) | 4;
|
||||
GPIO_PORT_DATA = (temp << 1) | 4;
|
||||
GPIO_PORT_DATA = (temp << 1) | 5;
|
||||
GPIO_PORT_DATA = (temp << 1) | CS_HI;
|
||||
GPIO_PORT_DATA = (temp << 1) | CS_HI;
|
||||
GPIO_PORT_DATA = (temp << 1) | CS_HI;
|
||||
GPIO_PORT_DATA = (temp << 1) | SCK_HI | CS_HI;
|
||||
}
|
||||
|
||||
// control reaches end of non-void function
|
||||
// Nothing uses the returned value from this function,
|
||||
// so the undefined behavior is harmless in the vanilla game.
|
||||
#ifdef UBFIX
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static u8 ReadData()
|
||||
|
|
@ -405,17 +427,21 @@ static u8 ReadData()
|
|||
u8 temp;
|
||||
u8 value;
|
||||
|
||||
#ifdef UBFIX
|
||||
value = 0;
|
||||
#endif
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
GPIO_PORT_DATA = 4;
|
||||
GPIO_PORT_DATA = 4;
|
||||
GPIO_PORT_DATA = 4;
|
||||
GPIO_PORT_DATA = 4;
|
||||
GPIO_PORT_DATA = 4;
|
||||
GPIO_PORT_DATA = 5;
|
||||
GPIO_PORT_DATA = CS_HI;
|
||||
GPIO_PORT_DATA = CS_HI;
|
||||
GPIO_PORT_DATA = CS_HI;
|
||||
GPIO_PORT_DATA = CS_HI;
|
||||
GPIO_PORT_DATA = CS_HI;
|
||||
GPIO_PORT_DATA = SCK_HI | CS_HI;
|
||||
|
||||
temp = ((GPIO_PORT_DATA & 2) >> 1);
|
||||
value = (value >> 1) | (temp << 7); // UB: accessing uninitialized var
|
||||
temp = ((GPIO_PORT_DATA & SIO_HI) >> 1);
|
||||
value = (value >> 1) | (temp << 7);
|
||||
}
|
||||
|
||||
return value;
|
||||
|
|
@ -423,10 +449,10 @@ static u8 ReadData()
|
|||
|
||||
static void EnableGpioPortRead()
|
||||
{
|
||||
GPIO_PORT_READ_ENABLE = 1;
|
||||
GPIO_PORT_READ_ENABLE = TRUE;
|
||||
}
|
||||
|
||||
static void DisableGpioPortRead()
|
||||
{
|
||||
GPIO_PORT_READ_ENABLE = 0;
|
||||
GPIO_PORT_READ_ENABLE = FALSE;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
.include "src/main.o"
|
||||
.include "src/rtc.o"
|
||||
.include "src/flash.o"
|
||||
.include "src/agb_flash.o"
|
||||
.include "src/save.o"
|
||||
.include "src/agb_flash.o"
|
||||
.include "src/siirtc.o"
|
||||
|
|
|
|||
|
|
@ -1,29 +1,4 @@
|
|||
.include "main.o"
|
||||
.include "rtc.o"
|
||||
|
||||
.align 4
|
||||
gFirstSaveSector: @ 0x03001220
|
||||
.space 0x4
|
||||
|
||||
gPrevSaveCounter: @ 0x03001224
|
||||
.space 0x4
|
||||
|
||||
gLastKnownGoodSector: @ 0x03001228
|
||||
.space 0x4
|
||||
|
||||
gDamagedSaveSectors: @ 0x0300122C
|
||||
.space 0x4
|
||||
|
||||
gSaveCounter: @ 0x03001230
|
||||
.space 0x4
|
||||
|
||||
gFastSaveSection: @ 0x03001234
|
||||
.space 0x4
|
||||
|
||||
gCurSaveChunk:
|
||||
.space 0x4
|
||||
|
||||
gFlashIdentIsValid: @ 0x0300123C
|
||||
.space 0x4
|
||||
|
||||
.include "agb_flash.o"
|
||||
.include "save.o"
|
||||
.include "agb_flash.o"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
.include "src/main.o"
|
||||
.include "src/rtc.o"
|
||||
.include "src/flash.o"
|
||||
.include "src/save.o"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user