mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
"Copy Global Settings" does not copy the settings you can see. It runs the whole configuration through a wrapper that writes every key unconditionally, so the file it leaves behind holds roughly seven hundred of them — network adapters, the debugger, trace logging, memory cards, sections no settings page ever shows. That was untidy and no worse, until a key present in a per-game file started meaning the player claimed it. Now one press of a button whose dialog promises only that "the configuration for this game will be replaced by the current global settings" turns off every automatic fix that game had, permanently and silently. A value is worth writing down only if it decides something, and there are two ways it can fail to. It can be the stock default, in which case the file carries it as noise. Or it can be what the game database is going to set anyway, in which case writing it can only become a claim that suppresses the fix it agrees with. So the copy now excludes both, and what lands is what the player actually chose. The comparison goes through the string form rather than the typed value, so a float or an enum name compares the way it will be stored rather than the way it happens to sit in memory. That is why the references are built with the same interface class: same formatting on both sides, exact comparison, one path for every type. The database reference is a default configuration with the entry applied, not this one with the entry applied. The question is what the database wants, not where it would leave the source. It matters for the handful of fixes that clamp rather than assign, and it errs towards writing the player's value — never towards dropping a fix, since a value is only skipped when it already equals what the fix would set. Working the reference out means running the apply functions for an outcome nobody is going to run with, so they take an apply mode. A hypothetical apply says nothing to the log, raises none of the recommendation messages, and does not allocate the four megabyte lookup table that the Goemon TLB fix asks for. The tests cover the precedence rule and the filter, but the ones that matter are the drift guards: they assert every gamefix, speedhack and clamp mode has a settings key, and that the only graphics fixes without one are the six that genuinely have no setting behind them — three renderer routine selectors and three that only raise a recommendation. A knob nobody maps is a setting that goes quietly back to being overridden, with no warning and no failure, and that is what these are here to catch.
186 lines
4.6 KiB
C++
186 lines
4.6 KiB
C++
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
|
|
// SPDX-License-Identifier: GPL-3.0+
|
|
|
|
#pragma once
|
|
|
|
#include "Config.h"
|
|
#include "Patch.h"
|
|
#include "PerGameOverrides.h"
|
|
|
|
#include "common/FPControl.h"
|
|
|
|
#include <cstring>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
enum GamefixId;
|
|
|
|
namespace GameDatabaseSchema
|
|
{
|
|
enum class Compatibility
|
|
{
|
|
Unknown = 0,
|
|
Nothing,
|
|
Intro,
|
|
Menu,
|
|
InGame,
|
|
Playable,
|
|
Perfect
|
|
};
|
|
|
|
enum class ClampMode
|
|
{
|
|
Undefined = -1,
|
|
Disabled = 0,
|
|
Normal,
|
|
Extra,
|
|
Full,
|
|
Exact, // eeClampMode only; the VUs stop at Extra+Preserve Sign.
|
|
Count
|
|
};
|
|
|
|
enum class GSHWFixId : u32
|
|
{
|
|
// boolean settings
|
|
AutoFlush,
|
|
CPUFramebufferConversion,
|
|
FlushTCOnClose,
|
|
DisableDepthSupport,
|
|
PreloadFrameData,
|
|
DisablePartialInvalidation,
|
|
TextureInsideRT,
|
|
Limit24BitDepth,
|
|
AlignSprite,
|
|
MergeSprite,
|
|
Mipmap,
|
|
AccurateAlphaTest,
|
|
ForceEvenSpritePosition,
|
|
BilinearUpscale,
|
|
NativePaletteDraw,
|
|
EstimateTextureRegion,
|
|
DrawBuffering,
|
|
PCRTCOffsets,
|
|
PCRTCOverscan,
|
|
CoalesceRenderPasses,
|
|
|
|
// integer settings
|
|
TrilinearFiltering,
|
|
SkipDrawStart,
|
|
SkipDrawEnd,
|
|
HalfPixelOffset,
|
|
RoundSprite,
|
|
NativeScaling,
|
|
TexturePreloading,
|
|
Deinterlace,
|
|
CPUSpriteRenderBW,
|
|
CPUSpriteRenderLevel,
|
|
CPUCLUTRender,
|
|
GPUTargetCLUT,
|
|
GPUPaletteConversion,
|
|
MinimumBlendingLevel,
|
|
MaximumBlendingLevel,
|
|
RecommendedBlendingLevel,
|
|
RecommendedAccurateAlphaTest,
|
|
RecommendedHWAA1,
|
|
HWDownloadMode,
|
|
GetSkipCount,
|
|
BeforeDraw,
|
|
MoveHandler,
|
|
|
|
Count
|
|
};
|
|
|
|
/// Whether the database's values are actually being adopted, or only being worked
|
|
/// out so something can be compared against them. A hypothetical apply says nothing
|
|
/// to the log or the screen and allocates nothing, because nothing is going to run
|
|
/// with the result.
|
|
enum class ApplyMode : u8
|
|
{
|
|
Live,
|
|
Hypothetical,
|
|
};
|
|
|
|
struct GameEntry
|
|
{
|
|
std::string name;
|
|
std::string name_sort;
|
|
std::string name_en;
|
|
std::string region;
|
|
Compatibility compat = Compatibility::Unknown;
|
|
FPRoundMode eeRoundMode = FPRoundMode::MaxCount;
|
|
FPRoundMode eeDivRoundMode = FPRoundMode::MaxCount;
|
|
FPRoundMode vu0RoundMode = FPRoundMode::MaxCount;
|
|
FPRoundMode vu1RoundMode = FPRoundMode::MaxCount;
|
|
ClampMode eeClampMode = ClampMode::Undefined;
|
|
ClampMode vu0ClampMode = ClampMode::Undefined;
|
|
ClampMode vu1ClampMode = ClampMode::Undefined;
|
|
std::vector<GamefixId> gameFixes;
|
|
std::vector<std::pair<SpeedHack, int>> speedHacks;
|
|
std::vector<std::pair<GSHWFixId, s32>> gsHWFixes;
|
|
std::vector<std::string> memcardFilters;
|
|
std::unordered_map<u32, std::string> patches;
|
|
std::vector<Patch::DynamicPatch> dynaPatches;
|
|
|
|
// Returns the list of memory card serials as a `/` delimited string
|
|
std::string memcardFiltersAsString() const;
|
|
const std::string* findPatch(u32 crc) const;
|
|
const char* compatAsString() const;
|
|
|
|
/// Applies Core game fixes to an existing config. Anything the player set for
|
|
/// this game specifically is left alone — `overrides` says which, and defaults
|
|
/// to nothing claimed, which is the old behaviour.
|
|
void applyGameFixes(Pcsx2Config& config, bool applyAuto, const PerGameOverrides& overrides = {},
|
|
ApplyMode mode = ApplyMode::Live) const;
|
|
|
|
/// Applies GS hardware fixes to an existing config, on the same terms.
|
|
void applyGSHardwareFixes(Pcsx2Config::GSOptions& config, const PerGameOverrides& overrides = {},
|
|
ApplyMode mode = ApplyMode::Live) const;
|
|
|
|
/// Returns true if the current config value for the specified hw fix id matches the value.
|
|
static bool configMatchesHWFix(const Pcsx2Config::GSOptions& config, GSHWFixId id, int value);
|
|
};
|
|
}; // namespace GameDatabaseSchema
|
|
|
|
namespace GameDatabase
|
|
{
|
|
void ensureLoaded();
|
|
const GameDatabaseSchema::GameEntry* findGame(const std::string_view serial);
|
|
|
|
struct TrackHash
|
|
{
|
|
static constexpr u32 SIZE = 16;
|
|
|
|
bool parseHash(const std::string_view str);
|
|
std::string toString() const;
|
|
|
|
#define MAKE_OPERATOR(op) \
|
|
bool operator op(const TrackHash& hash) const { return (std::memcmp(data, hash.data, sizeof(data)) op 0); }
|
|
MAKE_OPERATOR(==);
|
|
MAKE_OPERATOR(!=);
|
|
MAKE_OPERATOR(<);
|
|
MAKE_OPERATOR(<=);
|
|
MAKE_OPERATOR(>);
|
|
MAKE_OPERATOR(>=);
|
|
#undef MAKE_OPERATOR
|
|
|
|
u8 data[SIZE];
|
|
u64 size;
|
|
};
|
|
|
|
struct HashDatabaseEntry
|
|
{
|
|
std::string serial;
|
|
std::string name;
|
|
std::string version;
|
|
std::vector<TrackHash> tracks;
|
|
};
|
|
|
|
bool loadHashDatabase();
|
|
void unloadHashDatabase();
|
|
const HashDatabaseEntry* lookupHash(const TrackHash* tracks, size_t num_tracks, bool* tracks_matched, std::string* match_error);
|
|
}; // namespace GameDatabase
|