Taking it as a library
The command-line tool is a thin layer. Everything it does is in crates you can depend on directly, and the split between them is deliberate: it is what keeps cryptography out of a loader.
The spine
Each crate depends only on the ones before it.
| crate | what it holds |
|---|---|
selfish-abi |
the generation split, and nothing else. Depends on nothing. |
selfish-nid |
the import hash, both directions |
selfish-elf |
the executable format as the platform spells it |
selfish-container |
the signed-executable container |
selfish-pfs |
the filesystem carried inside a package |
selfish-pkg |
the package itself |
selfish-title sits off to one side. It depends on
nothing here and holds what a title says about itself -
PARAM.SFO and param.json.
selfish-pkg uses it because a package carries a
PARAM.SFO; nothing else does.
An emulator reading a bare executable takes elf
and stops. It never links a cryptography dependency it has no
use for, because the crate that needs one is further along the spine and
it never reaches it.
The generation is in the type system
Two hardware generations share one container and differ in four bytes. A builder that does not say which one it is targeting must not compile.
That is not tidiness. The one time this was a runtime parameter with a default, the default was wrong and the file was rejected by the machine it was built for. A type error at the call site costs a minute; a rejected package costs an install cycle and tells you nothing.
Read and write live together
A format library that can only parse is half a library, and the half that writes is where the errors are. Where both exist for one structure, the round trip is a test: parse what was written, write what was parsed, fail if they differ.
This is why selfish title --round-trip exists as a
command and not only as a unit test - the same property is worth
checking against a file you were handed, not just against fixtures.
What you are not promised
None of these crates gets an API shaped around one caller's convenience. A format library that grows a method because a consumer found it handy ends up encoding that consumer's assumptions, and the next consumer inherits them without knowing. If you need something shaped differently, wrap it.
The data is the source of truth, not the code: data/
holds one row per field with a provenance header naming every source,
and the crates read those tables rather than carrying their own copy of
the numbers.
The libraries stand alone, deliberately
The library crates depend on nothing outside this repository - a
format library that could only be built as part of a set would be a poor
thing to ask anyone to depend on. selfish-cli holds to the
same line: a clone of only this repository builds with a Rust toolchain
and nothing else. It briefly did not - the CLI took
oops-build and oops-log from
oops-libs for a --version commit stamp and a
logging subscriber - and both are gone (D104): the stamp is a few lines
of git in selfish-cli/build.rs, and the logging was dead
weight, since nothing here emits a tracing event. See BUILDING.md for the full account.