Skip to content

chore: land the reviewed logging, hooking and launch-option changes onto main - #27

Merged
ResurrectedTrader merged 3 commits into
mainfrom
land-stack
Sep 16, 2026
Merged

ResurrectedTrader merged 3 commits into
mainfrom
land-stack

Conversation

@ResurrectedTrader

Copy link
Copy Markdown
Owner

This is a re-land, not new work

#24, #25 and #26 were all merged — but each merged into its base branch, not into main. GitHub only retargets a stacked PR to main when the base branch is deleted after merging; the bases here were kept, so:

So main currently has the x64 change only, and the other three are sitting in intermediate branches.

What this PR is

The same three commits, replayed onto current main:

Commit Was reviewed as
refactor: give every component a named logger over one shared sink #24
refactor: put every Detours hook behind one typed slot API #25
feat: remove the parsed launch options from the process command line #26

Nothing changed. The resulting tree is byte-identical to origin/split/detour-api, which is the branch that holds all four merged changes — verified with git diff, empty output. If you already reviewed #24/#25/#26 there is nothing new to read here; this is purely about getting the content onto main.

After merging

Delete split/x64-platform, split/logging, split/detour-api and split/option-parser. They hold no unique content once this lands, and leaving them is what caused the mis-targeting in the first place.

🤖 Generated with Claude Code

ResurrectedTrader and others added 3 commits September 16, 2026 23:15
Log sites reached spdlog directly, so each one wrote wherever the default
logger happened to point, and a logger built before the host installed its
sinks stayed attached to whatever was there at the time.

utils::GetLogger(name) hands out a logger per component over a single
fan-out sink. Where output goes is decided once, by AddLogSink, and applies
to loggers that already exist as well as ones created later - so the
bring-up loggers the backend creates at DLL attach write to the same places
as everything else. Names are dotted and follow the source tree.

The host adds a rotating file sink next to the ini, named per profile so
concurrent instances do not share a file, and flushes every second. Loggers
flush themselves from warn: forcing one per record would put an fflush on
every line inside the shared sink lock, which a script logging in a loop
would make everyone else pay.

Default volume is unchanged - the framework logger at debug, everything else
at spdlog default. Raw spdlog:: calls are gone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Each call site had rolled its own transaction and its own real-function
pointer, and two of them shared a defect: a failed commit left the site
believing it had attached, so the matching remove detached hooks that were
never installed - eighteen of them in speedhack. Realms additionally dropped
its detach error and nulled its real-function pointers while in-flight hook
bodies could still be running.

core/detour is one API for all of them. Target says where a function lives,
Hook<Fn> is a slot whose target and replacement are checked against each
other at compile time and whose Real() reaches the original whether or not
it is attached, and Batch is one transaction that de-duplicates exports
sharing a body, refuses two different replacements over one body, and aborts
rather than committing an empty update.

A failed DetourTransactionBegin is aborted rather than left pending: one of
its two failure modes claims the process-wide transaction slot before
returning the error, and leaving it claimed wedges every later attach and
detach in the process. A refused detach is reported instead of being
swallowed, since the slot keeps a live detour into a DLL that is unloading.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Launch-option parsing moves out of the 1.14d backend into
core/config/OptionParser, since nothing about it is version-specific: a
backend declares its own options struct and builds a table, and the walker
is shared.

The table then doubles as the list of switches to take back out. Once the
options are parsed their values are cached, so the command line no longer
needs to carry them - and a CD key passed as -d2c, or a proxy password
inside -proxy, stops being readable through the PEB, Task Manager, WMI and
GetCommandLineA/W. The whole switch goes, not just the credential in it, so
neither the value nor the fact that it was passed survives.

Three buffers hold the line. The wide one and the PEB copy are cut in place
(usually the same storage; handled separately when not). The ANSI copy is
re-encoded from the cut wide line rather than cut with the same token
indices, because a byte-wise split disagrees with a UTF-16 one wherever a
DBCS trail byte is 0x5C - which would silently leave the whole line readable
through GetCommandLineA.

This defeats reading the command line afterwards, not capturing it at
creation: anything hooked into process creation has already seen it.

Twenty-three tests cover the cut, including the cases where the two splits
disagree and nothing may be touched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ResurrectedTrader
ResurrectedTrader merged commit 7cca9da into main Sep 16, 2026
1 check passed
@ResurrectedTrader
ResurrectedTrader deleted the land-stack branch September 16, 2026 22:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant