Add typedefs for MAPSEC and METLOC values (#2183)

Added typedefs: mapsec_t, metloc_t, and variants for MAPSEC and METLOC values. There are some rough edges that could do with smoothing out, but for now, this gets us close to ideal with a ROM that compares equal.

Per feedback, all typedefs to mention the underlying type within the typedef name. The documentation comments reflect and explain the naming convention.

Updated comments to reflect the fact that we're no longer using SET8 for a Pokemon's met locations, in favor of a new macro (added by this PR) that adjusts to match the width of whatever is being set.
This commit is contained in:
DavidJCobb
2025-10-19 18:37:13 +02:00
committed by GitHub
parent 0965dffe70
commit 7fd0029ed7
26 changed files with 185 additions and 102 deletions

67
include/gametypes.h Normal file
View File

@@ -0,0 +1,67 @@
#ifndef GUARD_GAMETYPES_H
#define GUARD_GAMETYPES_H
#include "gba/types.h"
//
// This header includes typedefs for fields that commonly appear throughout
// the codebase, and which ROM hacks might benefit from being able to widen.
//
// These typedefs include the underlying type in their name for two reasons:
//
// - Game Freak wasn't fully consistent about field widths throughout
// their codebase. For example, when Region Map Sections are persistently
// stored in savedata, they're stored as 8-bit values; but much of the
// codebase handles them as 16-bit values.
//
// - Although Pokemon Emerald doesn't come close to maxing out RAM, it *does*
// use nearly all of its EEPROM. That is: the vanilla game uses 96% of the
// flash memory available for storing players' save files, leaving 2172
// bytes to spare within each of the game's two save files (primary and
// backup). These spare bytes are not contiguous: SaveBlock1 can only grow
// by 84 bytes, and SaveBlock2 can only grow by 120 bytes, with the rest
// of the free space located after the player's PC-boxed Pokemon.
//
// With so little flash memory to spare, keeping track of how much space
// you're using is vital -- and so is arranging struct members to minimize
// compiler-inserted padding. It's easier to deal with this when you can
// see these types' widths at a glance.
//
// Accordingly, this file generally doesn't contain just single types, but
// rather families of types. For example, Region Map Sections are saved as
// u8s within the player's save file, but are sometimes handled as u16s or
// even s16s and ints; and so there are multiple typedefs for Map Sections
// corresponding to each of these underlying types, and each typedef has a
// name which indicates the underlying type.
//
// For a given family of typedefs, the smallest one should be considered
// the "real" or "canonical" type. Continuing with Map Sections as our
// example, the smallest type is an 8-bit integer, and so any values that
// can't fit in an 8-bit integer will be truncated and lost at some point
// within the codebase. Therefore mapsec_u8_t is the "canonical" type for
// Map Sections, and the larger typedefs just exist to describe situations
// where the game handles Map Sections inconsistently with that "canon."
//
// Map Sections are named areas that can appear in the region map. Each
// individual map can be assigned to a Map Section as appropriate. The
// possible values are in constants/region_map_sections.h.
//
// If you choose to widen Map Sections, be aware that Met Locations (below)
// are based on Map Sections and will also be widened.
typedef u8 mapsec_u8_t;
typedef u16 mapsec_u16_t;
typedef s16 mapsec_s16_t;
typedef s32 mapsec_s32_t;
// Met Locations for caught Pokemon use the same values as Map Sections,
// except that 0xFD, 0xFE, and 0xFF have special meanings.
//
// Because this value appears inside every Pokemon's data, widening it will
// consume a lot more space within flash memory. The space usage will be
// greater than you expect due to how Pokemon substructs are laid out; you
// would have to rearrange the substructs' contents in order to minimize
// how much more space a wider Met Location would consume.
typedef mapsec_u8_t metloc_u8_t;
#endif //GUARD_GAMETYPES_H

View File

@@ -167,7 +167,7 @@ struct MapHeader
/* 0x0C */ const struct MapConnections *connections;
/* 0x10 */ u16 music;
/* 0x12 */ u16 mapLayoutId;
/* 0x14 */ u8 regionMapSectionId;
/* 0x14 */ mapsec_u8_t regionMapSectionId;
/* 0x15 */ u8 cave;
/* 0x16 */ u8 weather;
/* 0x17 */ u8 mapType;

View File

@@ -5,6 +5,7 @@
#include <limits.h>
#include "config.h" // we need to define config before gba headers as print stuff needs the functions nulled before defines.
#include "gba/gba.h"
#include "gametypes.h"
#include "constants/global.h"
#include "constants/flags.h"
#include "constants/vars.h"

View File

@@ -226,7 +226,7 @@ typedef union // size = 0x24
/*0x04*/ u8 filler_04[2];
/*0x06*/ u16 itemIds[SMARTSHOPPER_NUM_ITEMS];
/*0x0C*/ u16 itemAmounts[SMARTSHOPPER_NUM_ITEMS];
/*0x12*/ u8 shopLocation;
/*0x12*/ mapsec_u8_t shopLocation;
/*0x13*/ u8 playerName[PLAYER_NAME_LENGTH + 1];
/*0x1B*/ //u8 padding;
} smartshopperShow;
@@ -241,7 +241,7 @@ typedef union // size = 0x24
/*0x0E*/ u16 species2;
/*0x10*/ u8 nBallsUsed;
/*0x11*/ u8 outcome;
/*0x12*/ u8 location;
/*0x12*/ mapsec_u8_t location;
/*0x13*/ u8 playerName[PLAYER_NAME_LENGTH + 1];
/*0x1B*/ //u8 padding;
} pokemonTodayFailed;
@@ -267,7 +267,7 @@ typedef union // size = 0x24
/*0x04*/ u16 caughtPoke;
/*0x06*/ u16 steps;
/*0x08*/ u16 species;
/*0x0A*/ u8 location;
/*0x0A*/ mapsec_u8_t location;
/*0x0B*/ u8 language;
/*0x0C*/ u8 filler_0C[7];
/*0x13*/ u8 playerName[PLAYER_NAME_LENGTH + 1];
@@ -282,7 +282,7 @@ typedef union // size = 0x24
/*0x04*/ u8 badgeCount;
/*0x05*/ u8 nSilverSymbols;
/*0x06*/ u8 nGoldSymbols;
/*0x07*/ u8 location;
/*0x07*/ mapsec_u8_t location;
/*0x08*/ u16 battlePoints;
/*0x0A*/ u16 mapLayoutId;
/*0x0C*/ u8 language;
@@ -309,7 +309,7 @@ typedef union // size = 0x24
/*0x00*/ u8 kind;
/*0x01*/ bool8 active;
/*0x02*/ u16 item;
/*0x04*/ u8 location;
/*0x04*/ mapsec_u8_t location;
/*0x05*/ u8 language;
/*0x06*/ u16 mapLayoutId;
/*0x08*/ u8 filler_08[11];
@@ -336,7 +336,7 @@ typedef union // size = 0x24
/*0x00*/ u8 kind;
/*0x01*/ bool8 active;
/*0x02*/ u16 lastOpponentSpecies;
/*0x04*/ u8 location;
/*0x04*/ mapsec_u8_t location;
/*0x05*/ u8 outcome;
/*0x06*/ u16 caughtMonBall;
/*0x08*/ u16 balls;
@@ -505,7 +505,7 @@ struct GabbyAndTyData
/*2BA6*/ u16 mon2;
/*2BA8*/ u16 lastMove;
/*2BAA*/ u16 quote[1];
/*2BAC*/ u8 mapnum;
/*2BAC*/ mapsec_u8_t mapnum;
/*2BAD*/ u8 battleNum;
/*2BAE*/ u8 battleTookMoreThanOneTurn:1;
u8 playerLostAMon:1;

