#ifndef SAVE_H
#define SAVE_H

#include "ultra64.h"
#include "versions.h"
#include "inventory.h"
#include "z_math.h"

typedef enum ZTargetSetting {
    /* 0 */ Z_TARGET_SETTING_SWITCH,
    /* 1 */ Z_TARGET_SETTING_HOLD
} ZTargetSetting;

typedef enum Language {
#if OOT_NTSC
    /* 0 */ LANGUAGE_JPN,
    /* 1 */ LANGUAGE_ENG,
#else
    /* 0 */ LANGUAGE_ENG,
    /* 1 */ LANGUAGE_GER,
    /* 2 */ LANGUAGE_FRA,
#endif
    /*   */ LANGUAGE_MAX
} Language;

// `_FORCE` means that this request will respond to `forceRisingButtonAlphas`.
// If set, the buttons will also raise alphas but will also account for disabled buttons

typedef enum HudVisibilityMode {
    /*  0 */ HUD_VISIBILITY_NO_CHANGE,
    /*  1 */ HUD_VISIBILITY_NOTHING,
    /*  2 */ HUD_VISIBILITY_NOTHING_ALT, // Identical to HUD_VISIBILITY_NOTHING
    /*  3 */ HUD_VISIBILITY_HEARTS_FORCE, // See above
    /*  4 */ HUD_VISIBILITY_A,
    /*  5 */ HUD_VISIBILITY_A_HEARTS_MAGIC_FORCE, // See above
    /*  6 */ HUD_VISIBILITY_A_HEARTS_MAGIC_MINIMAP_FORCE, // See above
    /*  7 */ HUD_VISIBILITY_ALL_NO_MINIMAP_BY_BTN_STATUS, // Only raises button alphas if not disabled
    /*  8 */ HUD_VISIBILITY_B,
    /*  9 */ HUD_VISIBILITY_HEARTS_MAGIC,
    /* 10 */ HUD_VISIBILITY_B_ALT, // Identical to HUD_VISIBILITY_B
    /* 11 */ HUD_VISIBILITY_HEARTS,
    /* 12 */ HUD_VISIBILITY_A_B_MINIMAP,
    /* 13 */ HUD_VISIBILITY_HEARTS_MAGIC_FORCE, // See above
    /* 50 */ HUD_VISIBILITY_ALL = 50, // Only raises button alphas if not disabled
    /* 52 */ HUD_VISIBILITY_NOTHING_INSTANT = 52
} HudVisibilityMode;

typedef enum MagicState {
    /* 0x0 */ MAGIC_STATE_IDLE, // Regular gameplay
    /* 0x1 */ MAGIC_STATE_CONSUME_SETUP, // Sets the speed at which magic border flashes
    /* 0x2 */ MAGIC_STATE_CONSUME, // Consume magic until target is reached or no more magic is available
    /* 0x3 */ MAGIC_STATE_METER_FLASH_1, // Flashes border and freezes Dark Link
    /* 0x4 */ MAGIC_STATE_METER_FLASH_2, // Flashes border and draws yellow magic to preview target consumption
    /* 0x5 */ MAGIC_STATE_RESET, // Reset colors and return to idle
    /* 0x6 */ MAGIC_STATE_METER_FLASH_3, // Flashes border with no additional behaviour
    /* 0x7 */ MAGIC_STATE_CONSUME_LENS, // Magic slowly consumed by lens.
    /* 0x8 */ MAGIC_STATE_STEP_CAPACITY, // Step `magicCapacity` to full capacity
    /* 0x9 */ MAGIC_STATE_FILL, // Add magic until magicFillTarget is reached.
    /* 0xA */ MAGIC_STATE_ADD // Add requested magic
} MagicState;

typedef enum MagicChangeType {
    /* 0 */ MAGIC_CONSUME_NOW, // Consume Magic immediately without preview
    /* 1 */ MAGIC_CONSUME_WAIT_NO_PREVIEW, // Sets consume target but waits to consume. No yellow magic preview to target consumption. Unused
    /* 2 */ MAGIC_CONSUME_NOW_ALT, // Identical behaviour to MAGIC_CONSUME_NOW. Unused
    /* 3 */ MAGIC_CONSUME_LENS, // Lens consumption
    /* 4 */ MAGIC_CONSUME_WAIT_PREVIEW, // Sets consume target but waits to consume. Draws yellow magic to target consumption
    /* 5 */ MAGIC_ADD // Sets a target to add magic
} MagicChangeType;

#define MAGIC_NORMAL_METER 0x30
#define MAGIC_DOUBLE_METER (2 * MAGIC_NORMAL_METER)

typedef struct ItemEquips {
    /* 0x00 */ u8 buttonItems[4];
    /* 0x04 */ u8 cButtonSlots[3];
    /* 0x08 */ u16 equipment; // a mask where each nibble corresponds to a type of equipment `EquipmentType`, and each nibble is a piece `EquipValue*`
} ItemEquips; // size = 0x0A

typedef struct Inventory {
    /* 0x00 */ u8 items[24];
    /* 0x18 */ s8 ammo[16];
    /* 0x28 */ u16 equipment; // a mask where each nibble corresponds to a type of equipment `EquipmentType`, and each bit to an owned piece `EquipInv*`
    /* 0x2C */ u32 upgrades;
    /* 0x30 */ u32 questItems;
    /* 0x34 */ u8 dungeonItems[20];
    /* 0x48 */ s8 dungeonKeys[19];
    /* 0x5B */ s8 defenseHearts;
    /* 0x5C */ s16 gsTokens;
} Inventory; // size = 0x5E

typedef struct Checksum {
    /* 0x00 */ u16 value;
} Checksum; // size = 0x02

typedef struct SavedSceneFlags {
    /* 0x00 */ u32 chest;
    /* 0x04 */ u32 swch;
    /* 0x08 */ u32 clear;
    /* 0x0C */ u32 collect;
    /* 0x10 */ u32 unk;
    /* 0x14 */ u32 rooms;
    /* 0x18 */ u32 floors;
} SavedSceneFlags; // size = 0x1C

typedef struct HorseData {
    /* 0x00 */ s16 sceneId;
    /* 0x02 */ Vec3s pos;
    /* 0x08 */ s16 angle;
} HorseData; // size = 0x0A

/**
 * The respawn mode names refer to the perceived player movement when respawning
 * "down": being on ground
 * "return": coming from the ground
 * "top": coming from the air
 */
typedef enum RespawnMode {
    /* 0x00 */ RESPAWN_MODE_DOWN,   /* Normal Void Outs */
    /* 0x01 */ RESPAWN_MODE_RETURN, /* Grotto Returnpoints */
    /* 0x02 */ RESPAWN_MODE_TOP,    /* Farore's Wind */
    /* 0x03 */ RESPAWN_MODE_MAX
} RespawnMode;

typedef struct RespawnData {
    /* 0x00 */ Vec3f pos;
    /* 0x0C */ s16 yaw;
    /* 0x0E */ s16 playerParams;
    /* 0x10 */ s16 entranceIndex;
    /* 0x12 */ u8 roomIndex;
    /* 0x13 */ s8 data;
    /* 0x14 */ u32 tempSwchFlags;
    /* 0x18 */ u32 tempCollectFlags;
} RespawnData; // size = 0x1C

typedef struct FaroresWindData {
    /* 0x00 */ Vec3i pos;
    /* 0x0C */ s32 yaw;
    /* 0x10 */ s32 playerParams;
    /* 0x14 */ s32 entranceIndex;
    /* 0x18 */ s32 roomIndex;
    /* 0x1C */ s32 set;
    /* 0x20 */ s32 tempSwchFlags;
    /* 0x24 */ s32 tempCollectFlags;
} FaroresWindData; // size = 0x28

