mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
iOS: fix per-game stick inversion silently discarding writes
PadTab always used the ...ForCurrentGame per-game INI accessors. Those resolve the game identity from the running VM and return early when there isn't one, so opening Per-Game Settings from the library made all four inversion pickers read "Use Global" no matter what was stored, and threw every write away without a word. Thread savesToRunningGame and the boot ISO into PadTab and branch on them, the same way every other per-game control in the panel already does. Also: - Drop the redundant ContainsValue probe in stickInversion(for:) -- the getter already falls back to the default, and this runs per stick sample. - Say in the Stick Inversion footer that the tab writes as you edit it. Nothing in PadTab is staged, so Save staying greyed is correct, but the panel never told anyone that.
This commit is contained in:
@@ -1572,11 +1572,11 @@ final class SettingsStore {
|
||||
/// Effective axis inversion for a stick, resolving a per-game override (current game INI)
|
||||
/// before the global default. Read live at the stick input choke point.
|
||||
func stickInversion(for side: StickSide) -> (x: Bool, y: Bool) {
|
||||
// Passing the global as the default covers "no override", "no per-game file"
|
||||
// and "no running game" in one read. A separate ContainsValue probe would
|
||||
// parse the INI twice on every stick sample.
|
||||
func resolve(_ key: String, global: Bool) -> Bool {
|
||||
if ARMSX2Bridge.hasPerGameINIValueForCurrentGame(Self.stickInversionSection, key: key) {
|
||||
return ARMSX2Bridge.getPerGameINIBoolForCurrentGame(Self.stickInversionSection, key: key, defaultValue: global)
|
||||
}
|
||||
return global
|
||||
ARMSX2Bridge.getPerGameINIBoolForCurrentGame(Self.stickInversionSection, key: key, defaultValue: global)
|
||||
}
|
||||
switch side {
|
||||
case .left: return (resolve("InvertLeftStickX", global: invertLeftStickX), resolve("InvertLeftStickY", global: invertLeftStickY))
|
||||
|
||||
@@ -334,6 +334,8 @@ struct PerGameSettingsPanel: View {
|
||||
}
|
||||
|
||||
/// Encodes the current editable per-game state so Save can be gated on real changes.
|
||||
/// Virtual Pad values are left out on purpose: that tab writes as you edit it, so
|
||||
/// there is never anything of its own left for Save to commit.
|
||||
private func perGameFingerprint() -> String {
|
||||
let fixes = SettingsStore.gameFixOptions.map { "\($0.key):\(perGameFixes[$0.key] ?? -1)" }.joined(separator: ",")
|
||||
return "\(enabled)|\(upscaleMultiplier)|\(aspectRatio)|\(textureFiltering)|\(hardwareMipmapping)|\(blendingAccuracy)|\(interlaceMode)|\(trilinearFiltering)|\(halfPixelOffset)|\(roundSprite)|\(alignSpriteOverride)|\(alignSprite)|\(mergeSpriteOverride)|\(mergeSprite)|\(wildArmsOffsetOverride)|\(wildArmsOffset)|\(textureOffsetXOverride)|\(textureOffsetX)|\(textureOffsetYOverride)|\(textureOffsetY)|\(skipDrawStartOverride)|\(skipDrawStart)|\(skipDrawEndOverride)|\(skipDrawEnd)|\(volumeOverride)|\(volumePercent)|\(eeCoreType)|\(mtvu)|\(eeCycleRate)|\(eeCycleSkip)|\(fastBoot)|\(enableCheats)|\(enablePatches)|\(enableGameFixes)|\(enableGameDBHardwareFixes)|\(perGameAAT)|\(perGameTextureInsideRt)|\(perGameRenderer)|\(perGameFXAA)|\(perGameUpscaler)|\(perGameShadeBoost)|\(perGameTVShader)|\(perGameCASMode)|\(perGameMaxAnisotropy)|\(perGameCASSharpness)|\(perGamePCRTCOffsets)|\(perGameIntegerScaling)|\(perGameSkipDupFrames)|\(perGamePCRTCOverscan)|\(perGamePCRTCAntiBlur)|\(perGameDisableInterlaceOffset)|\(perGameWidescreen)|\(perGameNoInterlace)|\(perGameShadeBoostBrightness)|\(perGameShadeBoostContrast)|\(perGameShadeBoostSaturation)|\(perGameShadeBoostGamma)|\(perGameDithering)|\(perGameFastForwardVolume)|\(perGameIOP)|\(perGameVU0)|\(perGameVU1)|\(perGameHWDownloadMode)|\(perGameCPUCLUT)|\(perGameGPUTargetCLUT)|\(perGameVsyncQueue)|\(perGameLoadTextureReplacements)|\(perGameLoadTextureReplacementsAsync)|\(perGamePrecacheTextureReplacements)|\(perGameSyncToHostRefresh)|\(perGameBufferMS)|\(perGameOutputLatencyMS)|\(perGameEEFpuRound)|\(perGameVU0Round)|\(perGameVU1Round)|\(perGameEEClamp)|\(perGameVUClamp)|\(raEnabledOverride)|\(raHardcoreOverride)|\(perGameFramePacingPreset)|\(perGameFrameLimiter)|\(perGameTargetFPS)|\(fixes)"
|
||||
@@ -703,7 +705,9 @@ struct PerGameSettingsPanel: View {
|
||||
padLayoutIdentity: $padLayoutIdentity,
|
||||
showPadLayoutEditor: $showPadLayoutEditor,
|
||||
layoutPresets: layoutPresets,
|
||||
skinLibrary: skinLibrary
|
||||
skinLibrary: skinLibrary,
|
||||
savesToRunningGame: savesToRunningGame,
|
||||
iso: game.bootName
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ struct PadTab: View {
|
||||
|
||||
let layoutPresets: PadLayoutPresetStore
|
||||
let skinLibrary: VPadSkinLibraryStore
|
||||
let savesToRunningGame: Bool
|
||||
let iso: String
|
||||
|
||||
@State private var inversionDrafts: [String: Int] = [:]
|
||||
private let inversionKeys = ["InvertLeftStickX", "InvertLeftStickY", "InvertRightStickX", "InvertRightStickY"]
|
||||
@@ -74,7 +76,7 @@ struct PadTab: View {
|
||||
layoutPresets.clearVPadOverrides(for: padLayoutIdentity)
|
||||
inversionDrafts = [:]
|
||||
for key in inversionKeys {
|
||||
ARMSX2Bridge.deletePerGameINIValueForCurrentGame(inversionSection, key: key)
|
||||
clearInversionOverride(key)
|
||||
}
|
||||
} label: {
|
||||
Label("Reset All VPad Overrides", systemImage: "arrow.counterclockwise")
|
||||
@@ -101,7 +103,7 @@ struct PadTab: View {
|
||||
} header: {
|
||||
Text("Stick Inversion")
|
||||
} footer: {
|
||||
Text("Overrides the global stick inversion for this game only.")
|
||||
Text("Overrides the global stick inversion for this game only. Everything on this page saves as you change it, so Save and Cancel don't apply here.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,21 +123,55 @@ struct PadTab: View {
|
||||
private func loadInversionDrafts() {
|
||||
var drafts: [String: Int] = [:]
|
||||
for key in inversionKeys {
|
||||
if ARMSX2Bridge.hasPerGameINIValueForCurrentGame(inversionSection, key: key) {
|
||||
drafts[key] = ARMSX2Bridge.getPerGameINIBoolForCurrentGame(inversionSection, key: key, defaultValue: false) ? 1 : 0
|
||||
if hasInversionOverride(key) {
|
||||
drafts[key] = inversionOverride(key) ? 1 : 0
|
||||
}
|
||||
}
|
||||
inversionDrafts = drafts
|
||||
}
|
||||
|
||||
// Commits on pick, like the layout and skin pickers. Nothing in this tab is
|
||||
// staged, so none of it feeds the panel's Save fingerprint.
|
||||
private func applyInversion(_ key: String, value: Int) {
|
||||
var drafts = inversionDrafts
|
||||
drafts[key] = value
|
||||
inversionDrafts = drafts
|
||||
if value == -1 {
|
||||
clearInversionOverride(key)
|
||||
} else {
|
||||
setInversionOverride(key, value == 1)
|
||||
}
|
||||
}
|
||||
|
||||
// The panel opens both in-game and from the library. The "current game" bridge
|
||||
// variants resolve their identity from the running VM and silently no-op without
|
||||
// one, so the library path has to address the per-game INI by ISO instead.
|
||||
|
||||
private func hasInversionOverride(_ key: String) -> Bool {
|
||||
savesToRunningGame
|
||||
? ARMSX2Bridge.hasPerGameINIValueForCurrentGame(inversionSection, key: key)
|
||||
: ARMSX2Bridge.hasPerGameINIValue(inversionSection, key: key, forISO: iso)
|
||||
}
|
||||
|
||||
private func inversionOverride(_ key: String) -> Bool {
|
||||
savesToRunningGame
|
||||
? ARMSX2Bridge.getPerGameINIBoolForCurrentGame(inversionSection, key: key, defaultValue: false)
|
||||
: ARMSX2Bridge.getPerGameINIBool(inversionSection, key: key, defaultValue: false, forISO: iso)
|
||||
}
|
||||
|
||||
private func setInversionOverride(_ key: String, _ value: Bool) {
|
||||
if savesToRunningGame {
|
||||
ARMSX2Bridge.setPerGameINIBoolForCurrentGame(inversionSection, key: key, value: value)
|
||||
} else {
|
||||
ARMSX2Bridge.setPerGameINIBool(inversionSection, key: key, value: value, forISO: iso)
|
||||
}
|
||||
}
|
||||
|
||||
private func clearInversionOverride(_ key: String) {
|
||||
if savesToRunningGame {
|
||||
ARMSX2Bridge.deletePerGameINIValueForCurrentGame(inversionSection, key: key)
|
||||
} else {
|
||||
ARMSX2Bridge.setPerGameINIBoolForCurrentGame(inversionSection, key: key, value: value == 1)
|
||||
ARMSX2Bridge.deletePerGameINIValue(inversionSection, key: key, forISO: iso)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user