6 Commits

Author SHA1 Message Date
chrg6
7a8bbad47e Remove the low-end GPU mode from the settings.
The user asked for it: the mode no longer gains frame rate, and the mesh shader fallback covers the cards it was written for. Its switch in the settings menu's Game tab is gone with the rows under it, and so are their 57 text ids and strings.

low_end_gpu itself does nothing any more. It is still a known key, so an older nx2navi.ini gives no unknown-key warning, and while it is on the log says once that the mode was removed in 0.10.2. The preset that gave nine keys other defaults is gone, and so is the rule that read the mode's keys only while it was on: each of them (mesh_emulation, tex_clobber, foliage, vram_heap_limit, vram_reserve_mb, upload_batch_mb, mirror_stage, mirror_hot, submit_finish_gpu and the others) is an ordinary setting with the default of its row or of its reader, so a line a player wrote by hand keeps counting and a file with only the switch runs the game as it runs for everyone else. guest_opt in nx2navi.ini counts for the next rebuild without the switch too (tools/make_game.py).

The recorder thread read the switch itself: on an AMD card without mesh shaders the mode turned it on. It is off on every AMD card now unless vk_recorder = 1 is set.

nx2navi.ini.example lists the former mode's settings as single settings, the 0.10.2 notes have a Removed entry and name the foliage fix of the commit before this one, and the comments that said what the mode sets say so in the past tense. tests/cfg_parse_check has the new reading of the switch and of the keys without it; the test matrix is at 90 ok with the same two not ok as before (installer_test, worktree_test), and a clean build compiles 169 runtime files and links. Not run in the game.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-14 06:13:18 -05:00
chrg6
8c9011d56f Read low_end_gpu's and shader_carry's off words the same in the low-end mode and in make_game.py as in the game's other readers.
The review of the commit that taught nx2navi.ini's readers off, false and no found three readers it left behind. cfg.c
lowend_of reads low_end_gpu through raw_get, which skips switch_word, and still took anything but "0" as on: with
"low_end_gpu = off" cfg_get, cfg_bool and the menu said off while cfg_low_end_active() was 1, so the mode's defaults
and its LOWEND_ONLY keys stayed in effect, and F-7's check, which now accepts the word, logged nothing. lowend_of now
takes the value through switch_of as cfg_bool does: 0 and the off words are off, anything else set is on (an
environment value "0 ; x" stays on, as cfg_bool reads it). tools/make_game.py read the file on its own too:
shader_carry took only "0" as off and kept a comment in the value, so with "shader_carry = off" or "shader_carry = 0 ;
note" an update skipped the full shader preparation and left the shaders to a game that no longer carries them over;
guest_opt_ini took low_end_gpu as on for anything but "" and "0". shader_carry now reads the switch as the game does
(the environment wins over the file and is taken as written, the file's last line with a value wins, a comment after
a blank is cut unless the value is in double quotes, make_game.py ini_choice, and 0, off, false and no in any case are
off); guest_opt_ini keeps its cut and takes the same words as off.

tests/cfg_parse_check gains a lowend case (low_end_gpu = Off, = 0 ; note and = yes from the file, FALSE and "0 ; x"
from the environment; two of them fail on the old lowend_of), and launcher_test's MakeGameTest two tests of
shader_carry's and guest_opt_ini's reading, which fail on the old make_game.py. cfg_rows_check reports 0 problems, and
the -Wall compile of cfg.c shows no warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 20:13:03 -05:00
chrg6
b6709da852 Read a comment after a setting's value, and off, false and no for a switch, as players mean them in nx2navi.ini.
A hand-edited "pace_smooth = 0 ; off" kept the comment in the value, which the readers took as on, "fps_limit = 144 #
monitor" gave 60, and "fullscreen = false" or "rtss = off" were on, since every reader took anything but "0" as on;
the log only named these lines (the check added for F-7). Now, for a key whose row has a set of values, cfg_load cuts
an unquoted value at the first blank followed by ';' or '#' after the '=' ("key = ; note" is unset) and logs the line
once; free text keeps its whole value (window_size, monitor, audio_device and keys without a row such as mods_dir,
pipeline_cache and the bindings, where a path or name may hold " #"), as do a value in double quotes and the
environment's values. A switch (a row whose set is 0|1) written as false, off or no reads as 0 and as true, on or yes
as 1 through cfg_get itself, so every reader agrees (cfg_bool, rt_knob, the readers that compare with "0" or parse a
number); cfg_bool and rt_knob also take those words for keys of other rows, and rt_knob no longer logs them. F-7's
check now names a switch value only when it is none of those, and a comment kept in a value only where it stays
(free text, the environment). The launcher's reader (launcher/gameini.py) cuts the same way for keys the caller
says have a set of values; its mods keys stay free text. The menu's save still writes a changed key's line again
without its comment.

