Compare commits
3 commits
2e9467c693
...
72ca194a05
Author | SHA1 | Date | |
---|---|---|---|
![]() |
72ca194a05 | ||
![]() |
3b7e291904 | ||
![]() |
fe72c33c5e |
3 changed files with 139 additions and 3 deletions
24
main.py
24
main.py
|
@ -1,14 +1,14 @@
|
|||
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')
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) == 2:
|
||||
# NDS(i) - https://dsibrew.org/wiki/DSi_cartridge_header
|
||||
if sys.argv[1].endswith('.nds'):
|
||||
if sys.argv[1].endswith('.nds') or sys.argv[1].endswith('.ids'):
|
||||
with open(sys.argv[1], mode='rb') as nds:
|
||||
logger.info('Title : {}'.format(nds_get_info('title', nds)))
|
||||
logger.info('Game code : {}'.format(nds_get_info('gamecode', nds)))
|
||||
|
@ -16,5 +16,23 @@ if __name__ == '__main__':
|
|||
logger.info('Unit code : {}'.format(nds_get_info('unitcode', nds)))
|
||||
logger.info('Encryption seed : {}'.format(nds_get_info('encryptionseed', nds)))
|
||||
logger.info('Device capacity : {}'.format(nds_get_info('devicecapacity', nds)))
|
||||
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.')
|
||||
|
||||
# Info taken from:
|
||||
# https://dsibrew.org/wiki/DSi_cartridge_header
|
||||
# https://scenegate.github.io/Ekona/specs/cartridge/header.html
|
||||
# https://problemkaputt.de/gbatek-ds-cartridge-header.htm
|
||||
# https://gist.github.com/pleonex/6265017
|
100
rominfo/gameboy_advance.py
Normal file
100
rominfo/gameboy_advance.py
Normal file
|
@ -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)'
|
|
@ -22,6 +22,12 @@ def nds_get_info(opt: str, inf: BinaryIO):
|
|||
case 'devicecapacity':
|
||||
inf.seek(0x014)
|
||||
return _capacity_lookup(int.from_bytes(inf.read(1), 'little'))
|
||||
case 'gamerevision':
|
||||
inf.seek(0x01C)
|
||||
return str(int.from_bytes(inf.read(2), 'little'))
|
||||
case 'region':
|
||||
inf.seek(0x01D)
|
||||
return _region_lookup(int.from_bytes(inf.read(1), 'little'))
|
||||
case _:
|
||||
return 'Unknown'
|
||||
|
||||
|
@ -53,3 +59,15 @@ def _capacity_lookup(code: int) -> str: # 512Mbit (67108864 bytes) (09h)
|
|||
return '{} MB ({})'.format(round(128 * pow(2, code) / 1024), '{}h'.format(str(code).zfill(2)))
|
||||
case _:
|
||||
return 'Unknown ({})'.format(code)
|
||||
|
||||
|
||||
def _region_lookup(code: int) -> str:
|
||||
match code:
|
||||
case 0:
|
||||
return 'All (0x00 (0))'
|
||||
case 64:
|
||||
return 'Korea (0x40 (64))'
|
||||
case 128:
|
||||
return 'China (0x80 (128))'
|
||||
case _:
|
||||
return 'Unknown ({})'.format(code)
|
||||
|
|
Loading…
Add table
Reference in a new issue