Python 3.12 or later is now required, up from 3.8.
A command line tool was added, pyctr.cmd with entrypoint pyctrcmd.
PyFilesystem2 (fs) is now a dependency.
RomFSReaderis now based onfs.base.FSpyctr.type.sdfsis a replacement forpyctr.type.sdthat usesfs.base.FSNANDnow contains 3 new methods:open_ctr_fat,open_twl_fat, andopen_bonus_fat- Most types that accept a file path or file-like object now accept an
fs=argument, which can be an FS URL or a filesystem. For example:CIAReader('mygame.cia', fs='zip://path/to/mygame.zip')CDNReader('tmd', fs=fs.zipfs.ZipFS('mycdngame.zip'))
- Fix setting TWLNAND key for dev consoles (thanks to @xprism1 for assistance)
RomFSReaderwas updated to use PyFilesystem2.- To match PyFilesystem2, the function signature for
openhas changed to addmodeandbufferingarguments betweenpathandencoding. This also means opening files is done in text mode by default. For compatibility, if the second argument is detected to be an encoding, the file will be opened like before, and aDeprecationWarningwill be raised. get_info_from_pathis deprecated and should be replaced withgetinfo,listdir, orscandir.
- To match PyFilesystem2, the function signature for
pyctr.type.sdfswas created to replacepyctr.type.sd, which is now deprecated.sdfsuses PyFilesystem2. The one deviation from the standard is thatSDFS.openwill only open files in binary mode.
- Add initial pyctr command line tool,
pyctr.cmd(entry pointpyctrcmd) with one command,checkenv - Move
seeddb_pathstopyctr.crypto.seeddbfrom a function, making it publicly accessible (and then use it inpyctr.cmd.checkenv) pyctr.crypto.enginenow includes a new function,setup_boot9_keys, which loads the keyblobs from boot9 instead of doing that withinCryptoEngineCryptoEnginenow generates the keys from the global key blobs on initialization- This should also fix issues with separate retail and dev versions of
CryptoEnginebeing used (some keys stored globally only stored one type of key) CryptoEngine.setup_keys_from_boot9andCryptoEngine.setup_keys_from_boot9_filewill now output a deprecation warning
CryptoEngine.setup_keys_from_otpwill now only update normal keys and setotp_decandotp_encat the end- Add
CryptoEngine.clonemethod to create a copy of theCryptoEnginestate CDNReaderandCIAReaderwill now clone theirCryptoEnginestate for eachNCCHReader- Use
fs.base.FSforRomFSReader- fs (PyFilesystem2) is now a dependency
- Tests have been updated to use the new FS methods
- Create
pyctr.type.sdfsas a replacement forpyctr.type.sd - Implement
open_ctr_fat,open_twl_fat, andopen_bonus_fatinNAND- pyfatfs is now a dependency
- Implement loading from SD in remaining types
- Add new example for getting version from a NAND backup
NANDNCSDHeadercan be converted back to bytes withbytes(my_nand_header)- Include NAND sighax signatures as the
SIGHAX_SIGSconstant - Always set fixed keys regardless of boot9 (in particular: TWLNAND Y, CTRNANDNew Y, ZeroKey N, FixedSystemKey N)
- Set fixed keys after attempting to load boot9 (fixes #44)
- Fix OTP crypto setup (PR #45, thanks @ZeroSkill1)
- Add Nix derivation and flake
- Various documentation updates
- Switch to pyproject-only format
- Fix NCCH ExeFS decryption with Original NCCH + seeded key
- Require Python 3.12
Python 3.8 or later is now required, up from 3.6.1.
A new pyctr.type.config package with save and blocks modules was added. These allow for reading the config savegame, both to read the raw blocks, and to parse the data into a usable format.
A new nand module with the NAND class is added to read and write to NAND images. This only provides raw access (for FAT32, try nathanhi/pyfatfs).
RomFSReader initialization performance was improved, especially with RomFS files containing large amounts of files or directories.
Documentation is being added and improved over time. Check it on Read the Docs.
- Add the module
pyctr.type.configsavewith the classConfigSaveReader(module name changed in a future commit) - Implement
to_bytesandremove_blockinConfigSaveReader - Split
pyctr.type.configsaveinto two packages:pyctr.type.config.blocksandpyctr.type.config.save- New
ConfigSaveBlockParserclass with 3 methods:get_username,get_user_time_offset, andget_system_model(plus convenience functionsloadandfrom_fileso aConfigSaveParserdoesn't need to be manually created) - New enum:
SystemModel - Both
ConfigSaveReaderandConfigSaveBlockParserare importable frompyctr.type.config
- New
- Add
flushtoSubsectionIO - Optimize
CTRFileIOto re-use existing cipher object when possible (seeking invalidates the current one) - Optimize
RomFSReaderby reading entire directory and file metadata at once before traversing, significantly reducing the amount of read calls to the underlying file - Optimize
RomFSReaderto reduce the read calls for the header (once for raw lv3, twice for IVFC) - Check for unformatted saves in
DISA(the first 0x20 bytes are all NULL and the rest is garbage) - Remove
crypto_method == 0check for NCCH files usingfixed_crypto_key - Add
nandmodule withNANDclass, to read and write to a NAND image - Add
__slots__to a bunch of classes (CCIReader,CDNReader,CIAReader,ExeFSReader,NAND,NCCHReader,RomFSReader,SDFilesystem,SDTitleReader,SMDH,ConfigSaveReader,TypeReaderBase,TypeReaderCryptoBase) - Update copyright year
- Various documentation and type hint changes
- Add new
FilePathandFilePathOrObjecttypes
- Add new
- Add
__slots__toSubsectionIOandSplitFileMerger - Add some tests for
romfsandsmdh - Load all boot9 keys in
CryptoEngine.setup_keys_from_boot9 - Moved package metadata to
setup.cfg - Fix setting fixed keys in
CryptoEngineand add debug logging to key setting - Separate NCSD header loading from
NANDto a separate classNANDNCSDHeader - Refactor the
nandmodule andNANDclass:- Add support for virtual sections (things that are not NCSD partitions)
- Rename
open_ncsd_partitiontooprn_raw_section - Add custom section IDs that always point to the correct partition, regardless of its physical location
- Add custom section ID for GodMode9 bonus volume
- Add documentation files created with sphinx-autodoc
- Switch Sphinx theme to rtd, add example-cia, add info to index page
- Move RomFS header loading in
RomFSReaderto a newRomFSLv3Headerclass - Many different documentation changes or additions
- Require Python 3.8
- Create new and update documentation pages -
example-nandandpyctr.type.nand - Fix
closefdbeing default to True always inNAND - Create documentation page for
pyctr.type.sd - Create documentation page for
pyctr.fileio - Create documentation page for
pyctr.util - Fixes to
ConfigSaveReader:data_offsetis not hardcoded to0x41E4, it's based on the amount of data from the blocks- CFG adds data to save from end to start when the block data is > 4 bytes, so now we are replicating this behavior and generating a proper
data_offsetand data sorting when usingto_bytes - Added sanity checks while parsing a config save
set_blockpreviously didn't allowedNonein flags despite being stated to default to0xE
- New attribute for SMDH:
SMDH.region_lockout, with a new NamedTupleSMDHRegionLockout - New attribute for CCIReader:
CCIReader.cart_region, with a new EnumCCICartRegion ConfigSaveReader.set_blockwill now check Block IDs, flags, and sizes against a known list of blocks- Passing
strict=Truetoset_blockwill bypass this
- Passing
KNOWN_BLOCKSinpyctr.type.config.savewas changed to have values be dicts with "flags" and "size" keys (instead of plain tuples)- Switch
get_*andset_*to getters and setters in `pyctr.type.config.blocks- e.g.
usernameinstead ofget_usernameandset_username
- e.g.
- Add new
from_bytesand__bytes__methods toAppTitleto load title structures (and modifySMDHto use this now)
Pillow is now an optional dependency. It is available through the extra feature images. This means to use pyctr[images] when adding to setup.py, requirements.txt, or pip install.
SMDH icon data is stored into an array like [[(1, 2, 3), (4, 5, 6), ...]]. It can be used with other libraries like the pure-python pypng, for example:
from pyctr.type.cia import CIAReader
from itertools import chain
import png
my_cia = CIAReader('game.cia')
# pypng expects an array like [[1, 2, 3, 4, 5, 6, ...]] so we need to flatten the inner lists
img = png.from_array(
(chain.from_iterable(x) for x in my_cia.contents[0].exefs.icon.icon_large_array),
'RGB', {'width': 48, 'height': 48}
)
img.save('icon.png')- Move around object attribute initialization in cci, cdn, cia, and sdtitle, to prevent extra exceptions if an error is raised early
- Use
open_raw_sectioninternally when initializing aCIAReaderobject, instead of manually seeking and reading - Make Pillow an optional dependency and make SMDH load icon data into an array (useful for other libraries like pypng)
- New functions in
smdh:rgb565_to_rgb888_tuple,load_tiled_rgb565_to_array,rgb888_array_to_image - Init arguments for
SMDHwere changed to accept icon data arrays instead of PillowImageobjects - Pillow is added to
extras_requireunder the featureimages
- New functions in
- Documentation tweaks to
smdh - Remove unused import in
cia
- Fix arbitrary reads in the first 0x10 block of
CBCFileIO
A new sdtitle module with SDTitleReader is added to read titles installed on an SD card. When used directly, it works with contents that are not SD encrypted. To open a title on a 3DS SD card that is SD encrypted, a new method is added to sd.SDFilesystem: open_title.
SMDH icons are now loaded using Pillow.
SMDH flags are now loaded.
The ExeFS in NCCH contents is now fully decrypted properly. This means there is no longer garbage at the last block of the .code section for titles that use extra NCCH keys. This was achieved by rewriting the ExeFS section to concatenate multiple file-like objects that use different keyslots with a new class, SplitFileMerger.
- Add
_raise_if_file_closed_generictopyctr.common - Add
SplitFileMergertopyctr.fileioto merge multiple file-like objects into one (currently no support for writing) - Support closing all subfiles in
SplitFileMerger - Rewrite ExeFS handling in
NCCHReaderto useSplitFileMergerto merge multipleSubsectionIOfiles to handle the parts that use different encryption, and updateFullDecryptedto use it when reading ExeFS - Add
from_bytesclassmethod toNCCHFlags - Always use the internal ExeFS file object when reading it for FullDecrypted
- Add docstrings to
NCCHFlags - Add dependency on
Pillow>=8.2 - Load SMDH icons using Pillow/PIL, stored in new attributes in the
SMDHclass:icon_small,icon_large - Load SMDH flags into a new
SMDHFlagsclass - Add
isfileandisdirmethods toSDFilesystem, convert path to string insd.normalize_sd_pathto make it easier to use anyos.PathLikeobject (e.g.pathlib.PurePosixPath) - Add
sdtitlemodule withSDTitleReaderclass, to read titles installed to the SD card inside "Nintendo 3DS" - Add
open_titlemethod toSDFilesystemto open a title usingSDTitleReader, and a newMissingTitleErrorexception - Update type hints in
sd
- Use absolute paths in
CDNReader - Use absolute paths in
SDFilesystem - Make
SubsectionIOobjects hashable (if the underlying file object is) - Make sure two different
CTRFileIO,TWLCTRFileIO, andCBCFileIOreturn different hashes, even with the same reader + key + iv/counter - Add
TWLCTRFileIOtocrypto.engine.__all__ - Use a
frozenseton a closedCDNReaderobject's internal open files set - Don't set
__del__directly tocloseinTypeReaderBase, in casecloseis overridden - Auto-close opened files based on a reader when the reader closes (applies to
CCIReader,CIAReader,ExeFSReader,NCCHReader, andRomFSReader) - Add new pseudo-keyslot
NCCHExtraKeyto store the second keyslot data for NCCH contents- This is important because there exist titles that use Original NCCH but with a seed. Before this change, the key in the
NCCHkeyslot would be overwritten, causing everything but the special regions (ExeFS .code and RomFS) to be improperly decrypted.
- This is important because there exist titles that use Original NCCH but with a seed. Before this change, the key in the
- Use
NCCHExtraKeyfor the second keyslot instead of the actual keyslot inNCCHReader - Set
_open_filesbefore opening the file inTypeReaderBaseto prevent an additional error if opening the file fails - Don't set KeyX and KeyY separately if fixed crypto key is used without an extra crypto method
- Fix sections in
CCIReadernot opening, raising an error
- Add pycryptodomex version requiremenet range (
>=3.9,<4) - Fix using bytes file paths for
CDNReaderandSDFilesystem - Support auto-closing underlying file for
CTRFileIOandCBCFileIO - Make
CTRFileIOandCBCFileIOobjects hashable (if the underlying file object is) - Update
CDNReaderto re-open files instead of using shared file objects, and internally open all files throughCDNReader.open_raw_section(fixes #6) - Store encrypted and decrypted OTP in
CryptoEngineasotp_encandotp_dec, and addotp_keys_setto check if an OTP was set - Verify OTP magic after decryption in
CryptoEngine.setup_keys_from_otp - Make
CryptoEngine.otp_device_idrequire OTP (raising an exception instead of returning None) - Add
TWLCTRFileIOas a subclass ofCTRFileIOwhich handles the special read/write crypto specific to TWLNAND
- Fix loading RomFS from a filename in
RomFSReader - Fix loading ExeFS from a filename in
ExeFSReader
- Support loading a decrypted titlekey for
CDNReader - Remove unused
sectionsattribute fromCDNReader - Add
available_sectionstoCDNReaderto provide a list of sections available in the title - Add
__author__,__copyright__,__license__,__version__, andversion_infotopyctr.__init__
- Fix endianness issue when converting a
TitleMetadataReaderobject tobytes
- Don't assume a Cygwin environment is Windows
- Change keyslot for New 3DS key sector keys from 0x11 to 0x43
- This adds a new Keyslot enum item:
Keyslot.New3DSKeySector
- This adds a new Keyslot enum item:
- Document more methods in pyctr.crypto.engine
- Add new
fileio.CloseWrapperclass to provide access to a file object while preventing closing it directly - Use
CloseWrapperinCDNReader.open_raw_section
- Support NCCH contents with fixed crypto key (zerokey and fixed system key)
- CryptoEngine adds these to fake keyslots 0x41 and 0x42 respectively.
- Fixed setting up keyslot 0x11, used for decrypting the New 3DS key sector
- Add DISA/DIFF reading and writing under
pyctr.type.save- This does NOT include Inner FAT support yet.
- This can read and write to IVFC level 4. For now, external tools can be used to read or write to the Inner FAT.
- Add CDN reading under
pyctr.type.cdn - Add more docstrings to various modules
- Add seed parameters to ncch, cia, and cdn
- Rename
Keyslot.UDSLocalWANtoKeyslot.UDSLocalWLAN - Many other internal changes done months ago
- Fix
setup.pynot including subpackages
- Document more classes, methods, and attributes
- Add a
pyctr.crypto.seeddbmodule for central SeedDB management- Loading SeedDB in
NCCHReaderis now removed
- Loading SeedDB in
- Add new
TypeReaderBaseandTypeReaderCryptoBasefor reader types that use a single file - Fix
PathLikeerror inNCCHReaderandRomFSReader - Changed type of
partition_idandprogram_idinNCCHReadertostr - Some other changes. I'll get better at documenting this!
- Remove debug print in
SDFilesystem.open
- Add the module
pyctr.type.sdwith the classSDFilesystem - Add docstrings to
pyctr.crypto - Allow
os.PathLike,str, andbytesas file paths in most methods - Allow Windows-style paths in
CryptoEngine.sd_path_to_iv
Initial standalone release. All previous commits are in the ninfs repo.