Example: Read contents from a CIA
In this example we will take a homebrew CIA file and extract some data from it. The example in this case will be Checkpoint 3.7.4. Download the example title here.
First we need to import CIAReader
and CIASection
. The former does the actual reading, the latter is an enum that can be used to access the contents. We will also import json
to read a JSON file inside the RomFS.
>>> import json
>>> from pyctr.type.cia import CIAReader, CIASection
Now we can open the file by creating a CIAReader
object.
>>> cia = CIAReader('Checkpoint.cia')
This will grant immediate access to all the contents inside, including the tmd, ticket, and NCCH contents. It also loads the data within the NCCH contents, such as the RomFS.
We can now check the Title ID by accessing it through the tmd
attribute:
>>> print('Title ID:', cia.tmd.title_id)
Title ID: 000400000bcfff00
Let’s also print the Title Version, using the title_version
attribute.
>>> print('Title Version:', '{0.major}.{0.minor}.{0.micro}'.format(cia.tmd.title_version))
Title Version: 3.7.4
Now let’s access the executable content using the contents
attribute. Here we’ll use CIASection.Application
to read the first (and only) content.
>>> app = cia.contents[CIASection.Application]
>>> app
<NCCHReader program_id: 000400000bcfff00 product_code: CTR-HB-CKPT title_name: 'Checkpoint'>
This has given us an NCCHReader
object.
Let’s get the application’s SMDH, which will give us access to the name and publisher shown on the HOME Menu. We’ll get it through the ExeFS and get an SMDH
object.
>>> app_title = app.exefs.icon.get_app_title('English')
>>> app_title
AppTitle(short_desc='Checkpoint', long_desc='Fast and simple save manager', publisher='Bernardo Giordano, FlagBrew')
>>> print('Application Title:', app_title.short_desc)
Application Title: Checkpoint
>>> print('Application Description:', app_title.long_desc)
Application Description: Fast and simple save manager
>>> print('Application Publisher:', app_title.publisher)
Application Publisher: Bernardo Giordano, FlagBrew
Next, we will list the contents of the RomFS. The NCCHReader
has a romfs
attribute that will give us a RomFSReader
object.
Using get_info_from_path
we will list the contents at the root.
>>> print('Contents in the root:', ', '.join(app.romfs.get_info_from_path('/').contents))
Contents in the root: gfx, cheats, config.json, PKSM.smdh
Using the same method, we can get information about a specific file.
>>> print('Size of /config.json in bytes:', app.romfs.get_info_from_path('/config.json').size)
Size of /config.json in bytes: 183
Finally, we can open the file and parse the JSON inside. We’ll pass an encoding
argument so that we get an io.TextIOWrapper
object. Then we use json.load()
and print a value from it.
>>> f = app.romfs.open('/config.json', encoding='utf-8')
>>> f
<_io.TextIOWrapper encoding='utf-8'>
>>> config = json.load(f)
>>> f.close()
>>> config
{'filter': [], 'favorites': [], 'additional_save_folders': {}, 'additional_extdata_folders': {}, 'nand_saves': False, 'scan_cart': False, 'version': 3}
>>> print('Config version:', config['version'])
Config version: 3
When you’re done, make sure to close the CIAReader
. You should also close any open files based on the CIA.
>>> cia.close()
You can also use CIAReader
in the form of a context manager.
with CIAReader('Checkpoint.cia') as cia:
with cia.contents[CIASection.Application].romfs.open('/config.json') as f:
config = json.load(f)
print('Config version:', config['version'])