mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
Default to the Switch's own button positions on every gamepad, and fix menu edge cases.
pad_layout defaults to nintendo: Switch A on the right face button, B bottom, X top, Y left, on an Xbox pad too, where auto had picked the Xbox layout. The settings probe checks every button's position. Menu: the "default" mark follows the Steam Deck profile's values (cfg_default); Left on a value the list does not hold goes to the last entry; the HDR row says "in use: off" when the swapchain is not HDR10; the open state is read again after taking the reset, so a menu opened by F1 while the overlay alone was drawn starts with its focus row; the font atlas is rebuilt only for a scale more than 12% off or one that held for half a second, so dragging the window's edge no longer rebuilds it every frame. A shader that fails to translate keeps its request and code in the shader cache. NX2NAVI_GPA_TWIN=0 turns the guest copy of device address table stores off (diagnosis). The memcpy override no longer prints its first 500 calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -12,9 +12,9 @@ amplitude before the strength (0.2 to 1.5, default 0.6; lower makes weak pulses
|
||||
|
||||
`NX2NAVI_PAD_LAYOUT` (`pad_layout`, the *Button layout* row on the Controls tab, live) picks which face button is Switch A:
|
||||
|
||||
- `nintendo`: by position, as on a Switch (the table below).
|
||||
- `nintendo` (default): by position, as on a Switch (the table below).
|
||||
- `xbox`: south A, east B, west X, north Y, as an Xbox pad labels them; on a PlayStation pad cross is A, circle B, square X, triangle Y.
|
||||
- `auto` (default): `nintendo` for a pad whose east button SDL reports labelled A (Switch Pro Controller, Nintendo-layout pads), `xbox` for the others.
|
||||
- `auto`: `nintendo` for a pad whose east button SDL reports labelled A (Switch Pro Controller, Nintendo-layout pads), `xbox` for the others.
|
||||
|
||||
The log names the layout in effect (`input: gamepad layout ...`) and each gamepad's first press
|
||||
(`input: gamepad "...": first press`); the menu shows the layout under *Automatic*. The game's prompts show the Switch
|
||||
|
||||
@@ -266,11 +266,11 @@
|
||||
# rumble_strength = 130
|
||||
# rumble_curve = 0.6
|
||||
# rumble_triggers = 0
|
||||
# Which gamepad button is Switch A: nintendo (by position: east A, south B, west Y, north X), xbox (south A, east B,
|
||||
# west X, north Y, as an Xbox pad labels them; cross A on a PlayStation pad) or auto (nintendo for a pad with Nintendo
|
||||
# labels, xbox for the others). The bind_ lines below apply on top. Changes while the game runs take effect at once.
|
||||
# 0.1.9's xbox_prompts is read as nintendo with prompts = xbox.
|
||||
# pad_layout = auto
|
||||
# Which gamepad button is Switch A: nintendo (the default: the Switch's buttons by position on any pad, east A,
|
||||
# south B, west Y, north X), xbox (south A, east B, west X, north Y, as an Xbox pad labels them; cross A on a
|
||||
# PlayStation pad) or auto (nintendo for a pad with Nintendo labels, xbox for the others). The bind_ lines below apply
|
||||
# on top. Changes while the game runs take effect at once. 0.1.9's xbox_prompts is read as nintendo with prompts = xbox.
|
||||
# pad_layout = nintendo
|
||||
# The buttons the game's prompts show: nintendo (the game's own), xbox (LB RB LT RT, Menu, View and a d-pad cross,
|
||||
# and A B X Y as labelled on the button pad_layout presses: with nintendo, A shows as B and X as Y). Read at start
|
||||
# (docs/controls.md, "Xbox button prompts"). pc: the key or mouse button the keyboard defaults press each button with
|
||||
|
||||
@@ -42,7 +42,7 @@ static const char *const known[] = {
|
||||
"DESC_TEMPLATE", "DEVICE_PROFILE", "DISCORD_APP_ID", "DISCORD_IMAGE", "DISCORD_RPC", "DLAA", "DLAA_JITTER", "DLAA_MV", "DLAA_MASKDEBUG", "DLAA_MVDEBUG", "DLAA_OBJMV", "DLAA_SURVEY", "DLAA_TIMES", "DLSS", "DLSS_CHAR_MASK", "DLSS_PRESET", "DLSS_REACTIVE", "DUMP", "DUMP_BUF", "DUMP_DIR",
|
||||
"DUMP_LOG", "LOG_CS_BINDS", "LOG_TEX_FLAGS", "LOG_MESH", "MESH", "POOL", "NATIVE_TENSOR", "LOG_RCOND", "LOG_RT_SIZES", "DUMP_WHITE", "DUMP_AFTER", "SUBMIT_FINISH", "ACQUIRE_WAIT", "CS_FALLBACK", "EXEFS", "FEEDBACK", "FIX_C0", "FIX_ZERO", "FLIGHT", "FLIGHT_AT", "FLIGHT_DIR", "FLIGHT_FRAMES",
|
||||
"FLIGHT_SPIKE_MS", "FLIGHT_THUMBS", "FLUSH_SPAN", "FOV", "FOV_PATCH", "FG_HUDLESS_SHOW", "FPS60", "FPS_LIMIT", "FRAMEGEN", "FREEZE_IDLE", "FREE_CORES",
|
||||
"FULLSCREEN", "FXAA", "GAME_ASPECT", "GAME_RES", "GPA", "GPA_EMPTY", "GPL", "GPL_DRAW_BUILD", "GPU", "GPU_INDEX", "GPU_NAME", "HDR", "HDR_GAME_NITS", "HDR_HUE",
|
||||
"FULLSCREEN", "FXAA", "GAME_ASPECT", "GAME_RES", "GPA", "GPA_EMPTY", "GPA_TWIN", "GPL", "GPL_DRAW_BUILD", "GPU", "GPU_INDEX", "GPU_NAME", "HDR", "HDR_GAME_NITS", "HDR_HUE",
|
||||
"HDR_PEAK_NITS", "HDR_UI_NITS", "HIGHFPS_FIX", "INPUT", "INPUT_RECORD", "INPUT_SCRIPT", "LANG", "LIB_PREBUILD",
|
||||
"LIB_PREBUILD_KEEP", "LIB_PREBUILD_THREADS", "LOCALE", "LOD_GUARD", "LSTICK_DEADZONE", "MEMORY", "MENU_LANG", "MENU_SOUND_VOLUME", "MENU_TEST",
|
||||
"MENU_CURSOR", "MENU_CURSOR_LOG", "MENU_TEST_DIR", "MENU_TEST_SET", "MINIMAL_LAYOUT", "MIRROR_GAP", "MODS_DIR", "MODS_DISABLED", "MODS_ORDER", "MODS_REBUILD", "MOTION", "MOUSE_MODE", "MOUSE_DIRECT_PATCH", "MOUSE_SENS", "MOVIE_HOST", "MOVIE_PACE",
|
||||
@@ -148,7 +148,7 @@ static const CfgMeta metas[] = {
|
||||
// Sound, Controls, Menu
|
||||
{"VOLUME", "100", "0..100", LIVE}, {"MENU_SOUND_VOLUME", "80", "0..100", LIVE}, {"AUDIO_CHANNELS", "stereo", "auto|stereo|5.1", RESTART},
|
||||
{"MOTION", "1", "0|1", LIVE}, {"RUMBLE_STRENGTH", "130", "0..300", LIVE}, {"RUMBLE_CURVE", "0.6", "0.2..1.5", LIVE},
|
||||
{"RUMBLE_TRIGGERS", "0", "0|1", LIVE}, {"PAD_LAYOUT", "auto", "auto|nintendo|xbox", LIVE},
|
||||
{"RUMBLE_TRIGGERS", "0", "0|1", LIVE}, {"PAD_LAYOUT", "nintendo", "auto|nintendo|xbox", LIVE},
|
||||
{"PROMPTS", "nintendo", "nintendo|xbox|pc", RESTART},
|
||||
{"STICK_DEADZONE", "8", "0..50", LIVE}, {"STICK_SENS", "1", "0.1..3", RESTART},
|
||||
{"LSTICK_DEADZONE", "", "0..50", LIVE}, {"RSTICK_DEADZONE", "", "0..50", LIVE},
|
||||
@@ -423,6 +423,17 @@ const CfgMeta *cfg_meta(const char *key) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *cfg_default(const char *key) {
|
||||
char k[64];
|
||||
snprintf(k, sizeof k, "%s", key);
|
||||
const char *name = norm_key(k);
|
||||
if (profile_of())
|
||||
for (size_t i = 0; i < sizeof DECK / sizeof *DECK; i++)
|
||||
if (!strcasecmp(DECK[i].key, name)) return DECK[i].value;
|
||||
const CfgMeta *m = cfg_meta(key);
|
||||
return m ? m->def : NULL;
|
||||
}
|
||||
|
||||
const CfgMeta *cfg_meta_at(int i) { return i >= 0 && (size_t)i < sizeof metas / sizeof *metas ? &metas[i] : NULL; }
|
||||
|
||||
const char *cfg_value(const char *name) {
|
||||
|
||||
@@ -50,6 +50,9 @@ typedef struct { const char *key, *def, *values; int live; } CfgMeta;
|
||||
const CfgMeta *cfg_meta(const char *key);
|
||||
// The i-th row (0-based, table order); NULL past the last.
|
||||
const CfgMeta *cfg_meta_at(int i);
|
||||
// What key reads when the player has not set it: the device profile's value (the Steam Deck's), else the row's default;
|
||||
// NULL for a key without a row. The menu's "default" mark follows it.
|
||||
const char *cfg_default(const char *key);
|
||||
|
||||
// The setting's value for consumers: the environment or nx2navi.ini value, else the CfgMeta default. name has the NX2NAVI_
|
||||
// prefix, like cfg_get; a name without a row is a bug (rt_fatal). A row with an empty default has no fixed value: its
|
||||
|
||||
@@ -334,6 +334,22 @@ int probe_cfg(void) {
|
||||
CHECK(a_east == EQ(input_prompts_folder(PROMPTS_XBOX, layouts[i]), "romfs-patches-xbox"),
|
||||
"prompts: the folder names A as B exactly when the layout puts A on the button labelled B");
|
||||
}
|
||||
// The default layout is the Switch's own buttons by position, whatever the pad's labels (an Xbox pad's east button
|
||||
// is labelled B and does Switch A): A right, B bottom, X top, Y left, L/R the bumpers, ZL/ZR the triggers.
|
||||
const CfgMeta *pl = cfg_meta("PAD_LAYOUT");
|
||||
CHECK(pl && input_pad_layout_pick(pl->def, 0) == PAD_LAYOUT_NINTENDO, "pad layout: the default is Nintendo, also for an Xbox pad");
|
||||
static const struct { int pad, button; } sw[] = {{PAD_EAST, B_A}, {PAD_SOUTH, B_B}, {PAD_NORTH, B_X}, {PAD_WEST, B_Y},
|
||||
{PAD_L, B_L}, {PAD_R, B_R}, {PAD_ZL, B_ZL}, {PAD_ZR, B_ZR},
|
||||
{PAD_START, B_PLUS}, {PAD_BACK, B_MINUS}, {PAD_LSTICK, B_STICK_L},
|
||||
{PAD_RSTICK, B_STICK_R}, {PAD_DPAD_UP, B_UP}, {PAD_DPAD_DOWN, B_DOWN},
|
||||
{PAD_DPAD_LEFT, B_LEFT}, {PAD_DPAD_RIGHT, B_RIGHT}};
|
||||
int sw_ok = 1;
|
||||
for (size_t i = 0; i < sizeof sw / sizeof *sw; i++) {
|
||||
uint8_t one[PAD_COUNT] = {0};
|
||||
one[sw[i].pad] = 1;
|
||||
sw_ok &= input_map_pad_buttons(one, PAD_LAYOUT_NINTENDO) == 1ull << sw[i].button;
|
||||
}
|
||||
CHECK(sw_ok, "pad layout: Nintendo puts every Switch button at its position on the Switch");
|
||||
uint8_t pd[PAD_COUNT] = {0};
|
||||
pd[PAD_SOUTH] = pd[PAD_NORTH] = 1;
|
||||
CHECK(input_map_pad_buttons(pd, PAD_LAYOUT_NINTENDO) == (1ull << B_B | 1ull << B_X) &&
|
||||
|
||||
@@ -1502,7 +1502,9 @@ static const GfxShader *translate_one(const Req *r, int stage, const uint8_t *co
|
||||
const char *k = cfg_get("NX2NAVI_XLATE_KEEP");
|
||||
keep = k && *k ? atoi(k) : 0;
|
||||
}
|
||||
int kept = keep >= 2 || (keep && a && a->mvprev);
|
||||
// A failed translation keeps its job and code too (rare; what a translator fix is tested on).
|
||||
int failed = ok && strncmp(reply, "ok ", 3);
|
||||
int kept = keep >= 2 || (keep && a && a->mvprev) || failed;
|
||||
if (kept) {
|
||||
char job_path[PATH_CAP + 80];
|
||||
snprintf(job_path, sizeof job_path, "%s/%s.job", cache_dir, hex);
|
||||
|
||||
@@ -57,15 +57,6 @@ void nvn_guest_written(uint64_t addr, uint64_t size) {
|
||||
// arguments are undefined for memcpy anyway.
|
||||
static void ov_memcpy(Cpu *c) {
|
||||
uint64_t dst = arg_x(c, 0), src = arg_x(c, 1), n = arg_x(c, 2);
|
||||
{
|
||||
static int logged;
|
||||
if (logged < 500) {
|
||||
logged++;
|
||||
fprintf(stderr, "[probe] memcpy dst=%#llx src=%#llx n=%#llx\n",
|
||||
(unsigned long long)dst, (unsigned long long)src, (unsigned long long)n);
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
if (n >= 1024) gfx_backend()->guest_read(src, n);
|
||||
memmove((void *)(uintptr_t)dst, (const void *)(uintptr_t)src, n);
|
||||
ret_x(c, dst);
|
||||
|
||||
@@ -58,6 +58,8 @@ void res_sizes(unsigned out[2], unsigned scene[2]);
|
||||
int res_adjustable(void);
|
||||
int wide_available(void);
|
||||
const char *const *vk_color_preset(const char *name);
|
||||
int vk_hdr_wanted(void);
|
||||
int vk_hdr_active(void);
|
||||
double frame_step_limit_hz(void);
|
||||
int frame_limit_adjustable(void);
|
||||
int frame_free_running(void);
|
||||
@@ -447,6 +449,21 @@ static int fonts_up(float scale) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
// The font atlas follows the window's scale: rebuilt at once when it is more than 12% off (text would blur or shrink
|
||||
// visibly), else once the scale has held for half a second, so dragging a window's edge does not rebuild it, and read the
|
||||
// system's fonts again, every frame. Until then io.FontGlobalScale stretches the atlas there is. False: fonts_up failed.
|
||||
static bool fonts_follow(float scale) {
|
||||
static float last;
|
||||
static uint64_t since;
|
||||
uint64_t now = plat_now_ns();
|
||||
if (scale != last) last = scale, since = now;
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
if (g_font_scale != scale && (!g_font_scale || fabsf(g_font_scale / scale - 1) > 0.12f || now - since > 500000000ull))
|
||||
if (!fonts_up(scale)) return false;
|
||||
io.FontGlobalScale = scale / g_font_scale;
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" void menu_vk_release(void) {
|
||||
if (g_ctx_ok) vk_down();
|
||||
}
|
||||
@@ -761,12 +778,22 @@ static void row_upscaling(void) {
|
||||
if (nv >= 2) opt_apply("NX2NAVI_TAA", nv >= 7 ? "fsr3" : "dlss");
|
||||
}
|
||||
|
||||
// "in use: off" under the HDR value when the game started with HDR on but the swapchain is not HDR10 (the display or
|
||||
// Windows' HDR switch is off, vk_hdr_surface_format); else the restart note.
|
||||
static const char *hdr_note(void) {
|
||||
const char *restart = menu_text(UI_MENU_HDR_NOTE);
|
||||
if (!vk_hdr_wanted() || vk_hdr_active() || !*gfx_window_present_mode() || !cfg_bool("NX2NAVI_HDR")) return restart;
|
||||
static char note[96];
|
||||
snprintf(note, sizeof note, menu_text(UI_MENU_PRESENT_IN_USE), menu_text(UI_MENU_FRAMEGEN_OFF));
|
||||
return note;
|
||||
}
|
||||
|
||||
// HDR (vk_hdr.c), after a restart: off, the runtime's own (the game's tonemap kept above SDR white) or the game's native
|
||||
// HDR. The brightness rows follow while it is on.
|
||||
static void row_hdr(void) {
|
||||
static const char *const values[] = {"0", "1", "native"};
|
||||
const char *names[] = {menu_text(UI_MENU_FRAMEGEN_OFF), menu_text(UI_MENU_HDR_ENHANCED), menu_text(UI_MENU_HDR_NATIVE)};
|
||||
opt_choice_list("NX2NAVI_HDR", UI_MENU_HDR, values, names, 3, UI_MENU_HDR_DESC, -1, menu_text(UI_MENU_HDR_NOTE));
|
||||
opt_choice_list("NX2NAVI_HDR", UI_MENU_HDR, values, names, 3, UI_MENU_HDR_DESC, -1, hdr_note());
|
||||
if (!cfg_bool("NX2NAVI_HDR")) return;
|
||||
opt_slider("NX2NAVI_HDR_PEAK_NITS", UI_MENU_HDR_PEAK, 400, 10000, UI_MENU_HDR_PEAK_DESC, 50, "%.0f nits");
|
||||
opt_slider("NX2NAVI_HDR_GAME_NITS", UI_MENU_HDR_GAME, 80, 500, UI_MENU_HDR_GAME_DESC, 5, "%.0f nits");
|
||||
@@ -1438,7 +1465,7 @@ extern "C" int menu_overlay_frame(uint64_t frame, const MenuTarget *t, const cha
|
||||
uint64_t t0 = plat_now_ns();
|
||||
if (!g_ctx_ok) make_context();
|
||||
float scale = menu_scale(t->height);
|
||||
if (!vk_up(t) || (g_font_scale != scale && !fonts_up(scale))) {
|
||||
if (!vk_up(t) || !fonts_follow(scale)) {
|
||||
rt_log("menu: Dear ImGui Vulkan back end set-up failed; the menu stays closed");
|
||||
g_failed.store(1);
|
||||
set_open(0);
|
||||
@@ -1452,6 +1479,10 @@ extern "C" int menu_overlay_frame(uint64_t frame, const MenuTarget *t, const cha
|
||||
list_gpus(t->instance);
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
int reset = g_reset.exchange(0);
|
||||
// Read again after taking the reset: the menu may have opened since the read above (F1 on the window thread while
|
||||
// the overlay alone was drawn), and that frame's reset would be spent on an overlay frame, so the first open frame
|
||||
// ran without its focus row and with the opening key still down.
|
||||
open = g_open.load(std::memory_order_acquire);
|
||||
if (reset) {
|
||||
io.ClearInputKeys();
|
||||
io.ClearInputMouse();
|
||||
@@ -1514,7 +1545,7 @@ extern "C" int menu_boot_frame(const MenuTarget *t) {
|
||||
if (g_failed_flag() || g_boot_phase.load(std::memory_order_acquire) < 0) return 0; // -1: done, a black frame
|
||||
if (!g_ctx_ok) make_context();
|
||||
float scale = menu_scale(t->height);
|
||||
if (!vk_up(t) || (g_font_scale != scale && !fonts_up(scale))) {
|
||||
if (!vk_up(t) || !fonts_follow(scale)) {
|
||||
rt_log("menu: Dear ImGui Vulkan back end set-up failed; no boot screen, and the menu stays closed");
|
||||
g_failed.store(1);
|
||||
return 0;
|
||||
@@ -1548,7 +1579,7 @@ extern "C" int menu_boot_frame(const MenuTarget *t) {
|
||||
ImFont *body = ImGui::GetFont();
|
||||
float body_px = ImGui::GetFontSize();
|
||||
ImFont *head = g_title_font ? g_title_font : body;
|
||||
float head_px = g_title_font ? g_title_font->FontSize : body_px * 1.2f;
|
||||
float head_px = g_title_font ? g_title_font->FontSize * ImGui::GetIO().FontGlobalScale : body_px * 1.2f;
|
||||
ImVec2 ts = head->CalcTextSizeA(head_px, FLT_MAX, 0, title), ss = body->CalcTextSizeA(body_px, FLT_MAX, 0, step),
|
||||
hs = body->CalcTextSizeA(body_px * 0.8f, FLT_MAX, 0, hint);
|
||||
float bar_h = 10 * scale;
|
||||
|
||||
@@ -274,8 +274,8 @@ int opt_save_tick(int force) {
|
||||
// ---- rows
|
||||
|
||||
bool opt_toggle(const char *key, UiTextId label, UiTextId desc, bool inverted) {
|
||||
const CfgMeta *m = cfg_meta(key);
|
||||
bool set = cfg_bool(key) != 0, def = m && strcmp(m->def, "0") != 0;
|
||||
const char *d = cfg_default(key);
|
||||
bool set = cfg_bool(key) != 0, def = d && *d && strcmp(d, "0") != 0;
|
||||
bool on = set != inverted; // what the row shows
|
||||
RowView v = {menu_text(on ? UI_MENU_ON : UI_MENU_OFF), set == def, false, NULL};
|
||||
if (!row(key, label, desc, v)) return false; // any step flips it
|
||||
@@ -322,19 +322,19 @@ void opt_text(UiTextId text, bool warn) {
|
||||
|
||||
bool opt_choice_list(const char *key, UiTextId label, const char *const *values, const char *const *names, int n,
|
||||
UiTextId desc, int cur, const char *note) {
|
||||
const CfgMeta *m = cfg_meta(key);
|
||||
const char *d = cfg_default(key);
|
||||
const char *now = cfg_value(key);
|
||||
if (cur < 0)
|
||||
for (int i = 0; i < n; i++)
|
||||
if (!strcmp(values[i], now)) cur = i;
|
||||
int def = -1;
|
||||
for (int i = 0; m && i < n; i++)
|
||||
if (!strcmp(values[i], m->def)) def = i;
|
||||
for (int i = 0; d && i < n; i++)
|
||||
if (!strcmp(values[i], d)) def = i;
|
||||
RowView v = {cur >= 0 ? names[cur] : now, cur == def, true, note};
|
||||
int step = row(key, label, desc, v);
|
||||
if (!step || n <= 0) return false;
|
||||
int nv;
|
||||
if (cur < 0) nv = 0;
|
||||
if (cur < 0) nv = step < 0 ? n - 1 : 0; // a value not in the list: left from the end, right / A from the start
|
||||
else if (step == 2) nv = (cur + 1) % n; // A / click: the next value, round to the first
|
||||
else nv = cur + step < 0 ? 0 : cur + step >= n ? n - 1 : cur + step; // left / right stop at the ends
|
||||
if (nv == cur) return false;
|
||||
@@ -347,7 +347,7 @@ int opt_choice_index(const char *key, UiTextId label, const char *const *names,
|
||||
RowView v = {cur >= 0 && cur < n ? names[cur] : "", cur == def, true, note};
|
||||
int step = row(key, label, desc, v);
|
||||
if (!step || n <= 0) return -1;
|
||||
int nv = cur < 0 ? 0 : step == 2 ? (cur + 1) % n : cur + step < 0 ? 0 : cur + step >= n ? n - 1 : cur + step;
|
||||
int nv = cur < 0 ? (step < 0 ? n - 1 : 0) : step == 2 ? (cur + 1) % n : cur + step < 0 ? 0 : cur + step >= n ? n - 1 : cur + step;
|
||||
if (nv == cur) return -1;
|
||||
menu_sound_play(MENU_SOUND_CURSOR);
|
||||
return nv;
|
||||
@@ -386,11 +386,11 @@ bool opt_slider(const char *key, UiTextId label, float lo, float hi, UiTextId de
|
||||
const char *note, const char *fallback) {
|
||||
const char *set = cfg_get(key);
|
||||
const char *src = fallback && !(set && *set) ? fallback : key; // unset: show the value in effect, the fallback's
|
||||
const CfgMeta *m = cfg_meta(src);
|
||||
// A key without a table default of its own (the per-stick dead zones) defaults to its fallback: the "default" mark
|
||||
const char *d = cfg_default(src);
|
||||
// A key without a default of its own (the per-stick dead zones) defaults to its fallback: the "default" mark
|
||||
// follows the fallback's default, not 0, even once the key is set.
|
||||
if (fallback && m && !m->def[0]) m = cfg_meta(fallback);
|
||||
double v = cfg_double(src), def = m && m->def[0] ? strtod(m->def, NULL) : lo;
|
||||
if (fallback && !(d && *d)) d = cfg_default(fallback);
|
||||
double v = cfg_double(src), def = d && *d ? strtod(d, NULL) : lo;
|
||||
char shown[48];
|
||||
snprintf(shown, sizeof shown, fmt, v);
|
||||
RowView rv = {shown, fabs(v - def) < step * 0.01, true, note};
|
||||
|
||||
@@ -243,10 +243,13 @@ uint32_t vk_gpa_remap(uint64_t lo, uint64_t hi, uint32_t old_rec, VkBuffer buf,
|
||||
uint64_t dev = vk_gpa_device_address(buf);
|
||||
uint64_t p0 = (lo + 4095) >> PAGE_SHIFT, p1 = hi >> PAGE_SHIFT;
|
||||
plat_mutex_lock(&gpa_lock);
|
||||
// the twin: the buffer the pages named before (the pool's imported buffer), so writes reach guest memory too
|
||||
uint64_t twin = old_rec ? (uint64_t)G.cpu[REC_WORD + old_rec * REC_WORDS] |
|
||||
(uint64_t)G.cpu[REC_WORD + old_rec * REC_WORDS + 1] << 32
|
||||
: dev - buf_lo;
|
||||
// the twin: the buffer the pages named before (the pool's imported buffer), so writes reach guest memory too.
|
||||
// NX2NAVI_GPA_TWIN=0 (diagnosis): no twin, the writes go to the mirror alone, as before patch 0034.
|
||||
static int twins = -1;
|
||||
if (twins < 0) { const char *e = cfg_get("NX2NAVI_GPA_TWIN"); twins = !(e && !strcmp(e, "0")); }
|
||||
uint64_t twin = old_rec && twins ? (uint64_t)G.cpu[REC_WORD + old_rec * REC_WORDS] |
|
||||
(uint64_t)G.cpu[REC_WORD + old_rec * REC_WORDS + 1] << 32
|
||||
: dev - buf_lo;
|
||||
uint32_t r = dev ? rec_alloc(dev - buf_lo, twin) : 0;
|
||||
if (!r) { plat_mutex_unlock(&gpa_lock); return old_rec; }
|
||||
for (uint64_t p = p0; p < p1; p++) {
|
||||
|
||||
Reference in New Issue
Block a user