forked from Ports/goldeneye-pc-port
M7 (v0.4.0, D387): ship AllUnlocked as loudly-documented EXPERIMENTAL/irreversible
Per the user's release decision, keep the toggle (default OFF) but make it impossible to miss in-game: the row is now labelled 'All unlocked (EXPERIMENTAL)' and its ON value renders 'ON - UNSAFE' in both the F10 overlay and the front PC Options screen (both read the shared row table). ON value now bright-highlights like a normal On. Port-only, display-only (config still stores 0/1); a safe architecture is v0.5.0 work. Housekeeping: track docs/dev/M7-ALLUNLOCKED-PLAN.md (records the decision); gitignore the stray repo-root Python/ install; refresh the D387 status.
This commit is contained in:
1 parent
471eecd6d3
commit
9fb381eab1
5 files changed
+63
-4
No files matched your search
@@ -195,3 +195,7 @@ build-pc-cmd.bat
|
||||
|
||||
# v0.4.0 modern-options wave: per-landing sweep capture dirs (capture.sh output)
|
||||
tools_pc/sweep-captures/capture-*/
|
||||
|
||||
# Stray full CPython install that landed in repo root (2026-09-28);
|
||||
# referenced by no project tooling. Never commit.
|
||||
/Python/
|
||||
@@ -0,0 +1,45 @@
|
||||
# M7 — Reversible AllUnlocked: investigation and fix plan
|
||||
|
||||
**v0.4.0 release decision (2026-09-28, user):** ship AllUnlocked as a
|
||||
**loudly-documented, EXPERIMENTAL, irreversible** option for this release rather
|
||||
than hiding/disabling it or landing a safe architecture. The toggle stays (default
|
||||
OFF); the in-game row is now labelled **"All unlocked (EXPERIMENTAL)"** with the ON
|
||||
value displayed as **"ON - UNSAFE"** in both the F10 overlay and the front PC
|
||||
Options screen, and the README Known-issues bullet already tells users to back up
|
||||
`data/ge007.eep` before enabling it. A proper fix (Candidate A port-only shadow or
|
||||
Candidate B non-mutating virtual query) remains the v0.5.0 work item.
|
||||
|
||||
Status: **investigation, NO FIX APPROVED OR LANDED**. Root cause/repro: `docs/dev/findings.md` D387; original feature: D257, fresh-save repair: D281, old-CRC migration: D297. This plan supersedes the M7 "no-code proof" in `docs/dev/notes/MODERN-OPTIONS-PLAN.md`. Follow `AGENTS.md` rule 2 and `docs/dev-process.md` §7 before any `src/game` edit.
|
||||
|
||||
## User-visible contract to settle
|
||||
|
||||
Recommended: OFF (after restart, as presently documented) must display only genuinely earned mission progress/cheats; ON may *temporarily* expose every level, difficulty, 007 mode and cheat, without modifying durable progress. Achievements **earned while ON without an active cheat** should survive OFF, including difficulty inheritance, timed cheats and profile operations. Enabling/disabling must never silently delete existing genuine save records. Do not auto-repair an EEPROM already containing baked fake unlocks: the bytes lack provenance. The user has not explicitly signed off on a game-code alternative.
|
||||
|
||||
## Confirmed facts (source + isolated data)
|
||||
|
||||
- `port/src/libultra.c:geEepromRW` first migrates legacy CRCs on the five-slot block-4 read, then `geEepromPatchAllCheats` fills unlocked-cheat bits and every zero completion time with 0x3FF and re-CRCs a **temporary copy**. `fileValidateSaves` (`src/game/file2.c:527`) accepts that copy into `saves[]`. `fileWriteSave` (`file2.c:86`) writes the entire 96-byte live slot, not only the setting/progress that changed.
|
||||
- Private data under `AppData/Local/Temp/ge-m7-20260927/`: the older `data/ge007 copy.eep` has two genuine times and only cheat `04 00 00` in active folder 0 (slot 4). ON or OFF boot without a save yields identical EEPROM bytes after the same legacy CRC migration. ON plus existing `GE_WSPROBE_FRONT=600` performs an ordinary Music-volume save to slot 4: cheats become `ff ff 0f`, 58 empty times become 0x3FF, two real times remain. OFF + restart validates the written CRC and retains all fakes. Full repro/logs: D387. Current `data/ge007.ini` and `data/ge007.eep` were never opened for write; copies and SHA-256s in that private test directory.
|
||||
- `main.c` seeds debug unlock flags only at boot; an in-session OFF toggle leaves those RAM flags set until restart. `Game.AllUnlocked` defaults OFF; the relevant UI currently gives no explicit restart warning.
|
||||
- More than a write-mask problem: a successful mission runs `end_of_mission_briefing` (`src/game/file.c:41–68`) → `fileUnlockStageInFolderAtDifficulty` (`file2.c:828`) → `fileOverwriteSaveSlotWithNewSave` (`file2.c:794`), **rotating the profile to a free EEPROM slot** and freeing the old slot. `fileCheckSaveStageDifficultyTime` only updates if the real time beats the synthetic 0x3FF. Genuine lower-difficulty completions and long (>1023-second) runs can also have legitimate 0x3FF times; value alone is not provenance. Fake cheat bits cause `fileGetIsCheatUnlocked` to return true, so `end_of_mission_briefing` skips a genuinely earned timed-cheat award. A port-only sanitizer keyed by physical slot or by `time==0x3FF` is incorrect.
|
||||
- The current user EEPROM already has two nonfree slots with 60/60 max times and all cheat bits, consistent with an earlier write-back. Two separate older EEPROM backups exist in `data/`, but replacing or auto-merging them would risk later real progress; do not touch or auto-repair originals.
|
||||
|
||||
## Architecture decision before code
|
||||
|
||||
**Candidate A — port-only EEPROM shadow/merge:** keep authoritative *raw* progress per logical profile outside patched `saves[]` and translate every write to durable real-only state. Must cover direct settings writes, profile rotation, copy/delete/reset, earned times (including 0x3FF), timed cheat grants currently suppressed by fake reads, five-slot CRCs, and old-CRC migration. Physical-slot-only interception demonstrably fails. A read/write-only adapter may have insufficient information to recognize some genuine completions or cheat awards; prove a full event/provenance model in an isolated prototype before promising this path. No new sidecar that can be lost/desynced without a recovery design.
|
||||
|
||||
**Candidate B — non-mutating virtual query:** retire `geEepromPatchAllCheats`, keep raw `saves[]` and EEPROM intact, and make the *opt-in* UI/gameplay availability queries report AllUnlocked without changing saved bits/times. Likely small PORT-gated touch points: `fileGetIsCheatUnlocked` (`src/game/file2.c:391`, existing D225 developer-only query override), `fileIsStageUnlockedAtDifficulty` (`file2.c:681`; cheat-enabled mission selection requires `STAGESTATUS_COMPLETED`, not merely the debug-flag `UNLOCKED`), and the raw earned-cheat check in `end_of_mission_briefing` (`src/game/file.c:41–68`), which must still record real cheat awards while virtual availability is ON. Audit every caller and the Rare/PD analogue first. A live-only toggle may require restoring debug flags safely; simplest contract is **takes effect on restart** and an explicit UI label. This alters opt-in `src/game` behavior and is **not** the ABI/layout exception: no implementation or implicit sign-off. If port-only is ruled out, prepare the exact file/line, reference, reason, scope and precedent required by `docs/dev-process.md` §7; obtain explicit user approval and document the grant in a new RULE-2-SIGNOFF finding before edits.
|
||||
|
||||
**Release fallback:** if neither safe architecture is ready, do not present the current toggle as a reversible enhancement. Get an explicit release decision on hiding/disabling it vs shipping a loudly irreversible option; default-off alone does not protect users who toggle it on. Keep existing saves intact.
|
||||
|
||||
## Test matrix (private files ONLY)
|
||||
|
||||
1. Capture baseline hashes of copied current ini/EEPROM; separate valid older-progress, fully fresh zero EEPROM, already-contaminated, legacy-CRC, and mixed/multiple-folder fixtures. Do not copy old saves back into `data/`.
|
||||
2. No-write ON boot then OFF restart: raw five slots unchanged except *explicit* D297 migration; correct UI availability in both states.
|
||||
3. ON settings writes via `GE_WSPROBE_FRONT` (normal slot), in-stage F10 watch writes, and save-file chooser/reset: only real settings/intentional reset persist; CRC valid; no synthetic completions/cheats in raw file.
|
||||
4. Real mission completion at agent and higher difficulty (including inherited lower difficulty), fast timed-cheat award, slow/capped 0x3FF completion, and cheat-enabled mission selection. Verify actual new earned records after OFF restart, no artificial ones. Build an isolated gameplay harness if manual replay is impractical; do not use a settings-write test as a substitute for mission completion.
|
||||
5. Save-slot rotation (`fileOverwriteSaveSlotWithNewSave`), profile copy/delete, all four folders plus free slot, and reopen across process restart. Confirm all five CRCs via `GE_SAVELOG=1` and raw bit-level diff; compare copied baseline profile identity rather than just physical slot index.
|
||||
6. Default-OFF NTSC/PAL/JP builds and post-landing 21-level default-render comparison; config/EEPROM originals hash-verified **after** all tests. Human test of ON/OFF UI text and behavior. Existing contaminated saves need an explicit *separate*, opt-in recovery plan; never silently rewrite them.
|
||||
|
||||
## Other wave item
|
||||
|
||||
M5 remains under user review: FPS cap and MSAA are independently exposed already; dropping the two-setting Low-end preset is a separate decision, not a dependency of this M7 investigation.
|
||||
@@ -671,7 +671,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
|
||||
| D384 | **Front PC Options key-bind navigation feedback + two-slot UI (2026-09-27).** full `## D384` entry at file tail | PARTIAL (NTSC build, isolated probes, user said fresh-settings retest looks good; visual gate owed): fixed menu arrows/Enter/Escape independent of game binds, Primary/Secondary only; old extra keys preserved on disk but ignored until that action is edited. |
|
||||
| D385 | **Real mouse 1–5 action rebinding in the two-slot editor (2026-09-27).** full `## D385` entry at file tail | PARTIAL (NTSC build + isolated probes; user spotted wrong Aim default, D386): Mouse 1–5 are actual bindings; fixed menu clicking and mouse wheel unchanged. |
|
||||
| D386 | **Correct Aim default from SDL middle (Mouse 2) to right (Mouse 3) (2026-09-27).** full `## D386` entry at file tail | PARTIAL (NTSC build and isolated ini migration test; live acceptance owed): version-2 default Aim safely migrates to Mouse 3; custom Aim binds stay untouched. |
|
||||
| D387 | **M7 AllUnlocked's read-time fake unlocks leak into saved EEPROM (2026-09-27).** full `## D387` entry at file tail | OPEN (isolated user-data-copy save/untoggle repro; no fix): a profile-settings save persists fake cheats/0x3FF times; OFF does not undo them. Rotating writes and cheat-award suppression make simple write masking unsafe. |
|
||||
| D387 | **M7 AllUnlocked's read-time fake unlocks leak into saved EEPROM (2026-09-27).** full `## D387` entry at file tail | OPEN (isolated user-data-copy save/untoggle repro; no fix): a profile-settings save persists fake cheats/0x3FF times; OFF does not undo them. Rotating writes and cheat-award suppression make simple write masking unsafe. **v0.4.0 (2026-09-28): shipped as a loudly-documented EXPERIMENTAL / irreversible option** (row = "All unlocked (EXPERIMENTAL)", ON shows "ON - UNSAFE", README backs it up); safe architecture deferred to v0.5.0. |
|
||||
| D388 | **Nest functional binding/HUD menus and remove redundant M5 toggle (2026-09-27).** full `## D388` entry at file tail | PARTIAL (NTSC build, isolated nested navigation/reset/capture probes; physical UI acceptance and visual release gate owed): five root sections, Input → Bindings → Movement/Actions; Gameplay → HUD; independent FPS/MSAA remain exposed and unchanged by removing Low-end mode. |
|
||||
| D389 | **F10 footer clipping and ambiguous submenu links (2026-09-27).** full `## D389` entry at file tail | PARTIAL (NTSC build + measured 320-wide footer variants; visual/user review owed): two-line help strip with reserved height, no default-scale title note, PD-style ellipses in place of "Open >". |
|
||||
| D390 | **F10 pinned Back selection did not visibly change (2026-09-27).** full `## D390` entry at file tail | PARTIAL (NTSC build + isolated page/tree probes; human visual check owed): Back now uses row-selected colour only when selected, title uses neutral ink; keep mixed-scope HUD under Gameplay, not Video. |
|
||||
|
||||
@@ -641,7 +641,9 @@ Gfx *frontOptionsMenuDraw(Gfx *DL)
|
||||
((u32)blue << 8) | 0xff);
|
||||
}
|
||||
if (buf[0]) {
|
||||
u32 col = (strcmp(buf, "On") == 0) ? INK_ON : INK; /* D346: kOnOff is now title-case */
|
||||
u32 col = (buf[0] == 'O' && buf[1] == 'N') ? INK_ON : INK;
|
||||
/* D346: kOnOff is title-case; M7 (D387) tags the
|
||||
* experimental AllUnlocked ON value "ON - UNSAFE". */
|
||||
strcat(buf, "\n");
|
||||
DL = inkR(DL, VAL_R, y, buf, col);
|
||||
}
|
||||
|
||||
@@ -94,6 +94,13 @@ static const int kMsaaSeq[] = { 1, 2, 4, 8 };
|
||||
/* v0.4.0 modern options wave: value names for the new rows (they land
|
||||
* in the functional sections -- Turok standard, D356 -- not a bucket). */
|
||||
static const char *const kOnOffRev[] = { "On", "Off", NULL }; /* 0 = On */
|
||||
/* M7 (v0.4.0, D387): AllUnlocked ships as a loudly-documented EXPERIMENTAL,
|
||||
* irreversible option. Enabling it and then saving (even a profile-settings
|
||||
* change) bakes synthetic cheat/completion times into the EEPROM that
|
||||
* switching OFF does not undo; it also breaks audio/aim on a fresh save with
|
||||
* no real save yet (D257/D259/D281). Back up data/ge007.eep before enabling.
|
||||
* Display-only value names; config still stores 0/1. */
|
||||
static const char *const kAllUnlocked[] = { "Off", "ON - UNSAFE", NULL };
|
||||
/* D379: the authored N64 sprite is red. Original is the identity path;
|
||||
* White at index 7 uses the same alpha-mask combiner as other true hues. */
|
||||
static const char *const kCrosshairColor[] = {
|
||||
@@ -281,7 +288,8 @@ static struct Row rows[] = {
|
||||
* start it OFF (0) -- the old "default ON" note was stale and is
|
||||
* corrected by D356. Consumed at startup by main.c -- applies from the
|
||||
* next launch. */
|
||||
{ .key="Game.AllUnlocked", .label="All unlocked", .kind=ROW_TOGGLE, .step=1, .names=kOnOff },
|
||||
{ .key="Game.AllUnlocked", .label="All unlocked (EXPERIMENTAL)",
|
||||
.kind=ROW_TOGGLE, .step=1, .names=kAllUnlocked },
|
||||
{ .key="__ResetGameplay", .label="Reset to defaults", .kind=ROW_ACTION },
|
||||
{ .key="__OpenHud", .label="HUD...", .kind=ROW_ACTION },
|
||||
/* D293: only quit path used to be the OS window-close / Alt+F4 -- no
|
||||
@@ -2156,7 +2164,7 @@ Gfx *optionsOverlayEmit(void)
|
||||
gdl = drawBody(gdl, bx0, rowY, val, ink);
|
||||
gdl = drawBodyR(gdl, o.valueR, rowY, "(restart)", 0x498053ff);
|
||||
} else {
|
||||
u32 valueInk = (strcmp(val, "On") == 0 ||
|
||||
u32 valueInk = (strncmp(val, "ON", 2) == 0 ||
|
||||
(isResetRow(r) && strcmp(val, "Confirm") == 0))
|
||||
? 0xa0ffa0ff : ink;
|
||||
gdl = drawBodyR(gdl, o.valueR, rowY, val, valueInk);
|
||||
|
||||
Reference in new issue
Block a user