3 Commits
0.11 ... 1.1

Author SHA1 Message Date
Lesserkuma
3e0a6e89cb 1.1 2024-02-16 13:55:23 +01:00
Lesserkuma
89b22c8205 1.0 2024-02-07 16:12:44 +01:00
Lesserkuma
39ab8936ec - 2024-01-06 19:27:44 +01:00
5 changed files with 68 additions and 29 deletions

View File

@@ -23,6 +23,7 @@ Set `type` to `1` or `2`:
- `1` = MSP55LV100S (e.g. The Legend of Zelda Collection - Classic Edition 7-in-1, 64 MiB) - `1` = MSP55LV100S (e.g. The Legend of Zelda Collection - Classic Edition 7-in-1, 64 MiB)
- `2` = 6600M0U0BE (e.g. 369IN1 2048M, 256 MiB) - `2` = 6600M0U0BE (e.g. 369IN1 2048M, 256 MiB)
- `3` = MSP54LV100 (e.g. The Legend of Zelda Collection - Classic Edition 7-in-1, 128 MiB) - `3` = MSP54LV100 (e.g. The Legend of Zelda Collection - Classic Edition 7-in-1, 128 MiB)
- `4` = F0095H0 (e.g. 53 in one 4G, 512 MiB)
Set `battery_present` to `true` or `false`. This will enable enhanced save data handling which will only be functional with a working battery. Set `battery_present` to `true` or `false`. This will enable enhanced save data handling which will only be functional with a working battery.
@@ -51,7 +52,7 @@ In the `games` section, you can edit the game-related stuff:
- `6` = Pokémon Black & White condensed battle font - `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. - `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. - `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.
- `keys` will let you specify a list of keys that must be held down at startup for this ROM to appear in the menu, e. g. `[ "L", "R", "DOWN" ]`. - `keys` will let you specify a list of keys that must be held down at startup for this ROM to appear in the menu, e.g. `[ "L", "R", "DOWN" ]`.
### ROM Builder Command Line Arguments ### ROM Builder Command Line Arguments
@@ -62,6 +63,7 @@ No command line arguments are required for creating a compilation, however there
--no-wait don't wait for user input when finished --no-wait don't wait for user input when finished
--no-log don't write a log file --no-log don't write a log file
--config config.json sets the config file to use --config config.json sets the config file to use
--bg bg.png sets the background image to use
--output output.gba sets the file name of the compilation ROM --output output.gba sets the file name of the compilation ROM
``` ```
@@ -83,11 +85,12 @@ Tested repro cartridges:
- 100SOP with MSP55LV100S - 100SOP with MSP55LV100S
- 100BS6600_48BALL_V4 with 6600M0U0BE - 100BS6600_48BALL_V4 with 6600M0U0BE
- SUN100S_MSP54_XXX_BGA48 with MSP54LV100 - SUN100S_MSP54_XXX_BGA48 with MSP54LV100
- F0095_4G_V1 with F0095H0
The generated compilation ROM can be written and read using a [GBxCart RW v1.4+](https://www.gbxcart.com/) device by insideGadgets and the [FlashGBX](https://github.com/lesserkuma/FlashGBX) software. The generated compilation ROM can be written and read using a [GBxCart RW v1.4+](https://www.gbxcart.com/) device by insideGadgets and the [FlashGBX](https://github.com/lesserkuma/FlashGBX) software.
## Thanks ## Thanks
Thanks to FraX, Ausar, liuyunx, BennVenn, Jenetrix Thanks to FraX, Ausar, liuyunx, BennVenn, Jenetrix, Matt
## Screenshots ## Screenshots

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 756 B

BIN
rom_builder/bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 756 B

View File

@@ -5,7 +5,7 @@
import sys, os, glob, json, math, re, struct, hashlib, argparse, datetime import sys, os, glob, json, math, re, struct, hashlib, argparse, datetime
# Configuration # Configuration
app_version = "0.11" app_version = "1.1"
default_file = "LK_MULTIMENU_<CODE>.gba" default_file = "LK_MULTIMENU_<CODE>.gba"
################################ ################################
@@ -14,7 +14,6 @@ def UpdateSectorMap(start, length, c):
sector_map[start + 1:start + length] = c * (length - 1) sector_map[start + 1:start + length] = c * (length - 1)
sector_map[start] = c.upper() sector_map[start] = c.upper()
def formatFileSize(size): def formatFileSize(size):
if size == 1: if size == 1:
return "{:d} Byte".format(size) return "{:d} Byte".format(size)
@@ -57,6 +56,12 @@ cartridge_types = [
"sector_size":0x20000, "sector_size":0x20000,
"block_size":0x80000, "block_size":0x80000,
}, },
{
"name":"F0095H0",
"flash_size":0x20000000,
"sector_size":0x40000,
"block_size":0x80000,
},
] ]
now = datetime.datetime.now() now = datetime.datetime.now()
log = "" log = ""
@@ -68,6 +73,7 @@ parser.add_argument("--split", help="splits output files into 32 MiB parts", act
parser.add_argument("--no-wait", help="dont wait for user input when finished", action="store_true", default=False) parser.add_argument("--no-wait", help="dont wait for user input when finished", action="store_true", default=False)
parser.add_argument("--no-log", help="dont write a log file", action="store_true", default=False) parser.add_argument("--no-log", help="dont write a log file", action="store_true", default=False)
parser.add_argument("--config", type=str, default="config.json", help="sets the config file to use") parser.add_argument("--config", type=str, default="config.json", help="sets the config file to use")
parser.add_argument("--bg", type=str, help="sets the background image to use")
parser.add_argument("--output", type=str, default=default_file, help="sets the file name of the compilation ROM") parser.add_argument("--output", type=str, default=default_file, help="sets the file name of the compilation ROM")
args = parser.parse_args() args = parser.parse_args()
output_file = args.output output_file = args.output
@@ -153,7 +159,36 @@ sector_map = list("." * sector_count)
# Read menu ROM # Read menu ROM
with open("lk_multimenu.gba", "rb") as f: with open("lk_multimenu.gba", "rb") as f:
menu_rom = f.read() menu_rom = bytearray(f.read())
menu_rom += bytearray([0xFF] * ((len(menu_rom) + 0x10 - (len(menu_rom) % 0x10)) - len(menu_rom)))
menu_rom += bytearray([0xFF] * 0x20)
build_timestamp_offset = len(menu_rom) - 0x20
build_timestamp = datetime.datetime.now().astimezone().replace(microsecond=0).isoformat().encode("ASCII")
menu_rom[build_timestamp_offset:build_timestamp_offset+len(build_timestamp)] = build_timestamp
# Change background image
if args.bg or os.path.exists("bg.png"):
try:
from PIL import Image
if args.bg:
img = Image.open(args.bg)
else:
img = Image.open("bg.png")
img = img.convert('P')
palette = img.getpalette()
palette_rgb555 = [((b >> 3) << 10) | ((g >> 3) << 5) | (r >> 3) for r, g, b in zip(palette[::3], palette[1::3], palette[2::3])]
raw_bitmap = bytearray(list(img.tobytes()))
raw_palette = bytearray(0x200)
pos = 0
for color in palette_rgb555:
raw_palette[pos:pos+2] = struct.pack("<H", color)
pos += 2
menu_rom_bg_offset = menu_rom.find(b"RTFN\xFF\xFE") - 0x9800
menu_rom[menu_rom_bg_offset:menu_rom_bg_offset+0x9600] = raw_bitmap
menu_rom[menu_rom_bg_offset+0x9600:menu_rom_bg_offset+0x9800] = raw_palette
except ImportError:
print("Error: Couldnt update background image. Pillow library is not installed.")
menu_rom_size = menu_rom.find(b"dkARM\0\0\0") + 8 menu_rom_size = menu_rom.find(b"dkARM\0\0\0") + 8
compilation[0:len(menu_rom)] = menu_rom compilation[0:len(menu_rom)] = menu_rom
UpdateSectorMap(start=0, length=math.ceil(len(menu_rom) / sector_size), c="m") UpdateSectorMap(start=0, length=math.ceil(len(menu_rom) / sector_size), c="m")
@@ -314,11 +349,11 @@ logp("{:.2f}% ({:d} of {:d} sectors) used\n".format(sectors_used / sector_count
logp(f"Added {len(games)} ROM(s) to the compilation\n") logp(f"Added {len(games)} ROM(s) to the compilation\n")
if battery_present: if battery_present:
logp (" | Offset | Map Size | Save Slot | Title") logp (" | Offset | Map Size | Save Slot | Title")
toc_sep = "----+-----------+-----------+----------------+---------------------------------" toc_sep = "----+------------+-----------+----------------+--------------------------------"
else: else:
logp (" | Offset | Map Size | Title") logp (" | Offset | Map Size | Title")
toc_sep = "----+-----------+-----------+--------------------------------------------------" toc_sep = "----+------------+-----------+-------------------------------------------------"
item_list = bytearray() item_list = bytearray()
@@ -331,9 +366,9 @@ for key in roms_keys:
if len(title) > 0x30: title = title[:0x2F] + "" if len(title) > 0x30: title = title[:0x2F] + ""
table_line = \ table_line = \
f"{game['index'] + 1:3d} | " \ f"{game['index'] + 1:3d} | " + \
f"0x{game['block_offset'] * block_size:07X} | "\ f"0x{game['block_offset'] * block_size:X} | ".rjust(13, " ") + \
f"0x{game['block_count'] * block_size:07X} | " f"0x{game['block_count'] * block_size:X} | ".rjust(12, " ")
if battery_present: if battery_present:
if game['save_type'] > 0: if game['save_type'] > 0:
table_line += f"{game['save_slot']+1:2d} (0x{(save_data_sector_offset + game['save_slot']) * sector_size:07X}) | " table_line += f"{game['save_slot']+1:2d} (0x{(save_data_sector_offset + game['save_slot']) * sector_size:07X}) | "
@@ -372,14 +407,15 @@ for i in range(0xA0, 0xBD):
checksum = (checksum - 0x19) & 0xFF checksum = (checksum - 0x19) & 0xFF
compilation[0xBD] = checksum compilation[0xBD] = checksum
logp("") logp("")
logp("Menu ROM: 0x{:07X}0x{:07X}".format(0, len(menu_rom))) logp("Menu ROM: 0x{:08X}0x{:08X}".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("Game List: 0x{:08X}0x{:08X}".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("Status Area: 0x{:08X}0x{:08X}".format(status_offset * sector_size, status_offset * sector_size + 0x1000))
logp("") logp("")
logp("Cartridge Type: {:d} ({:s}) {:s}".format(cartridge_type + 1, cartridge_types[cartridge_type]["name"], "with battery" if battery_present else "without battery")) logp("Cartridge Type: {:d} ({:s}) {:s}".format(cartridge_type + 1, 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 Size: {:.2f} MiB".format(rom_size / 1024 / 1024))
logp("Output ROM Code: {:s}".format(rom_code)) logp("Output ROM Code: {:s}".format(rom_code))
output_file = output_file.replace("<CODE>", rom_code) output_file = output_file.replace("<CODE>", rom_code)
if args.split: if args.split:
for i in range(0, math.ceil(flash_size / 0x2000000)): for i in range(0, math.ceil(flash_size / 0x2000000)):
pos = i * 0x2000000 pos = i * 0x2000000

View File

@@ -118,19 +118,6 @@ int main(void) {
} }
} }
memcpy(&sFlashStatus, (void *)(AGB_ROM + flash_status_sector_offset * flash_sector_size), sizeof(sFlashStatus));
if ((sFlashStatus.magic != MAGIC_FLASH_STATUS) || (sFlashStatus.last_boot_menu_index >= roms_total)) {
sFlashStatus.magic = MAGIC_FLASH_STATUS;
sFlashStatus.version = 0;
sFlashStatus.battery_present = 1;
sFlashStatus.last_boot_menu_index = 0xFFFF;
sFlashStatus.last_boot_save_index = 0xFF;
sFlashStatus.last_boot_save_type = SRAM_NONE;
} else {
cursor_pos = sFlashStatus.last_boot_menu_index % 8;
page_active = sFlashStatus.last_boot_menu_index / 8;
}
// Count number of ROMs // Count number of ROMs
for (roms_total = 0; roms_total < 512; roms_total++) { for (roms_total = 0; roms_total < 512; roms_total++) {
memcpy(&sItemConfig, ((u8*)itemlist+itemlist_offset)+(0x70*roms_total), sizeof(sItemConfig)); memcpy(&sItemConfig, ((u8*)itemlist+itemlist_offset)+(0x70*roms_total), sizeof(sItemConfig));
@@ -154,6 +141,19 @@ int main(void) {
} }
page_total = (roms_total + 8.0 - 1) / 8.0; page_total = (roms_total + 8.0 - 1) / 8.0;
memcpy(&sFlashStatus, (void *)(AGB_ROM + flash_status_sector_offset * flash_sector_size), sizeof(sFlashStatus));
if ((sFlashStatus.magic != MAGIC_FLASH_STATUS) || (sFlashStatus.last_boot_menu_index >= roms_total)) {
sFlashStatus.magic = MAGIC_FLASH_STATUS;
sFlashStatus.version = 0;
sFlashStatus.battery_present = 1;
sFlashStatus.last_boot_menu_index = 0xFFFF;
sFlashStatus.last_boot_save_index = 0xFF;
sFlashStatus.last_boot_save_type = SRAM_NONE;
} else {
cursor_pos = sFlashStatus.last_boot_menu_index % 8;
page_active = sFlashStatus.last_boot_menu_index / 8;
}
s32 wait = 0; s32 wait = 0;
u8 f = 0; u8 f = 0;
while (1) { while (1) {
@@ -188,7 +188,7 @@ int main(void) {
} }
memcpy(&sItemConfig, ((u8*)itemlist+itemlist_offset)+0x70*(page_active*8+cursor_pos), sizeof(sItemConfig)); memcpy(&sItemConfig, ((u8*)itemlist+itemlist_offset)+0x70*(page_active*8+cursor_pos), sizeof(sItemConfig));
// Draw cursor // Draw cursor
LoadFont(1); LoadFont(1);
ClearList((void*)AGB_VRAM+0xA000, SCREEN_HEIGHT - sFontSpecs.max_height - 1, sFontSpecs.max_height); ClearList((void*)AGB_VRAM+0xA000, SCREEN_HEIGHT - sFontSpecs.max_height - 1, sFontSpecs.max_height);