typedef enum TimerState {
    /* 0x0 */ TIMER_STATE_OFF,
    /* 0x1 */ TIMER_STATE_ENV_HAZARD_INIT, // Init env timer that counts down, total time based on health, resets on void-out, kills at 0
    /* 0x2 */ TIMER_STATE_ENV_HAZARD_PREVIEW, // Display initial time, keep it fixed at the screen center
    /* 0x3 */ TIMER_STATE_ENV_HAZARD_MOVE, // Move to top-left corner
    /* 0x4 */ TIMER_STATE_ENV_HAZARD_TICK, // Counting down
    /* 0x5 */ TIMER_STATE_DOWN_INIT, // Init timer that counts down
    /* 0x6 */ TIMER_STATE_DOWN_PREVIEW, // Display initial time, keep it fixed at the screen center
    /* 0x7 */ TIMER_STATE_DOWN_MOVE, // Move to top-left corner
    /* 0x8 */ TIMER_STATE_DOWN_TICK, // Counting down
    /* 0xA */ TIMER_STATE_STOP = 10,
    /* 0xB */ TIMER_STATE_UP_INIT, // Init timer that counts up
    /* 0xC */ TIMER_STATE_UP_PREVIEW, // Display initial time, keep it fixed at the screen center
    /* 0xD */ TIMER_STATE_UP_MOVE, // Move to top-left corner
    /* 0xE */ TIMER_STATE_UP_TICK, // Counting up
    /* 0xF */ TIMER_STATE_UP_FREEZE  // Stop counting the timer
} TimerState;

typedef enum SubTimerState {
    /* 0x0 */ SUBTIMER_STATE_OFF,
    /* 0x1 */ SUBTIMER_STATE_DOWN_INIT, // Init timer that counts down
    /* 0x2 */ SUBTIMER_STATE_DOWN_PREVIEW, // Display initial time, keep it fixed at the screen center
    /* 0x3 */ SUBTIMER_STATE_DOWN_MOVE, // Move to top-left corner
    /* 0x4 */ SUBTIMER_STATE_DOWN_TICK, // Counting down
    /* 0x5 */ SUBTIMER_STATE_RESPAWN, // Time is up, trigger a transition, reset button items, spoil trade quest items
    /* 0x6 */ SUBTIMER_STATE_STOP, // Time is up, stop counting
    /* 0x7 */ SUBTIMER_STATE_UP_INIT, // Init timer that counts up
    /* 0x8 */ SUBTIMER_STATE_UP_PREVIEW, // Display initial time, keep it fixed at the screen center
    /* 0x9 */ SUBTIMER_STATE_UP_MOVE, // Move to top-left corner
    /* 0xA */ SUBTIMER_STATE_UP_TICK // Counting up
} SubTimerState;

typedef enum TimerId {
    /* 0 */ TIMER_ID_MAIN, // Takes priority in both counting and drawing. See `timerState` and `timerSeconds`
    /* 1 */ TIMER_ID_SUB, // See `subTimerState` and `subTimerSeconds`
    /* 2 */ TIMER_ID_MAX
} TimerId;

#define MARATHON_TIME_LIMIT 240 // 4 minutes

#define ENV_HAZARD_TEXT_TRIGGER_HOTROOM (1 << 0)
#define ENV_HAZARD_TEXT_TRIGGER_UNDERWATER (1 << 1)

typedef enum WorldMapArea {
    /*  0 */ WORLD_MAP_AREA_HYRULE_FIELD,
    /*  1 */ WORLD_MAP_AREA_KAKARIKO_VILLAGE,
    /*  2 */ WORLD_MAP_AREA_GRAVEYARD,
    /*  3 */ WORLD_MAP_AREA_ZORAS_RIVER,
    /*  4 */ WORLD_MAP_AREA_KOKIRI_FOREST,
    /*  5 */ WORLD_MAP_AREA_SACRED_FOREST_MEADOW,
    /*  6 */ WORLD_MAP_AREA_LAKE_HYLIA,
    /*  7 */ WORLD_MAP_AREA_ZORAS_DOMAIN,
    /*  8 */ WORLD_MAP_AREA_ZORAS_FOUNTAIN,
    /*  9 */ WORLD_MAP_AREA_GERUDO_VALLEY,
    /* 10 */ WORLD_MAP_AREA_LOST_WOODS,
    /* 11 */ WORLD_MAP_AREA_DESERT_COLOSSUS,
    /* 12 */ WORLD_MAP_AREA_GERUDOS_FORTRESS,
    /* 13 */ WORLD_MAP_AREA_HAUNTED_WASTELAND,
    /* 14 */ WORLD_MAP_AREA_MARKET,
    /* 15 */ WORLD_MAP_AREA_HYRULE_CASTLE,
    /* 16 */ WORLD_MAP_AREA_DEATH_MOUNTAIN_TRAIL,
    /* 17 */ WORLD_MAP_AREA_DEATH_MOUNTAIN_CRATER,
    /* 18 */ WORLD_MAP_AREA_GORON_CITY,
    /* 19 */ WORLD_MAP_AREA_LON_LON_RANCH,
    /* 20 */ WORLD_MAP_AREA_QUESTION_MARK, // Windmill/Dampé's Grave (and a Lon Lon Ranch cutscene). Labeled as "?"
    /* 21 */ WORLD_MAP_AREA_GANONS_CASTLE,
    /* 22 */ WORLD_MAP_AREA_MAX // also grottos and fairy's/great fairy's fountains (scenes with varying locations)
} WorldMapArea;

// offsets in SavePlayerData and SaveContext/Save
typedef struct SavePlayerData {
    /* 0x00  0x001C */ char newf[6]; // string "ZELDAZ"
    /* 0x06  0x0022 */ u16 deaths;
    /* 0x08  0x0024 */ u8 playerName[8];
    /* 0x10  0x002C */ s16 n64ddFlag;
    /* 0x12  0x002E */ s16 healthCapacity; // "max_life"
    /* 0x14  0x0030 */ s16 health; // "now_life"
    /* 0x16  0x0032 */ s8 magicLevel; // 0 for no magic/new load, 1 for magic, 2 for double magic
    /* 0x17  0x0033 */ s8 magic; // current magic available for use
    /* 0x18  0x0034 */ s16 rupees;
    /* 0x1A  0x0036 */ u16 swordHealth;
    /* 0x1C  0x0038 */ u16 naviTimer;
    /* 0x1E  0x003A */ u8 isMagicAcquired;
    /* 0x1F  0x003B */ char unk_3B[0x01];
    /* 0x20  0x003C */ u8 isDoubleMagicAcquired;
    /* 0x21  0x003D */ u8 isDoubleDefenseAcquired;
    /* 0x22  0x003E */ u8 bgsFlag;
    /* 0x23  0x003F */ u8 ocarinaGameRoundNum;
    /* 0x24  0x0040 */ ItemEquips childEquips;
    /* 0x2E  0x004A */ ItemEquips adultEquips;
    /* 0x38  0x0054 */ u32 unk_54; // this may be incorrect, currently used for alignment
    /* 0x3C  0x0058 */ char unk_58[0x0E];
    /* 0x4A  0x0066 */ s16 savedSceneId;
} SavePlayerData;

// offsets in SaveInfo and SaveContext/Save
typedef struct SaveInfo {
    /* 0x0000  0x001C */ SavePlayerData playerData; // "S_Private"
    /* 0x004C  0x0068 */ ItemEquips equips;
    /* 0x0058  0x0074 */ Inventory inventory;
    /* 0x00B8  0x00D4 */ SavedSceneFlags sceneFlags[124];
    /* 0x0E48  0x0E64 */ FaroresWindData fw;
    /* 0x0E70  0x0E8C */ char unk_E8C[0x10];
    /* 0x0E80  0x0E9C */ s32 gsFlags[6];
    /* 0x0E98  0x0EB4 */ char unk_EB4[0x4];
    /* 0x0E9C  0x0EB8 */ s32 highScores[7];
    /* 0x0EB8  0x0ED4 */ u16 eventChkInf[14]; // "event_chk_inf"
    /* 0x0ED4  0x0EF0 */ u16 itemGetInf[4]; // "item_get_inf"
    /* 0x0EDC  0x0EF8 */ u16 infTable[30]; // "inf_table"
    /* 0x0F18  0x0F34 */ char unk_F34[0x04];
    /* 0x0F1C  0x0F38 */ u32 worldMapAreaData; // "area_arrival"
    /* 0x0F20  0x0F3C */ char unk_F3C[0x4];
    /* 0x0F24  0x0F40 */ u8 scarecrowLongSongSet;
    /* 0x0F25  0x0F41 */ u8 scarecrowLongSong[0x360];
    /* 0x1285  0x12A1 */ char unk_12A1[0x24];
    /* 0x12A9  0x12C5 */ u8 scarecrowSpawnSongSet;
    /* 0x12AA  0x12C6 */ u8 scarecrowSpawnSong[0x80];
    /* 0x132A  0x1346 */ char unk_1346[0x02];
    /* 0x132C  0x1348 */ HorseData horseData;
    /* 0x1336  0x1352 */ Checksum checksum; // "check_sum"
} SaveInfo;