This changes what existing files with such lines do, which is what was approved. tests/cfg_parse_check.py,
tests/rt_knob_check.py and launcher_test PlayerIniTest cover the cut, the words, free text, quotes and the
environment, and pass; cfg_rows_check reports 0 problems; the runtime compile check and the -Wall comparison of cfg.c
and perf.c show no new warnings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 20:13:03 -05:00
chrg6
153295b64c Check cfg_meta's result before reading a row's default in default_number and gfx_cfg_window_size.
cfg.c default_number, which cfg_int and cfg_double use for a set value that does not parse, read cfg_meta(name)->def
without a check, so a key without a row whose value was set and unparsable crashed there (the verifier's probe: a
segmentation fault inside default_number). gfx_present.c gfx_cfg_window_size did the same for WINDOW_SIZE. Neither
is reachable today: every unguarded value read has a row (tests/cfg_rows_check.py holds it so), the three guarded
ones use cfg_bool, and WINDOW_SIZE has a row.

default_number gives 0 for a key without a row; gfx_cfg_window_size falls back to 1920x1080 when the row or its
default is missing. Taking the device profile's default there (1280x800 on a Steam Deck) would change what players
get and is left for approval.

Reproduced before the change with the new case in tests/cfg_parse_check.py (cfg_int and cfg_double of an unset row's
key set to "abc" crashed the program); with the change both give 0 and the check passes, as do the cfg rows checks
and the compile checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 18:42:34 -05:00
chrg6
5b9ed9ed12 Say so in the log when the settings table is full and a key or a menu change is not kept.
cfg.c keeps at most CFG_MAX_KEYS (512) keys. add, cfg_set, cfg_reset and migrate stopped silently at the cap, so a
file with more keys (every cheat ever switched keeps a cheat_<hash> line, unknown keys count too) had its later keys
ignored and a later menu change lost, with nothing in the log (the audit's probe: low_latency after 600 cheat lines
read as unset, a cfg_set of vk_present lost, 0 log lines). Only a file far past what players have reaches it.

A line now says so: at load once per load, naming the first key and line not read, and once a run for the changes
cfg_set, cfg_reset and migrate could not keep. Raising the cap would change which keys take effect and is left for
approval.

Not reproduced before the change by a game run; tests/cfg_parse_check.py gains the case: a file of 600 cheat keys
and then low_latency gives one line at load from line 513 on, and two cfg_set past the full table one line. The cfg
rows checks and the compile checks pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 18:42:34 -05:00
chrg6
8c1ec42406 Log the nx2navi.ini and environment values that are outside their settings' sets, with the line they came from.
cfg.c takes a value as written and logged nothing about it: "pace_smooth = 0 ; off" keeps the comment in the value
and cfg_bool reads it as on, "fullscreen = false" is on too, and "sharpness = 250" is past 0..100 (the audit's
probe on cfg.c printed exactly that, with 0 log lines during the load). A player who edits the file by hand had no
way to see why a setting did not take.

After cfg_load (and after migrate, so a value it rewrites is checked as read) each file value is checked against
its row: a 0|1 switch with any other value, a value that is not a number in a lo..hi range, and a word that is
none of a list of words, case aside. A list with a number in it is left alone: it is the menu's presets and its
consumer takes more (fps_limit any rate from 30 to 240). A list of words is reported as "none of the menu's
choices" only, since a consumer may take more (dlss_preset a..m). A value holding " ;" or " #" is noted on any row:
the comment is part of it. cfg_init checks the environment's NX2NAVI_ values the same way. At most 16 lines a load,
then a count. The values read are unchanged; reading words for off or cutting comments (F-8) needs approval.

Not reproduced before the change by a game run; the new tests/cfg_parse_check.py builds cfg.c with stand-ins and
checks each kind of line with its line number, the environment's, the cap of 16 and a count, and no line for a file
whose values fit (including fps_limit 144, render_scale 300, dlss_preset k, pad_layout xbox_prompts). Every value
nx2navi.ini.example shows passes without a line. launcher_test PlayerIniTest, the cfg rows check and the compile
checks pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 18:42:10 -05:00