Three loaders instead of one

Fourteen emulators had been cloned and exactly one was ever run. Everything the project believed about its behaviour under an emulator came from shadPS4, and several conclusions were phrased as facts about the platform when they were facts about that loader (D061).

Kyty: the drawn report was never necessary

Its console output genuinely is invisible to a parent process. --printf-direction File --printf-output-file removes the problem entirely, and the whole obstacle was a setting nobody had looked for. The drawn report stays - it will matter on hardware - but it was built to route around a command-line flag.

Also: the checked-out source describes a Lua configuration interface and the binary on disk is 0.2.2, which takes arguments. Read the clone to understand the loader; ask the binary how to drive it.

The census means different things under different loaders

shadPS4 resolves every import through a generic stub, so all 313 census symbols report present while roughly forty per cent behave. Kyty patches each unresolved import individually and says which - 238 functions it does not have, including all 63 of libScePosix.

Neither is wrong. Presence under shadPS4 measures its stubbing policy; absence under Kyty measures what it implements. Census results had been read as statements about the platform, and this is the correction.

scripts/unresolved.py turns that log into the useful direction - which functions are missing, by name, grouped by library. A lookup rather than a crack: this program knows the name of every symbol it imports and computed the NIDs itself, so hashing our own list gives an exact table. 237 of 238 named.

craziiEmu: the module had been declaring the wrong console generation

Built it from source; dotnet was already present. It reported Generation: Gen4 (PS4) for a probe aimed at the current generation, because e_ident[EI_ABIVERSION] was zero - which every linker leaves it at, none of them knowing about either console.

Setting it to 2 flips craziiEmu to Gen5 (PS5) and takes its relocation-descriptor count from zero to one. Corroborated before it was believed: Kyty's Elf64::IsNextGen() is exactly that byte against 2.

And then it broke shadPS4 completely

A correction to what was reported an hour earlier. The change was described as safe for shadPS4 because that emulator "reads this field only to name it in a log line and does not gate on it". It rejects the module outright:

IsElfFile: e_ident[EI_ABIVERSION] expected 0x00 is (0x2)

666 records to zero. The claim came from grepping for the field, finding a function that formats it for a log, and stopping - the actual check is forty lines away in the same file. Finding an answer is not finding the answer, and this is the second time today a too-early stop produced a confident wrong statement.

The conflict itself is real and nobody is at fault: shadPS4 is a previous-generation emulator, and refusing a module marked for the current generation is what the field is for. So the generation became a build option - GEN=5 by default, GEN=4 for shadPS4 - rather than a constant either loader has to be worked around (D062). A GEN=4 build says something true about what it was built as; the previous state said something false to every current-generation loader.

shadPS4 restored: 667 records, complete, 65 pass / 6 partial / 38 fail / 7 skip.

Smaller things

Clippy flagged the dispatcher as too long once mkmodule gained a flag, so its arguments moved into their own struct rather than the lint being suppressed. 88 tool tests.

The Makefile edit went in with a literal \n instead of a line continuation - the same escaping mistake that has caught this session repeatedly. Fixed with an editor rather than a string-escaping script, which is the actual lesson.