typedef struct Save {
    /* 0x00 */ s32 entranceIndex;
    /* 0x04 */ s32 linkAge; // 0: Adult; 1: Child (see enum `LinkAge`)
    /* 0x08 */ s32 cutsceneIndex;
    /* 0x0C */ u16 dayTime; // "zelda_time"
    /* 0x10 */ s32 nightFlag;
    /* 0x14 */ s32 totalDays;
    /* 0x18 */ s32 bgsDayCount; // increments with totalDays, can be cleared with `Environment_ClearBgsDayCount`
    /* 0x1C */ SaveInfo info; // "information"
} Save;

typedef struct SaveContext {
    /* 0x0000 */ Save save; // "memory"
    /* 0x1354 */ s32 fileNum; // "file_no"
    /* 0x1358 */ char unk_1358[0x0004];
    /* 0x135C */ s32 gameMode;
    /* 0x1360 */ s32 sceneLayer; // "counter"
    /* 0x1364 */ s32 respawnFlag; // "restart_flag"
    /* 0x1368 */ RespawnData respawn[RESPAWN_MODE_MAX]; // "restart_data"
    /* 0x13BC */ f32 entranceSpeed;
    /* 0x13C0 */ u16 entranceSound;
    /* 0x13C2 */ char unk_13C2[0x0001];
    /* 0x13C3 */ u8 retainWeatherMode;
    /* 0x13C4 */ s16 dogParams;
    /* 0x13C6 */ u8 envHazardTextTriggerFlags;
    /* 0x13C7 */ u8 showTitleCard;
    /* 0x13C8 */ s16 nayrusLoveTimer;
    /* 0x13CA */ char unk_13CA[0x0002];
    /* 0x13CC */ s16 rupeeAccumulator;
    /* 0x13CE */ s16 timerState; // See `TimerState`
    /* 0x13D0 */ s16 timerSeconds;
    /* 0x13D2 */ s16 subTimerState; // See `SubTimerState`
    /* 0x13D4 */ s16 subTimerSeconds;
    /* 0x13D6 */ s16 timerX[TIMER_ID_MAX];
    /* 0x13DA */ s16 timerY[TIMER_ID_MAX];
    /* 0x13DE */ char unk_13DE[0x0002];
    /* 0x13E0 */ u8 seqId;
    /* 0x13E1 */ u8 natureAmbienceId;
    /* 0x13E2 */ u8 buttonStatus[5];
    /* 0x13E7 */ u8 forceRisingButtonAlphas; // if btn alphas are updated through Interface_DimButtonAlphas, instead update them through Interface_RaiseButtonAlphas
    /* 0x13E8 */ u16 nextHudVisibilityMode; // triggers the hud to change visibility mode to the requested value. Reset to HUD_VISIBILITY_NO_CHANGE when target is reached
    /* 0x13EA */ u16 hudVisibilityMode; // current hud visibility mode
    /* 0x13EC */ u16 hudVisibilityModeTimer; // number of frames in the transition to a new hud visibility mode. Used to step alpha
    /* 0x13EE */ u16 prevHudVisibilityMode; // used to store and recover hud visibility mode for pause menu and text boxes
    /* 0x13F0 */ s16 magicState; // determines magic meter behavior on each frame
    /* 0x13F2 */ s16 prevMagicState; // used to resume the previous state after adding or filling magic
    /* 0x13F4 */ s16 magicCapacity; // maximum magic available
    /* 0x13F6 */ s16 magicFillTarget; // target used to fill magic. Target can either be full capacity (Magic_Fill, magic upgrades), or the saved magic amount (loading a file, game over)
    /* 0x13F8 */ s16 magicTarget; // target for magic to step to when adding or consuming magic
    /* 0x13FA */ u16 eventInf[4]; // "event_inf"
    /* 0x1402 */ u16 mapIndex; // intended for maps/minimaps but commonly used as the dungeon index
    /* 0x1404 */ u16 minigameState;
    /* 0x1406 */ u16 minigameScore; // "yabusame_total"
    /* 0x1408 */ char unk_1408[0x0001];
    /* 0x1409 */ u8 language; // NTSC 0: Japanese; 1: English | PAL 0: English; 1: German; 2: French (see enum `Language`)
    /* 0x140A */ u8 soundSetting; // 0: Stereo; 1: Mono; 2: Headset; 3: Surround (see enum `SoundSetting`)
    /* 0x140B */ char unk_140B[0x0001];
    /* 0x140C */ u8 zTargetSetting; // 0: Switch; 1: Hold (see enum `ZTargetSetting`)
    /* 0x140E */ u16 forcedSeqId; // immediately start playing the sequence if set
    /* 0x1410 */ u8 cutsceneTransitionControl; // context dependent usage: can either trigger a delayed fade or control fill alpha
    /* 0x1411 */ char unk_1411[0x0001];
    /* 0x1412 */ u16 nextCutsceneIndex;
    /* 0x1414 */ u8 cutsceneTrigger;
    /* 0x1415 */ u8 chamberCutsceneNum;
    /* 0x1416 */ u16 nextDayTime; // "next_zelda_time"
    /* 0x1418 */ u8 transFadeDuration;
    /* 0x1419 */ u8 transWipeSpeed;
    /* 0x141A */ u16 skyboxTime;
    /* 0x141C */ u8 dogIsLost;
    /* 0x141D */ u8 nextTransitionType;
    /* 0x141E */ char unk_141E[0x0002];
    /* 0x1420 */ s16 worldMapArea;
    /* 0x1422 */ s16 sunsSongState; // controls the effects of suns song
    /* 0x1424 */ s16 healthAccumulator;
} SaveContext; // size = 0x1428

typedef enum ButtonStatus {
    /* 0x00 */ BTN_ENABLED,
    /* 0xFF */ BTN_DISABLED = 0xFF
} ButtonStatus;

typedef enum ChamberCutsceneNum {
    /* 0 */ CHAMBER_CS_FOREST,
    /* 1 */ CHAMBER_CS_FIRE,
    /* 2 */ CHAMBER_CS_WATER,
    /* 3 */ CHAMBER_CS_SPIRIT,
    /* 4 */ CHAMBER_CS_SHADOW,
    /* 5 */ CHAMBER_CS_LIGHT
} ChamberCutsceneNum;

typedef enum HighScores {
    /* 0x00 */ HS_HBA,          // horseback archery
    /* 0x01 */ HS_POE_POINTS,
    /* 0x02 */ HS_FISHING,
    /* 0x03 */ HS_HORSE_RACE,
    /* 0x04 */ HS_MARATHON,
    /* 0x05 */ HS_UNK_05,
    /* 0x06 */ HS_DAMPE_RACE
} HighScores;

// the score value for the fishing minigame also stores many flags.
#define HS_FISH_LENGTH_CHILD 0x7F // mask for record length of catch as child.
#define HS_FISH_LENGTH_ADULT 0x7F000000 // mask for record length of catch as adult.
#define HS_FISH_PLAYED_CHILD 0x100 // set when first talking to owner as child
#define HS_FISH_PLAYED_ADULT 0x200 // set when first talking to owner as adult
#define HS_FISH_PRIZE_CHILD 0x400 // won the Piece of Heart
#define HS_FISH_PRIZE_ADULT 0x800 // won the Golden Scale
#define HS_FISH_STOLE_HAT 0x1000 // Pond owner is visibly bald as Adult Link.
#define HS_FISH_CHEAT_CHILD 0x80 // used Sinking Lure as child to catch record fish
#define HS_FISH_CHEAT_ADULT 0x80000000 // used Sinking Lure as adult to catch record fish
#define HS_FISH_PLAYED 0x10000 // incremented for every play. controls weather.

