mirror of
https://github.com/ihaveamac/custom-install.git
synced 2025-12-08 13:45:00 +00:00
pyctr: update
This commit is contained in:
111
pyctr/type/smdh.py
Normal file
111
pyctr/type/smdh.py
Normal file
@@ -0,0 +1,111 @@
|
||||
# This file is a part of ninfs.
|
||||
#
|
||||
# Copyright (c) 2017-2019 Ian Burgwin
|
||||
# This file is licensed under The MIT License (MIT).
|
||||
# You can find the full license text in LICENSE.md in the root of this project.
|
||||
|
||||
from types import MappingProxyType
|
||||
from typing import TYPE_CHECKING, NamedTuple
|
||||
|
||||
from ..common import PyCTRError
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import BinaryIO, Dict, Mapping, Optional, Tuple, Union
|
||||
|
||||
SMDH_SIZE = 0x36C0
|
||||
|
||||
region_names = (
|
||||
'Japanese',
|
||||
'English',
|
||||
'French',
|
||||
'German',
|
||||
'Italian',
|
||||
'Spanish',
|
||||
'Simplified Chinese',
|
||||
'Korean',
|
||||
'Dutch',
|
||||
'Portuguese',
|
||||
'Russian',
|
||||
'Traditional Chinese',
|
||||
)
|
||||
|
||||
# the order of the SMDH names to check. the difference here is that English is put before Japanese.
|
||||
_region_order_check = (
|
||||
'English',
|
||||
'Japanese',
|
||||
'French',
|
||||
'German',
|
||||
'Italian',
|
||||
'Spanish',
|
||||
'Simplified Chinese',
|
||||
'Korean',
|
||||
'Dutch',
|
||||
'Portuguese',
|
||||
'Russian',
|
||||
'Traditional Chinese',
|
||||
)
|
||||
|
||||
|
||||
class SMDHError(PyCTRError):
|
||||
"""Generic exception for SMDH operations."""
|
||||
|
||||
|
||||
class InvalidSMDHError(SMDHError):
|
||||
"""Invalid SMDH contents."""
|
||||
|
||||
|
||||
class AppTitle(NamedTuple):
|
||||
short_desc: str
|
||||
long_desc: str
|
||||
publisher: str
|
||||
|
||||
|
||||
class SMDH:
|
||||
"""
|
||||
Class for 3DS SMDH. Icon data is currently not supported.
|
||||
|
||||
https://www.3dbrew.org/wiki/SMDH
|
||||
"""
|
||||
|
||||
# TODO: support other settings
|
||||
|
||||
def __init__(self, names: 'Dict[str, AppTitle]'):
|
||||
self.names: Mapping[str, AppTitle] = MappingProxyType({n: names.get(n, None) for n in region_names})
|
||||
|
||||
def __repr__(self):
|
||||
return f'<{type(self).__name__} title: {self.get_app_title().short_desc}>'
|
||||
|
||||
def get_app_title(self, language: 'Union[str, Tuple[str, ...]]' = _region_order_check) -> 'Optional[AppTitle]':
|
||||
if isinstance(language, str):
|
||||
language = (language,)
|
||||
|
||||
for l in language:
|
||||
apptitle = self.names[l]
|
||||
if apptitle:
|
||||
return apptitle
|
||||
|
||||
# if, for some reason, it fails to return...
|
||||
return AppTitle('unknown', 'unknown', 'unknown')
|
||||
|
||||
@classmethod
|
||||
def load(cls, fp: 'BinaryIO') -> 'SMDH':
|
||||
"""Load an SMDH from a file-like object."""
|
||||
smdh = fp.read(SMDH_SIZE)
|
||||
if len(smdh) != SMDH_SIZE:
|
||||
raise InvalidSMDHError(f'invalid size (expected: {SMDH_SIZE:#6x}, got: {len(smdh):#6x}')
|
||||
if smdh[0:4] != b'SMDH':
|
||||
raise InvalidSMDHError('SMDH magic not found')
|
||||
|
||||
app_structs = smdh[8:0x2008]
|
||||
names: Dict[str, AppTitle] = {}
|
||||
# due to region_names only being 12 elements, this will only process 12. the other 4 are unused.
|
||||
for app_title, region in zip((app_structs[x:x + 0x200] for x in range(0, 0x2000, 0x200)), region_names):
|
||||
names[region] = AppTitle(app_title[0:0x80].decode('utf-16le').strip('\0'),
|
||||
app_title[0x80:0x180].decode('utf-16le').strip('\0'),
|
||||
app_title[0x180:0x200].decode('utf-16le').strip('\0'))
|
||||
return cls(names)
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, fn: str) -> 'SMDH':
|
||||
with open(fn, 'rb') as f:
|
||||
return cls.load(f)
|
||||
Reference in New Issue
Block a user