Highlights
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](https://www.3dbrew.org/wiki/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](https://github.com/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](https://pyctr.readthedocs.io/en/latest/).
Changelog
* Add the module `pyctr.type.configsave` with the class `ConfigSaveReader` (_module name changed in a future commit_)
* Implement `to_bytes` and `remove_block` in `ConfigSaveReader`
* Split `pyctr.type.configsave` into two packages: `pyctr.type.config.blocks` and `pyctr.type.config.save`
* New `ConfigSaveBlockParser` class with 3 methods: `get_username`, `get_user_time_offset`, and `get_system_model` (plus convenience functions `load` and `from_file` so a `ConfigSaveParser` doesn't need to be manually created)
* New enum: `SystemModel`
* Both `ConfigSaveReader` and `ConfigSaveBlockParser` are importable from `pyctr.type.config`
* Add `flush` to `SubsectionIO`
* Optimize `CTRFileIO` to re-use existing cipher object when possible (seeking invalidates the current one)
* Optimize `RomFSReader` by reading entire directory and file metadata at once before traversing, significantly reducing the amount of read calls to the underlying file
* Optimize `RomFSReader` to 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 == 0` check for NCCH files using `fixed_crypto_key`
* Add `nand` module with `NAND` class, 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 `FilePath` and `FilePathOrObject` types
* Add `__slots__` to `SubsectionIO` and `SplitFileMerger`
* Add some tests for `romfs` and `smdh`
* Load all boot9 keys in `CryptoEngine.setup_keys_from_boot9`
* Moved package metadata to `setup.cfg`
* Fix setting fixed keys in `CryptoEngine` and add debug logging to key setting
* Separate NCSD header loading from `NAND` to a separate class `NANDNCSDHeader`
* Refactor the `nand` module and `NAND` class:
* Add support for virtual sections (things that are not NCSD partitions)
* Rename `open_ncsd_partition` to `oprn_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 `RomFSReader` to a new `RomFSLv3Header` class
* Many different documentation changes or additions
* Require Python 3.8
* Create new and update documentation pages - `example-nand` and `pyctr.type.nand`
* Fix `closefd` being default to True always in `NAND`
* Create documentation page for `pyctr.type.sd`
* Create documentation page for `pyctr.fileio`
* Create documentation page for `pyctr.util`
* Fixes to `ConfigSaveReader`:
* `data_offset` is not hardcoded to `0x41E4`, 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_offset` and data sorting when using `to_bytes`
* Added sanity checks while parsing a config save
* `set_block` previously didn't allowed `None` in flags despite being stated to default to `0xE`
* New attribute for SMDH: `SMDH.region_lockout`, with a new NamedTuple `SMDHRegionLockout`
* New attribute for CCIReader: `CCIReader.cart_region`, with a new Enum `CCICartRegion`
* `ConfigSaveReader.set_block` will now check Block IDs, flags, and sizes against a known list of blocks
* Passing `strict=True` to `set_block` will bypass this
* `KNOWN_BLOCKS` in `pyctr.type.config.save` was changed to have values be dicts with "flags" and "size" keys (instead of plain tuples)
* Switch `get_*` and `set_*` to getters and setters in `pyctr.type.config.blocks
* e.g. `username` instead of `get_username` and `set_username`
* Add new `from_bytes` and `__bytes__` methods to `AppTitle` to load title structures (and modify `SMDH` to use this now)