typedef enum SunsSongState {
    /* 0 */ SUNSSONG_INACTIVE,
    /* 1 */ SUNSSONG_START, // the suns ocarina effect signals that the song has finished playing
    /* 2 */ SUNSSONG_SPEED_TIME, // suns was played where time passes, speed up the advancement of time
    /* 3 */ SUNSSONG_SPECIAL // time does not advance, but signals the song was played. used for freezing redeads
} SunsSongState;

typedef enum GameMode {
    /* 0 */ GAMEMODE_NORMAL,
    /* 1 */ GAMEMODE_TITLE_SCREEN,
    /* 2 */ GAMEMODE_FILE_SELECT, // Note: only instance type transitions swap to file select
    /* 3 */ GAMEMODE_END_CREDITS
} GameMode;

typedef enum SceneLayer {
    /* 0 */ SCENE_LAYER_CHILD_DAY,
    /* 1 */ SCENE_LAYER_CHILD_NIGHT,
    /* 2 */ SCENE_LAYER_ADULT_DAY,
    /* 3 */ SCENE_LAYER_ADULT_NIGHT,
    /* 4 */ SCENE_LAYER_CUTSCENE_FIRST
} SceneLayer;

#define IS_CUTSCENE_LAYER (gSaveContext.sceneLayer >= SCENE_LAYER_CUTSCENE_FIRST)
#define GET_CUTSCENE_LAYER(index) (SCENE_LAYER_CUTSCENE_FIRST + (index & 0xF))

typedef enum LinkAge {
    /* 0 */ LINK_AGE_ADULT,
    /* 1 */ LINK_AGE_CHILD
} LinkAge;


// Usage in Map Select suggests that `gSaveContext.save.cutsceneIndex` was,
// at one point in development, a variable related to the time.
// This is further supported by debug strings showing that its original name was "day_time".
// These macros exist for the rare cases in the codebase where `cutsceneIndex` is treated as a time value.
// In practice, both values do not lead to a cutscene being played.
// See `CS_INDEX_NONE` below for the more common usage of value 0x0000.
#define CS_INDEX_NIGHT 0x0000
#define CS_INDEX_DAY 0x8000

// Indicates that no scripted cutscene is playing (or should be played).
// While this value overlaps with `CS_INDEX_NIGHT` defined above, it is not related to the time.
// This is the more common interpretation of this value, as much of the codebase uses 0x0000 to mean "no cutscene"
// except for Map Select.
#define CS_INDEX_NONE 0x0000

// Values 0xFFF0-0xFFFF indicate that a cutscene script should be played.
// If the value of `nextCutsceneIndex` is 0xFFF0-0xFFFF on scene load,
// `Play_Init` will copy the value to `gSaveContext.cutsceneIndex`, load a
// corresponding scene layer and start the scripted cutscene in the scene layer
// (except for the value 0xFFFD, which is special-cased to do nothing in `Play_Init`).
// It loads layer 4 for 0xFFF0, layer 5 for 0xFFF1, and so on.
//
// 0xFFFD is used by the cutscene system to indicate a scripted cutscene has been triggered.
// This is why `Play_Init` ignores that value, so that if the cutscene is interrupted
// by a new play state load (e.g. if the player falls out of bounds), it does not then
// try to wrongly load a cutscene layer.
#define CS_INDEX_0 0xFFF0
#define CS_INDEX_1 0xFFF1
#define CS_INDEX_2 0xFFF2
#define CS_INDEX_3 0xFFF3
#define CS_INDEX_4 0xFFF4
#define CS_INDEX_5 0xFFF5
#define CS_INDEX_6 0xFFF6
#define CS_INDEX_7 0xFFF7
#define CS_INDEX_8 0xFFF8
#define CS_INDEX_9 0xFFF9
#define CS_INDEX_A 0xFFFA
#define CS_INDEX_B 0xFFFB
#define CS_INDEX_C 0xFFFC
#define CS_INDEX_D 0xFFFD // does not load a cutscene scene layer (see above)
#define CS_INDEX_E 0xFFFE
#define CS_INDEX_F 0xFFFF

// Sentinel value for `nextCutsceneIndex` to indicate that no cutscene should be played next.
#define NEXT_CS_INDEX_NONE 0xFFEF


#define LINK_IS_ADULT (gSaveContext.save.linkAge == LINK_AGE_ADULT)
#define LINK_IS_CHILD (gSaveContext.save.linkAge == LINK_AGE_CHILD)

#define YEARS_CHILD 5
#define YEARS_ADULT 17
#define LINK_AGE_IN_YEARS (!LINK_IS_ADULT ? YEARS_CHILD : YEARS_ADULT)

#define CLOCK_TIME(hr, min) ((s32)(((hr) * 60 + (min)) * (f32)0x10000 / (24 * 60) + 0.5f))

#define IS_DAY (gSaveContext.save.nightFlag == 0)
#define IS_NIGHT (gSaveContext.save.nightFlag == 1)

#define SLOT(item) gItemSlots[item]
#define INV_CONTENT(item) gSaveContext.save.info.inventory.items[SLOT(item)]
#define AMMO(item) gSaveContext.save.info.inventory.ammo[SLOT(item)]
#define BEANS_BOUGHT AMMO(ITEM_MAGIC_BEAN + 1)

#define ALL_EQUIP_VALUE(equip) ((s32)(gSaveContext.save.info.inventory.equipment & gEquipMasks[equip]) >> gEquipShifts[equip])
#define CUR_EQUIP_VALUE(equip) ((s32)(gSaveContext.save.info.equips.equipment & gEquipMasks[equip]) >> gEquipShifts[equip])
#define OWNED_EQUIP_FLAG(equip, value) (gBitFlags[value] << gEquipShifts[equip])
#define OWNED_EQUIP_FLAG_ALT(equip, value) ((1 << (value)) << gEquipShifts[equip])
#define CHECK_OWNED_EQUIP(equip, value) (gSaveContext.save.info.inventory.equipment & OWNED_EQUIP_FLAG(equip, value))
#define CHECK_OWNED_EQUIP_ALT(equip, value) (gSaveContext.save.info.inventory.equipment & gBitFlags[(value) + (equip) * 4])

#define SWORD_EQUIP_TO_PLAYER(swordEquip) (swordEquip)
#define SHIELD_EQUIP_TO_PLAYER(shieldEquip) (shieldEquip)
#define TUNIC_EQUIP_TO_PLAYER(tunicEquip) ((tunicEquip) - 1)
#define BOOTS_EQUIP_TO_PLAYER(bootsEquip) ((bootsEquip) - 1)

#define CUR_UPG_VALUE(upg) ((s32)(gSaveContext.save.info.inventory.upgrades & gUpgradeMasks[upg]) >> gUpgradeShifts[upg])
#define CAPACITY(upg, value) gUpgradeCapacities[upg][value]
#define CUR_CAPACITY(upg) CAPACITY(upg, CUR_UPG_VALUE(upg))

#define CHECK_QUEST_ITEM(item) (gSaveContext.save.info.inventory.questItems & gBitFlags[item])
#define CHECK_DUNGEON_ITEM(item, dungeonIndex) (gSaveContext.save.info.inventory.dungeonItems[dungeonIndex] & gBitFlags[item])

#define GET_GS_FLAGS(index) \
    ((gSaveContext.save.info.gsFlags[(index) >> 2] & gGsFlagsMasks[(index) & 3]) >> gGsFlagsShifts[(index) & 3])
#define SET_GS_FLAGS(index, value) \
    (gSaveContext.save.info.gsFlags[(index) >> 2] |= (value) << gGsFlagsShifts[(index) & 3])

#define HIGH_SCORE(score) (gSaveContext.save.info.highScores[score])

#define B_BTN_ITEM ((gSaveContext.buttonStatus[0] == ITEM_NONE)                     \
                        ? ITEM_NONE                                                 \
                        : (gSaveContext.save.info.equips.buttonItems[0] == ITEM_GIANTS_KNIFE) \
                            ? ITEM_SWORD_BIGGORON                                   \
                            : gSaveContext.save.info.equips.buttonItems[0])

