A custom start menu for Fire Red.
Normal:
Safari Zone:
- BW styled UI
- Configurable to add more than 6 default options.
Same as CFRU, python 3.6+ and devkitARM are required to compile.
Copy your rom to this directory and rename it to BPRE0.gba
Compile Time Constants
Open scripts/make.py in a text editor to set some compile-time configuration.
The build system is smart enough to find enough free space on its own, and if you want it to be inserted at a particular address, you can specify it by updating the definition of OFFSET_TO_PUT:
OFFSET_TO_PUT = 0x1C88650
SEARCH_FREE_SPACE = True # Set to True if you want the script to search for free space
# Set to False if you don't want to search for free space as you for example update the engineThe build system will use OFFSET_TO_PUT to determine where in the ROM it should start looking for free space if SEARCH_FREE_SPACE is True. Otherwise, the build system places the code to insert directly at OFFSET_TO_PUT.
Once you are ready run:
$ python scripts/make.pyThe code will start compiling, and generate a output rom test.gba in same directory.
Naturally, test it in an emulator.
New options can be added to the start menu. Let's try to add this town map option for instance.
The icon should be 32x32, 16 colors sprite with 2 frames. For Town Map, we'll be taking this icon.
↶ First Frame: When Town Map is Unselected
← Second Frame: When Town Map is currently selected in start menu
Now, having the icon TownMap.png ready, indexed to 16 colors, move it to graphics/sprites.
Next declare the gfx data in src/graphics.h like this:
. . .
// Town Map Gfx Data
extern const u8 TownMapTiles[];
extern const u16 TownMapPal[];
. . .Note: Rules for declaring the gfx data
The Format for declaring the gfx data is:extern u8 [FILNAME]Tiles[];
extern u16 [FILENAME]Pal[];Here [FILENAME] is the name of your icon image file(without extension).
For example, if your icon image file name is TownMap.png then [FILENAME] = TownMap and the data would be declared as follows:
extern u8 TownMapTiles[];
extern u16 TownMapPal[];Now, we'll want to have a name of our option which will be displayed in start menu list.
Open strings/start_menu.string and add the name of the option at the end of the file.
For Town Map we'll do as follows:
#org @gText_TownMap
Town Map
gText_TownMap is the label for the text data (you can choose anything for your option).
Now, open include/start_menu.h and add this lines at suitable line:
extern u8 gText_TownMap[];We need to define what will it do when the option is used. Now, there are two ways this can be done, first, through functions and second, through script.
For this, we need to define a function which will trigger all the action when A button is clicked while the option is currently selected.
You can either write your own function to get the job done or just use some function from fire red which can get your job done.
For Town Map we'll be using this function defining it in src/start_menu.c:
static void ShowTownMap(void) {
QuestLog_CutRecording();
InitRegionMapWithExitCB(REGIONMAP_TYPE_WALL, CB2_ReturnToFieldWithOpenMenu);
}Also remember to add a static declaration of this function at start of the code after the includes.
static void ShowTownMap(void);Using pre-defined functions from fire red rom (Check if you don't know)
For using a pre-defined function from the rom, you'll require to declare the function(no definition require) and the address(same thing we use with callasm to call a function/asm) of the function in the rom.- Now as long as your function does not take any parameters and does not return anything, find it and declare it any header file which is included in
src/start_menu.c.
extern void func(void);- Now define the address of the
funcinBPRE.ld.
func = 0xXXXXXXX|1;
If we were to use the predefined function for the town map option we could use ShowTownMap function located at 0x80ca7ec in fire red.
Therefore, in `include/start_menu.h:
extern void ShowTownMap(void);And, in BPRE.ld:
...
ShowTownMap = 0x80ca7ec|1;
We can also add functionality to the options using the scripts.
- First, write your script in
asesembly/script.s.
.global Script_Name
Script_Name:
@ script here
- After writing the script, declare it in
include/start_menu.hat suitable line:
extern u8 Script_Name[];Note: When scripts are used, it first go to overworld and then script is launched.
Now, if script were to used for the town map option:
- In
assembly/script.s:
.global Script_TownMap
Script_TownMap:
special 0xFB
waitstate
endNow, we need to fill up some structs and enums in order to make it work.
- Open
src/graphics.hand find there theenum GfxTagsand add an entry to at end of it, for town map we'll be usingGFXTAG_TOWNMAP.
enum GfxTags
{
GFXTAG_PANEL,
GFXTAG_EXIT,
GFXTAG_POKEDEX,
GFXTAG_POKEMON,
GFXTAG_BAG,
GFXTAG_PLAYER,
GFXTAG_SAVE,
GFXTAG_OPTIONS,
GFXTAG_SCROLLBAR,
GFXTAG_RETIRE,
GFXTAG_TOWNMAP,
};- Open
include/start_menu.hand findenum StartMenuOptionsand add an entry of name of your choice, just before theMAX_STARTMENU_ITEMS. For Town Map, we'll be usingSTARTMENU_TOWNMAP.
enum StartMenuOptions
{
STARTMENU_POKEDEX = 0,
STARTMENU_POKEMON,
STARTMENU_BAG,
STARTMENU_PLAYER,
STARTMENU_SAVE,
STARTMENU_OPTION,
STARTMENU_RETIRE,
STARTMENU_TOWNMAP,
MAX_STARTMENU_ITEMS
};- Open
include/start-menu.hand findStartMenuIconTable, here the important data related to icons is stored to access from. At the end of it add an entry for your option.
static struct StartMenuIcon StartMenuIconTable[] =
{
[STARTMENU_POKEDEX] =
{
.spritesheet = {pokedexTiles, 32*32, GFXTAG_POKEDEX},
.spritepalette = {pokedexPal, GFXTAG_POKEDEX},
.sprtemplate = icon_template(GFXTAG_POKEDEX)
},
[STARTMENU_POKEMON] =
{
.spritesheet = {pokemonTiles, 32*32, GFXTAG_POKEMON},
.spritepalette = {pokemonPal, GFXTAG_POKEMON},
.sprtemplate = icon_template(GFXTAG_POKEMON)
},
[STARTMENU_BAG] =
{
.spritesheet = {bagTiles, 32*32, GFXTAG_BAG},
.spritepalette = {bagPal, GFXTAG_BAG},
.sprtemplate = icon_template(GFXTAG_BAG)
},
[STARTMENU_PLAYER] =
{
.spritesheet = {playerTiles, 32*32, GFXTAG_PLAYER},
.spritepalette = {playerPal, GFXTAG_PLAYER},
.sprtemplate = icon_template(GFXTAG_PLAYER)
},
[STARTMENU_SAVE] =
{
.spritesheet = {saveTiles, 32*32, GFXTAG_SAVE},
.spritepalette = {savePal, GFXTAG_SAVE},
.sprtemplate = icon_template(GFXTAG_SAVE)
},
[STARTMENU_OPTION] =
{
.spritesheet = {optionsTiles, 32*32, GFXTAG_OPTIONS},
.spritepalette = {optionsPal, GFXTAG_OPTIONS},
.sprtemplate = icon_template(GFXTAG_OPTIONS)
},
[STARTMENU_RETIRE] =
{
.spritesheet = {exitTiles, 16*16 , GFXTAG_RETIRE},
.spritepalette = {exitPal, GFXTAG_RETIRE},
.sprtemplate =
{
.tileTag = GFXTAG_RETIRE,
.paletteTag = GFXTAG_RETIRE,
.oam = &sExitIconOam,
.anims = sAnimCmdTable_Exit,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = PanelCallBack,
},
},
[STARTMENU_TOWNMAP] =
{
.spritesheet = {TownMapTiles, 32*32, GFXTAG_TOWNMAP},
.spritepalette = {TownMapPal, GFXTAG_TOWNMAP},
.sprtemplate = icon_template(GFXTAG_TOWNMAP)
}
};- Open
src/start_menu.cand find and add an entry for your option.
Filling out the struct for the option
sStartMenuOptions is a table of struct StartMenuOption which has the following members:
struct StartMenuOption
{
u8 id;
u8 * text;
u16 flag;
u8 * script;
void (*func);
};- id: This is the option id, should be the same one as filled in
enum StartMenuOPtions. - text: It holds the pointer to the text for the option.
- flag: The flag which should be set for the option to appear in start menu list. Set to 0 if option should appear in start menu always.
- script: The script to use as the option's function. Set to NULL if a function is used.
- func: The function to use when option is used. Set to NULL if a script is used instead. Please refer to the other options' entry in the table for example.
static const struct StartMenuOption sStartMenuOptionsTable[] =
{
{
.id = STARTMENU_POKEDEX,
.text = (u8*) gText_StartMenu_Pokedex,
.flag = FLAG_SYS_POKEDEX_GET,
.script = NULL,
.func = CB2_OpenPokedexFromStartMenu
},
{
.id = STARTMENU_POKEMON,
.text = (u8*) gText_StartMenu_Pokemon,
.flag = FLAG_SYS_POKEMON_GET,
.script = NULL,
.func = CB2_PartyMenuFromStartMenu
},
{
.id = STARTMENU_BAG,
.text = (u8*) gText_StartMenu_Bag,
.flag = 0,
.script = NULL,
.func = CB2_BagMenuFromStartMenu
},
{
.id = STARTMENU_PLAYER,
.text = NULL, // [PLAYER] doesn't work for reason
.flag = 0,
.script = NULL,
.func = CB2_PlayerTrainerCardFromStartMenu
},
{
.id = STARTMENU_SAVE,
.text = (u8*) gText_StartMenu_Save,
.flag = 0,
.script = Script_SaveGame,
.func = NULL
},
{
.id = STARTMENU_OPTION,
.text = (u8*) gText_StartMenu_Option,
.flag = 0,
.script = NULL,
.func = CB2_OptionMenuFromStartMenu
},
{
.id = STARTMENU_RETIRE,
.text = (u8*) gText_StartMenu_Retire,
.flag = FLAG_SYS_SAFARI_MODE,
.script = Script_Retire,
.func = NULL
},
{
.id = STARTMENU_TOWNMAP,
.text = (u8*) gText_TownMap,
.flag = 0,
.script = NULL, // Script label here if you are using a script, else NULL if using a function
.func = ShowTownMap // set to NULL if using a script instead
}
}; And that's all you need to do to add a new option to start menu
- The clock in start menu requires the rtc hack to be applied to the rom in order to work.
- If you are using CFRU which also adds rtc to the rom, please update the address of the
gClockinBPRE.ldto match with CFRU's rtc gClock address before injecting the code into your rom.