From d8971fded065bd88f9d8f27d6d37ca5413c2a0b1 Mon Sep 17 00:00:00 2001 From: James Dansereau <106631954+jkdansereau@users.noreply.github.com> Date: Sun, 27 Sep 2026 15:03:24 -0400 Subject: [PATCH] options UI: settle file terminology on the end-user term 'Profile' (D359) 'Game file' (the manual's term) reads, in modern PC usage, as the program's own files, not the user's save. 'Profile' is the clearest end-user term for what these files actually are -- a per-player bundle of progress + per-file settings -- and '(per profile)' expresses the tag's scope where '(save)' read as a verb. Strings (both UIs: front options + F10 overlay): - front top row: 'Save file' / 'File N' -> 'Profile' / 'N' - section annotation: '(File N)' / '(no file)' -> '(Profile N)' / '(none)' - per-file row tag: '(game file)' -> '(per profile)' - F10 unavailable value: 'Select game file' -> 'Select a profile' Display layer only: identifiers and ini keys (Bond.* rows, watchSettings*, ini) are untouched. D357 plan section 4 is the terminology record; new D359 finding + index row; manual 'game file' -> 'profile' sync noted as a TODO (port-owned doc, not in this repo). Build + headless smoke clean. --- docs/dev/D357-SETTINGS-VALUES-PLAN.md | 43 ++++++++++++++++----------- docs/dev/findings-index.csv | 1 + docs/dev/findings.md | 36 +++++++++++++++++++--- port/src/frontoptions.c | 26 ++++++++-------- port/src/optionsoverlay.c | 23 +++++++------- 5 files changed, 85 insertions(+), 44 deletions(-) diff --git a/docs/dev/D357-SETTINGS-VALUES-PLAN.md b/docs/dev/D357-SETTINGS-VALUES-PLAN.md index 60ff6264..2cad493c 100644 --- a/docs/dev/D357-SETTINGS-VALUES-PLAN.md +++ b/docs/dev/D357-SETTINGS-VALUES-PLAN.md @@ -8,10 +8,11 @@ user notes, not checked independently*), and the N64 watch (ground truth for the `Bond.*` rows only). Triggered by maintainer report: the current values are "a bit confusing". -Companion change landed with this doc's review: the dim per-file row tag is -now **"(game file)"** (was "(save)"); the front top row reads **"Game -file"** and the F10 unavailable value reads **"Select game file"** — the -instruction manual's term (§4). +Companion change landed with this doc's review: the terminology settled on +**"Profile"** as the end-user term for a per-player game file -- the +front top row reads **Profile**, the per-file row tag reads +**"(per profile)"** and the F10 unavailable value reads **"Select a +profile"** (§4). ## 1. Problem @@ -113,23 +114,31 @@ ranges (2026-09-27). "Display" = what the value column shows today. | Frame rate cap | 60 · 0–1000 · 1 · `Uncapped/N FPS` | Turok: "up to 120 FPS" / uncapped | **Preset grid**: 0/30/60/120/144 (step = next preset, wraps to Uncapped); value text already `N FPS`/`Uncapped`. Decision: preset grid vs free 1-step (recommend presets; 117 FPS has no purpose). | | Show FPS | Off/On | both | Keep. | -## 4. Terminology (instruction manual = "game file") +## 4. Terminology (end-user term = "Profile") -The instruction manual calls the per-player save a **"game file"**; the -settings UI anchors on that term (2026-09-27, maintainer request). Final -string set: +Decision (2026-09-27, UX review): the end-user term for a per-player +game file is **"Profile"** — the per-player bundle of progress + +per-file settings. Rejected: **"game file"** (the instruction manual's +current term) reads, in modern PC usage, as the *program's* own files +("Verify integrity of game files"), not the user's save; **"Save"** was +the fallback (universally understood) but reads as a verb in the row tag +and as progress-only, which these files are not. "Profile" covers both +and fits the dossier styling of the screen. -| String | Where | State | -|---|---|---| -| **"Game file"** | front options top-row label (was "Save file") | changed | -| **"(game file)"** | per-file row tag, both UIs (was "(save)", briefly "(profile)") | changed | -| **"Select game file"** | F10 unavailable value (was "Select file") | changed | -| `File N` | row values + the `(File N)` section annotation | kept (value-column short form, PD "Player N" pattern) | -| `(no file)` | unavailable state, dim annotation | kept | +Final string set (both UIs: front options + F10 overlay): + +| String | Where | +|---|---| +| **"Profile"** + value `N` | front options top row (was "Save file: File N") | +| **"(Profile N)"** / **"(none)"** | section-title annotation on save-scoped sections (was "(File N)"/"(no file)") | +| **"(per profile)"** | per-file row tag (was "(save)", then "(profile)", then "(game file)") | +| **"Select a profile"** | F10 unavailable value (was "Select file") | + +**Manual sync TODO:** the instruction manual's "game file" should be +updated to "profile" (the manual is a port document we own). "Bond file" remains in code identifiers/config keys (`Bond.*` rows, -`watchSettingsChooseFile`, ini keys) only -- never user-visible; the manual -and UI say "game file". +`watchSettingsChooseFile`, ini keys) only -- never user-visible. ## 5. Gates diff --git a/docs/dev/findings-index.csv b/docs/dev/findings-index.csv index 5c6f4324..71da7749 100644 --- a/docs/dev/findings-index.csv +++ b/docs/dev/findings-index.csv @@ -235,3 +235,4 @@ D354,FIXED,Front-end F10 Bond rows silently disabled:,"FIXED (port-only, port/sr D355,FIXED,F10/options Music slider inaudible in-stage:,"FIXED (port-only, port/src/watchsettings.c):" D356,FIXED,"Settings regroup: Turok-style pages renamed to functional sections (INPUT/GAMEPLAY/GRAPHICS/AUDIO/VIDEO) with PD-style ""(File N)"" scoping annotations, the redu…","FIXED (port-only, port/src/optionsoverlay.{h,c} + port/src/watchsettings.{h,c} + port/src/frontoptions.c; plan docs/dev/D356-SETTINGS-REGROUP-PLAN.md):" D358,FIXED,D356 level-1 overlap:,"FIXED (port-only, port/src/frontoptions.c):" +D359,PARTIAL,"Settings UI file terminology settled on the end-user term ""Profile"":","LANDED (port-only, frontoptions.c + optionsoverlay.c + comments):" diff --git a/docs/dev/findings.md b/docs/dev/findings.md index 490f9ece..da36d167 100644 --- a/docs/dev/findings.md +++ b/docs/dev/findings.md @@ -643,6 +643,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121). | D355 | **F10/options Music slider inaudible in-stage: the slider drives only the X-track (track 2) via `set_mTrack2Vol`, but in-stage BGM is track 1, which the game pins to `VOLUME_MAX` at level start (lv.c:364) and never rescales from `mTrack2Vol` in solo -- so neither the N64 watch nor the F10 could change the music the player was hearing; maintainer-reported (2026-09-27).** — full `## D355` entry at file tail | FIXED (port-only, `port/src/watchsettings.c`): the Music commit now also calls `musicTrack1ApplySeqpVol(v)` (the exact resync the MP path already does, mpmenu.c:354) and a once-per-activation resync runs on stage entry; by-ear confirmed by the maintainer. N64 watch path untouched. | | D356 | **Settings regroup: Turok-style pages renamed to functional sections (INPUT/GAMEPLAY/GRAPHICS/AUDIO/VIDEO) with PD-style "(File N)" scoping annotations, the redundant N64-watch rows (`Bond.Look`, `Bond.AimControl`) off the menu surface, and a real per-section Reset-to-defaults (two-step arm->confirm, watch rows to BLANKSAVEDATA via the D352 commit path, ini rows to the port's C initializers; `__Resolution` a documented exclusion) replacing the Turok reset rows the PC build never honored (2026-09-27).** — full `## D356` entry at file tail | FIXED (port-only, `port/src/optionsoverlay.{h,c}` + `port/src/watchsettings.{h,c}` + `port/src/frontoptions.c`; plan `docs/dev/D356-SETTINGS-REGROUP-PLAN.md`): 5 sections all below MAX_PROWS, hidden rows D181/D216-style (keys + watch paths stay live), edge-triggered reset with 3 s arm window + maintain/clear disarm, GE_WSPROBE_RESET front + in-stage probes headless-verified (all sections failures=0, scope isolation OK, saved bytes 0xFF), GE_WSPROBE/GE_WSPROBE_FRONT regressions clean. | | D358 | **D356 level-1 overlap: the front options save-file row (item 0) painted under the first content row, and `s_hl` (item-numbered) was indexed straight into `s_rowIdx` (content-numbered) -- wrong-row adjusts, a dead last row, a one-past read, wrong highlight (2026-09-27).** — full `## D358` entry at file tail | FIXED (port-only, `port/src/frontoptions.c`): content rows draw at `rowY(k+1)`; all `s_hl` -> `s_rowIdx` uses take `s_hl-1`; A-press guards `s_hl >= 1 && s_hl <= s_rowN`; hit-test/cursor untouched (already item-numbered). Companion: "(save)" tag -> "(profile)". | +| D359 | **Settings UI file terminology settled on the end-user term "Profile": the screen mixed five terms ("Save file"/"File N"/"(profile)"/"Select file"/"(no file)"), and the manual's "game file" reads as the program's own files in modern PC usage (2026-09-27).** — full `## D359` entry at file tail | LANDED (port-only, `frontoptions.c` + `optionsoverlay.c` + comments): top row `Profile`/`N`, annotation `(Profile N)`/`(none)`, per-file row tag `(per profile)`, F10 value `Select a profile`; identifiers/ini keys unchanged (display-layer only). Manual "game file" -> "profile" sync TODO (port-owned doc, not in repo). | Phase 2 replaced the Phase-1 demo loop with the real `mainproc()` on real OS threads, compiled GE's real `src/sched.c`, and brought in PD's fast3d software @@ -13965,7 +13966,34 @@ and are untouched. Verified by build + headless boot smoke; visual check of the screen owed (D357 plan gate 4). **Companion change (same commit):** the per-file row tag now reads -"(profile)" (was "(save)", maintainer request), both UIs. (Later the same -day the tag and the rest of the file terminology were anchored on the -instruction manual's "game file" term: "(game file)" tag, "Game file" top -row, "Select game file" -- D357 plan §4.) +"(profile)" (was "(save)", maintainer request), both UIs. (Full term +history: "(save)" -> "(profile)" -> "(game file)" -> the final +"(per profile)" set anchored on the end-user "Profile" term -- D357 +plan section 4.) + +## D359 — Settings UI file terminology settled on the end-user term "Profile" (2026-09-27) + +**Reported:** maintainer English/terminology review of the settings UI: +the screen mixed five terms for the same thing ("Save file" label, +"File N" values, "(profile)" tag, "Select file" fallback, "(no file)" +state), and the instruction manual's "game file" was questioned for +clarity. + +**Decision:** the end-user term is **"Profile"** — a per-player bundle +of progress + per-file settings. "Game file" rejected: in modern PC +usage it reads as the program's own files (Steam's "Verify integrity of +game files"), not the user's save. "Save" was the fallback but reads as +a verb in the row tag and implies progress-only. Rationale + the full +string table: `docs/dev/D357-SETTINGS-VALUES-PLAN.md` §4. + +**Landed (port-only, `frontoptions.c` + `optionsoverlay.c`, both draw +paths + comments):** top row `Profile` / value `N` (was `Save file` / +`File N`); section-title annotation `(Profile N)` / `(none)` (was +`(File N)` / `(no file)`); per-file row tag `(per profile)` (was +`(save)` -> `(profile)` -> `(game file)`); F10 unavailable value +`Select a profile` (was `Select file`). Code identifiers and ini keys +(`Bond.*` rows, `watchSettings*`, ini) are unchanged — terminology is a +display-layer concern only (principle 3). + +**TODO:** update the instruction manual's "game file" to "profile" +(port-owned doc, not in this repo). diff --git a/port/src/frontoptions.c b/port/src/frontoptions.c index 8497ca04..e233f3a3 100644 --- a/port/src/frontoptions.c +++ b/port/src/frontoptions.c @@ -495,16 +495,17 @@ Gfx *frontOptionsMenuDraw(Gfx *DL) if (optionsRowIsSaveScoped(s_pageHdr[s_page])) { int f = watchSettingsActiveFolder(); char note[16]; - if (f < 0) snprintf(note, sizeof(note), "(no file)"); - else snprintf(note, sizeof(note), "(File %d)", f + 1); + if (f < 0) snprintf(note, sizeof(note), "(none)"); + else snprintf(note, sizeof(note), "(Profile %d)", f + 1); DL = ink(DL, ROW_X + titleW + 8, TITLE_Y, note, INK_DIM); } - /* D356: the top game-file row (item 0, not in the row table; the - * label reads "Game file", the instruction manual's term): the - * single file control of the front screen (F10 always targets the - * active file, so it has no file row). L/R (and A) cycle folders; - * (no file) is the unavailable state until one is created. */ + /* D356: the top profile row (item 0, not in the row table; the label + * reads "Profile" -- the end-user term for a per-player game file, + * see D357 plan section 4): the single file control of the front + * screen (F10 always targets the active file, so it has no file + * row). L/R (and A) cycle folders; (none) is the unavailable state + * until one is created. */ { int y = rowY(0); char sv[32]; @@ -512,10 +513,10 @@ Gfx *frontOptionsMenuDraw(Gfx *DL) DL = microcode_constructor_related_to_menus(DL, ROW_X - 2, y - 1, ROW_X + 140, y + 0xE, HILITE); } - DL = ink(DL, ROW_X, y, "Game file\n", INK); + DL = ink(DL, ROW_X, y, "Profile\n", INK); int f = watchSettingsActiveFolder(); - if (f < 0) snprintf(sv, sizeof(sv), "(no file)"); - else snprintf(sv, sizeof(sv), "File %d\n", f + 1); + if (f < 0) snprintf(sv, sizeof(sv), "(none)"); + else snprintf(sv, sizeof(sv), "%d\n", f + 1); DL = inkR(DL, VAL_R, y, sv, INK); } @@ -534,9 +535,10 @@ Gfx *frontOptionsMenuDraw(Gfx *DL) DL = ink(DL, ROW_X, y, label, INK); /* D357: per-file rows in the mixed-scope section carry a dim - * "(game file)" tag (the section title already says which file). */ + * "(per profile)" tag -- "stored separately in each profile"; + * the section title says which one is active. */ if (optionsRowIsSaveScoped(i)) { - DL = ink(DL, ROW_X + measureW(label) + 4, y, "(game file)\n", INK_DIM); + DL = ink(DL, ROW_X + measureW(label) + 4, y, "(per profile)\n", INK_DIM); } if (optionsRowIsSlider(i)) { diff --git a/port/src/optionsoverlay.c b/port/src/optionsoverlay.c index 8132c0aa..5d975f13 100644 --- a/port/src/optionsoverlay.c +++ b/port/src/optionsoverlay.c @@ -146,7 +146,7 @@ struct Row { int dispDiv; /* D356: 1 = this row's value lives in the selected save file (per-file - * watch rows). Carries the dim "(game file)" tag on the only section that + * watch rows). Carries the dim "(per profile)" tag on the only section that * mixes scopes (GAMEPLAY); on headers the flag is recomputed at init to * mean "this section contains per-file rows" (drives the "(File N)" * title annotation in both UIs). */ @@ -158,7 +158,7 @@ static struct Row rows[] = { * GAMEPLAY, GRAPHICS, AUDIO, VIDEO; docs/dev/D356-SETTINGS-REGROUP-PLAN.md). * The D353 "BOND FILE" section -- the one named after WHERE a value is * saved -- is gone: its surviving rows split into GAMEPLAY (the per-file - * watch toggles, dim "(game file)" tagged since the section mixes scopes) and + * watch toggles, dim "(per profile)" tagged since the section mixes scopes) and * AUDIO (the per-file volume sliders, homogeneous so title annotation * only), and the "Edit file" chooser row is retired in favour of the * single top save-file row on the front options screen (frontoptions.c). @@ -198,7 +198,7 @@ static struct Row rows[] = { * Bond.AimControl collides with Input.AimMode (two rows fighting over * the same N64-vs-PC aim model). * The four surviving per-file toggles move here from the D353 BOND FILE - * section and carry .saveScoped (dim "(game file)" tag, the only mixed-scope + * section and carry .saveScoped (dim "(per profile)" tag, the only mixed-scope * section). */ /* { .key="Bond.Look", .label="Look up/down (watch; stacks)", .kind=ROW_TOGGLE, .names=kReverse, .found=1, .uiMax=1, .cfgMax=1 }, */ @@ -1231,12 +1231,12 @@ static void valueText(int i, char *out, int n) double v = rowGet(r); if (r->kind == ROW_BOND_FILE) { int f = watchSettingsFolder(); - if (f < 0) snprintf(out, n, "Select game file"); - else snprintf(out, n, "File %d", f + 1); + if (f < 0) snprintf(out, n, "Select a profile"); + else snprintf(out, n, "%d", f + 1); return; } if (watchSettingsFieldForKey(r->key) >= 0 && !watchSettingsAvailable()) { - snprintf(out, n, "Select game file"); + snprintf(out, n, "Select a profile"); return; } if (r->kind == ROW_RES) { @@ -1428,8 +1428,8 @@ Gfx *optionsOverlayEmit(void) if (r->saveScoped) { int f = watchSettingsActiveFolder(); char note[16]; - if (f < 0) snprintf(note, sizeof(note), "(no file)"); - else snprintf(note, sizeof(note), "(File %d)", f + 1); + if (f < 0) snprintf(note, sizeof(note), "(none)"); + else snprintf(note, sizeof(note), "(Profile %d)", f + 1); gdl = drawText(gdl, OV_X0 + measureText(r->label) + 6, rowY, note, 0x909090ff); } continue; @@ -1437,10 +1437,11 @@ Gfx *optionsOverlayEmit(void) gdl = drawText(gdl, OV_LABEL_X, rowY, (char *)r->label, r->found ? col : 0x808080ff); /* D357: per-file rows in the mixed-scope section carry a dim - * "(game file)" tag (the section title already says which file). */ + * "(per profile)" tag -- "stored separately in each profile"; + * the section title says which one is active. */ if (r->saveScoped && r->kind != ROW_HEADER) { gdl = drawText(gdl, OV_LABEL_X + measureText(r->label) + 5, rowY, - "(game file)", 0x909090ff); + "(per profile)", 0x909090ff); } if (!r->found) { gdl = drawTextR(gdl, right, rowY, "(n/a)", 0x808080ff); @@ -1520,7 +1521,7 @@ int optionsRowIsBondChooser(int i) /* D356: content rows carry the literal per-file flag from the table; header * rows were recomputed at init to "this section contains per-file rows". - * Both drive UI decoration (the "(game file)" tag, the "(File N)" annotation). + * Both drive UI decoration (the "(per profile)" tag, the "(Profile N)" annotation). * The F10 overlay and the front options screen are the only consumers. */ int optionsRowIsSaveScoped(int i) {