#define C_BTN_ITEM(button) ((gSaveContext.buttonStatus[(button) + 1] != BTN_DISABLED) \
                                ? gSaveContext.save.info.equips.buttonItems[(button) + 1]       \
                                : ITEM_NONE)


/*
 *
 * SaveContext flags
 *
 */


/*
 * SaveContext.eventChkInf
 */

#define EVENTCHKINF_INDEX(flag) ((flag) >> 4)
#define EVENTCHKINF_MASK(flag) (1 << ((flag) & 0xF))

#define GET_EVENTCHKINF(flag) (gSaveContext.save.info.eventChkInf[EVENTCHKINF_INDEX(flag)] & EVENTCHKINF_MASK(flag))
#define SET_EVENTCHKINF(flag) (gSaveContext.save.info.eventChkInf[EVENTCHKINF_INDEX(flag)] |= EVENTCHKINF_MASK(flag))
#define CLEAR_EVENTCHKINF(flag) (gSaveContext.save.info.eventChkInf[EVENTCHKINF_INDEX(flag)] &= ~EVENTCHKINF_MASK(flag))

// EVENTCHKINF 0x00-0x0F
#define EVENTCHKINF_INDEX_0 0
#define EVENTCHKINF_00_UNUSED 0x00 // flag is set in the debug save, but has no functionality
#define EVENTCHKINF_01_UNUSED 0x01 // flag is set in the debug save, but has no functionality
#define EVENTCHKINF_MIDO_DENIED_DEKU_TREE_ACCESS 0x02
#define EVENTCHKINF_SARIA_WAS_TOLD_ABOUT_MIDO 0x03
#define EVENTCHKINF_04 0x04
#define EVENTCHKINF_05 0x05
#define EVENTCHKINF_07 0x07
#define EVENTCHKINF_09 0x09
#define EVENTCHKINF_0A 0x0A
#define EVENTCHKINF_0B 0x0B
#define EVENTCHKINF_0C 0x0C
#define EVENTCHKINF_0F 0x0F
#define EVENTCHKINF_TALKED_TO_MALON_FIRST_TIME 0x10
#define EVENTCHKINF_11 0x11
#define EVENTCHKINF_RECEIVED_WEIRD_EGG 0x12
#define EVENTCHKINF_TALON_WOKEN_IN_CASTLE 0x13
#define EVENTCHKINF_TALON_RETURNED_FROM_CASTLE 0x14
#define EVENTCHKINF_TALKED_TO_CHILD_MALON_AT_RANCH 0x15
#define EVENTCHKINF_CAN_LEARN_EPONAS_SONG 0x16
#define EVENTCHKINF_EPONA_OBTAINED 0x18
#define EVENTCHKINF_1B 0x1B
#define EVENTCHKINF_1C 0x1C
#define EVENTCHKINF_1D 0x1D
#define EVENTCHKINF_HORSE_RACE_COW_UNLOCK 0x1E
#define EVENTCHKINF_20 0x20
#define EVENTCHKINF_21 0x21
#define EVENTCHKINF_22 0x22
#define EVENTCHKINF_23 0x23
#define EVENTCHKINF_25 0x25
#define EVENTCHKINF_2A 0x2A
#define EVENTCHKINF_2B 0x2B
#define EVENTCHKINF_2C 0x2C
#define EVENTCHKINF_2D 0x2D
#define EVENTCHKINF_2F 0x2F
#define EVENTCHKINF_30 0x30
#define EVENTCHKINF_31 0x31
#define EVENTCHKINF_32 0x32
#define EVENTCHKINF_GAVE_LETTER_TO_KING_ZORA 0x33
#define EVENTCHKINF_37 0x37
#define EVENTCHKINF_OBTAINED_SILVER_SCALE 0x38
#define EVENTCHKINF_39 0x39
#define EVENTCHKINF_OPENED_JABU_JABU 0x3A
#define EVENTCHKINF_3B 0x3B
#define EVENTCHKINF_DEFEATED_NABOORU_KNUCKLE 0x3C

// EVENTCHKINF 0x40
#define EVENTCHKINF_INDEX_OBTAINED_ZELDAS_LETTER EVENTCHKINF_INDEX(EVENTCHKINF_OBTAINED_ZELDAS_LETTER)
#define EVENTCHKINF_OBTAINED_ZELDAS_LETTER 0x40

#define EVENTCHKINF_41 0x41
#define EVENTCHKINF_42 0x42
#define EVENTCHKINF_43 0x43
#define EVENTCHKINF_OBTAINED_MASTER_SWORD 0x45
#define EVENTCHKINF_48 0x48
#define EVENTCHKINF_49 0x49
#define EVENTCHKINF_4A 0x4A
#define EVENTCHKINF_OPENED_DOOR_OF_TIME 0x4B
#define EVENTCHKINF_4C 0x4C
#define EVENTCHKINF_CREATED_RAINBOW_BRIDGE 0x4D
#define EVENTCHKINF_CAUGHT_BY_CASTLE_GUARDS 0x4E // set but unused
#define EVENTCHKINF_REVEALED_MASTER_SWORD 0x4F // Cutscene in Temple of Time when entering the Master Sword chamber for the first time
#define EVENTCHKINF_50 0x50
#define EVENTCHKINF_51 0x51
#define EVENTCHKINF_52 0x52
#define EVENTCHKINF_54 0x54
#define EVENTCHKINF_55 0x55
#define EVENTCHKINF_59 0x59
#define EVENTCHKINF_5A 0x5A
#define EVENTCHKINF_5B 0x5B
#define EVENTCHKINF_5C 0x5C
#define EVENTCHKINF_65 0x65
#define EVENTCHKINF_DRAINED_WELL 0x67
#define EVENTCHKINF_68 0x68
#define EVENTCHKINF_RESTORED_LAKE_HYLIA 0x69
#define EVENTCHKINF_TALON_WOKEN_IN_KAKARIKO 0x6A

// EVENTCHKINF 0x6B
#define EVENTCHKINF_INDEX_TALON_RETURNED_FROM_KAKARIKO EVENTCHKINF_INDEX(EVENTCHKINF_TALON_RETURNED_FROM_KAKARIKO)
#define EVENTCHKINF_TALON_RETURNED_FROM_KAKARIKO 0x6B

#define EVENTCHKINF_6E 0x6E
#define EVENTCHKINF_6F 0x6F
#define EVENTCHKINF_BEGAN_GOHMA_BATTLE 0x70
#define EVENTCHKINF_BEGAN_KING_DODONGO_BATTLE 0x71
#define EVENTCHKINF_BEGAN_PHANTOM_GANON_BATTLE 0x72
#define EVENTCHKINF_BEGAN_VOLVAGIA_BATTLE 0x73
#define EVENTCHKINF_BEGAN_MORPHA_BATTLE 0x74
#define EVENTCHKINF_BEGAN_TWINROVA_BATTLE 0x75
#define EVENTCHKINF_BEGAN_BARINADE_BATTLE 0x76
#define EVENTCHKINF_BEGAN_BONGO_BONGO_BATTLE 0x77
#define EVENTCHKINF_BEGAN_GANONDORF_BATTLE 0x78
#define EVENTCHKINF_ZELDA_FLED_CASTLE 0x80
#define EVENTCHKINF_82 0x82
#define EVENTCHKINF_PAID_BACK_KEATON_MASK 0x8C
#define EVENTCHKINF_PAID_BACK_SKULL_MASK 0x8D
#define EVENTCHKINF_PAID_BACK_SPOOKY_MASK 0x8E
#define EVENTCHKINF_PAID_BACK_BUNNY_HOOD 0x8F

// EVENTCHKINF 0x90-0x93
// Carpenters rescued from Gerudo Fortress
#define EVENTCHKINF_INDEX_CARPENTERS_RESCUED 0x9
#define EVENTCHKINF_CARPENTER_0_RESCUED 0x90
#define EVENTCHKINF_CARPENTER_1_RESCUED 0x91
#define EVENTCHKINF_CARPENTER_2_RESCUED 0x92
#define EVENTCHKINF_CARPENTER_3_RESCUED 0x93

