From 72ca194a05d63cba553bb6879332e81c105a34f1 Mon Sep 17 00:00:00 2001 From: Yuuki Chan Date: Sun, 22 Oct 2023 19:46:29 +0900 Subject: [PATCH] Updated main.py - added GBA stuff. Added gameboy_advance.py --- main.py | 12 ++++- rominfo/gameboy_advance.py | 100 +++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 rominfo/gameboy_advance.py diff --git a/main.py b/main.py index 124c9fb..2eec0c0 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,8 @@ import sys from logger import Logger -from rominfo.nintendo_ds import * +from rominfo.nintendo_ds import nds_get_info +from rominfo.gameboy_advance import gba_get_info logger = Logger('RomInfo') @@ -18,6 +19,15 @@ if __name__ == '__main__': if 'DSi ' in nds_get_info('unitcode', nds): logger.info('Game revision : {} (DSi only)'.format(nds_get_info('gamerevision', nds))) logger.info('Game region : {}'.format(nds_get_info('region', nds))) + + if sys.argv[1].endswith('.gba'): + with open(sys.argv[1], mode='rb') as gba: + logger.info('Logo : {}'.format(gba_get_info('nintendologo', gba))) + logger.info('Title : {}'.format(gba_get_info('title', gba))) + logger.info('Game code : {}'.format(gba_get_info('gamecode', gba))) + logger.info('Maker code : {}'.format(gba_get_info('makercode', gba))) + logger.info('Software version : {}'.format(gba_get_info('softwareversion', gba))) + logger.info('Complement check : {}'.format(gba_get_info('complement', gba))) else: logger.error('No ROM specified. App requires one argument.') diff --git a/rominfo/gameboy_advance.py b/rominfo/gameboy_advance.py new file mode 100644 index 0000000..a71a679 --- /dev/null +++ b/rominfo/gameboy_advance.py @@ -0,0 +1,100 @@ +from typing import BinaryIO + +LOGO = '0x24ffae51699aa2213d84820a84e409ad11248b98c0817f21a352be199309ce2010464a4af82731ec58c7e83382e3cebf85f4df94ce4b09c194568ac01372a7fc9f844d73a3ca9a615897a327fc039876231dc7610304ae56bf38840040a70efdff52fe036f9530f197fbc08560d68025a963be03014e38e2f9a234ffbb3e0344780090cb88113a9465c07c6387f03cafd625e48b380aac7221d4f807' + + +def gba_get_info(opt: str, inf: BinaryIO): + match opt.lower(): + case 'nintendologo': + inf.seek(0x004) + if LOGO == hex(int.from_bytes(inf.read(156))): + return 'OK' + else: + return 'NOK' + case 'title': + inf.seek(0x0A0) + return inf.read(12).decode() + case 'gamecode': + inf.seek(0x0AC) + return _gamecode_lookup(inf.read(4).decode()) + case 'makercode': + inf.seek(0x0B0) + return _maker_lookup(inf.read(2).decode()) + case 'softwareversion': + inf.seek(0x0BC) + return str(int.from_bytes(inf.read(1))) + case 'complement': + return complement_check(inf) + case _: + return 'Unknown' + + +def _gamecode_lookup(code: str) -> str: + gamecode = [] + + match code[:1]: + case 'A': + gamecode.append('Normal game (2001-2003)') + case 'B': + gamecode.append('Normal game (2003+)') + case 'C': + gamecode.append('Normal game (not yet used)') + case 'F': + gamecode.append('Famicom/NES') + case 'K': + gamecode.append('Acceleration sensor') + case 'P': + gamecode.append('For e-Reader (dot-code scanner)') + case 'R': + gamecode.append('Rumble and Z-axis gyro sensor') + case 'U': + gamecode.append('RTC and solar sensor') + case 'V': + gamecode.append('Rumble motor') + case '_': + gamecode.append('Unknown') + + match code[3:]: + case 'D': + gamecode.append('German') + case 'E': + gamecode.append('USA/English') + case 'F': + gamecode.append('French') + case 'I': + gamecode.append('Italian') + case 'J': + gamecode.append('Japanese') + case 'P': + gamecode.append('Europe/elsewhere') + case 'S': + gamecode.append('Spanish') + case '_': + gamecode.append('Unknown') + + return '{} ({})'.format(code, f'Type: {gamecode[0]} | Language: {gamecode[1]})') + + +def _maker_lookup(code: str) -> str: + match code: + case '01': + return 'Nintendo ({})'.format(code) + case 'A4': + return 'Konami ({})'.format(code) + case _: + return 'Unknown ({})'.format(code) + + +def complement_check(data: BinaryIO) -> str: + data.seek(0x000) + data = data.read() + + if len(data) >= 189: + checksum = 0 + for b in data[0x0A0:0x0BC]: + checksum = (checksum - b) & 0xFF + + return '{} ({}/{})'.format((checksum - 0x19) & 0xFF == int.from_bytes(data[0x0BD:0x0BE]), + (checksum - 0x19) & 0xFF, int.from_bytes(data[0x0BD:0x0BE])) + else: + return 'False (Invalid data length)'