2026-05-19 17:41:17 +02:00
# LibRay — Agent Quick Reference
# Important
2026-05-21 09:14:20 +02:00
Make sure to use the virtual environment in `~/.venv/libray` (activate with `source ~/.venv/libray/bin/activate` ) and not global pip.
2026-05-19 17:41:17 +02:00
## Repo
- Python 3 CLI tool for decrypting/encrypting/examining PS3 Blu-Ray ISOs
2026-05-21 09:14:20 +02:00
- Entry point: `libray/libray.py` (defines `main()` ); installed console script is `libray=libray.libray:main` (see `setup.py` ). `libray/libray` is a symlink to `libray.py` for running from a source checkout.
2026-05-19 17:41:17 +02:00
- Package: `libray/` — modules: `core.py` (main logic), `iso.py` (ISO parsing), `ird.py` (IRD parsing), `sfo.py` (PARAM.SFO)
- Tests: `tests/` — `test_iso.py` , `test_interface.py` (interface test is currently skipped/broken)
- Tools: `tools/keys2db.py` (builds `libray/data/keys.db` from redump keys), `tools/rpcs3.py` (fetches compat data)
## Commands
- Install deps: `pip install -r requirements.txt`
- Run tests: `python -m unittest discover -b`
- Build sdist + wheel: `python3 setup.py sdist bdist_wheel`
- Publish: `twine upload dist/*`
## Parallelization
2026-05-21 09:14:20 +02:00
- Decrypt and re-encrypt support multiprocessing via `-p` /`--threads` CLI argument
2026-05-19 17:41:17 +02:00
- Default: auto-detects CPU core count via `os.cpu_count()`
- Each sector is independently decrypted (per-sector IV in AES-CBC), making it embarrassingly parallel
2026-05-21 09:14:20 +02:00
- Uses `concurrent.futures.ProcessPoolExecutor` — true multi-core parallelism by spawning OS processes
- Each process gets its own Python interpreter (own GIL), so pycryptodome's partial GIL release is irrelevant
2026-05-19 17:41:17 +02:00
- Unencrypted regions are always copied sequentially (no crypto needed)
2026-05-21 09:14:20 +02:00
- Module-level `_process_sector_chunk_mp()` function is picklable for use with ProcessPoolExecutor; it processes a contiguous run of sectors per task to amortise IPC cost
- A bounded window of chunks is kept in flight (memory stays bounded for large ISOs); results are written back to their absolute offsets in the output file as they complete
2026-05-19 17:41:17 +02:00
## Gotchas
- **Crypto package conflict**: `pycrypto` /`crypto` will break `pycryptodome` . If `ImportError: No module named Crypto.Cipher` , run:
```
pip uninstall crypto pycrypto & & pip install pycryptodome
```
- **keys.db is generated**, not committed. Build it with `python3 tools/keys2db.py` (requires keys in `tools/keys/` ). It's listed in `.gitignore` via `libray/data/*.db` .
2026-05-21 09:14:20 +02:00
- **`libray/__init__.py` ** dynamically imports submodules via `pkgutil.walk_packages` + `importlib.import_module` (skips the `libray` entry-point module to avoid shadowing the package) — don't expect explicit imports.
2026-05-19 17:41:17 +02:00
- **`test_interface.py` ** is skipped (`@unittest.skip('currently broken')` ) — the interface test won't run.
- `.editorconfig` enforces 4-space indent for `.py` , 2-space for `.yml` /`.yaml` .
- No linting/typechecking config exists — plain `unittest` , no pytest, no pre-commit.