#define EVENTCHKINF_CARPENTERS_ALL_RESCUED_MASK                                                              \
    (EVENTCHKINF_MASK(EVENTCHKINF_CARPENTER_0_RESCUED) | EVENTCHKINF_MASK(EVENTCHKINF_CARPENTER_1_RESCUED) | \
     EVENTCHKINF_MASK(EVENTCHKINF_CARPENTER_2_RESCUED) | EVENTCHKINF_MASK(EVENTCHKINF_CARPENTER_3_RESCUED))

#define GET_EVENTCHKINF_CARPENTERS_ALL_RESCUED() \
    ((gSaveContext.save.info.eventChkInf[EVENTCHKINF_INDEX_CARPENTERS_RESCUED] & EVENTCHKINF_CARPENTERS_ALL_RESCUED_MASK) == (EVENTCHKINF_CARPENTERS_ALL_RESCUED_MASK))

#define GET_EVENTCHKINF_CARPENTERS_ALL_RESCUED2() \
    ((gSaveContext.save.info.eventChkInf[EVENTCHKINF_INDEX_CARPENTERS_RESCUED] & (EVENTCHKINF_CARPENTERS_ALL_RESCUED_MASK | 0xF0) & EVENTCHKINF_CARPENTERS_ALL_RESCUED_MASK) == (EVENTCHKINF_CARPENTERS_ALL_RESCUED_MASK))

#define ENDAIKU_CARPENTER_RESCUED_MASK(carpenterType) (1 << (carpenterType))

#define ENDAIKU_IS_CARPENTER_RESCUED(carpenterType)                            \
    gSaveContext.save.info.eventChkInf[EVENTCHKINF_INDEX_CARPENTERS_RESCUED] & \
        ENDAIKU_CARPENTER_RESCUED_MASK(carpenterType)

#define ENDAIKU_SET_CARPENTER_RESCUED(carpenterType)                            \
    gSaveContext.save.info.eventChkInf[EVENTCHKINF_INDEX_CARPENTERS_RESCUED] |= \
        ENDAIKU_CARPENTER_RESCUED_MASK((carpenterType))

#define GET_EVENTCHKINF_CARPENTERS_RESCUED_FLAGS() \
    gSaveContext.save.info.eventChkInf[EVENTCHKINF_INDEX_CARPENTERS_RESCUED] & EVENTCHKINF_CARPENTERS_ALL_RESCUED_MASK

#define EVENTCHKINF_94 0x94
#define EVENTCHKINF_95 0x95
#define EVENTCHKINF_96 0x96
#define EVENTCHKINF_9C 0x9C
#define EVENTCHKINF_A0 0xA0
#define EVENTCHKINF_A1 0xA1
#define EVENTCHKINF_A3 0xA3
#define EVENTCHKINF_A4 0xA4
#define EVENTCHKINF_A5 0xA5
#define EVENTCHKINF_A6 0xA6
#define EVENTCHKINF_A7 0xA7
#define EVENTCHKINF_A8 0xA8
#define EVENTCHKINF_A9 0xA9
#define EVENTCHKINF_AA 0xAA
#define EVENTCHKINF_AC 0xAC
#define EVENTCHKINF_AD 0xAD
#define EVENTCHKINF_B0 0xB0
#define EVENTCHKINF_B1 0xB1
#define EVENTCHKINF_B2 0xB2
#define EVENTCHKINF_B3 0xB3
#define EVENTCHKINF_B4 0xB4
#define EVENTCHKINF_B5 0xB5
#define EVENTCHKINF_B6 0xB6
#define EVENTCHKINF_B7 0xB7
#define EVENTCHKINF_B8 0xB8
#define EVENTCHKINF_B9 0xB9
#define EVENTCHKINF_BA 0xBA
#define EVENTCHKINF_BB 0xBB
#define EVENTCHKINF_BC 0xBC
#define EVENTCHKINF_BD 0xBD
#define EVENTCHKINF_BE 0xBE
#define EVENTCHKINF_BF 0xBF
#define EVENTCHKINF_C0 0xC0
#define EVENTCHKINF_C1 0xC1
#define EVENTCHKINF_C3 0xC3
#define EVENTCHKINF_C4 0xC4
#define EVENTCHKINF_C5 0xC5
#define EVENTCHKINF_C6 0xC6
#define EVENTCHKINF_GERUDO_CAUGHT_TOWER_FALL 0xC7 // This flag is mainly used for when first caught by the Gerudo, but Zelda will later purge it for use in the Tower collapse sequence.
#define EVENTCHKINF_C8 0xC8
#define EVENTCHKINF_C9 0xC9

// EVENTCHKINF 0xD0-0xD6
#define EVENTCHKINF_INDEX_SONGS_FOR_FROGS EVENTCHKINF_INDEX(EVENTCHKINF_SONGS_FOR_FROGS_CHOIR)
#define EVENTCHKINF_SONGS_FOR_FROGS_CHOIR  0xD0
#define EVENTCHKINF_SONGS_FOR_FROGS_ZL     0xD1
#define EVENTCHKINF_SONGS_FOR_FROGS_EPONA  0xD2
#define EVENTCHKINF_SONGS_FOR_FROGS_SUNS   0xD3
#define EVENTCHKINF_SONGS_FOR_FROGS_SARIA  0xD4
#define EVENTCHKINF_SONGS_FOR_FROGS_SOT    0xD5
#define EVENTCHKINF_SONGS_FOR_FROGS_STORMS 0xD6

// EVENTCHKINF 0xDA-0xDE
#define EVENTCHKINF_INDEX_SKULLTULA_REWARD 0xD
#define EVENTCHKINF_SKULLTULA_REWARD_10 0xDA
#define EVENTCHKINF_SKULLTULA_REWARD_20 0xDB
#define EVENTCHKINF_SKULLTULA_REWARD_30 0xDC
#define EVENTCHKINF_SKULLTULA_REWARD_40 0xDD
#define EVENTCHKINF_SKULLTULA_REWARD_50 0xDE


/*
 * SaveContext.itemGetInf
 */

#define ITEMGETINF_INDEX(flag) ((flag) >> 4)
#define ITEMGETINF_MASK(flag) (1 << ((flag) & 0xF))

#define GET_ITEMGETINF(flag) (gSaveContext.save.info.itemGetInf[ITEMGETINF_INDEX(flag)] & ITEMGETINF_MASK(flag))
#define SET_ITEMGETINF(flag) (gSaveContext.save.info.itemGetInf[ITEMGETINF_INDEX(flag)] |= ITEMGETINF_MASK(flag))

#define ITEMGETINF_TALON_BOTTLE 0x02
#define ITEMGETINF_03 0x03
#define ITEMGETINF_04 0x04
#define ITEMGETINF_05 0x05
#define ITEMGETINF_06 0x06
#define ITEMGETINF_07 0x07
#define ITEMGETINF_08 0x08
#define ITEMGETINF_09 0x09
#define ITEMGETINF_0A 0x0A
#define ITEMGETINF_DEKU_HEART_PIECE 0x0B
#define ITEMGETINF_0C 0x0C
#define ITEMGETINF_0D 0x0D
#define ITEMGETINF_0E 0x0E
#define ITEMGETINF_0F 0x0F
#define ITEMGETINF_10 0x10
#define ITEMGETINF_BOWLING_BOMB_BAG 0x11
#define ITEMGETINF_BOWLING_HEART_PIECE 0x12
#define ITEMGETINF_13 0x13
#define ITEMGETINF_15 0x15
#define ITEMGETINF_16 0x16
#define ITEMGETINF_17 0x17

// ITEMGETINF 0x18-0x1A
#define ITEMGETINF_INDEX_SPELLS 1
#define ITEMGETINF_FARORES_WIND 0x18
#define ITEMGETINF_DINS_FIRE 0x19
#define ITEMGETINF_NAYRUS_LOVE 0x1A