View File

@@ -1,6 +1,6 @@
#ifndef GUARD_LANDMARK_H
#define GUARD_LANDMARK_H
const u8 *GetLandmarkName(u8 mapSection, u8 id, u8 count);
const u8 *GetLandmarkName(mapsec_u8_t mapSection, u8 id, u8 count);
#endif // GUARD_LANDMARK_H

View File

@@ -121,8 +121,8 @@ u8 GetLastUsedWarpMapType(void);
bool8 IsMapTypeOutdoors(u8 mapType);
bool8 Overworld_MapTypeAllowsTeleportAndFly(u8 mapType);
bool8 IsMapTypeIndoors(u8 mapType);
u8 GetSavedWarpRegionMapSectionId(void);
u8 GetCurrentRegionMapSectionId(void);
mapsec_u8_t GetSavedWarpRegionMapSectionId(void);
mapsec_u8_t GetCurrentRegionMapSectionId(void);
u8 GetCurrentMapBattleScene(void);
void CleanupOverworldWindowsAndTilemaps(void);
bool32 IsOverworldLinkActive(void);

View File

@@ -131,7 +131,7 @@ struct PokemonSubstruct2
struct PokemonSubstruct3
{
/* 0x00 */ u8 pokerus;
/* 0x01 */ u8 metLocation;
/* 0x01 */ metloc_u8_t metLocation;
/* 0x02 */ u16 metLevel:7;
/* 0x02 */ u16 metGame:4;

View File

@@ -17,7 +17,7 @@ struct PokenavMonListItem
struct PokenavMatchCallEntry
{
bool8 isSpecialTrainer;
u8 mapSec;
mapsec_u8_t mapSec;
u16 headerId;
};
@@ -410,7 +410,7 @@ void FreeMatchCallSubstruct1(void);
int IsMatchCallListInitFinished(void);
int GetNumberRegistered(void);
struct PokenavMatchCallEntry *GetMatchCallList(void);
u16 GetMatchCallMapSec(int index);
mapsec_u16_t GetMatchCallMapSec(int index);
bool32 ShouldDrawRematchPokeballIcon(int index);
void ClearRematchPokeballIcon(u16 windowId, u32 tileOffset);
int GetMatchCallTrainerPic(int index);
@@ -419,7 +419,7 @@ const u8 *GetMatchCallMessageText(int index, bool8 *newRematchRequest);
u16 GetMatchCallOptionCursorPos(void);
u16 GetMatchCallOptionId(int optionId);
void BufferMatchCallNameAndDesc(struct PokenavMatchCallEntry *matchCallEntry, u8 *str);
u8 GetMatchTableMapSectionId(int rematchIndex);
mapsec_u8_t GetMatchTableMapSectionId(int rematchIndex);
int GetIndexDeltaOfNextCheckPageDown(int index);
int GetIndexDeltaOfNextCheckPageUp(int index);
bool32 IsRematchEntryRegistered(int rematchIndex);

View File

@@ -26,7 +26,7 @@ enum {
};
struct RegionMap {
/*0x000*/ u16 mapSecId;
/*0x000*/ mapsec_u16_t mapSecId;
/*0x002*/ u8 mapSecType;
/*0x003*/ u8 posWithinMapSec;
/*0x004*/ u8 mapSecName[20];
@@ -99,14 +99,14 @@ void InitRegionMap(struct RegionMap *regionMap, bool8 zoomed);
u8 DoRegionMapInputCallback(void);
bool8 UpdateRegionMapZoom(void);
void FreeRegionMapIconResources(void);
u16 GetRegionMapSecIdAt(u16 x, u16 y);
mapsec_u16_t GetRegionMapSecIdAt(u16 x, u16 y);
void CreateRegionMapPlayerIcon(u16 tileTag, u16 paletteTag);
void CreateRegionMapCursor(u16 tileTag, u16 paletteTag);
bool32 IsEventIslandMapSecId(u8 mapSecId);
u8 *GetMapName(u8 *dest, u16 regionMapId, u16 padLength);
u8 *GetMapNameGeneric(u8 *dest, u16 mapSecId);
u8 *GetMapNameHandleAquaHideout(u8 *dest, u16 mapSecId);
u16 CorrectSpecialMapSecId(u16 mapSecId);
bool32 IsEventIslandMapSecId(mapsec_u8_t mapSecId);
u8 *GetMapName(u8 *dest, mapsec_u16_t regionMapId, u16 padLength);
u8 *GetMapNameGeneric(u8 *dest, mapsec_u16_t mapSecId);
u8 *GetMapNameHandleAquaHideout(u8 *dest, mapsec_u16_t mapSecId);
mapsec_u16_t CorrectSpecialMapSecId(mapsec_u16_t mapSecId);
void ShowRegionMapForPokedexAreaScreen(struct RegionMap *regionMap);
void PokedexAreaScreen_UpdateRegionMapVariablesAndVideoRegs(s16 x, s16 y);
void CB2_OpenFlyMap(void);