mirror of https://github.com/PCSX2/pcsx2.git
GameDatabase: Add ability to override GS fixes
This commit is contained in:
parent
d35db63d73
commit
96269db93e
|
@ -141,8 +141,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||||
// HW Renderer Fixes
|
// HW Renderer Fixes
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.halfScreenFix, "EmuCore/GS", "UserHacks_Half_Bottom_Override", -1, -1);
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.halfScreenFix, "EmuCore/GS", "UserHacks_Half_Bottom_Override", -1, -1);
|
||||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.skipDrawRangeStart, "EmuCore/GS", "UserHacks_SkipDraw", 0);
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.skipDrawStart, "EmuCore/GS", "UserHacks_SkipDraw_Offset", 0);
|
||||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.skipDrawRangeCount, "EmuCore/GS", "UserHacks_SkipDraw_Offset", 0);
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.skipDrawEnd, "EmuCore/GS", "UserHacks_SkipDraw", 0);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hwAutoFlush, "EmuCore/GS", "UserHacks_AutoFlush", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hwAutoFlush, "EmuCore/GS", "UserHacks_AutoFlush", false);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.frameBufferConversion, "EmuCore/GS", "UserHacks_CPU_FB_Conversion", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.frameBufferConversion, "EmuCore/GS", "UserHacks_CPU_FB_Conversion", false);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableDepthEmulation, "EmuCore/GS", "UserHacks_DisableDepthSupport", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableDepthEmulation, "EmuCore/GS", "UserHacks_DisableDepthSupport", false);
|
||||||
|
|
|
@ -586,7 +586,7 @@
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QCheckBox" name="enableHWFixes">
|
<widget class="QCheckBox" name="enableHWFixes">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable Hardware Renderer Fixes</string>
|
<string>Manual Hardware Renderer Fixes</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -661,10 +661,10 @@
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QSpinBox" name="skipDrawRangeStart"/>
|
<widget class="QSpinBox" name="skipDrawStart"/>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QSpinBox" name="skipDrawRangeCount"/>
|
<widget class="QSpinBox" name="skipDrawEnd"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -442,7 +442,7 @@ struct Pcsx2Config
|
||||||
WrapGSMem : 1,
|
WrapGSMem : 1,
|
||||||
Mipmap : 1,
|
Mipmap : 1,
|
||||||
AA1 : 1,
|
AA1 : 1,
|
||||||
UserHacks : 1,
|
ManualUserHacks : 1,
|
||||||
UserHacks_AlignSpriteX : 1,
|
UserHacks_AlignSpriteX : 1,
|
||||||
UserHacks_AutoFlush : 1,
|
UserHacks_AutoFlush : 1,
|
||||||
UserHacks_CPUFBConversion : 1,
|
UserHacks_CPUFBConversion : 1,
|
||||||
|
@ -510,8 +510,8 @@ struct Pcsx2Config
|
||||||
int SWExtraThreads{2};
|
int SWExtraThreads{2};
|
||||||
int SWExtraThreadsHeight{4};
|
int SWExtraThreadsHeight{4};
|
||||||
int TVShader{0};
|
int TVShader{0};
|
||||||
int SkipDraw{0};
|
int SkipDrawStart{0};
|
||||||
int SkipDrawOffset{0};
|
int SkipDrawEnd{0};
|
||||||
|
|
||||||
int UserHacks_HalfBottomOverride{-1};
|
int UserHacks_HalfBottomOverride{-1};
|
||||||
int UserHacks_HalfPixelOffset{0};
|
int UserHacks_HalfPixelOffset{0};
|
||||||
|
|
|
@ -310,8 +310,6 @@ bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* b
|
||||||
|
|
||||||
GSConfig = config;
|
GSConfig = config;
|
||||||
GSConfig.Renderer = renderer;
|
GSConfig.Renderer = renderer;
|
||||||
GSConfig.MaskUserHacks();
|
|
||||||
GSConfig.MaskUpscalingHacks();
|
|
||||||
|
|
||||||
if (!Host::AcquireHostDisplay(GetAPIForRenderer(renderer)))
|
if (!Host::AcquireHostDisplay(GetAPIForRenderer(renderer)))
|
||||||
{
|
{
|
||||||
|
@ -724,8 +722,6 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
|
||||||
Pcsx2Config::GSOptions old_config(std::move(GSConfig));
|
Pcsx2Config::GSOptions old_config(std::move(GSConfig));
|
||||||
GSConfig = new_config;
|
GSConfig = new_config;
|
||||||
GSConfig.Renderer = (GSConfig.Renderer == GSRendererType::Auto) ? GSUtil::GetPreferredRenderer() : GSConfig.Renderer;
|
GSConfig.Renderer = (GSConfig.Renderer == GSRendererType::Auto) ? GSUtil::GetPreferredRenderer() : GSConfig.Renderer;
|
||||||
GSConfig.MaskUserHacks();
|
|
||||||
GSConfig.MaskUpscalingHacks();
|
|
||||||
if (!s_gs)
|
if (!s_gs)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -1053,7 +1053,7 @@ bool GSState::IsBadFrame()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_skip == 0 && GSConfig.SkipDraw > 0)
|
if (m_skip == 0 && GSConfig.SkipDrawEnd > 0)
|
||||||
{
|
{
|
||||||
if (fi.TME)
|
if (fi.TME)
|
||||||
{
|
{
|
||||||
|
@ -1061,8 +1061,8 @@ bool GSState::IsBadFrame()
|
||||||
// General, often problematic post processing
|
// General, often problematic post processing
|
||||||
if (GSLocalMemory::m_psm[fi.TPSM].depth || GSUtil::HasSharedBits(fi.FBP, fi.FPSM, fi.TBP0, fi.TPSM))
|
if (GSLocalMemory::m_psm[fi.TPSM].depth || GSUtil::HasSharedBits(fi.FBP, fi.FPSM, fi.TBP0, fi.TPSM))
|
||||||
{
|
{
|
||||||
m_skip_offset = GSConfig.SkipDrawOffset;
|
m_skip_offset = GSConfig.SkipDrawStart;
|
||||||
m_skip = std::max(GSConfig.SkipDraw, m_skip_offset);
|
m_skip = GSConfig.SkipDrawEnd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -328,7 +328,7 @@ HacksTab::HacksTab(wxWindow* parent)
|
||||||
PaddedBoxSizer<wxBoxSizer> tab_box(wxVERTICAL);
|
PaddedBoxSizer<wxBoxSizer> tab_box(wxVERTICAL);
|
||||||
|
|
||||||
auto hw_prereq = [this]{ return m_is_hardware; };
|
auto hw_prereq = [this]{ return m_is_hardware; };
|
||||||
auto* hacks_check_box = m_ui.addCheckBox(tab_box.inner, "Enable HW Hacks", "UserHacks", -1, hw_prereq);
|
auto* hacks_check_box = m_ui.addCheckBox(tab_box.inner, "Manual HW Hacks", "UserHacks", -1, hw_prereq);
|
||||||
auto hacks_prereq = [this, hacks_check_box]{ return m_is_hardware && hacks_check_box->GetValue(); };
|
auto hacks_prereq = [this, hacks_check_box]{ return m_is_hardware && hacks_check_box->GetValue(); };
|
||||||
auto upscale_hacks_prereq = [this, hacks_check_box]{ return !m_is_native_res && hacks_check_box->GetValue(); };
|
auto upscale_hacks_prereq = [this, hacks_check_box]{ return !m_is_native_res && hacks_check_box->GetValue(); };
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
#include "PrecompiledHeader.h"
|
#include "PrecompiledHeader.h"
|
||||||
|
|
||||||
#include "GameDatabase.h"
|
#include "GameDatabase.h"
|
||||||
#include "Config.h"
|
|
||||||
#include "Host.h"
|
#include "Host.h"
|
||||||
|
#include "Patch.h"
|
||||||
|
|
||||||
#include "common/FileSystem.h"
|
#include "common/FileSystem.h"
|
||||||
#include "common/Path.h"
|
#include "common/Path.h"
|
||||||
|
@ -31,6 +31,20 @@
|
||||||
#include "fmt/ranges.h"
|
#include "fmt/ranges.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace GameDatabaseSchema
|
||||||
|
{
|
||||||
|
static const char* getHWFixName(GSHWFixId id);
|
||||||
|
static std::optional<GSHWFixId> parseHWFixName(const std::string_view& name);
|
||||||
|
static bool isUserHackHWFix(GSHWFixId id);
|
||||||
|
} // namespace GameDatabaseSchema
|
||||||
|
|
||||||
|
namespace GameDatabase
|
||||||
|
{
|
||||||
|
static void parseAndInsert(const std::string_view& serial, const c4::yml::NodeRef& node);
|
||||||
|
static void initDatabase();
|
||||||
|
} // namespace GameDatabase
|
||||||
|
|
||||||
static constexpr char GAMEDB_YAML_FILE_NAME[] = "GameIndex.yaml";
|
static constexpr char GAMEDB_YAML_FILE_NAME[] = "GameIndex.yaml";
|
||||||
|
|
||||||
|
@ -85,7 +99,7 @@ const char* GameDatabaseSchema::GameEntry::compatAsString() const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseAndInsert(const std::string_view& serial, const c4::yml::NodeRef& node)
|
void GameDatabase::parseAndInsert(const std::string_view& serial, const c4::yml::NodeRef& node)
|
||||||
{
|
{
|
||||||
GameDatabaseSchema::GameEntry gameEntry;
|
GameDatabaseSchema::GameEntry gameEntry;
|
||||||
if (node.has_child("name"))
|
if (node.has_child("name"))
|
||||||
|
@ -195,6 +209,25 @@ void parseAndInsert(const std::string_view& serial, const c4::yml::NodeRef& node
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (node.has_child("gsHWFixes"))
|
||||||
|
{
|
||||||
|
for (const ryml::NodeRef& n : node["gsHWFixes"].children())
|
||||||
|
{
|
||||||
|
const std::string_view id_name(n.key().data(), n.key().size());
|
||||||
|
std::optional<GameDatabaseSchema::GSHWFixId> id = GameDatabaseSchema::parseHWFixName(id_name);
|
||||||
|
std::optional<s32> value = n.has_val() ? StringUtil::FromChars<s32>(std::string_view(n.val().data(), n.val().size())) : 1;
|
||||||
|
if (!id.has_value() || !value.has_value())
|
||||||
|
{
|
||||||
|
Console.Error("[GameDB] Invalid GS HW Fix: '%*s' specified for serial '%*s'. Dropping!",
|
||||||
|
static_cast<int>(id_name.size()), id_name.data(),
|
||||||
|
static_cast<int>(serial.size()), serial.data());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
gameEntry.gsHWFixes.emplace_back(id.value(), value.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Memory Card Filters - Store as a vector to allow flexibility in the future
|
// Memory Card Filters - Store as a vector to allow flexibility in the future
|
||||||
// - currently they are used as a '\n' delimited string in the app
|
// - currently they are used as a '\n' delimited string in the app
|
||||||
if (node.has_child("memcardFilters") && node["memcardFilters"].has_children())
|
if (node.has_child("memcardFilters") && node["memcardFilters"].has_children())
|
||||||
|
@ -231,16 +264,188 @@ void parseAndInsert(const std::string_view& serial, const c4::yml::NodeRef& node
|
||||||
s_game_db.emplace(std::move(serial), std::move(gameEntry));
|
s_game_db.emplace(std::move(serial), std::move(gameEntry));
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::ifstream getFileStream(std::string path)
|
static const char* s_gs_hw_fix_names[] = {
|
||||||
|
"autoFlush",
|
||||||
|
"conservativeFramebuffer",
|
||||||
|
"cpuFramebufferConversion",
|
||||||
|
"disableDepthSupport",
|
||||||
|
"wrapGSMem",
|
||||||
|
"preloadFrameData",
|
||||||
|
"fastTextureInvalidation",
|
||||||
|
"textureInsideRT",
|
||||||
|
"alignSprite",
|
||||||
|
"mergeSprite",
|
||||||
|
"wildArmsHack",
|
||||||
|
"mipmap",
|
||||||
|
"trilinearFiltering",
|
||||||
|
"skipDrawStart",
|
||||||
|
"skipDrawEnd",
|
||||||
|
"halfBottomOverride",
|
||||||
|
"halfPixelOffset",
|
||||||
|
"roundSprite",
|
||||||
|
"texturePreloading",
|
||||||
|
};
|
||||||
|
static_assert(std::size(s_gs_hw_fix_names) == static_cast<u32>(GameDatabaseSchema::GSHWFixId::Count), "HW fix name lookup is correct size");
|
||||||
|
|
||||||
|
const char* GameDatabaseSchema::getHWFixName(GSHWFixId id)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
return s_gs_hw_fix_names[static_cast<u32>(id)];
|
||||||
return std::ifstream(StringUtil::UTF8StringToWideString(path));
|
|
||||||
#else
|
|
||||||
return std::ifstream(path.c_str());
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void initDatabase()
|
static std::optional<GameDatabaseSchema::GSHWFixId> GameDatabaseSchema::parseHWFixName(const std::string_view& name)
|
||||||
|
{
|
||||||
|
for (u32 i = 0; i < std::size(s_gs_hw_fix_names); i++)
|
||||||
|
{
|
||||||
|
if (name.compare(s_gs_hw_fix_names[i]) == 0)
|
||||||
|
return static_cast<GameDatabaseSchema::GSHWFixId>(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GameDatabaseSchema::isUserHackHWFix(GSHWFixId id)
|
||||||
|
{
|
||||||
|
switch (id)
|
||||||
|
{
|
||||||
|
case GSHWFixId::Mipmap:
|
||||||
|
case GSHWFixId::TexturePreloading:
|
||||||
|
case GSHWFixId::ConservativeFramebuffer:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifdef PCSX2_CORE
|
||||||
|
// Trifiltering isn't a hack in Qt.
|
||||||
|
case GSHWFixId::TrilinearFiltering:
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions& config) const
|
||||||
|
{
|
||||||
|
// Only apply GS HW fixes if the user hasn't manually enabled HW fixes.
|
||||||
|
const bool apply_auto_fixes = !config.ManualUserHacks;
|
||||||
|
if (!apply_auto_fixes)
|
||||||
|
Console.Warning("[GameDB] Hardware fixes are enabled, not using automatic fixes.");
|
||||||
|
|
||||||
|
u32 num_applied_fixes = 0;
|
||||||
|
for (const auto& [id, value] : gsHWFixes)
|
||||||
|
{
|
||||||
|
if (isUserHackHWFix(id) && !apply_auto_fixes)
|
||||||
|
{
|
||||||
|
PatchesCon->Warning("[GameDB] Skipping GS Hardware Fix: %s to [mode=%d]", getHWFixName(id), value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (id)
|
||||||
|
{
|
||||||
|
case GSHWFixId::AutoFlush:
|
||||||
|
config.UserHacks_AutoFlush = (value > 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSHWFixId::ConservativeFramebuffer:
|
||||||
|
config.ConservativeFramebuffer = (value > 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSHWFixId::CPUFramebufferConversion:
|
||||||
|
config.UserHacks_CPUFBConversion = (value > 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSHWFixId::DisableDepthSupport:
|
||||||
|
config.UserHacks_DisableDepthSupport = (value > 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSHWFixId::WrapGSMem:
|
||||||
|
config.WrapGSMem = (value > 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSHWFixId::PreloadFrameData:
|
||||||
|
config.PreloadFrameWithGSData = (value > 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSHWFixId::FastTextureInvalidation:
|
||||||
|
config.UserHacks_DisablePartialInvalidation = (value > 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSHWFixId::TextureInsideRT:
|
||||||
|
config.UserHacks_TextureInsideRt = (value > 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSHWFixId::AlignSprite:
|
||||||
|
config.UserHacks_AlignSpriteX = (value > 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSHWFixId::MergeSprite:
|
||||||
|
config.UserHacks_MergePPSprite = (value > 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSHWFixId::WildArmsHack:
|
||||||
|
config.UserHacks_WildHack = (value > 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSHWFixId::Mipmap:
|
||||||
|
{
|
||||||
|
if (value >= 0 && value <= static_cast<int>(HWMipmapLevel::Full))
|
||||||
|
{
|
||||||
|
if (config.HWMipmap == HWMipmapLevel::Automatic)
|
||||||
|
config.HWMipmap = static_cast<HWMipmapLevel>(value);
|
||||||
|
else if (config.HWMipmap == HWMipmapLevel::Off)
|
||||||
|
Console.Warning("[GameDB] Game requires mipmapping but it has been force disabled.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSHWFixId::TrilinearFiltering:
|
||||||
|
{
|
||||||
|
if (value >= 0 && value <= static_cast<int>(TriFiltering::Forced))
|
||||||
|
config.UserHacks_TriFilter = static_cast<TriFiltering>(value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSHWFixId::SkipDrawStart:
|
||||||
|
config.SkipDrawStart = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSHWFixId::SkipDrawEnd:
|
||||||
|
config.SkipDrawEnd = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSHWFixId::HalfBottomOverride:
|
||||||
|
config.UserHacks_HalfBottomOverride = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSHWFixId::HalfPixelOffset:
|
||||||
|
config.UserHacks_HalfPixelOffset = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSHWFixId::RoundSprite:
|
||||||
|
config.UserHacks_RoundSprite = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GSHWFixId::TexturePreloading:
|
||||||
|
{
|
||||||
|
if (value >= 0 && value <= static_cast<int>(TexturePreloadingLevel::Full))
|
||||||
|
config.TexturePreloading = std::min(config.TexturePreloading, static_cast<TexturePreloadingLevel>(value));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
PatchesCon->WriteLn("[GameDB] Enabled GS Hardware Fix: %s to [mode=%d]", getHWFixName(id), value);
|
||||||
|
num_applied_fixes++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fixup skipdraw range just in case the db has a bad range (but the linter should catch this)
|
||||||
|
config.SkipDrawEnd = std::max(config.SkipDrawStart, config.SkipDrawEnd);
|
||||||
|
|
||||||
|
return num_applied_fixes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameDatabase::initDatabase()
|
||||||
{
|
{
|
||||||
ryml::Callbacks rymlCallbacks = ryml::get_callbacks();
|
ryml::Callbacks rymlCallbacks = ryml::get_callbacks();
|
||||||
rymlCallbacks.m_error = [](const char* msg, size_t msg_len, ryml::Location loc, void*) {
|
rymlCallbacks.m_error = [](const char* msg, size_t msg_len, ryml::Location loc, void*) {
|
||||||
|
@ -291,8 +496,6 @@ static void initDatabase()
|
||||||
ryml::reset_callbacks();
|
ryml::reset_callbacks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void GameDatabase::ensureLoaded()
|
void GameDatabase::ensureLoaded()
|
||||||
{
|
{
|
||||||
std::call_once(s_load_once_flag, []() {
|
std::call_once(s_load_once_flag, []() {
|
||||||
|
|
|
@ -15,16 +15,18 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
enum GamefixId;
|
enum GamefixId;
|
||||||
enum SpeedhackId;
|
enum SpeedhackId;
|
||||||
|
|
||||||
class GameDatabaseSchema
|
namespace GameDatabaseSchema
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
enum class Compatibility
|
enum class Compatibility
|
||||||
{
|
{
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
|
@ -54,6 +56,34 @@ public:
|
||||||
Full
|
Full
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class GSHWFixId : u32
|
||||||
|
{
|
||||||
|
// boolean settings
|
||||||
|
AutoFlush,
|
||||||
|
ConservativeFramebuffer,
|
||||||
|
CPUFramebufferConversion,
|
||||||
|
DisableDepthSupport,
|
||||||
|
WrapGSMem,
|
||||||
|
PreloadFrameData,
|
||||||
|
FastTextureInvalidation,
|
||||||
|
TextureInsideRT,
|
||||||
|
AlignSprite,
|
||||||
|
MergeSprite,
|
||||||
|
WildArmsHack,
|
||||||
|
|
||||||
|
// integer settings
|
||||||
|
Mipmap,
|
||||||
|
TrilinearFiltering,
|
||||||
|
SkipDrawStart,
|
||||||
|
SkipDrawEnd,
|
||||||
|
HalfBottomOverride,
|
||||||
|
HalfPixelOffset,
|
||||||
|
RoundSprite,
|
||||||
|
TexturePreloading,
|
||||||
|
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
|
||||||
using Patch = std::vector<std::string>;
|
using Patch = std::vector<std::string>;
|
||||||
|
|
||||||
struct GameEntry
|
struct GameEntry
|
||||||
|
@ -67,6 +97,7 @@ public:
|
||||||
ClampMode vuClampMode = ClampMode::Undefined;
|
ClampMode vuClampMode = ClampMode::Undefined;
|
||||||
std::vector<GamefixId> gameFixes;
|
std::vector<GamefixId> gameFixes;
|
||||||
std::vector<std::pair<SpeedhackId, int>> speedHacks;
|
std::vector<std::pair<SpeedhackId, int>> speedHacks;
|
||||||
|
std::vector<std::pair<GSHWFixId, s32>> gsHWFixes;
|
||||||
std::vector<std::string> memcardFilters;
|
std::vector<std::string> memcardFilters;
|
||||||
std::unordered_map<std::string, Patch> patches;
|
std::unordered_map<std::string, Patch> patches;
|
||||||
|
|
||||||
|
@ -74,6 +105,9 @@ public:
|
||||||
std::string memcardFiltersAsString() const;
|
std::string memcardFiltersAsString() const;
|
||||||
const Patch* findPatch(const std::string_view& crc) const;
|
const Patch* findPatch(const std::string_view& crc) const;
|
||||||
const char* compatAsString() const;
|
const char* compatAsString() const;
|
||||||
|
|
||||||
|
/// Applies GS hardware fixes to an existing config. Returns the number of applied fixes.
|
||||||
|
u32 applyGSHardwareFixes(Pcsx2Config::GSOptions& config) const;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -314,7 +314,7 @@ Pcsx2Config::GSOptions::GSOptions()
|
||||||
Mipmap = true;
|
Mipmap = true;
|
||||||
AA1 = true;
|
AA1 = true;
|
||||||
|
|
||||||
UserHacks = false;
|
ManualUserHacks = false;
|
||||||
UserHacks_AlignSpriteX = false;
|
UserHacks_AlignSpriteX = false;
|
||||||
UserHacks_AutoFlush = false;
|
UserHacks_AutoFlush = false;
|
||||||
UserHacks_CPUFBConversion = false;
|
UserHacks_CPUFBConversion = false;
|
||||||
|
@ -385,8 +385,8 @@ bool Pcsx2Config::GSOptions::OptionsAreEqual(const GSOptions& right) const
|
||||||
OpEqu(SWExtraThreads) &&
|
OpEqu(SWExtraThreads) &&
|
||||||
OpEqu(SWExtraThreadsHeight) &&
|
OpEqu(SWExtraThreadsHeight) &&
|
||||||
OpEqu(TVShader) &&
|
OpEqu(TVShader) &&
|
||||||
OpEqu(SkipDraw) &&
|
OpEqu(SkipDrawEnd) &&
|
||||||
OpEqu(SkipDrawOffset) &&
|
OpEqu(SkipDrawStart) &&
|
||||||
|
|
||||||
OpEqu(UserHacks_HalfBottomOverride) &&
|
OpEqu(UserHacks_HalfBottomOverride) &&
|
||||||
OpEqu(UserHacks_HalfPixelOffset) &&
|
OpEqu(UserHacks_HalfPixelOffset) &&
|
||||||
|
@ -513,7 +513,7 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
|
||||||
GSSettingBoolEx(WrapGSMem, "wrap_gs_mem");
|
GSSettingBoolEx(WrapGSMem, "wrap_gs_mem");
|
||||||
GSSettingBoolEx(Mipmap, "mipmap");
|
GSSettingBoolEx(Mipmap, "mipmap");
|
||||||
GSSettingBoolEx(AA1, "aa1");
|
GSSettingBoolEx(AA1, "aa1");
|
||||||
GSSettingBoolEx(UserHacks, "UserHacks");
|
GSSettingBoolEx(ManualUserHacks, "UserHacks");
|
||||||
GSSettingBoolEx(UserHacks_AlignSpriteX, "UserHacks_align_sprite_X");
|
GSSettingBoolEx(UserHacks_AlignSpriteX, "UserHacks_align_sprite_X");
|
||||||
GSSettingBoolEx(UserHacks_AutoFlush, "UserHacks_AutoFlush");
|
GSSettingBoolEx(UserHacks_AutoFlush, "UserHacks_AutoFlush");
|
||||||
GSSettingBoolEx(UserHacks_CPUFBConversion, "UserHacks_CPU_FB_Conversion");
|
GSSettingBoolEx(UserHacks_CPUFBConversion, "UserHacks_CPU_FB_Conversion");
|
||||||
|
@ -555,8 +555,9 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
|
||||||
GSSettingIntEx(SWExtraThreads, "extrathreads");
|
GSSettingIntEx(SWExtraThreads, "extrathreads");
|
||||||
GSSettingIntEx(SWExtraThreadsHeight, "extrathreads_height");
|
GSSettingIntEx(SWExtraThreadsHeight, "extrathreads_height");
|
||||||
GSSettingIntEx(TVShader, "TVShader");
|
GSSettingIntEx(TVShader, "TVShader");
|
||||||
GSSettingIntEx(SkipDraw, "UserHacks_SkipDraw");
|
GSSettingIntEx(SkipDrawStart, "UserHacks_SkipDraw_Offset");
|
||||||
GSSettingIntEx(SkipDrawOffset, "UserHacks_SkipDraw_Offset");
|
GSSettingIntEx(SkipDrawEnd, "UserHacks_SkipDraw");
|
||||||
|
SkipDrawEnd = std::max(SkipDrawStart, SkipDrawEnd);
|
||||||
|
|
||||||
GSSettingIntEx(UserHacks_HalfBottomOverride, "UserHacks_Half_Bottom_Override");
|
GSSettingIntEx(UserHacks_HalfBottomOverride, "UserHacks_Half_Bottom_Override");
|
||||||
GSSettingIntEx(UserHacks_HalfPixelOffset, "UserHacks_HalfPixelOffset");
|
GSSettingIntEx(UserHacks_HalfPixelOffset, "UserHacks_HalfPixelOffset");
|
||||||
|
@ -588,7 +589,7 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
|
||||||
|
|
||||||
void Pcsx2Config::GSOptions::MaskUserHacks()
|
void Pcsx2Config::GSOptions::MaskUserHacks()
|
||||||
{
|
{
|
||||||
if (UserHacks)
|
if (ManualUserHacks)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
UserHacks_AlignSpriteX = false;
|
UserHacks_AlignSpriteX = false;
|
||||||
|
@ -605,8 +606,8 @@ void Pcsx2Config::GSOptions::MaskUserHacks()
|
||||||
UserHacks_TextureInsideRt = false;
|
UserHacks_TextureInsideRt = false;
|
||||||
UserHacks_TCOffsetX = 0;
|
UserHacks_TCOffsetX = 0;
|
||||||
UserHacks_TCOffsetY = 0;
|
UserHacks_TCOffsetY = 0;
|
||||||
SkipDraw = 0;
|
SkipDrawStart = 0;
|
||||||
SkipDrawOffset = 0;
|
SkipDrawEnd = 0;
|
||||||
|
|
||||||
// in wx, we put trilinear filtering behind user hacks, but not in qt.
|
// in wx, we put trilinear filtering behind user hacks, but not in qt.
|
||||||
#ifndef PCSX2_CORE
|
#ifndef PCSX2_CORE
|
||||||
|
@ -616,7 +617,7 @@ void Pcsx2Config::GSOptions::MaskUserHacks()
|
||||||
|
|
||||||
void Pcsx2Config::GSOptions::MaskUpscalingHacks()
|
void Pcsx2Config::GSOptions::MaskUpscalingHacks()
|
||||||
{
|
{
|
||||||
if (UpscaleMultiplier == 1 || UserHacks)
|
if (UpscaleMultiplier == 1 || ManualUserHacks)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
UserHacks_AlignSpriteX = false;
|
UserHacks_AlignSpriteX = false;
|
||||||
|
|
|
@ -218,6 +218,10 @@ void VMManager::LoadSettings()
|
||||||
InputManager::ReloadSources(*si);
|
InputManager::ReloadSources(*si);
|
||||||
InputManager::ReloadBindings(*si);
|
InputManager::ReloadBindings(*si);
|
||||||
|
|
||||||
|
// Remove any user-specified hacks in the config (we don't want stale/conflicting values when it's globally disabled).
|
||||||
|
EmuConfig.GS.MaskUserHacks();
|
||||||
|
EmuConfig.GS.MaskUpscalingHacks();
|
||||||
|
|
||||||
if (HasValidVM())
|
if (HasValidVM())
|
||||||
ApplyGameFixes();
|
ApplyGameFixes();
|
||||||
}
|
}
|
||||||
|
@ -295,6 +299,8 @@ void VMManager::ApplyGameFixes()
|
||||||
if (id == Fix_GoemonTlbMiss && true)
|
if (id == Fix_GoemonTlbMiss && true)
|
||||||
vtlb_Alloc_Ppmap();
|
vtlb_Alloc_Ppmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s_active_game_fixes += game->applyGSHardwareFixes(EmuConfig.GS);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string VMManager::GetGameSettingsPath(u32 game_crc)
|
std::string VMManager::GetGameSettingsPath(u32 game_crc)
|
||||||
|
|
|
@ -333,6 +333,8 @@ static int loadGameSettings(Pcsx2Config& dest, const GameDatabaseSchema::GameEnt
|
||||||
vtlb_Alloc_Ppmap();
|
vtlb_Alloc_Ppmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gf += game.applyGSHardwareFixes(dest.GS);
|
||||||
|
|
||||||
return gf;
|
return gf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,6 +406,10 @@ static void _ApplySettings(const Pcsx2Config& src, Pcsx2Config& fixup)
|
||||||
fixup.GS.VsyncEnable = VsyncMode::Off;
|
fixup.GS.VsyncEnable = VsyncMode::Off;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove any user-specified hacks in the config (we don't want stale/conflicting values when it's globally disabled).
|
||||||
|
fixup.GS.MaskUserHacks();
|
||||||
|
fixup.GS.MaskUpscalingHacks();
|
||||||
|
|
||||||
wxString gamePatch;
|
wxString gamePatch;
|
||||||
wxString gameFixes;
|
wxString gameFixes;
|
||||||
wxString gameCheats;
|
wxString gameCheats;
|
||||||
|
|
Loading…
Reference in New Issue