#define ITEMGETINF_1B 0x1B
#define ITEMGETINF_1C 0x1C
#define ITEMGETINF_1D 0x1D
#define ITEMGETINF_FOREST_STAGE_STICK_UPGRADE 0x1E
// This flag is shared by two events; It is set when obtaining the Deku Nut upgrade at the Forest Stage and when obtaining Poachers Saw.
// This will make obtaining the Deku Nut upgrade impossible if Poachers Saw is obtained first.
// This flag is never read for the Poachers Saw event, so the overlap only causes an issue for the Deku Nut Upgrade. It will not prevent obtaining Poachers Saw.
#define ITEMGETINF_FOREST_STAGE_NUT_UPGRADE 0x1F
#define ITEMGETINF_23 0x23
#define ITEMGETINF_24 0x24
#define ITEMGETINF_25 0x25
#define ITEMGETINF_26 0x26
#define ITEMGETINF_2A 0x2A
#define ITEMGETINF_2C 0x2C
#define ITEMGETINF_2E 0x2E
#define ITEMGETINF_30 0x30
#define ITEMGETINF_31 0x31
#define ITEMGETINF_38 0x38
#define ITEMGETINF_39 0x39
#define ITEMGETINF_3A 0x3A
#define ITEMGETINF_3B 0x3B
#define ITEMGETINF_3F 0x3F


/*
 * SaveContext.infTable
 */

#define INFTABLE_INDEX(flag) ((flag) >> 4)
#define INFTABLE_MASK(flag) (1 << ((flag) & 0xF))

#define GET_INFTABLE(flag) (gSaveContext.save.info.infTable[INFTABLE_INDEX(flag)] & INFTABLE_MASK(flag))
#define SET_INFTABLE(flag) (gSaveContext.save.info.infTable[INFTABLE_INDEX(flag)] |= INFTABLE_MASK(flag))
#define CLEAR_INFTABLE(flag) (gSaveContext.save.info.infTable[INFTABLE_INDEX(flag)] &= ~INFTABLE_MASK(flag))

// INFTABLE 0x0-0xF
#define INFTABLE_INDEX_0 0
#define INFTABLE_SARIA_GREETED_LINK 0x00
#define INFTABLE_SARIA_NOTICED_FAIRY 0x01
#define INFTABLE_SARIA_WAS_TOLD_ABOUT_MIDO 0x03
#define INFTABLE_SARIA_SPOKE_IN_HER_HOUSE 0x05
#define INFTABLE_0C 0x0C
#define INFTABLE_0E 0x0E
#define INFTABLE_10 0x10
#define INFTABLE_15 0x15
#define INFTABLE_17 0x17
#define INFTABLE_19 0x19
#define INFTABLE_1E 0x1E
#define INFTABLE_22 0x22
#define INFTABLE_24 0x24
#define INFTABLE_26 0x26
#define INFTABLE_28 0x28
#define INFTABLE_2A 0x2A
#define INFTABLE_2B 0x2B
#define INFTABLE_2E 0x2E
#define INFTABLE_2F 0x2F
#define INFTABLE_30 0x30
#define INFTABLE_41 0x41
#define INFTABLE_47 0x47
#define INFTABLE_51 0x51
#define INFTABLE_59 0x59
#define INFTABLE_61 0x61
#define INFTABLE_66 0x66
#define INFTABLE_6A 0x6A
#define INFTABLE_6C 0x6C
#define INFTABLE_71 0x71
#define INFTABLE_76 0x76
#define INFTABLE_77 0x77
#define INFTABLE_TALKED_TO_TALON_IN_RANCH_HOUSE 0x7E
#define INFTABLE_TALKED_TO_MALON_FIRST_TIME 0x84
#define INFTABLE_TOLD_EPONA_IS_SCARED 0x85
#define INFTABLE_MALON_SPAWNED_AT_HYRULE_CASTLE 0x8B
#define INFTABLE_TALKED_TO_ADULT_MALON_AFTER_SONG 0x8C
#define INFTABLE_8D 0x8D // unreachable flag in EnMa2
#define INFTABLE_PLAYED_SONG_FOR_ADULT_MALON 0x8E
#define INFTABLE_INGO_TALKED_TO_CHILD_LINK_BEFORE_TALON_RETURNED 0x94
#define INFTABLE_INGO_TALKED_TO_CHILD_LINK_AFTER_TALON_RETURNED 0x97
#define INFTABLE_INGO_TALKED_TO_ADULT_LINK 0x9A
#define INFTABLE_A2 0xA2
#define INFTABLE_AB 0xAB
#define INFTABLE_B0 0xB0
#define INFTABLE_B1 0xB1
#define INFTABLE_B4 0xB4
#define INFTABLE_B6 0xB6
#define INFTABLE_B7 0xB7
#define INFTABLE_B8 0xB8
#define INFTABLE_B9 0xB9
#define INFTABLE_BC 0xBC
#define INFTABLE_C0 0xC0
#define INFTABLE_C1 0xC1
#define INFTABLE_C2 0xC2
#define INFTABLE_C3 0xC3
#define INFTABLE_C4 0xC4
#define INFTABLE_C5 0xC5
#define INFTABLE_C6 0xC6
#define INFTABLE_C7 0xC7
#define INFTABLE_C8 0xC8
#define INFTABLE_C9 0xC9
#define INFTABLE_CA 0xCA
#define INFTABLE_CB 0xCB
#define INFTABLE_CC 0xCC
#define INFTABLE_CD 0xCD
#define INFTABLE_CE 0xCE
#define INFTABLE_D0 0xD0
#define INFTABLE_D2 0xD2
#define INFTABLE_D4 0xD4
#define INFTABLE_D6 0xD6
#define INFTABLE_D8 0xD8
#define INFTABLE_D9 0xD9
#define INFTABLE_E0 0xE0
#define INFTABLE_E3 0xE3
#define INFTABLE_E6 0xE6
#define INFTABLE_EB 0xEB
#define INFTABLE_F0 0xF0
#define INFTABLE_F4 0xF4
#define INFTABLE_F8 0xF8
#define INFTABLE_FC 0xFC
#define INFTABLE_109 0x109
#define INFTABLE_10A 0x10A
#define INFTABLE_10B 0x10B
#define INFTABLE_10C 0x10C
#define INFTABLE_10D 0x10D
#define INFTABLE_10E 0x10E
#define INFTABLE_10F 0x10F
#define INFTABLE_113 0x113
#define INFTABLE_11A 0x11A
#define INFTABLE_11E 0x11E
#define INFTABLE_124 0x124
#define INFTABLE_129 0x129
#define INFTABLE_12A 0x12A
#define INFTABLE_138 0x138
#define INFTABLE_139 0x139
#define INFTABLE_RUTO_PLACED_ON_SWITCH 0x140
#define INFTABLE_RUTO_MET_FIRST_TIME 0x141
#define INFTABLE_RUTO_EXPLAINED_WHY_IN_JABU 0x142
#define INFTABLE_RUTO_LET_LINK_CARRY 0x143
#define INFTABLE_RUTO_BROUGHT_TO_SAPPHIRE_ROOM 0x144
#define INFTABLE_RUTO_HAS_SAPPHIRE 0x145
#define INFTABLE_RUTO_ABDUCTED 0x146
#define INFTABLE_RUTO_BROUGHT_BACK_TO_HOLES_ROOM 0x147
#define INFTABLE_160 0x160
#define INFTABLE_161 0x161
#define INFTABLE_162 0x162
#define INFTABLE_163 0x163
#define INFTABLE_164 0x164
#define INFTABLE_166 0x166
#define INFTABLE_16A 0x16A
#define INFTABLE_16C 0x16C
#define INFTABLE_170 0x170
#define INFTABLE_171 0x171
#define INFTABLE_172 0x172
#define INFTABLE_176 0x176
#define INFTABLE_178 0x178
#define INFTABLE_17C 0x17C
#define INFTABLE_17F 0x17F
#define INFTABLE_190 0x190
#define INFTABLE_191 0x191
#define INFTABLE_HAS_DEKU_STICK_UPGRADE 0x192
#define INFTABLE_HAS_DEKU_NUT_UPGRADE 0x193
#define INFTABLE_195 0x195
#define INFTABLE_196 0x196
#define INFTABLE_197 0x197
#define INFTABLE_198 0x198

