{"id":51407758,"url":"https://github.com/shdwmtr/resident","last_synced_at":"2026-07-04T12:02:04.565Z","repository":{"id":368503085,"uuid":"1284450536","full_name":"shdwmtr/resident","owner":"shdwmtr","description":"Resident: An incredibly small, performant, independent utility tool that un-obfuscates minified class names in Steams CEF on-the-fly.","archived":false,"fork":false,"pushed_at":"2026-06-30T20:58:27.000Z","size":4085,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-30T22:24:48.869Z","etag":null,"topics":["css-loader","decky","deobfuscator","millennium","patcher","sfp","steam"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shdwmtr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-29T21:56:42.000Z","updated_at":"2026-06-30T20:57:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/shdwmtr/resident","commit_stats":null,"previous_names":["shdwmtr/resident"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/shdwmtr/resident","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shdwmtr%2Fresident","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shdwmtr%2Fresident/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shdwmtr%2Fresident/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shdwmtr%2Fresident/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shdwmtr","download_url":"https://codeload.github.com/shdwmtr/resident/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shdwmtr%2Fresident/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35120682,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-04T02:00:05.987Z","response_time":113,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["css-loader","decky","deobfuscator","millennium","patcher","sfp","steam"],"created_at":"2026-07-04T12:02:03.900Z","updated_at":"2026-07-04T12:02:04.559Z","avatar_url":"https://github.com/shdwmtr.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Resident 🪑[^1]\n\n**RES**tore Steam's internal class **IDENT**ifiers.\n\nAn incredibly small, performant, independent utility tool that un-obfuscates minified class names in Steams CEF on-the-fly in memory by modifying the heap. There is **no runtime overhead**. This tool is particularly useful for creating stable, future proof themes.\n\n```diff\ndiff --git a/tmp/before.html b/tmp/after.html\nindex ec676a2..1ca47d7 100644\n--- a/tmp/before.html\n+++ b/tmp/after.html\n@@ -1,17 +1,17 @@\n-\u003cdiv class=\"_1rDh5rXSFZJOqCa4UpnI4z\" style=\"position: relative;\"\u003e\n+\u003cdiv class=\"_1rDh5rXSFZJOqCa4UpnI4z ContentFrame\" style=\"position: relative;\"\u003e\n```\n\n\n`resident` is fully native, dependencyless, self-contained, portable, does not require CEF remote debugging, has virtually no memory footprint, and does NOT write to disk. \nIt entirely self-bootstraps itself, requiring **absolutely zero** user intervention.\n\nThis tool is entirely plugin loader agnostic, and supports both the Steam deck and client. \n\nIf you like this tool, consider starring it ⭐\n\n# Installation[^2]\n\nPacking notice: resident is entirely stateless, and has no config. If you are packaging resident, it is recommended to simply keep it in your installation folder, and\nsimply hard-link/soft-link (on windows, by default, softlinks need admin. hard don't) the soname/dll into the webhelpers directory. Killing Steam's webhelper will cause Steam to restart it, which will load/unload resident depending.\n\n## Linux\n```bash\n# Installing resident\n$ mv /path/to/resident.so ~/.steam/steam/ubuntu12_64/libXtst.so.6\n# uninstalling resident\n$ rm ~/.steam/steam/ubuntu12_64/libXtst.so.6\n```\n## Windows\nCopy the installed `resident.dll` binary into the `steamwebhelper.exe`'s owning directory, renaming it to `version.dll`. \nSame applies if you are hardlinking to the webhelpers owning directory; it must be named `version.dll`. \n\n# Parser[^3]\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"50%\"\u003e\n\n```mermaid\nstateDiagram-v2\n    [*] --\u003e Scanning\n    Scanning --\u003e ReadKey : identifier or '\"'\n    Scanning --\u003e Scanning : other\n    ReadKey --\u003e AwaitHash : key + ':\"'\n    ReadKey --\u003e Scanning : no match / rewind\n    AwaitHash --\u003e Validate : 18–30 chars + '\"'\n    AwaitHash --\u003e Scanning : out of range\n    Validate --\u003e Rewrite : is_css_hash ✓\n    Validate --\u003e Scanning : plain word / false positive\n    Rewrite --\u003e Scanning : flush + emit 'hash key'\n```\n\n\u003c/td\u003e\n\u003ctd width=\"50%\"\u003e\n\u003cimg src=\"https://github.com/user-attachments/assets/ecc853b4-fcab-4c64-ab31-7ca3ca53478c\" width=\"100%\"/\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n`resident` implements a linear-time lexical transducer (a [finite-state transducer](https://en.wikipedia.org/wiki/Finite-state_transducer)) over a byte stream. \nIt uses a [greedy](https://en.wikipedia.org/wiki/Greedy_algorithm), left-anchored PEG recognize with a single bounded lookahead rewind, scanning for two token patterns defined by the EBNF grammar below.\n\n## [EBNF](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form) (Extended Backus–Naur) grammar\n\n```\npattern  ::= quoted_key | bare_key\nquoted_key ::= '\"' key_q '\"' ':\"' hash '\"'\nbare_key   ::= key_b ':\"' hash '\"'\nkey_q    ::= [a-zA-Z_][a-zA-Z0-9_-]*\nkey_b    ::= [a-zA-Z_][a-zA-Z0-9_]*\nhash     ::= [a-zA-Z0-9_-]{18,30}\n```\n\n## High Level\n\nClass modules are rewritten as diagramed below. This happens at the CEF level, both Steam and CEF are under the impression that content `B` is the original, they both have no idea.\nAt the native C bindings, CDP (Inspector Network tab), and JS level, this *is* Steam's original JS - even though it's not. \n\n```js\ne.exports = {\n    \"duration-app-launch\": \"800ms\",\n    VCenter: \"_1T7c8767I5SNmJ3DC5uSr8\",\n    BackgroundAnimation: \"_1noPMemGV6O50ZVadg1Cxf\",\n    \"ItemFocusAnim-darkGrey\": \"_1ulNzAcgjuBmpgvc5wqLMD\",\n    \"ItemFocusAnim-translucent-white-10\": \"_3pmHfms_Y73-eEZ6-BSy3j\",\n    \"ItemFocusAnimBorder-darkGrey\": \"_335Bbo4P8V_X7iOeO9gc2e\",\n    focusAnimation: \"_2v_k6SupaG0hlrpoCWsND4\"\n}\n```\n\n```js\ne.exports = {\n    \"duration-app-launch\": \"800ms\", // skipped, not a class defined by our lang.\n    VCenter:\"_1T7c8767I5SNmJ3DC5uSr8 VCenter\",\n    BackgroundAnimation:\"_1noPMemGV6O50ZVadg1Cxf BackgroundAnimation\",\n    \"ItemFocusAnim-darkGrey\":\"_1ulNzAcgjuBmpgvc5wqLMD ItemFocusAnim-darkGrey\",\n    \"ItemFocusAnim-translucent-white-10\":\"_3pmHfms_Y73-eEZ6-BSy3j ItemFocusAnim-translucent-white-10\",\n    \"ItemFocusAnimBorder-darkGrey\":\"_335Bbo4P8V_X7iOeO9gc2e ItemFocusAnimBorder-darkGrey\",\n    focusAnimation:\"_2v_k6SupaG0hlrpoCWsND4 focusAnimation\"\n}\n```\n\n## Other Rewrites\n\n`*.classList.add/remove`: Both of these methods expect a single class, which used to be the case. Not after our patches though.\n\n* detects: `classList.add(IDENT[()].IDENT) and classList.remove(IDENT[()].IDENT)`\n* rewrites: `classList.add(...IDENT[()].IDENT.split(\" \"))`\n\nWithout this patch, add/remove will be directly called with a hooked class.\n\n### Before:\n```js\n// focusAnimation: \"_2v_k6SupaG0hlrpoCWsND4 focusAnimation\"\n...classList.add(A().focusAnimation) // err: add/remove only accept 1 class per\n```\n\n### After:\n```js\n// focusAnimation: \"_2v_k6SupaG0hlrpoCWsND4 focusAnimation\"\n...classList.add(...A().focusAnimation.split(\" \")) // adds both, hell yeah\n```\n\nFrom what I've googled, this is the only situation where we would face issues. \nAll other API's are safe at runtime. (excluding `document.querySelector`, etc. Valve dev's are intelligent and wouldn't logically use raw DOM APIs with react)\n\n# Benchmarks[^4]\n\nThese are benchmarks are the average patch time over 100 runs on an Intel i9-14900k.\nIn fact; this libraries server is an order of magnitude faster than Steams inbuilt loopback. \nOn startup, Steam reads ALL files from steamui/ linearly, (seemingly) no chunking\nand caches them in page cache. With this library, steam *forcefully* misses `chunk~*.js` when initially caching.\nThis leads to faster, less resourceful startup (measured to be about 3 seconds on my machine).\n\nThis is why Millennium moved themes outside of steamui/ as any very deep directories with alot of individual reads (.git, node_modules/ etc) cause the CPU to panic\nunder L3 cache pressure, making startup sometimes 15,20, or even n seconds (linearly) slower depending on your setup. \n\n\u003cimg src=\"https://github.com/user-attachments/assets/ac75dce0-b719-49f9-9050-f0cca811a46b\" /\u003e\n\n# Accuracy \u0026 Reliability[^5]\n\nThe patcher cannot generate invalid syntax, it's fully rulled out of the language. It *technically* can produce runtime errors by modifying strings that \naren't in class modules, but the parameters are very strict - this likely won't happen. \n\n# Hooking[^6]\n\n## Windows\n\nHooking relies on DLL lookup path hijacking. The **steamwebhelper.exe** *links* **version.dll** (meaning the wloader loads version.dll into the process before `main()/__constructor__()`) for version utilities. \n**version.dll** is officially shipped with Windows as a builtin system component, in System32. Historically, **version.dll** was never made a [**KnownDLL**](https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order) for security reasons (debated), meaning\nwhen wloader calls LoadLibrary on **version.dll**, it will actually try to look it up in the cwd instead of System32 directly. This means we can create a *fake* **version.dll** wherever the **steamwebhelper.exe** lives, and if we import and re-export the real **version.dll**s symbol table \nfrom System32, we have internal memory access to steams webhelper. From there we setup the hooks documented below. \n\n## Unix\n\nSame idea as Windows, but using an X11 dependency the hijack target. `libXtst.so.6` is always loaded by the webhelper which is spawned under Steams pressure vessel. The pressure vessel points `LD_LIBRARY_PATH` to a container at `~/.steam/steam/ubuntu12_32/`. \nHowever, their library path specifications aren't tight enough. We can sneak in a shim at `~/.steam/steam/ubuntu12_64/libXtst.so.6`, take precedence over `~/.steam/steam/ubuntu12_32/steam-runtime/amd64/usr/lib/x86_64-linux-gnu/libXtst.so.6` in search path resolution, `HOOK_FUNC` to pipe calls back to the original, and an `__attribute__((constructor))` to setup the hooks documented below. \n\n```mermaid\nflowchart LR\n    S[steam.exe] --\u003e W[Starts steamwebhelper.exe]\n    W --\u003e K[Requests version.dll\\nchecks KnownDLLs registry key]\n    K --\u003e N[Not listed in KnownDLLs\\nfalls back to standard search order]\n    N --\u003e A[Loader loads our ./version.dll]\n    A --\u003e R[Re-exports real version.dll exports]\n    R --\u003e SW[steamwebhelper.exe runs]\n\n    A --\u003e CT[constructor runs\\ninstall_hook]\n    CT --\u003e DL[dl_iterate_phdr\\nfinds cef_browser_host_create_browser\\nin libcef.so]\n    DL --\u003e IN[snare_inline_new\\npatches bytes at cef fn entry\\nto redirect to tramp_]\n\n    SW --\u003e CEF[CEF calls\\ncef_browser_host_create_browser\\nclient, url, settings...]\n    IN --\u003e CEF\n\n    CEF --\u003e TR[tramp_ intercepts\\ncef_browser_host_create_browser]\n    TR --\u003e PH[Patches client vtable\\nclient-\u003eget_request_handler\\n-\u003e hooked_get_request_handler]\n    PH --\u003e OC[Calls original\\ncef_browser_host_create_browser\\nvia snare trampoline]\n    OC --\u003e SW\n\n    SW --\u003e GRQ[CEF calls\\nclient-\u003eget_request_handler]\n    GRQ --\u003e HRQ[hooked_get_request_handler\\ncalls original get_request_handler\\ngets Steam's cef_request_handler_t]\n    HRQ --\u003e PRQ[Patches returned handler\\nhandler-\u003eget_resource_request_handler\\n-\u003e hooked_get_resource]\n    PRQ --\u003e RH[Returns modified\\ncef_request_handler_t to CEF]\n\n    RH --\u003e GRES[CEF calls\\nget_resource_request_handler\\nwith cef_request_t]\n    GRES --\u003e HRS[hooked_get_resource\\nreads request-\u003eget_url]\n    HRS --\u003e CHK{chunk~*.js\\n+ has local file?}\n    CHK --\u003e|No| PASS[Calls orig_get_resource\\nreturns Steam's handler]\n    CHK --\u003e|Yes| DROP[Disposes Steam's handler\\nif it returned one]\n    DROP --\u003e CUST[Returns custom\\nsteamloopback_request_handler_t\\nwith resident read callbacks]\n```\n\n# Building[^7]\n\nAll build instructions assume the host is linux. It compiles fine on windows, I just don't have docs for it. \n\n```bash\n# build resident\n$ make\n\n# permanently install resident into steam\n$ make install\n\n# Cross-compile for Windows from Linux (requires `mingw-w64-gcc`)\n$ make cross\n```\n\nOn Arch Linux, install the cross-compiler with:\n\n```bash\n$ sudo pacman -S mingw-w64-gcc\n```\n\n# Third party libraries[^8]\n\n[libsnare.h](https://github.com/shdwmtr/libsnare.h) \n\nMy highly stable c/cxx/asm compatible single-header hooking library for x86/x64/arm64. inline hooks and PLT/IAT hooks. linux/windows/macos.\n\n[^1]: About resident\n[^2]: Installing resident\n[^3]: Learn about residents parser semantics\n[^4]: Resident benchmarking results\n[^5]: Accuracy and reliability\n[^6]: Hooking algorithms\n[^7]: Building libresident\n[^8]: Thirdparty library notices\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshdwmtr%2Fresident","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshdwmtr%2Fresident","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshdwmtr%2Fresident/lists"}