One pipeline turns the user's own copy of the disc into the fully-patched English app
tree, and tracks every file we touch in one auditable place. It is the foundation for the
end deliverable: an English installer re-wrapped from the user's own setup.exe.
We never ship a SYGNAS byte. Git holds only the recipe — patch/manifest.toml, the engine
tools/build_patch.py, and the patch sources under patch/. The user runs the build against
their own originals/ (gitignored); the output out/ (gitignored) is derived-from-originals
and also never redistributed. What we eventually hand a user is the tooling + the delta, which
they apply to their own disc.
| Path | Role |
|---|---|
patch/manifest.toml |
What we patch — one declarative entry per file + op + a note. The single source of truth. |
tools/build_patch.py |
How — mirrors originals/installed/ → out/patched/, applies each op, writes the audit. Stdlib only (py3.11+ for tomllib). |
out/patched/ |
The full patched English tree (passthrough files copied verbatim, patched files transformed). Gitignored. |
out/patched/PATCH-LOG.txt |
Generated audit — every op applied + everything deferred. |
patch/launcher/*.ini |
Launcher speech sources (cp932), generated by tools/build_launcher_en.py; repacked by the xvi op. |
patch/text/* |
Tracked EN replacements (readme, …), authored UTF-8 and transcoded on build. |
Run it (in nix develop): python tools/build_patch.py → out/patched/ + PATCH-LOG.txt.
The build is reproducible (two runs hash-identical) and idempotent (wipes+rebuilds out/).
Applied in this fixed order (rename always last). Any op may set active = false to record
intent without applying — logged as DEFERRED. String fields template {install_root_jp} /
{install_root_en} from [meta].
| Op | Does | Used for |
|---|---|---|
xvi |
repack a translated cp932 Ini into each .Xvi (via sygnas_repack.py) |
the 22 launcher characters |
text_keys |
replace INI KEY= values, preserve everything else |
Launch.ini menu titles |
text_subst |
literal find→replace pairs in a text file | wallpaper UI text; (deferred) install-root path rewrite |
text_file |
replace a whole file with a tracked EN version (encoding/crlf transcode) |
the readme |
binpatch |
replace whole NUL-terminated (wide/narrow) strings, NUL-padded in place | gcalcore.dll/gcal.exe host→localhost |
rename |
rename a file in the patched tree | (deferred) .mink/.scr/install root |
binpatch matches a complete NUL-terminated string (old + terminator), so the bare host
www.google.com and the host inside a longer URL are disambiguated; the replacement is
written in place and the freed tail is zeroed (the new string stays shorter and NUL-terminated),
so the file size never changes and the PE stays valid.
The app reads text via ANSI APIs (GetPrivateProfileStringA, DrawTextA), so any non-ASCII
byte mojibakes on a non-JP system. Rules baked into the manifest:
- App-read text → pure ASCII.
Launch.inititles use "Lucky Star", not "Lucky☆Star". (The.Xviserifs are drawn withDrawTextAtoo; a few still carry☆→ an ASCII pass is a follow-up.) - Notepad-only text → UTF-8 + BOM (
utf-8-sig). XP Notepad honours the BOM on any locale, so the readme keeps☆ × ■ ○. - HTML → UTF-8 (its
<meta charset>drives the browser); anything renders. - File paths → ASCII (the
らき☆マスinstall root, the JP.mink/.scrnames). This is the bulk of the locale fix and is tracked as deferredrename+text_substentries — it needs the install to actually live at the ASCII path, which the installer stage pins.
So the mascots' calendar works offline and the XP box keeps real internet (no hosts
blackhole): binpatch rewrites the wide host string in both gcalcore.dll and gcal.exe
(www.google.com → localhost; also the allcalendars URL and gcal.exe's add-event deep-link).
The server side matches: gcalsrv.lua returns a localhost event-feed link, and the embedded
cert is regenerated CN=localhost (tools/gcal-emu/make-xp-cert.sh, SAN also lists the google
names so the legacy hosts-redirect still validates). After applying, remove the XP hosts
line. Validate with tools/gcal-xp/test/clientlogin.vbs (defaults to https://localhost/...).
Everything not yet safe to apply is recorded in the manifest as active = false (so nothing is
lost and the audit shows it): the install-root path rewrite, the .mink (10) / .scr (4) /
wallpaper-JPG renames, plus prose TODOs for the MinkIt copy-engine path config (no INI ships →
RE where it reads its folder), autorun.inf (installer regenerates it), and the PE-resource UI
strings (lang 1041) in the 4 exes + 4 .scr (a future pe-res op). Flip active = true once the
gating RE / installer work lands.
The next major stage consumes out/patched/: extract the user's own setup.exe
(innoextract), drop in the patched tree, translate the Inno script/UI ([Languages] + custom
messages) and pin an English {app} install path, recompile with ISCC under wine → an
English setup.exe the user builds from their own disc. (Distribution as an xdelta3/IPS delta off
out/patched/ is the lighter alternative.)