// INFTABLE 0x199-0x19F
#define INFTABLE_INDEX_199_19A_19B_19C_19D_19E_19F 25
#define INFTABLE_199 0x199
#define INFTABLE_19A 0x19A
#define INFTABLE_19B 0x19B
#define INFTABLE_19C 0x19C
#define INFTABLE_19D 0x19D
#define INFTABLE_19E 0x19E
#define INFTABLE_19F 0x19F

// INFTABLE 0x1A0-0x1AF
#define INFTABLE_INDEX_1AX 26
#define INFTABLE_1A0_SHIFT 0
#define INFTABLE_1A1_SHIFT 1
#define INFTABLE_1A2_SHIFT 2
#define INFTABLE_1A3_SHIFT 3
#define INFTABLE_1A4_SHIFT 4
#define INFTABLE_1A5_SHIFT 5
#define INFTABLE_1A6_SHIFT 6
#define INFTABLE_1A7_SHIFT 7
#define INFTABLE_1A8_SHIFT 8
#define INFTABLE_1A9_SHIFT 9
#define INFTABLE_1AB_SHIFT 11
#define INFTABLE_1AD_SHIFT 13

// INFTABLE 0x1D0-0x1DF
#define INFTABLE_INDEX_1DX INFTABLE_INDEX(INFTABLE_1D0)
#define INFTABLE_1D0 0x1D0


/*
 * SaveContext.eventInf
 */

#define EVENTINF_INDEX(flag) ((flag) >> 4)
#define EVENTINF_MASK(flag) (1 << ((flag) & 0xF))

#define GET_EVENTINF(flag) (gSaveContext.eventInf[EVENTINF_INDEX(flag)] & EVENTINF_MASK(flag))
#define SET_EVENTINF(flag) (gSaveContext.eventInf[EVENTINF_INDEX(flag)] |= EVENTINF_MASK(flag))
#define CLEAR_EVENTINF(flag) (gSaveContext.eventInf[EVENTINF_INDEX(flag)] &= ~EVENTINF_MASK(flag))

// EVENTINF 0x00-0x0F
// Ingo Race, Lon Lon Ranch minigames, and Horseback Archery minigame flags
#define EVENTINF_INDEX_HORSES 0
// EVENTINF 0x00-0x03 reserved for IngoRaceState
#define EVENTINF_INGO_RACE_STATE_MASK (0xF << 0x00)
typedef enum IngoRaceState {
    /* 0 */ INGO_RACE_STATE_OFFER_RENTAL,
    /* 1 */ INGO_RACE_STATE_HORSE_RENTAL_PERIOD,
    /* 2 */ INGO_RACE_STATE_RACING,
    /* 3 */ INGO_RACE_STATE_PLAYER_LOSE,
    /* 4 */ INGO_RACE_STATE_FIRST_WIN,
    /* 5 */ INGO_RACE_STATE_TRAPPED_WIN_UNUSED,
    /* 6 */ INGO_RACE_STATE_TRAPPED_WIN_EPONA, // Ingo Traps you in Lon Lon
    /* 7 */ INGO_RACE_STATE_REMATCH
} IngoRaceState;

#define EVENTINF_INGO_RACE_HORSETYPE 0x04
#define EVENTINF_INGO_RACE_LOST_ONCE 0x05
#define EVENTINF_INGO_RACE_SECOND_RACE 0x06
// Used in z_en_ta (Talon) to store Cucco game winning status
// and in z_en_ge1 (Gerudo) to store archery in-progress status
#define EVENTINF_HORSES_08 0x08
#define EVENTINF_CUCCO_GAME_WON EVENTINF_HORSES_08
// Used in z_en_ta (Talon) and z_en_ma3 (Malon) to store minigame finishing status
#define EVENTINF_HORSES_0A 0x0A
#define EVENTINF_CUCCO_GAME_FINISHED EVENTINF_HORSES_0A
#define EVENTINF_INGO_RACE_0F 0x0F // unused?

// "InRaceSeq"
#define GET_EVENTINF_INGO_RACE_STATE() (gSaveContext.eventInf[EVENTINF_INDEX_HORSES] & EVENTINF_INGO_RACE_STATE_MASK)

#define SET_EVENTINF_INGO_RACE_STATE(v)            \
    gSaveContext.eventInf[EVENTINF_INDEX_HORSES] = \
        (gSaveContext.eventInf[EVENTINF_INDEX_HORSES] & ~EVENTINF_INGO_RACE_STATE_MASK) | (v)

#define GET_EVENTINF_INGO_RACE_FLAG(flag) \
    ((gSaveContext.eventInf[EVENTINF_INDEX_HORSES] & EVENTINF_MASK(flag)) >> ((flag) & 0xF))

#define SET_EVENTINF_INGO_RACE_FLAG(flag)          \
    gSaveContext.eventInf[EVENTINF_INDEX_HORSES] = \
        (gSaveContext.eventInf[EVENTINF_INDEX_HORSES] & 0xFFFF) | EVENTINF_MASK(flag)

#define WRITE_EVENTINF_INGO_RACE_FLAG(flag, v)     \
    gSaveContext.eventInf[EVENTINF_INDEX_HORSES] = \
        (gSaveContext.eventInf[EVENTINF_INDEX_HORSES] & ~EVENTINF_MASK(flag)) | ((v) << ((flag) & 0xF))

#define GET_EVENTINF_INGO_RACE_HORSETYPE() GET_EVENTINF_INGO_RACE_FLAG(EVENTINF_INGO_RACE_HORSETYPE)
#define WRITE_EVENTINF_INGO_RACE_HORSETYPE(v) WRITE_EVENTINF_INGO_RACE_FLAG(EVENTINF_INGO_RACE_HORSETYPE, v)

#define WRITE_EVENTINF_INGO_RACE_0F(v) WRITE_EVENTINF_INGO_RACE_FLAG(EVENTINF_INGO_RACE_0F, v)

// Is the running man race active
#define EVENTINF_MARATHON_ACTIVE 0x10

// EVENTINF 0x20-0x24
#define EVENTINF_INDEX_HAGGLING_TOWNSFOLK 0x2
#define EVENTINF_HAGGLING_TOWNSFOLK_MESG_0 0x20
#define EVENTINF_HAGGLING_TOWNSFOLK_MESG_1 0x21
#define EVENTINF_HAGGLING_TOWNSFOLK_MESG_2 0x22
#define EVENTINF_HAGGLING_TOWNSFOLK_MESG_3 0x23
#define EVENTINF_HAGGLING_TOWNSFOLK_MESG_4 0x24

#define EVENTINF_HAGGLING_TOWNSFOLK_MASK                                                                     \
    (EVENTINF_MASK(EVENTINF_HAGGLING_TOWNSFOLK_MESG_0) | EVENTINF_MASK(EVENTINF_HAGGLING_TOWNSFOLK_MESG_1) | \
     EVENTINF_MASK(EVENTINF_HAGGLING_TOWNSFOLK_MESG_2) | EVENTINF_MASK(EVENTINF_HAGGLING_TOWNSFOLK_MESG_3) | \
     EVENTINF_MASK(EVENTINF_HAGGLING_TOWNSFOLK_MESG_4))

#define GET_EVENTINF_ENMU_TALK_FLAGS() \
    gSaveContext.eventInf[EVENTINF_INDEX_HAGGLING_TOWNSFOLK] & EVENTINF_HAGGLING_TOWNSFOLK_MASK

#define SET_EVENTINF_ENMU_TALK_FLAGS(talkFlags) \
    gSaveContext.eventInf[EVENTINF_INDEX_HAGGLING_TOWNSFOLK] |= (talkFlags);

#define RESET_EVENTINF_ENMU_TALK_FLAGS() \
    gSaveContext.eventInf[EVENTINF_INDEX_HAGGLING_TOWNSFOLK] &= ~(EVENTINF_HAGGLING_TOWNSFOLK_MASK);

#define EVENTINF_30 0x30

void SaveContext_Init(void);

extern SaveContext gSaveContext;

#endif
