# t5de Patch workflow for the IMVU Classic client (Windows, py2exe, Python 2.7). Rewrite of the original t5de project, now hosted at git.t5de.dev. Later goals: a proxy backend for IMVU's servers, and possibly a client rewrite on a modern stack. ## Rules - Never commit IMVU's code: no decompiled source, extracted UI files, or client binaries. `work/` and `.cache/` are gitignored for this reason. Patches in `patches/` are diffs and are fine to commit. - Releases are bring-your-own-client: users run the patcher on their own copy; never redistribute the full client. - Never commit secrets (VPN configs, keys, tokens). - Test with a throwaway IMVU account, never a real one. ## How it works - `t5de setup` downloads the installer pinned in `t5de.toml` (sha256-checked), extracts it with 7-Zip, decompiles the first-party modules in `library.zip` (`imvu`, `main`, `im`, `Ext`) with uncompyle6, and extracts `ui/chrome/imvuContent.jar`. The result becomes the `base` commit of the git repo `work/` (`library/`, `imvuContent/`), and `patches/*.patch` are applied on top with `git am --3way`, in filename order, with a fixed committer (so it works without a git identity, and applied commits hash identically everywhere). - `t5de save` exports every commit after `base` with `git format-patch`, using flags that make the output deterministic, so unchanged commits re-export byte-identically. - The base must be identical on every machine: uncompyle6/xdis are pinned, output is normalized (`base.py`), and the base commit has a fixed author and date. `t5de.toml`'s `base` pins the expected commit (554.8: `5b8137f`), and setup fails if the decompiled base differs; CI then prints the base's per-file blob hashes to compare. Normalizing drops uncompyle6's `# Compiled at:` header, which is in the decompiling machine's local time zone (bump `NORMALIZE_VERSION` when normalization changes). Windows paths over 260 characters break setup (deep jar paths), so keep the checkout in a short path. - Tests: `pytest` (unit tests; `tests/py27/` scripts run under Python 2.7 for splicing and for checks on a real build). `T5DE_REQUIRE_ALL=1` turns skips for missing Python 2.7 or build into failures. CI (`.gitea/workflows/ci.yml`, Gitea 1.25) builds Python 2.7 from source (cached), runs setup, build and all tests; never upload the build (it's IMVU's client). `imvu-update.yml` runs `t5de check-update` daily and opens an issue. - Patches carry metadata as commit trailers: `T5de-Id` (lowercase-words), `T5de-Group` (core, privacy, features, annoyances; core and privacy need `T5de-Required: true`), optional `T5de-Depends: id, id`, optional `T5de-Name` (a few words, shown in the installer; the user wants short UI text). Subject = title, body = description (shown on hover). `bundle.py` validates them. - `t5de bundle` writes `build/bundle/manifest.json` (format 1): per patch, its metadata, its compiled overrides per library module, and its UI changes (byte-offset edits with the exact old bytes, or added/removed files); `core` operations applied with every selection (remove `devicefingerprint.exe` and `npfpbase_vc10.dll`, create `ui/profile` (empty in a real install, so 7-Zip skips it; the client crashes on startup without it), and rewrite the QualityAgent's crash report host to `127.0.0.1`); and sha256s of the client files modified. It holds no IMVU files. Each patch is compiled on its own (`splice27.py`, Python 2.7, comparing the module before and after the patch's commit), so users can choose patches: a patch that changes a definition or UI file an earlier patch changed, or uses a definition one added (same module), must depend on it. - `splice27.py` compiles only changed/added functions and methods (no `-O`: the decompiler shows explicit `raise AssertionError` as `assert`); methods are compiled in a same-named class body (private names mangle right) and copied onto the original class. Module- and class-level changes, removals, and decorators that use class-body names are errors; import-time references to changed functions (aliases, `from M import f`, star imports) are warnings. Replaced definitions stay reachable as `_t5de_original_` (class attribute for methods, module global for functions), always IMVU's original (later patches don't overwrite it), so patches can wrap them. It warns when a replaced definition's decompiled base doesn't recompile to IMVU's bytecode (e.g. `Network.urlopen`), unless the patch calls its original. Overrides are marshalled with version 0 (no interning). - `patcher/` (Go; `t5de-patcher apply|list`) applies a bundle to a client folder: verifies it (every file against IMVU's `checksum.txt`, and the bundle's sha256s), copies it, splices library.zip (each patched module's code: a small code object, written by the patcher in marshal version 0, that execs the original module code, embedded verbatim and first in the stream so its interned-string refs stay valid, then each selected patch's overrides in order), applies UI changes to the jar, applies `core`, recomputes `checksum.txt`, and writes `t5de-install.json` (it only replaces folders with that record). Archives keep entry order and metadata; both are uncompressed. - `t5de build` = `t5de bundle`, then the patcher (`go run`, or `$T5DE_PATCHER`) applying every patch to the cached install into `build/IMVUClient/`. Builds are byte-reproducible. - Patches can add t5de's own files under `client/` in work/ (never IMVU's); the bundle carries them per patch (`files`) and the patcher writes them into the install. Two patches writing the same file need a dependency. - Window icons: Python dialogs load `imvu.ico` from the program folder (`win32/win32window.py`, which isn't first-party, so not in work/); the native main window (`SceneWindow.dll`) loads `imvu.ico` from the working directory. `imvu.ico` isn't in `checksum.txt` (the installer creates it). The `t5de-branding` patch writes T5DE's icon as `client/imvu.ico`, sets the main window's icon explicitly, retitles windows "T5DE", and adds a login badge. `TopLevelWindow.title` is a property bound at class-creation time, so it's changed by wrapping `setActiveMode`, not `set_title`. - Reporting endpoints share hosts with the API (`client-dynamic`, `secure`, `www.imvu.com`), so they're blocked by URL in `Network.urlopen`/`post_multipart` (patch 16), which logs each refusal as `t5de: blocked request to`. Only `bugs.imvu.com` is reporting-only, so OS-level blocking (hosts/firewall) can't do much more. ## Verified facts (client 554.8) - The NSIS installer extracts with 7-Zip byte-identically to a real install (all files in `checksum.txt` match), so CI can run on Linux without running the installer. - `checksum.txt` holds decimal CRC32s and paths; the old patcher removed the `library`, `imvuContent` and `devicefingerprint` lines. The build should recompute it. - Bytecode is Python 2.7 (magic 62211); 357 first-party modules. `imvu/tool/RadioTool.py` fails to decompile and is stubbed. - The decompiler is deterministic but not faithful: recompiling decompiled code gives identical bytecode for only ~60% of modules, and 6 don't compile at all. So never ship whole recompiled files. The 6 (mostly mangled string escapes): `imvu/bugreport/__init__.py`, `imvu/geography.py`, `imvu/translation.py`, `imvu/task/util.py`, `imvu/platform/windows.py` (null bytes), `main/login.py`. Patches avoid them. - `IMVUQualityAgent.exe` launches and restarts the client, and on a crash it sends `GET /catalog/imvu_monitor_submit.php?cid=` to `www.imvu.com` itself (raw WSOCK32), out of reach of Python patches. - `npfpbase_vc10.dll` is a BlueCava device-fingerprinting browser plugin that the embedded Gecko can load. - Patch 1 also filters Creator Mode facts; patch 14 drops all facts. The filter is kept so patch 1 stands alone. - `imvu.expectModules` (called from the exe's boot script) rejects unexpected modules loaded at startup; `--no-expected-module-check` bypasses it. - The UI is XULRunner/Gecko; IMVU's own JS in the jar isn't minified. ## Next - The build runs on a real client (launched directly, without `IMVUQualityAgent.exe`), sends no reporting requests, and the user has checked the patches' features. - End-user release: `T5DE-Setup.exe`, the Go patcher with a Wails wizard and the bundle embedded (no IMVU files; bring-your-own-client), per-user (no admin). Wizard: welcome; find IMVU Classic (`HKCU\Software\IMVU` default value = install folder) and verify it; choose patches (core/privacy shown locked); options (install folder, default `%LOCALAPPDATA%\T5DEClient`, shortcuts); install (apply, shortcuts, "Apps & features" uninstall entry under HKCU); finish. Re-running offers modify/repair/update/uninstall via `t5de-install.json`. Shortcuts launch `IMVUClient.exe` directly (the agent is untested). Done: `t5de-patcher install|uninstall` (`internal/install`; Windows parts behind a `System` interface, faked in tests, real one tested on Windows only). Install keeps `t5de-setup.exe` and `t5de-bundle.json` in the install, records shortcuts in `t5de-install.json`, registers `HKCU\...\Uninstall\T5DE` (NoModify/NoRepair; the setup does those). Uninstall run from inside the install relaunches from a `%TEMP%\t5de-uninstall-.exe` copy, which deletes itself afterwards; `-remove-data` also removes `HKCU\Software\T5DE` and `%APPDATA%\T5DE`. Reinstalling replaces the folder, including the browser profile (cookies). Verified end to end on a real install. Done: `cmd/t5de-setup`, the wizard (Wails v2 with a plain HTML/CSS/JS `frontend/`, no npm; build tags `desktop, production`, `-H=windowsgui`; Linux CI vets and cross-compiles it for Windows only). Modes: install; manage (when an install exists, or run from the copy in one: change patches, repair/update, uninstall); uninstall (what "Apps & features" runs). Run from inside an install, it relaunches from a temporary copy. Bundle: embedded from `cmd/t5de-setup/bundle/manifest.json` (gitignored; copied in by release builds), else `-bundle`, else the install's `t5de-bundle.json`. `T5DE_SETUP_SELFTEST=` makes the page report what it rendered and close. Look: IMVU Classic's dialog palette (`imvuContent/dialogs/css/imvu_jquery_dialog.css`: dark gray `#383838`, gold `#ffe795`->`#ffd200` buttons with black text, gray `#616161`->`#3e3e3e` secondary), always dark. Copy is terse (the user dislikes wordy titles and descriptions). Wails loads the window icon from icon resource #3, so the setup's resources come from `winres/winres.json` (`go-winres make`), not `simply` (which uses #1). Logo and icon: `patcher/assets` (the user's); both exes link `rsrc_windows_amd64.syso` (icon, version info, manifest), generated by go-winres v0.3.3 via `go generate ./cmd/...` (reproducible). Still to do: release CI (embed the bundle, set the version; tag releases, a rolling nightly pre-release), code signing. - Contributor setup: revisit a single bundled `t5de` executable once the Go patcher exists. Findings so far: `msiexec /a` extracts python.org's 2.7.18 MSI unelevated into a working portable Python 2.7 (tests pass with it), and contributors who have the pinned client installed could skip the installer download and 7-Zip. - Installs keep the exe named `IMVUClient.exe` (`imvu/program/windows.py` finds the agent by replacing that name). The patched client's user data is `%APPDATA%\T5DE`, settings `HKCU\Software\T5DE` (patch 15).