Files
t5de/CLAUDE.md
T
dhkatzandClaude Opus 5.5 746068b4fa
CI / test (push) Successful in 1m41s
Document diagnostics
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 23:25:39 -07:00

13 KiB

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_<name> (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. Windows started from a shortcut get the shortcut's icon on the taskbar, and IMVUClient.exe's icon can't be extracted (blank), so shortcuts and the uninstall entry use imvu.ico.
  • 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-<pid>.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=<file> 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). Done: release CI. ci.yml builds dist/T5DE-Setup.exe (bundle embedded, version via -X .../internal/buildinfo. Version and go-winres --product-version/--file-version), uploads it as a run artifact, and .gitea/scripts/release.py publishes it with the job token: vX.Y.Z tags make release X.Y.Z; pushes to main replace the nightly pre-release (version X.Y.Z-nightly.N+commit, N commits after the last release tag). The version is shown in the setup and "Apps & features", and recorded in t5de-install.json (version). Code signing: postponed by the user.
  • Diagnostics (internal/diagnostics): anonymous usage statistics to the user's self-hosted Aptabase (its /api/v0/event API) and crash reports to their Bugsink (Sentry envelopes, hand-built), only from builds with the CI secrets APTABASE_HOST, APTABASE_APP_KEY, BUGSINK_DSN (embedded via -X .../buildinfo.*), and only with consent: HKCU\Software\T5DE\Diagnostics (UsageStatistics, CrashReports DWORDs; unset is no). The user wants both on by default: the setup's "Help improve T5DE" page (install and change flows) starts ticked unless a choice is recorded, and records the choice. The setup sends setup_<action>, setup_failed (plus a report) and setup_uninstall events with patch.<id> properties. The client is to send through the install's t5de-setup.exe track|report (headless, helper.go), since its Python 2.7 can't do modern HTTPS. Still to do: client launch events, client errors to Bugsink, and settings in the client.
  • Self-updates (planned): take over the client's own updater (bypassed by the no-update-check patch) to offer T5DE releases instead of IMVU's.
  • Forks of the old GitHub repo (surveyed 2026-10-27): only one has new patches, apparently AI-generated and untested. Ideas worth redoing from scratch: a shop "Try on" button (imvu.call('tryOn', pid) in shop/ShopMode.js) and a creator product search (its api.imvu.com endpoints are unverified).
  • 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).