GBA database updates (#128)

This commit is contained in:
Harrison 2023-08-22 19:30:01 -04:00 committed by GitHub
parent 0f55ad941c
commit 92c591da22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 331 additions and 265 deletions

7
.gitignore vendored
View File

@ -7,9 +7,10 @@ arm9/open_agb_firm9.bin
arm11/open_agb_firm11.bin
arm9/open_agb_firm9.elf
arm11/open_agb_firm11.elf
tools/gba-db-builder/gba_db.bin
tools/gba-db-builder/gba.dat
tools/gba-db-builder/gba.xml
tools/gba-db/gba_db.log
tools/gba-db/gba.xml
tools/gba-db/gba.dat
tools/gba-db/gba_db.bin
open_agb_firm.firm
open_agb_firm*.7z
TODO.txt

Binary file not shown.

View File

@ -100,11 +100,11 @@ typedef struct
typedef struct
{
char name[200];
char serial[4];
u8 sha1[20];
char serial[4];
u32 attr;
} GameDbEntry;
} GbaDbEntry;
static_assert(sizeof(GbaDbEntry) == 28, "Error: GBA DB entry struct is not packed!");
// Default config.
@ -317,7 +317,7 @@ static u16 detectSaveType(u32 romSize)
}
// Search for entry with first u64 of the SHA1 = x using binary search.
static Result searchGbaDb(u64 x, GameDbEntry *const db, s32 *const entryPos)
static Result searchGbaDb(u64 x, GbaDbEntry *const db, s32 *const entryPos)
{
debug_printf("Database search: '%016" PRIX64 "'\n", __builtin_bswap64(x));
@ -326,14 +326,14 @@ static Result searchGbaDb(u64 x, GameDbEntry *const db, s32 *const entryPos)
if((res = fOpen(&f, "gba_db.bin", FA_OPEN_EXISTING | FA_READ)) == RES_OK)
{
s32 l = 0;
s32 r = fSize(f) / sizeof(GameDbEntry) - 1; // TODO: Check for 0!
s32 r = fSize(f) / sizeof(GbaDbEntry) - 1; // TODO: Check for 0!
while(1)
{
const s32 mid = l + (r - l) / 2;
debug_printf("l: %ld r: %ld mid: %ld\n", l, r, mid);
if((res = fLseek(f, sizeof(GameDbEntry) * mid)) != RES_OK) break;
if((res = fRead(f, db, sizeof(GameDbEntry), NULL)) != RES_OK) break;
if((res = fLseek(f, sizeof(GbaDbEntry) * mid)) != RES_OK) break;
if((res = fRead(f, db, sizeof(GbaDbEntry), NULL)) != RES_OK) break;
const u64 tmp = *(u64*)db->sha1; // Unaligned access.
if(tmp == x)
{
@ -343,7 +343,7 @@ static Result searchGbaDb(u64 x, GameDbEntry *const db, s32 *const entryPos)
if(r <= l)
{
debug_printf("Not found!");
debug_printf("Not found!\n");
res = RES_NOT_FOUND;
break;
}
@ -369,7 +369,7 @@ static u16 getSaveType(u32 romSize, const char *const savePath)
sha((u32*)LGY_ROM_LOC, romSize, (u32*)sha1, SHA_IN_BIG | SHA_1_MODE, SHA_OUT_BIG);
Result res;
GameDbEntry dbEntry;
GbaDbEntry dbEntry;
s32 dbPos = -1;
u16 saveType = SAVE_TYPE_NONE;
res = searchGbaDb(*sha1, &dbEntry, &dbPos);
@ -383,72 +383,71 @@ static u16 getSaveType(u32 romSize, const char *const savePath)
}
debug_printf("saveType: %u\n", saveType);
if(saveOverride)
if(!saveOverride) goto end;
consoleClear();
ee_printf("==Save Type Override Menu==\n"
"Save file: %s\n"
"Save type (autodetected): %u\n"
"Save type (from gba_db.bin): ", (saveExists ? "Found" : "Not found"), autoSaveType);
if(res == RES_NOT_FOUND)
ee_puts("Not found");
else
ee_printf("%u\n", saveType);
ee_puts("\n"
"=Save Types=\n"
" EEPROM 8k (0, 1)\n"
" EEPROM 64k (2, 3)\n"
" Flash 512k RTC (4, 6, 8)\n"
" Flash 512k (5, 7, 9)\n"
" Flash 1m RTC (10, 12)\n"
" Flash 1m (11, 13)\n"
" SRAM 256k (14)\n"
" None (15)\n\n"
"=Controls=\n"
"Up/Down: Navigate\n"
"A: Select\n"
"X: Delete save file");
static const u8 saveTypeCursorLut[16] = {0, 0, 1, 1, 2, 3, 2, 3, 2, 3, 4, 5, 4, 5, 6, 7};
u8 oldCursor = 0;
u8 cursor;
if(!g_oafConfig.useGbaDb || res == RES_NOT_FOUND)
cursor = saveTypeCursorLut[autoSaveType];
else
cursor = saveTypeCursorLut[saveType];
while(1)
{
consoleClear();
ee_printf("==Save Type Override Menu==\n"
"Save file: %s\n"
"Save type (autodetected): %u\n"
"Save type (from gba_db.bin): ", (saveExists ? "Found" : "Not found"), autoSaveType);
if(res == RES_NOT_FOUND)
ee_puts("Not found");
else
ee_printf("%u\n", saveType);
ee_puts("\n"
"=Save Types=\n"
" EEPROM 8k (0, 1)\n"
" EEPROM 64k (2, 3)\n"
" Flash 512k RTC (4, 6, 8)\n"
" Flash 512k (5, 7, 9)\n"
" Flash 1m RTC (10, 12)\n"
" Flash 1m (11, 13)\n"
" SRAM 256k (14)\n"
" None (15)\n\n"
"=Controls=\n"
"Up/Down: Navigate\n"
"A: Select\n"
"X: Delete save file");
ee_printf("\x1b[%u;H ", oldCursor + 6);
ee_printf("\x1b[%u;H>", cursor + 6);
oldCursor = cursor;
static const u8 saveTypeCursorLut[16] = {0, 0, 1, 1, 2, 3, 2, 3, 2, 3, 4, 5, 4, 5, 6, 7};
u8 oldCursor = 0;
u8 cursor;
if(!g_oafConfig.useGbaDb || res == RES_NOT_FOUND)
cursor = saveTypeCursorLut[autoSaveType];
else
cursor = saveTypeCursorLut[saveType];
while(1)
u32 kDown;
do
{
ee_printf("\x1b[%u;H ", oldCursor + 6);
ee_printf("\x1b[%u;H>", cursor + 6);
oldCursor = cursor;
GFX_waitForVBlank0();
u32 kDown;
do
{
GFX_waitForVBlank0();
hidScanInput();
if(hidGetExtraKeys(0) & (KEY_POWER_HELD | KEY_POWER)) goto end;
kDown = hidKeysDown();
} while(kDown == 0);
hidScanInput();
if(hidGetExtraKeys(0) & (KEY_POWER_HELD | KEY_POWER)) goto end;
kDown = hidKeysDown();
} while(kDown == 0);
if((kDown & KEY_DUP) && cursor > 0) cursor--;
else if((kDown & KEY_DDOWN) && cursor < 7) cursor++;
else if(kDown & KEY_X)
{
fUnlink(savePath);
ee_printf("\x1b[1;11HDeleted ");
}
else if(kDown & KEY_A) break;
}
static const u8 cursorSaveTypeLut[8] = {0, 2, 8, 9, 10, 11, 14, 15};
saveType = cursorSaveTypeLut[cursor];
if(saveType == SAVE_TYPE_EEPROM_8k || saveType == SAVE_TYPE_EEPROM_64k)
if((kDown & KEY_DUP) && cursor > 0) cursor--;
else if((kDown & KEY_DDOWN) && cursor < 7) cursor++;
else if(kDown & KEY_X)
{
// If ROM bigger than 16 MiB --> SAVE_TYPE_EEPROM_8k_2 or SAVE_TYPE_EEPROM_64k_2.
if(romSize > 0x1000000) saveType++;
fUnlink(savePath);
ee_printf("\x1b[1;11HDeleted ");
}
else if(kDown & KEY_A) break;
}
static const u8 cursorSaveTypeLut[8] = {0, 2, 8, 9, 10, 11, 14, 15};
saveType = cursorSaveTypeLut[cursor];
if(saveType == SAVE_TYPE_EEPROM_8k || saveType == SAVE_TYPE_EEPROM_64k)
{
// If ROM bigger than 16 MiB --> SAVE_TYPE_EEPROM_8k_2 or SAVE_TYPE_EEPROM_64k_2.
if(romSize > 0x1000000) saveType++;
}
end:

View File

@ -1,21 +0,0 @@
Title,Serial,SHA-1,Size,Save Type
0246 - Kinniku Banzuke - Kimero! Kiseki no Kanzen Seiha (Japan),AK5J,CF0A6C1C473BA6C85027B6071AA1CF6E21336974,8388608,14
1364 - Famicom Mini 01 - Super Mario Bros. (Japan) (En) (Rev 1),FSMJ,F08B1F60E41FC2080C50C65EA2B2AF912661ED99,1048576,0
1366 - Famicom Mini 02 - Donkey Kong (Japan) (En),FDKJ,B5AFC36A8203C2C485344819D069300ECAFD9657,1048576,0
"1492 - Classic NES Series - The Legend of Zelda (USA, Europe)",FZLE,28AAC26365BF41BA84E67F97E98D15C4678CB99D,1048576,2
"1494 - Classic NES Series - Super Mario Bros. (USA, Europe)",FSME,8CA35864AE33C9462DD66CEFF1FAC5A79E2E0A6F,1048576,0
"1496 - Classic NES Series - Bomberman (USA, Europe)",FBME,741EB2874C526CC014BF3E642B4EE37F18312735,1048576,0
"1498 - Classic NES Series - Xevious (USA, Europe)",FXVE,B2088582808480E0D70C63A777B046409D4E15C4,1048576,0
"1499 - Classic NES Series - Pac-Man (USA, Europe)",FP7E,843D853ED28A116C85A5357F9A94E9179F36A6D0,1048576,0
"1500 - Classic NES Series - Ice Climber (USA, Europe)",FICE,64E965D61B2D1BE5DFADB0236FED83FEA5995724,1048576,0
"1501 - Classic NES Series - Donkey Kong (USA, Europe)",FDKE,8E3B203630F10C32AA7896AFE9B7FBED7E1C8D30,1048576,0
"1721 - Classic NES Series - Zelda II - The Adventure of Link (USA, Europe)",FLBE,22EA42AD9A99A6BC0FCBA8721CF8F484F68C3BF7,1048576,2
1722 - Classic NES Series - Castlevania (USA),FADE,47A60315ED4074A8C986723B95B3C90513D3B35C,1048576,0
"1723 - Classic NES Series - Dr. Mario (USA, Europe)",FDME,FC396F0EAE55CF19E573AA322F525427E03D3854,1048576,0
"1724 - Classic NES Series - Metroid (USA, Europe)",FMRE,838DA11F879E2FA3FFDEF95E1C6D5A54246C1846,1048576,0
2830 - NES Classics - Castlevania (Europe),FADP,8BC8740A681E4D365419DBCEE73619FE4429D66E,1048576,0
"2841 - King Kong - The Official Game of the Movie (Europe) (En,Sv,No,Da,Fi)",BKQX,ECFB6409ABB7FF429AADC65A6B953DBAEF3D09D8,8388608,0
2846 - Dogz 2 (USA) (Rev 1),BIME,FF2772E347212264D1A1C4D322A08685ADC1E6A7,16777216,2
x025 - Famicom Mini - Dai-2-ji Super Robot Taisen (Japan) (Promo),FSRJ,8FA03B1E23E7DEA0DBC841D23046D03D246A1CDF,1048576,2
x026 - Famicom Mini - Kidou Senshi Z Gundam - Hot Scramble (Japan) (Promo),FGZJ,B4C1A3582F596D3912A1A7EA2D1EB6681B769448,1048576,0
x027 - Super Mario Bros. (Japan) (Hot Mario Campaign),FSMJ,6701010F7C195CF3FBDEDB19E4627835A3158748,1048576,15
1 Title Serial SHA-1 Size Save Type
2 0246 - Kinniku Banzuke - Kimero! Kiseki no Kanzen Seiha (Japan) AK5J CF0A6C1C473BA6C85027B6071AA1CF6E21336974 8388608 14
3 1364 - Famicom Mini 01 - Super Mario Bros. (Japan) (En) (Rev 1) FSMJ F08B1F60E41FC2080C50C65EA2B2AF912661ED99 1048576 0
4 1366 - Famicom Mini 02 - Donkey Kong (Japan) (En) FDKJ B5AFC36A8203C2C485344819D069300ECAFD9657 1048576 0
5 1492 - Classic NES Series - The Legend of Zelda (USA, Europe) FZLE 28AAC26365BF41BA84E67F97E98D15C4678CB99D 1048576 2
6 1494 - Classic NES Series - Super Mario Bros. (USA, Europe) FSME 8CA35864AE33C9462DD66CEFF1FAC5A79E2E0A6F 1048576 0
7 1496 - Classic NES Series - Bomberman (USA, Europe) FBME 741EB2874C526CC014BF3E642B4EE37F18312735 1048576 0
8 1498 - Classic NES Series - Xevious (USA, Europe) FXVE B2088582808480E0D70C63A777B046409D4E15C4 1048576 0
9 1499 - Classic NES Series - Pac-Man (USA, Europe) FP7E 843D853ED28A116C85A5357F9A94E9179F36A6D0 1048576 0
10 1500 - Classic NES Series - Ice Climber (USA, Europe) FICE 64E965D61B2D1BE5DFADB0236FED83FEA5995724 1048576 0
11 1501 - Classic NES Series - Donkey Kong (USA, Europe) FDKE 8E3B203630F10C32AA7896AFE9B7FBED7E1C8D30 1048576 0
12 1721 - Classic NES Series - Zelda II - The Adventure of Link (USA, Europe) FLBE 22EA42AD9A99A6BC0FCBA8721CF8F484F68C3BF7 1048576 2
13 1722 - Classic NES Series - Castlevania (USA) FADE 47A60315ED4074A8C986723B95B3C90513D3B35C 1048576 0
14 1723 - Classic NES Series - Dr. Mario (USA, Europe) FDME FC396F0EAE55CF19E573AA322F525427E03D3854 1048576 0
15 1724 - Classic NES Series - Metroid (USA, Europe) FMRE 838DA11F879E2FA3FFDEF95E1C6D5A54246C1846 1048576 0
16 2830 - NES Classics - Castlevania (Europe) FADP 8BC8740A681E4D365419DBCEE73619FE4429D66E 1048576 0
17 2841 - King Kong - The Official Game of the Movie (Europe) (En,Sv,No,Da,Fi) BKQX ECFB6409ABB7FF429AADC65A6B953DBAEF3D09D8 8388608 0
18 2846 - Dogz 2 (USA) (Rev 1) BIME FF2772E347212264D1A1C4D322A08685ADC1E6A7 16777216 2
19 x025 - Famicom Mini - Dai-2-ji Super Robot Taisen (Japan) (Promo) FSRJ 8FA03B1E23E7DEA0DBC841D23046D03D246A1CDF 1048576 2
20 x026 - Famicom Mini - Kidou Senshi Z Gundam - Hot Scramble (Japan) (Promo) FGZJ B4C1A3582F596D3912A1A7EA2D1EB6681B769448 1048576 0
21 x027 - Super Mario Bros. (Japan) (Hot Mario Campaign) FSMJ 6701010F7C195CF3FBDEDB19E4627835A3158748 1048576 15

View File

@ -1,173 +0,0 @@
#!/usr/bin/env python3
# open_agb_firm gba_db.bin Builder v3.0
# By HTV04
#
# This script parses MAME's gba.xml (found here: https://github.com/mamedev/mame/blob/master/hash/gba.xml) and converts it to a gba_db.bin file for open_agb_firm.
# No-Intro's GBA DAT (with scene numbers) is also used for filtering and naming (found here: https://datomatic.no-intro.org/). The DAT should be renamed to "gba.dat".
# Unless otherwise specified, entries from an addentries.csv file are also added. This file usually includes entries that cannot be not found or are wrong in MAME's gba.xml.
#
# This script should work with any updates to MAME's gba.xml and the No-Intro DAT, unless something this script expects is changed.
import csv
import math
import re
import sys
import xml.etree.ElementTree as ET
# Use title, serial, SHA-1, size, and save type to generate gba_db entry as binary string
def gbadbentry(title, serial, sha, size, savetype):
entry = []
if len(sha) != 40:
sha = '0000000000000000000000000000000000000000'
shabytes = bytes.fromhex(sha)
entry.append(int.from_bytes(shabytes[:8], 'little')) # Sorting key
entry.append(title.encode().ljust(200, b'\x00')[:200])
if len(serial) != 4 or bool(re.search('[^A-Z0-9]', serial)):
entry.append(b'\x00\x00\x00\x00')
else:
entry.append(serial.encode())
entry.append(shabytes)
entry.append((int(math.log(size, 2)) << 27 | savetype).to_bytes(4, 'little'))
return entry
# Prepare gba_db list for gba_db.bin
def preparegbadb(gbadb):
# Use sort key to sort the gba_db list and delete it from each entry
gbadb = sorted(gbadb, key=lambda l:l[0])
for i in range(len(gbadb)):
gbadb[i].pop(0)
# Compile gba_db binary
gbadbbin = b''
for i in gbadb:
for j in i:
gbadbbin += j
return gbadbbin
if __name__ == '__main__':
# Arguments (could totally be done better but this will do for now)
noaddentries = False
if len(sys.argv) >= 2 and sys.argv[1] == 'noaddentries': # Don't include anything that isn't in gba.xml and gba.dat
noaddentries = True
# Start adding entries
gbadb = []
skipcount = 0
count = 0
gba = ET.parse('gba.xml').getroot() # MAME gba.xml
nointro = ET.parse('gba.dat').getroot() # No-Intro GBA DAT
for software in gba.findall('software'):
for part in software.findall('part'):
if part.get('name') == 'cart':
# Obtain SHA-1
for dataarea in part.findall('dataarea'):
if dataarea.get('name') == 'rom':
sha = dataarea.find('rom').get('sha1')
break
# Obtain title, serial, SHA-1, and size from No-Intro DAT
matchfound = False
for game in nointro.findall('game'):
for rom in game.findall('rom'):
if rom.get('sha1').lower() == sha:
title = game.get('name')
serial = rom.get('serial')
if serial == None:
serial = ''
size = int(rom.get('size'))
matchfound = True
break
# If not in No-Intro DAT, skip entry
if not matchfound:
break
# Obtain save type
savetype = 15 # SAVE_TYPE_NONE
for feature in part.findall('feature'):
if feature.get('name') == 'slot':
slottype = feature.get('value')
if slottype in ('gba_eeprom_4k', 'gba_yoshiug', 'gba_eeprom'):
savetype = 0 # SAVE_TYPE_EEPROM_8k
if size > 0x1000000:
savetype += 1 # SAVE_TYPE_EEPROM_8k_2
elif slottype in ('gba_eeprom_64k', 'gba_boktai'):
savetype = 2 # SAVE_TYPE_EEPROM_64k
if size > 0x1000000:
savetype += 1 # SAVE_TYPE_EEPROM_64k_2
elif slottype == 'gba_flash_rtc':
savetype = 8 # SAVE_TYPE_FLASH_512k_PSC_RTC
elif slottype in ('gba_flash', 'gba_flash_512'):
savetype = 9 # SAVE_TYPE_FLASH_512k_PSC
elif slottype == 'gba_flash_1m_rtc':
savetype = 10 # SAVE_TYPE_FLASH_1m_MRX_RTC
elif slottype == 'gba_flash_1m':
savetype = 11 # SAVE_TYPE_FLASH_1m_MRX
elif slottype in ('gba_sram', 'gba_drilldoz', 'gba_wariotws'):
savetype = 14 # SAVE_TYPE_SRAM_256k
break
# If not in No-Intro DAT, skip entry
if not matchfound:
print ('Skipped "' + software.find('description').text + '"')
skipcount += 1
continue
# Add entry to gba_db
entry = gbadbentry(title, serial, sha, size, savetype)
for i in range(len(gbadb)):
if gbadb[i][3].hex() == sha:
print('Duplicate entry "' + gbadb[i][1].decode() + '" replaced')
gbadb[i] = entry
skipcount += 1
break
else:
gbadb.append(entry)
count += 1
print('Added entry "' + title + '"')
# Add additional entries from addentries.csv if "noaddentries" is false
if not noaddentries:
with open('addentries.csv') as f:
addentries = list(csv.reader(f))
addentries.pop(0)
for i in addentries:
i[3] = int(i[3])
i[4] = int(i[4])
print()
for title, serial, sha, size, savetype in addentries:
entry = gbadbentry(title, serial, sha, size, savetype)
for i in range(len(gbadb)):
if gbadb[i][3].hex().upper() == sha:
print('Duplicate entry "' + gbadb[i][1].decode() + '" replaced')
gbadb[i] = entry
skipcount += 1
break
else:
gbadb.append(entry)
count += 1
print('Added additional entry "' + title + '"')
gbadbbin = preparegbadb(gbadb)
# Create and write to gba_db.bin
with open('gba_db.bin', 'wb') as f:
f.write(gbadbbin)
print('\n' + str(count) + ' entries added, ' + str(skipcount) + ' entries skipped')

239
tools/gba-db/gba-db.py Executable file
View File

@ -0,0 +1,239 @@
#!/usr/bin/env python3
# open_agb_firm gba_db.bin Builder v4.0
# By HTV04
#
# This script parses MAME's "gba.xml" (https://github.com/mamedev/mame/blob/master/hash/gba.xml)
# and converts it to a "gba_db.bin" file for open_agb_firm.
#
# The official gba_db.bin is built using "--dat" and "--csv." The former uses No-Intro's "gba.dat"
# (https://datomatic.no-intro.org/) to verify the SHA-1 and size of each entry. The latter uses
# "gba.csv" to add additional entries and overrides.
#
# Note that, for efficiency, this script does not check for formatting errors and assumes that all
# entries are valid. Errors may occur otherwise.
# MIT License
#
# Copyright (c) 2023 HTV04
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import enum
import sys
import re
import xml.etree.ElementTree
import csv
class Entry:
def __init__(self):
self.sha1 = '\x00' * 20
self.serial = '\x00' * 4
self.attr = '\x00' * 4
class Database:
def __init__(self, log):
class SaveType(enum.IntEnum):
EEPROM_8K = 0
EEPROM_8K_2 = 1
EEPROM_64K = 2
EEPROM_64K_2 = 3
FLASH_512K_PSC_RTC = 8
FLASH_512K_PSC = 9
FLASH_1M_MRX_RTC = 10
FLASH_1M_MRX = 11
SRAM_256K = 14
NONE = 15
class XmlData:
def __init__(self, software):
def find_name(node, child, name):
for found in node.findall(child):
if found.get('name') == name:
return found
else:
return None
cart = find_name(software, 'part', 'cart')
serial = find_name(software, 'info', 'serial')
if serial is not None:
serial = re.search(r'[A-Z0-9]{4}', serial.get('value'))
rom = find_name(cart, 'dataarea', 'rom').find('rom')
size = int(rom.get('size'), base=0)
slot = find_name(cart, 'feature', 'slot')
save_type = SaveType.NONE
if slot is not None:
match slot.get('value'):
case 'gba_eeprom_4k' | 'gba_yoshiug' | 'gba_eeprom':
if size > 0x1000000:
save_type = SaveType.EEPROM_8K_2
else:
save_type = SaveType.EEPROM_8K
case 'gba_eeprom_64k' | 'gba_boktai':
if size > 0x1000000:
save_type = SaveType.EEPROM_64K_2
else:
save_type = SaveType.EEPROM_64K
case 'gba_flash_rtc':
save_type = SaveType.FLASH_512K_PSC_RTC
case 'gba_flash' | 'gba_flash_512':
save_type = SaveType.FLASH_512K_PSC
case 'gba_flash_1m_rtc':
save_type = SaveType.FLASH_1M_MRX_RTC
case 'gba_flash_1m':
save_type = SaveType.FLASH_1M_MRX
case 'gba_sram' | 'gba_drilldoz' | 'gba_wariotws':
save_type = SaveType.SRAM_256K
self.sha1 = bytes.fromhex(rom.get('sha1'))
if serial is None:
self.serial = b'\x00\x00\x00\x00'
else:
self.serial = serial.group().encode('ascii')
self.attr = int(save_type).to_bytes(4, byteorder='little')
class DatData:
def __init__(self, dat, xml_data):
sha1 = xml_data.sha1.hex()
self.exists = True
for game in dat.findall('game'):
rom = game.find('rom')
if rom.get('sha1') == sha1:
serial = rom.find('serial')
self.size = int(rom.get('size'))
if serial is None:
self.serial = xml_data.serial
else:
self.serial = serial.encode('ascii')
break
else:
self.exists = False
def get_entry(software, dat):
entry = Entry()
xml_data = XmlData(software)
entry.sha1 = xml_data.sha1
for test in self.entries:
if test.sha1 == entry.sha1:
return 'Duplicate SHA-1 "' + entry.sha1.hex() + '" '
entry.attr = xml_data.attr
if dat is None:
entry.serial = xml_data.serial
else:
dat_data = DatData(dat, xml_data)
if dat_data.exists:
entry.serial = dat_data.serial
else:
return 'SHA-1 "' + entry.sha1.hex() + '" not found in No-Intro DAT'
if len(entry.sha1) + len(entry.serial) + len(entry.attr) != 28:
raise Exception('Database size is not a multiple of 28')
return entry
self.entries = []
log('Starting log...\n\n')
fail_count = 0
count = 0
if '--dat' in sys.argv:
dat = xml.etree.ElementTree.parse('gba.dat').getroot()
log('Using gba.dat!\n\n')
else:
dat = None
for software in xml.etree.ElementTree.parse('gba.xml').getroot().findall('software'):
log('Adding "' + software.find('description').text + '"\n')
ret = get_entry(software, dat)
if isinstance(ret, Entry):
count += 1
self.entries.append(ret)
log('Successfully added entry: ' + ret.sha1.hex())
else:
fail_count += 1
log('Failed to add entry: ' + ret)
log('\n\n')
if '--csv' in sys.argv:
log('Using gba.csv!\n\n')
with open('gba.csv') as f:
for sha1, serial, save_type in list(csv.reader(f))[1:]:
entry = Entry()
entry.sha1 = bytes.fromhex(sha1)
entry.serial = serial.encode('ascii')
entry.attr = int(SaveType(int(save_type))).to_bytes(4, byteorder='little')
for i in range(len(self.entries)):
if self.entries[i].sha1 == entry.sha1:
self.entries[i] = entry
log('Duplicate SHA-1 "' + entry.sha1.hex() + '," replaced')
break
else:
count += 1
self.entries.append(entry)
log('Added "' + entry.sha1.hex() + '"')
log('\n')
log('\n')
log('Compiled with ' + str(count) + ' entries, ' + str(fail_count) + ' failures.\n')
def compile(self, out):
for entry in sorted(self.entries, key=lambda a: int.from_bytes(a.sha1[:8], byteorder='little')):
out(entry.sha1)
out(entry.serial)
out(entry.attr)
if __name__ == '__main__':
if '--help' in sys.argv:
print('open_agb_firm gba_db.bin Builder v4.0')
print('By HTV04')
print()
print('Usage: gba-db.py [options]')
print(' --log: Write log to "gba_db.log"')
print()
print(' --dat: Use No-Intro "gba.dat" for verification')
print(' --csv: Use "gba.csv" for additional entries and overrides')
print()
print(' --out [file]: Output to [file] instead of "gba_db.bin"')
print()
print(' --help: Display this help message and exit')
sys.exit(0)
if '--log' in sys.argv:
with open('gba_db.log', 'w') as f:
db = Database(f.write)
else:
db = Database(lambda text: None)
# Compile to gba_db.bin
out = 'gba_db.bin'
if '--out' in sys.argv:
out = sys.argv[sys.argv.index('--out') + 1]
with open(out, 'wb') as f:
db.compile(f.write)

21
tools/gba-db/gba.csv Normal file
View File

@ -0,0 +1,21 @@
SHA-1,Serial,Save Type
cf0a6c1c473ba6c85027b6071aa1cf6e21336974,AK5J,14
f08b1f60e41fc2080c50c65ea2b2af912661ed99,FSMJ,0
b5afc36a8203c2c485344819d069300ecafd9657,FDKJ,0
28aac26365bf41ba84e67f97e98d15c4678cb99d,FZLE,2
8ca35864ae33c9462dd66ceff1fac5a79e2e0a6f,FSME,0
741eb2874c526cc014bf3e642b4ee37f18312735,FBME,0
b2088582808480e0d70c63a777b046409d4e15c4,FXVE,0
843d853ed28a116c85a5357f9a94e9179f36a6d0,FP7E,0
64e965d61b2d1be5dfadb0236fed83fea5995724,FICE,0
8e3b203630f10c32aa7896afe9b7fbed7e1c8d30,FDKE,0
22ea42ad9a99a6bc0fcba8721cf8f484f68c3bf7,FLBE,2
47a60315ed4074a8c986723b95b3c90513d3b35c,FADE,0
fc396f0eae55cf19e573aa322f525427e03d3854,FDME,0
838da11f879e2fa3ffdef95e1c6d5a54246c1846,FMRE,0
8bc8740a681e4d365419dbcee73619fe4429d66e,FADP,0
ecfb6409abb7ff429aadc65a6b953dbaef3d09d8,BKQX,0
ff2772e347212264d1a1c4d322a08685adc1e6a7,BIME,2
8fa03b1e23e7dea0dbc841d23046d03d246a1cdf,FSRJ,2
b4c1a3582f596d3912a1a7ea2d1eb6681b769448,FGZJ,0
6701010f7c195cf3fbdedb19e4627835a3158748,FSMJ,15
1 SHA-1 Serial Save Type
2 cf0a6c1c473ba6c85027b6071aa1cf6e21336974 AK5J 14
3 f08b1f60e41fc2080c50c65ea2b2af912661ed99 FSMJ 0
4 b5afc36a8203c2c485344819d069300ecafd9657 FDKJ 0
5 28aac26365bf41ba84e67f97e98d15c4678cb99d FZLE 2
6 8ca35864ae33c9462dd66ceff1fac5a79e2e0a6f FSME 0
7 741eb2874c526cc014bf3e642b4ee37f18312735 FBME 0
8 b2088582808480e0d70c63a777b046409d4e15c4 FXVE 0
9 843d853ed28a116c85a5357f9a94e9179f36a6d0 FP7E 0
10 64e965d61b2d1be5dfadb0236fed83fea5995724 FICE 0
11 8e3b203630f10c32aa7896afe9b7fbed7e1c8d30 FDKE 0
12 22ea42ad9a99a6bc0fcba8721cf8f484f68c3bf7 FLBE 2
13 47a60315ed4074a8c986723b95b3c90513d3b35c FADE 0
14 fc396f0eae55cf19e573aa322f525427e03d3854 FDME 0
15 838da11f879e2fa3ffdef95e1c6d5a54246c1846 FMRE 0
16 8bc8740a681e4d365419dbcee73619fe4429d66e FADP 0
17 ecfb6409abb7ff429aadc65a6b953dbaef3d09d8 BKQX 0
18 ff2772e347212264d1a1c4d322a08685adc1e6a7 BIME 2
19 8fa03b1e23e7dea0dbc841d23046d03d246a1cdf FSRJ 2
20 b4c1a3582f596d3912a1a7ea2d1eb6681b769448 FGZJ 0
21 6701010f7c195cf3fbdedb19e4627835a3158748 FSMJ 15