mirror of
https://github.com/lesserkuma/GBA_MultiMenu.git
synced 2026-04-16 21:56:01 -05:00
0.3
This commit is contained in:
parent
34b1b4b8dc
commit
a33f0ed9db
1
Makefile
1
Makefile
|
|
@ -179,6 +179,7 @@ soundbank.bin soundbank.h : $(AUDIOFILES)
|
|||
|
||||
#replacement rule for gbafix
|
||||
#---------------------------------------------------------------------------------
|
||||
$(shell touch $(CURDIR)/../$(SOURCES)/version.h)
|
||||
%.gba: %.elf
|
||||
@$(OBJCOPY) -O binary $< $@
|
||||
@gbafix $@ "-tLK MULTIMENU" "-cAGBJ" "-mLK" "-r0"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ In the `games` section, you can edit the game-related stuff:
|
|||
- `5` = Nintendo DSi IPL font (KOR)
|
||||
- `6` = Pokémon Black & White condensed battle font
|
||||
- `save_slot` defines which save slot your game uses. Set it to `null` for no saving or a number starting from `1`. Multiple games can share a save slot.
|
||||
- `map_256m`, if set to `true`, can serve as a workaround for a glitch with the cartridge mapper that causes games to freeze with screeching noises upon launch.
|
||||
|
||||
### ROM Builder Command Line Arguments
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
import sys, os, glob, json, math, re, struct, hashlib, argparse, datetime
|
||||
|
||||
# Configuration
|
||||
app_version = "0.2"
|
||||
app_version = "0.3"
|
||||
default_file = "LK_MULTIMENU_<CODE>.gba"
|
||||
|
||||
################################
|
||||
|
|
@ -39,12 +39,14 @@ def logp(*args, **kwargs):
|
|||
################################
|
||||
|
||||
cartridge_types = [
|
||||
{ # "MSP55LV100S
|
||||
{
|
||||
"name":"MSP55LV100S",
|
||||
"flash_size":0x4000000,
|
||||
"sector_size":0x20000,
|
||||
"block_size":0x80000,
|
||||
},
|
||||
{ # 6600M0U0BE
|
||||
{
|
||||
"name":"6600M0U0BE",
|
||||
"flash_size":0x10000000,
|
||||
"sector_size":0x40000,
|
||||
"block_size":0x80000,
|
||||
|
|
@ -64,11 +66,11 @@ parser.add_argument("--output", type=str, default=default_file, help="sets the f
|
|||
args = parser.parse_args()
|
||||
output_file = args.output
|
||||
if output_file == "lk_multimenu.gba":
|
||||
logp("Error: The file must not be named lk_multimenu.gba")
|
||||
logp("Error: The file must not be named “lk_multimenu.gba”")
|
||||
if not args.no_wait: input("\nPress ENTER to exit.\n")
|
||||
sys.exit(1)
|
||||
if not os.path.exists("lk_multimenu.gba"):
|
||||
logp("Error: The Menu ROM is missing.\nPlease put it in the same directory that you are running this tool from.\nExpected file name: \"lk_multimenu.gba\"")
|
||||
logp("Error: The Menu ROM is missing.\nPlease put it in the same directory that you are running this tool from.\nExpected file name: “lk_multimenu.gba”")
|
||||
if not args.no_wait: input("\nPress ENTER to exit.\n")
|
||||
sys.exit()
|
||||
|
||||
|
|
@ -97,11 +99,22 @@ if not os.path.exists(args.config):
|
|||
},
|
||||
"games": games,
|
||||
}
|
||||
with open(args.config, "w", encoding="UTF-8-SIG") as f:
|
||||
f.write(json.dumps(obj=obj, indent=4, ensure_ascii=False))
|
||||
if len(games) == 0:
|
||||
logp("Error: No usable ROM files were found in the “roms” folder.")
|
||||
else:
|
||||
with open(args.config, "w", encoding="UTF-8-SIG") as f:
|
||||
f.write(json.dumps(obj=obj, indent=4, ensure_ascii=False))
|
||||
logp(f"A new configuration file ({args.config:s}) was created based on the files inside the “roms” folder.\nPlease edit the file to your liking in a text editor, then run this tool again.")
|
||||
if not args.no_wait: input("\nPress ENTER to exit.\n")
|
||||
sys.exit()
|
||||
else:
|
||||
with open(args.config, "r", encoding="UTF-8-SIG") as f:
|
||||
j = json.load(f)
|
||||
try:
|
||||
j = json.load(f)
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
logp(f"Error: The configuration file ({args.config:s}) is malformed and could not be loaded.\n" + str(e))
|
||||
if not args.no_wait: input("\nPress ENTER to exit.\n")
|
||||
sys.exit()
|
||||
games = j["games"]
|
||||
cartridge_type = j["cartridge"]["type"] - 1
|
||||
battery_present = j["cartridge"]["battery_present"]
|
||||
|
|
@ -128,7 +141,7 @@ UpdateSectorMap(start=0, length=math.ceil(len(menu_rom) / sector_size), c="m")
|
|||
item_list_offset = len(menu_rom)
|
||||
item_list_offset = 0x40000 - (item_list_offset % 0x40000) + item_list_offset
|
||||
item_list_offset = math.ceil(item_list_offset / sector_size)
|
||||
UpdateSectorMap(start=item_list_offset, length=1, c="i")
|
||||
UpdateSectorMap(start=item_list_offset, length=1, c="l")
|
||||
status_offset = item_list_offset + 1
|
||||
UpdateSectorMap(start=status_offset, length=1, c="c")
|
||||
if battery_present:
|
||||
|
|
@ -191,7 +204,7 @@ else:
|
|||
|
||||
games = [game for game in games if not ("missing" in game and game["missing"])]
|
||||
if len(games) == 0:
|
||||
logp("No ROMs found")
|
||||
logp(f"No ROMs found. Delete the “{args.config:s}” file to reset your configuration.")
|
||||
sys.exit()
|
||||
|
||||
# Add index
|
||||
|
|
@ -206,14 +219,20 @@ c = 0
|
|||
for game in games:
|
||||
found = False
|
||||
for i in range(save_end_offset, len(sector_map)):
|
||||
if i % game["sector_count"] == 0:
|
||||
sector_count_map = game["sector_count"]
|
||||
|
||||
if "map_256m" in game and game["map_256m"] == True:
|
||||
# Map as 256M ROM, but don't waste space; some games may need this for unknown reasons
|
||||
sector_count_map = (32 * 1024 * 1024) // sector_size
|
||||
|
||||
if i % sector_count_map == 0:
|
||||
if sector_map[i:i + game["sector_count"]] == ["."] * game["sector_count"]:
|
||||
UpdateSectorMap(i, game["sector_count"], "r")
|
||||
with open(f"roms/{game['file']}", "rb") as f: rom = f.read()
|
||||
compilation[i * sector_size:i * sector_size + len(rom)] = rom
|
||||
game["sector_offset"] = i
|
||||
game["block_offset"] = game["sector_offset"] * sector_size // block_size
|
||||
game["block_count"] = game["sector_count"] * sector_size // block_size
|
||||
game["block_count"] = sector_count_map * sector_size // block_size
|
||||
found = True
|
||||
|
||||
if not boot_logo_found and hashlib.sha1(rom[0x04:0xA0]).digest() == bytearray([ 0x17, 0xDA, 0xA0, 0xFE, 0xC0, 0x2F, 0xC3, 0x3C, 0x0F, 0x6A, 0xBB, 0x54, 0x9A, 0x8B, 0x80, 0xB6, 0x61, 0x3B, 0x48, 0xEE ]):
|
||||
|
|
@ -240,10 +259,10 @@ logp("{:.2f}% ({:d} of {:d} sectors) used\n".format(sectors_used / sector_count
|
|||
logp(f"Added {len(games)} ROM(s) to the compilation\n")
|
||||
|
||||
if battery_present:
|
||||
logp (" | Offset | Size | Save Slot | Title")
|
||||
logp (" | Offset | Map Size | Save Slot | Title")
|
||||
toc_sep = "----+-----------+-----------+----------------+---------------------------------"
|
||||
else:
|
||||
logp (" | Offset | Size | Title")
|
||||
logp (" | Offset | Map Size | Title")
|
||||
toc_sep = "----+-----------+-----------+--------------------------------------------------"
|
||||
|
||||
item_list = bytearray()
|
||||
|
|
@ -259,12 +278,11 @@ for game in games:
|
|||
if game['save_type'] > 0:
|
||||
table_line += f"{game['save_slot']+1:2d} (0x{(save_data_sector_offset + game['save_slot']) * sector_size:07X}) | "
|
||||
else:
|
||||
table_line += "-------------- | "
|
||||
table_line += " | "
|
||||
table_line += f"{title}"
|
||||
if c % 8 == 0: logp(toc_sep)
|
||||
logp(table_line)
|
||||
c += 1
|
||||
#logp(f"0x{game['block_offset'] * block_size:07X} |")
|
||||
|
||||
title = title.ljust(0x30, "\0")
|
||||
item_list += bytearray(struct.pack("B", game["title_font"]))
|
||||
|
|
@ -275,6 +293,7 @@ for game in games:
|
|||
item_list += bytearray(struct.pack("B", game["save_slot"]))
|
||||
item_list += bytearray([0] * 8)
|
||||
item_list += bytearray(title.encode("UTF-16LE"))
|
||||
|
||||
compilation[item_list_offset * sector_size:item_list_offset * sector_size + len(item_list)] = item_list
|
||||
rom_code = "L{:s}".format(hashlib.sha1(status + item_list).hexdigest()[:3]).upper()
|
||||
|
||||
|
|
@ -287,8 +306,13 @@ for i in range(0xA0, 0xBD):
|
|||
checksum = (checksum - 0x19) & 0xFF
|
||||
compilation[0xBD] = checksum
|
||||
logp("")
|
||||
logp("Compilation ROM size: {:.2f} MiB".format(rom_size / 1024 / 1024))
|
||||
logp("ROM code: {:s}".format(rom_code))
|
||||
logp("Menu ROM: 0x{:07X}–0x{:07X}".format(0, len(menu_rom)))
|
||||
logp("Game List: 0x{:07X}–0x{:07X}".format(item_list_offset * sector_size, item_list_offset * sector_size + len(item_list)))
|
||||
logp("Status Area: 0x{:07X}–0x{:07X}".format(status_offset * sector_size, status_offset * sector_size + 0x1000))
|
||||
logp("")
|
||||
logp("Cartridge Type: {:d} ({:s} {:s})".format(cartridge_type, cartridge_types[cartridge_type]["name"], "with battery" if battery_present else "without battery"))
|
||||
logp("Output ROM Size: {:.2f} MiB".format(rom_size / 1024 / 1024))
|
||||
logp("Output ROM Code: {:s}".format(rom_code))
|
||||
output_file = output_file.replace("<CODE>", rom_code)
|
||||
if args.split:
|
||||
for i in range(0, math.ceil(flash_size / 0x2000000)):
|
||||
|
|
|
|||
|
|
@ -216,16 +216,20 @@ IWRAM_CODE u8 BootGame(ItemConfig config, FlashStatus status)
|
|||
return 1;
|
||||
|
||||
// Temporarily store SRAM values at mapper registers
|
||||
sram_register_backup[0] = *(vu8 *)MAPPER_CONFIG1;
|
||||
sram_register_backup[1] = *(vu8 *)MAPPER_CONFIG2;
|
||||
sram_register_backup[2] = *(vu8 *)MAPPER_CONFIG3;
|
||||
sram_register_backup[3] = *(vu8 *)MAPPER_CONFIG4;
|
||||
if (status.last_boot_save_type == SRAM_NONE)
|
||||
{
|
||||
sram_register_backup[0] = *(vu8 *)MAPPER_CONFIG1;
|
||||
sram_register_backup[1] = *(vu8 *)MAPPER_CONFIG2;
|
||||
sram_register_backup[2] = *(vu8 *)MAPPER_CONFIG3;
|
||||
sram_register_backup[3] = *(vu8 *)MAPPER_CONFIG4;
|
||||
}
|
||||
|
||||
// Enable SRAM access
|
||||
*(vu8 *)MAPPER_CONFIG4 = 1;
|
||||
|
||||
// Write previous SRAM to flash
|
||||
if (status.battery_present) {
|
||||
if (status.battery_present)
|
||||
{
|
||||
if (status.last_boot_save_type != SRAM_NONE)
|
||||
{
|
||||
for (int i = 0; i < SRAM_SIZE; i++)
|
||||
|
|
@ -279,7 +283,7 @@ IWRAM_CODE u8 BootGame(ItemConfig config, FlashStatus status)
|
|||
*(vu8 *)MAPPER_CONFIG3 = 0x40 - config.rom_size; // accessible ROM size (in 512 KB blocks)
|
||||
|
||||
// Wait until menu ROM is no longer visible
|
||||
u16 timeout = 0xFFFF;
|
||||
u32 timeout = 0x2FFF;
|
||||
while (((vu16 *)AGB_ROM)[0x58] == 0x4B4C)
|
||||
{
|
||||
if (!timeout--)
|
||||
|
|
@ -294,16 +298,20 @@ IWRAM_CODE u8 BootGame(ItemConfig config, FlashStatus status)
|
|||
// Lock mapper
|
||||
*(vu8 *)MAPPER_CONFIG2 |= 0x80;
|
||||
|
||||
// Restore SRAM values at mapper registers
|
||||
*(vu8 *)MAPPER_CONFIG1 = sram_register_backup[0];
|
||||
*(vu8 *)MAPPER_CONFIG2 = sram_register_backup[1];
|
||||
*(vu8 *)MAPPER_CONFIG3 = sram_register_backup[2];
|
||||
*(vu8 *)MAPPER_CONFIG4 = sram_register_backup[3];
|
||||
|
||||
// Write buffer to SRAM
|
||||
for (int i = 0; i < SRAM_SIZE; i++)
|
||||
if (config.save_type != SRAM_NONE)
|
||||
{
|
||||
((vu8 *)AGB_SRAM)[i] = data_buffer[i];
|
||||
for (int i = 0; i < SRAM_SIZE; i++)
|
||||
{
|
||||
((vu8 *)AGB_SRAM)[i] = data_buffer[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*(vu8 *)MAPPER_CONFIG1 = sram_register_backup[0];
|
||||
*(vu8 *)MAPPER_CONFIG2 = sram_register_backup[1];
|
||||
*(vu8 *)MAPPER_CONFIG3 = sram_register_backup[2];
|
||||
*(vu8 *)MAPPER_CONFIG4 = sram_register_backup[3];
|
||||
}
|
||||
|
||||
// Clear palette
|
||||
|
|
@ -313,7 +321,7 @@ IWRAM_CODE u8 BootGame(ItemConfig config, FlashStatus status)
|
|||
}
|
||||
REG_BLDY = 0;
|
||||
|
||||
// Soft Reset system call
|
||||
// Boot ROM
|
||||
__asm("swi 0"); // Soft reset
|
||||
|
||||
return 3;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ Author: Lesserkuma (github.com/lesserkuma)
|
|||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "version.h"
|
||||
#include "main.h"
|
||||
#include "font.h"
|
||||
#include "flash.h"
|
||||
|
|
@ -186,14 +187,10 @@ int main(void) {
|
|||
}
|
||||
} else if (show_credits) {
|
||||
LoadFont(0);
|
||||
#ifdef __TIMESTAMP_ISO__
|
||||
memset(temp_unicode, 0, sizeof(temp_unicode));
|
||||
snprintf(temp_ascii, 48, "Menu by LK - %s", __TIMESTAMP_ISO__);
|
||||
AsciiToUnicode(temp_ascii, temp_unicode);
|
||||
DrawText(6, SCREEN_HEIGHT - sFontSpecs.max_height - 3 - FontMarginBottom, ALIGN_LEFT, temp_unicode, 48, font, (void*)AGB_VRAM+0xA000, FALSE);
|
||||
#else
|
||||
DrawText(6, SCREEN_HEIGHT - sFontSpecs.max_height - 3 - FontMarginBottom, ALIGN_LEFT, u"Menu by LK", 48, font, (void*)AGB_VRAM+0xA000, FALSE);
|
||||
#endif
|
||||
memset(temp_unicode, 0, sizeof(temp_unicode));
|
||||
snprintf(temp_ascii, 48, "Menu by LK - %s", BUILDTIME);
|
||||
AsciiToUnicode(temp_ascii, temp_unicode);
|
||||
DrawText(6, SCREEN_HEIGHT - sFontSpecs.max_height - 3 - FontMarginBottom, ALIGN_LEFT, temp_unicode, 48, font, (void*)AGB_VRAM+0xA000, FALSE);
|
||||
} else if (show_debug) {
|
||||
LoadFont(0);
|
||||
u8 a = ((sItemConfig.rom_offset / 0x40) & 0xF) << 4;
|
||||
|
|
|
|||
15
source/version.h
Normal file
15
source/version.h
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
GBA Multi Game Menu
|
||||
Author: Lesserkuma (github.com/lesserkuma)
|
||||
*/
|
||||
|
||||
#ifndef VERSION_H_
|
||||
#define VERSION_H_
|
||||
|
||||
#ifdef __TIMESTAMP_ISO__
|
||||
#define BUILDTIME __TIMESTAMP_ISO__
|
||||
#else
|
||||
#define BUILDTIME __TIMESTAMP__
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue
Block a user