GS: Move HW hacks into game database

This commit is contained in:
Stenzek 2023-01-05 22:08:33 +10:00 committed by refractionpcsx2
parent 904ccc7a91
commit fe1e432f9e
18 changed files with 838 additions and 618 deletions

File diff suppressed because it is too large Load Diff

View File

@ -648,7 +648,6 @@ struct Pcsx2Config
PreloadFrameWithGSData : 1, PreloadFrameWithGSData : 1,
WrapGSMem : 1, WrapGSMem : 1,
Mipmap : 1, Mipmap : 1,
PointListPalette : 1,
ManualUserHacks : 1, ManualUserHacks : 1,
UserHacks_AlignSpriteX : 1, UserHacks_AlignSpriteX : 1,
UserHacks_AutoFlush : 1, UserHacks_AutoFlush : 1,
@ -716,6 +715,9 @@ struct Pcsx2Config
int SWExtraThreads{2}; int SWExtraThreads{2};
int SWExtraThreadsHeight{4}; int SWExtraThreadsHeight{4};
int TVShader{0}; int TVShader{0};
s16 GetSkipCountFunctionId{-1};
s16 BeforeDrawFunctionId{-1};
s16 AfterDrawFunctionId{-1};
int SkipDrawStart{0}; int SkipDrawStart{0};
int SkipDrawEnd{0}; int SkipDrawEnd{0};

View File

@ -166,11 +166,6 @@
"minimum": 0, "minimum": 0,
"maximum": 1 "maximum": 1
}, },
"pointListPalette": {
"type": "integer",
"minimum": 0,
"maximum": 1
},
"mipmap": { "mipmap": {
"type": "integer", "type": "integer",
"minimum": 0, "minimum": 0,
@ -230,6 +225,15 @@
"type": "integer", "type": "integer",
"minimum": 0, "minimum": 0,
"maximum": 2 "maximum": 2
},
"getSkipCount": {
"type": "string"
},
"beforeDraw": {
"type": "string"
},
"afterDraw": {
"type": "string"
} }
}, },
"additionalProperties": false "additionalProperties": false

View File

@ -331,7 +331,7 @@ bool GSreopen(bool recreate_display, const Pcsx2Config::GSOptions& old_config)
return false; return false;
} }
g_gs_renderer->SetGameCRC(gamecrc, GSUtil::GetEffectiveCRCHackLevel(GSConfig.Renderer, GSConfig.CRCHack)); g_gs_renderer->SetGameCRC(gamecrc);
return true; return true;
} }
@ -560,7 +560,7 @@ void GSThrottlePresentation()
void GSsetGameCRC(u32 crc) void GSsetGameCRC(u32 crc)
{ {
g_gs_renderer->SetGameCRC(crc, GSUtil::GetEffectiveCRCHackLevel(GSConfig.Renderer, GSConfig.CRCHack)); g_gs_renderer->SetGameCRC(crc);
} }
GSVideoMode GSgetDisplayMode() GSVideoMode GSgetDisplayMode()
@ -692,7 +692,6 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
// Options which aren't using the global struct yet, so we need to recreate all GS objects. // Options which aren't using the global struct yet, so we need to recreate all GS objects.
if ( if (
GSConfig.UpscaleMultiplier != old_config.UpscaleMultiplier || GSConfig.UpscaleMultiplier != old_config.UpscaleMultiplier ||
GSConfig.CRCHack != old_config.CRCHack ||
GSConfig.SWExtraThreads != old_config.SWExtraThreads || GSConfig.SWExtraThreads != old_config.SWExtraThreads ||
GSConfig.SWExtraThreadsHeight != old_config.SWExtraThreadsHeight) GSConfig.SWExtraThreadsHeight != old_config.SWExtraThreadsHeight)
{ {
@ -706,9 +705,12 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
// For example, flushing the texture cache when mipmap settings change. // For example, flushing the texture cache when mipmap settings change.
if (GSConfig.CRCHack != old_config.CRCHack || if (GSConfig.CRCHack != old_config.CRCHack ||
GSConfig.PointListPalette != old_config.PointListPalette) GSConfig.UpscaleMultiplier != old_config.UpscaleMultiplier ||
GSConfig.GetSkipCountFunctionId != old_config.GetSkipCountFunctionId ||
GSConfig.BeforeDrawFunctionId != old_config.BeforeDrawFunctionId ||
GSConfig.AfterDrawFunctionId != old_config.BeforeDrawFunctionId)
{ {
g_gs_renderer->SetGameCRC(g_gs_renderer->GetGameCRC(), GSUtil::GetEffectiveCRCHackLevel(GSConfig.Renderer, GSConfig.CRCHack)); g_gs_renderer->UpdateCRCHacks();
} }
// renderer-specific options (e.g. auto flush, TC offset) // renderer-specific options (e.g. auto flush, TC offset)

View File

@ -22,6 +22,8 @@
#include "gsl/span" #include "gsl/span"
#include <map> #include <map>
#include <string>
#include <string_view>
// ST_WRITE is defined in libc, avoid this // ST_WRITE is defined in libc, avoid this
enum stateType enum stateType
@ -46,6 +48,11 @@ extern Pcsx2Config::GSOptions GSConfig;
class HostDisplay; class HostDisplay;
// Returns the ID for the specified function, otherwise -1.
s16 GSLookupGetSkipCountFunctionId(const std::string_view& name);
s16 GSLookupBeforeDrawFunctionId(const std::string_view& name);
s16 GSLookupAfterDrawFunctionId(const std::string_view& name);
int GSinit(); int GSinit();
void GSshutdown(); void GSshutdown();
bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* basemem); bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* basemem);

View File

@ -23,349 +23,100 @@
const CRC::Game CRC::m_games[] = const CRC::Game CRC::m_games[] =
{ {
// Note: IDs 0x7ACF7E03, 0x7D4EA48F, 0x37C53760 - shouldn't be added as it's from the multiloaders when packing games. // Note: IDs 0x7ACF7E03, 0x7D4EA48F, 0x37C53760 - shouldn't be added as it's from the multiloaders when packing games.
{0x00000000, NoTitle, NoRegion, 0}, {0x00000000, NoTitle /* NoRegion */},
{0xF95F37EE, ArTonelico2, US, 0}, {0x9AAC5309, FFX2 /* EU */},
{0x68CE6801, ArTonelico2, JP, 0}, {0x9AAC530C, FFX2 /* FR */},
{0xCE2C1DBF, ArTonelico2, EU, 0}, {0x9AAC530A, FFX2 /* ES */},
{0x5C891FF1, Black, US, 0}, {0x9AAC530D, FFX2 /* DE */},
{0xCAA04879, Black, EU, 0}, {0x9AAC530B, FFX2 /* IT */},
{0xADDFF505, Black, EU, 0}, {0x48FE0C71, FFX2 /* US */},
{0xB3A9F9ED, Black, JP, 0}, {0x8A6D7F14, FFX2 /* JP */},
{0x2113EA2E, MetalSlug6, JP, 0}, {0xE1FD9A2D, FFX2 /* JP */}, // int.
{0xA6167B59, Lamune, JP, 0}, {0x11624CD6, FFX2 /* KO */},
{0xA39517AB, FFX, EU, 0}, {0x08C1ED4D, HauntingGround /* EU */},
{0x78D83FD5, FFX, EU, 0}, // Demo {0x2CD5794C, HauntingGround /* EU */},
{0xA39517AE, FFX, FR, 0}, {0x867BB945, HauntingGround /* JP */},
{0x941BB7D9, FFX, DE, 0}, {0xE263BC4B, HauntingGround /* JP */},
{0xA39517A9, FFX, IT, 0}, {0x901AAC09, HauntingGround /* US */},
{0x941BB7DE, FFX, ES, 0}, {0x6F8545DB, ICO /* US */},
{0xA80F497C, FFX, ES, 0}, {0x48CDF317, ICO /* US */}, // Demo
{0xB4414EA1, FFX, RU, 0}, {0xB01A4C95, ICO /* JP */},
{0xEE97DB5B, FFX, RU, 0}, {0x2DF2C1EA, ICO /* KO */},
{0xAEC495CC, FFX, RU, 0}, {0x5C991F4E, ICO /* EU */},
{0xBB3D833A, FFX, US, 0}, {0x788D8B4F, ICO /* EU */},
{0x6A4EFE60, FFX, JP, 0}, {0x29C28734, ICO /* CH */},
{0x3866CA7E, FFX, ASIA, 0}, // int. {0x60013EBD, PolyphonyDigitalGames /* EU */}, // Gran Turismo Concept
{0x658597E2, FFX, JP, 0}, // int. {0x6810C3BC, PolyphonyDigitalGames /* CH */}, // Gran Turismo Concept 2002 Tokyo-Geneva
{0x9AAC5309, FFX2, EU, 0}, {0x0EEF32A3, PolyphonyDigitalGames /* KO */}, // Gran Turismo Concept 2002 Tokyo-Seoul
{0x9AAC530C, FFX2, FR, 0}, {0x3E9D448A, PolyphonyDigitalGames /* CH */}, // GT3
{0x9AAC530A, FFX2, ES, 0}, {0xAD66643C, PolyphonyDigitalGames /* CH */}, // GT3
{0x9AAC530D, FFX2, DE, 0}, {0x85AE91B3, PolyphonyDigitalGames /* US */}, // GT3
{0x9AAC530B, FFX2, IT, 0}, {0x8AA991B0, PolyphonyDigitalGames /* US */}, // GT3
{0x48FE0C71, FFX2, US, 0}, {0xC220951A, PolyphonyDigitalGames /* JP */}, // GT3
{0x8A6D7F14, FFX2, JP, 0}, {0x9DE5CF65, PolyphonyDigitalGames /* JP */}, // GT3
{0xE1FD9A2D, FFX2, JP, 0}, // int. {0x706DFF80, PolyphonyDigitalGames /* JP */}, // GT3 Store Disc Vol. 2
{0x11624CD6, FFX2, KO, 0}, {0x55CE5111, PolyphonyDigitalGames /* JP */}, // Gran Turismo 2000 Body Omen
{0x78DA0252, FFXII, EU, 0}, {0xE9A7E08D, PolyphonyDigitalGames /* JP */}, // Gran Turismo 2000 Body Omen
{0xC1274668, FFXII, EU, 0}, {0xB590CE04, PolyphonyDigitalGames /* EU */}, // GT3
{0xDC2A467E, FFXII, EU, 0}, {0xC02C653E, PolyphonyDigitalGames /* CH */}, // GT4
{0xCA284668, FFXII, EU, 0}, {0x7ABDBB5E, PolyphonyDigitalGames /* CH */}, // GT4
{0xC52B466E, FFXII, EU, 0}, // ES {0xAEAD1CA3, PolyphonyDigitalGames /* JP */}, // GT4
{0xE5E71BF9, FFXII, FR, 0}, {0xA3AF15A0, PolyphonyDigitalGames /* JP */}, // GT4 PS2 Racing Pack
{0x0779FBDB, FFXII, US, 0}, {0xE906EA37, PolyphonyDigitalGames /* JP */}, // GT4 First Preview
{0x280AD120, FFXII, JP, 0}, {0xCA6243B9, PolyphonyDigitalGames /* JP */}, // GT4 Prologue
{0x08C1ED4D, HauntingGround, EU, 0}, {0xDD764BBE, PolyphonyDigitalGames /* JP */}, // GT4 Prologue
{0x2CD5794C, HauntingGround, EU, 0}, {0xE1258846, PolyphonyDigitalGames /* JP */}, // GT4 Prologue
{0x867BB945, HauntingGround, JP, 0}, {0x27B8F05F, PolyphonyDigitalGames /* JP */}, // GT4 Prius Trial Version
{0xE263BC4B, HauntingGround, JP, 0}, {0x30E41D93, PolyphonyDigitalGames /* KO */}, // GT4
{0x901AAC09, HauntingGround, US, 0}, {0x715CF2EC, PolyphonyDigitalGames /* EU */}, // GT4
{0x21068223, Okami, US, 0}, {0x44A61C8F, PolyphonyDigitalGames /* EU */}, // GT4
{0x891F223F, Okami, EU, 0}, // PAL DE, ES & FR. {0x0086E35B, PolyphonyDigitalGames /* EU */}, // GT4
{0xC5DEFEA0, Okami, JP, 0}, {0x3FB69323, PolyphonyDigitalGames /* EU */}, // GT4 Prologue
{0xCCC8F3A4, Okami, KO, 0}, {0x77E61C8A, PolyphonyDigitalGames /* US */}, // GT4
{0x278722BF, DBZBT2, EU, 0}, {0x33C6E35E, PolyphonyDigitalGames /* US */}, // GT4
{0xFE961D28, DBZBT2, US, 0}, {0x70538747, PolyphonyDigitalGames /* US */}, // GT4 Toyota Prius Trial
{0x0393B6BE, DBZBT2, EU, 0}, {0x32A1C752, PolyphonyDigitalGames /* US */}, // GT4 Online Beta
{0xE2F289ED, DBZBT2, JP, 0}, // Sparking Neo! {0x2A84A1E2, PolyphonyDigitalGames /* US */}, // GT4 Mazda MX-5 Edition
{0xE29C09A3, DBZBT2, KO, 0}, // DragonBall Z Sparking Neo {0x0087EEC4, PolyphonyDigitalGames /* NoRegion */}, // GT4 Online Beta, JP and US versions have the same CRC
{0x0BAA4387, DBZBT2, JP, 0}, {0x5AC7E79C, PolyphonyDigitalGames /* CH */}, // TouristTrophy
{0x35AA84D1, DBZBT2, NoRegion, 0}, {0xFF9C0E93, PolyphonyDigitalGames /* US */}, // TouristTrophy
{0xBE6A9CFB, DBZBT2, NoRegion, 0}, {0xCA9AA903, PolyphonyDigitalGames /* EU */}, // TouristTrophy
{0x428113C2, DBZBT3, US, 0}, {0xAC3C1147, SVCChaos /* EU */}, // SVC Chaos: SNK vs. Capcom
{0xA422BB13, DBZBT3, EU, 0}, {0xB00FF2ED, SVCChaos /* JP */},
{0xCE93CB30, DBZBT3, JP, 0}, {0x94834BD3, SVCChaos /* JP */},
{0xF28D21F1, DBZBT3, JP, 0}, {0xCF1D71EE, KOF2002 /* EU */}, // The King of Fighters 2002
{0x983C53D2, DBZBT3, NoRegion, 0}, {0xABD16263, KOF2002 /* JP */},
{0x983C53D3, DBZBT3, EU, 0}, {0x424A8601, KOF2002 /* JP */},
{0x9B0E119F, DBZBT3, KO, 0}, // DragonBall Z Sparking Meteo {0x7F74D8D0, KOF2002 /* US */},
{0x5E13E6D6, SFEX3, EU, 0}, {0xFC46EA61, Tekken5 /* JP */},
{0x72B3802A, SFEX3, US, 0}, {0x1F88EE37, Tekken5 /* EU */},
{0x71521863, SFEX3, US, 0}, {0x1F88BECD, Tekken5 /* EU */}, // language selector...
{0x63642E9F, SFEX3, JP, 0}, {0x652050D2, Tekken5 /* US */},
{0xCA1F6E53, SFEX3, JP, 0}, // Taikenban Disc (Demo/Trial) {0xEA64EF39, Tekken5 /* KO */},
{0x6F8545DB, ICO, US, 0}, {0xE8FCF8EC, SMTNocturne /* US */},
{0x48CDF317, ICO, US, 0}, // Demo {0xF0A31EE3, SMTNocturne /* EU */}, // SMTNocturne (Lucifers Call in EU)
{0xB01A4C95, ICO, JP, 0}, {0xAE0DE7B7, SMTNocturne /* EU */}, // SMTNocturne (Lucifers Call in EU)
{0x2DF2C1EA, ICO, KO, 0}, {0xD60DA6D4, SMTNocturne /* JP */}, // SMTNocturne
{0x5C991F4E, ICO, EU, 0}, {0x0E762E8D, SMTNocturne /* JP */}, // SMTNocturne Maniacs
{0x788D8B4F, ICO, EU, 0}, {0x47BA9034, SMTNocturne /* JP */}, // SMTNocturne Maniacs Chronicle
{0x29C28734, ICO, CH, 0}, {0xD3FFC263, SMTNocturne /* KO */},
{0x60013EBD, PolyphonyDigitalGames, EU, 0}, // Gran Turismo Concept {0x84D1A8DA, SMTNocturne /* KO */},
{0x6810C3BC, PolyphonyDigitalGames, CH, 0}, // Gran Turismo Concept 2002 Tokyo-Geneva {0xE21404E2, GetawayGames /* US */}, // Getaway
{0x0EEF32A3, PolyphonyDigitalGames, KO, 0}, // Gran Turismo Concept 2002 Tokyo-Seoul {0xE8249852, GetawayGames /* JP */}, // Getaway
{0x3E9D448A, PolyphonyDigitalGames, CH, 0}, // GT3 {0x458485EF, GetawayGames /* EU */}, // Getaway
{0xAD66643C, PolyphonyDigitalGames, CH, 0}, // GT3 {0x5DFBE144, GetawayGames /* EU */}, // Getaway
{0x85AE91B3, PolyphonyDigitalGames, US, 0}, // GT3 {0xE78971DF, GetawayGames /* US */}, // GetawayBlackMonday
{0x8AA991B0, PolyphonyDigitalGames, US, 0}, // GT3 {0x342D97FA, GetawayGames /* US */}, // GetawayBlackMonday Demo
{0xC220951A, PolyphonyDigitalGames, JP, 0}, // GT3 {0xE8C0AD1A, GetawayGames /* JP */}, // GetawayBlackMonday
{0x9DE5CF65, PolyphonyDigitalGames, JP, 0}, // GT3 {0x09C3DF79, GetawayGames /* EU */}, // GetawayBlackMonday
{0x706DFF80, PolyphonyDigitalGames, JP, 0}, // GT3 Store Disc Vol. 2
{0x55CE5111, PolyphonyDigitalGames, JP, 0}, // Gran Turismo 2000 Body Omen
{0xE9A7E08D, PolyphonyDigitalGames, JP, 0}, // Gran Turismo 2000 Body Omen
{0xB590CE04, PolyphonyDigitalGames, EU, 0}, // GT3
{0xC02C653E, PolyphonyDigitalGames, CH, 0}, // GT4
{0x7ABDBB5E, PolyphonyDigitalGames, CH, 0}, // GT4
{0xAEAD1CA3, PolyphonyDigitalGames, JP, 0}, // GT4
{0xA3AF15A0, PolyphonyDigitalGames, JP, 0}, // GT4 PS2 Racing Pack
{0xE906EA37, PolyphonyDigitalGames, JP, 0}, // GT4 First Preview
{0xCA6243B9, PolyphonyDigitalGames, JP, 0}, // GT4 Prologue
{0xDD764BBE, PolyphonyDigitalGames, JP, 0}, // GT4 Prologue
{0xE1258846, PolyphonyDigitalGames, JP, 0}, // GT4 Prologue
{0x27B8F05F, PolyphonyDigitalGames, JP, 0}, // GT4 Prius Trial Version
{0x30E41D93, PolyphonyDigitalGames, KO, 0}, // GT4
{0x715CF2EC, PolyphonyDigitalGames, EU, 0}, // GT4
{0x44A61C8F, PolyphonyDigitalGames, EU, 0}, // GT4
{0x0086E35B, PolyphonyDigitalGames, EU, 0}, // GT4
{0x3FB69323, PolyphonyDigitalGames, EU, 0}, // GT4 Prologue
{0x77E61C8A, PolyphonyDigitalGames, US, 0}, // GT4
{0x33C6E35E, PolyphonyDigitalGames, US, 0}, // GT4
{0x70538747, PolyphonyDigitalGames, US, 0}, // GT4 Toyota Prius Trial
{0x32A1C752, PolyphonyDigitalGames, US, 0}, // GT4 Online Beta
{0x2A84A1E2, PolyphonyDigitalGames, US, 0}, // GT4 Mazda MX-5 Edition
{0x0087EEC4, PolyphonyDigitalGames, NoRegion, 0}, // GT4 Online Beta, JP and US versions have the same CRC
{0x5AC7E79C, PolyphonyDigitalGames, CH, 0}, // TouristTrophy
{0xFF9C0E93, PolyphonyDigitalGames, US, 0}, // TouristTrophy
{0xCA9AA903, PolyphonyDigitalGames, EU, 0}, // TouristTrophy
{0x8B029334, Manhunt2, EU, 0},
{0x3B0ADBEF, Manhunt2, US, 0},
{0x09F49E37, CrashBandicootWoC, NoRegion, 0},
{0x103B5706, CrashBandicootWoC, US, 0}, // American Greatest Hits release
{0x75182BE5, CrashBandicootWoC, US, 0},
{0x5188ABCA, CrashBandicootWoC, US, 0},
{0xEB1EB7FE, CrashBandicootWoC, US, 0}, // Regular Demo
{0x34E2EEC7, CrashBandicootWoC, RU, 0},
{0x00A074A7, CrashBandicootWoC, KO, 0},
{0xF8643F9B, CrashBandicootWoC, JP, 0},
{0x3A03D62F, CrashBandicootWoC, EU, 0},
{0x35D70452, CrashBandicootWoC, EU, 0},
{0x1E935600, CrashBandicootWoC, EU, 0},
{0x72E1E60E, Spartan, EU, 0},
{0x26689C87, Spartan, JP, 0},
{0x08277A9E, Spartan, US, 0},
{0xAC3C1147, SVCChaos, EU, 0}, // SVC Chaos: SNK vs. Capcom
{0xB00FF2ED, SVCChaos, JP, 0},
{0x94834BD3, SVCChaos, JP, 0},
{0xCF1D71EE, KOF2002, EU, 0}, // The King of Fighters 2002
{0xABD16263, KOF2002, JP, 0},
{0x424A8601, KOF2002, JP, 0},
{0x7F74D8D0, KOF2002, US, 0},
{0xA32F7CD0, AceCombat4, US, 0}, // Also needed for automatic mipmapping
{0x5ED8FB53, AceCombat4, JP, 0},
{0x1B9B7563, AceCombat4, EU, 0},
{0xFC46EA61, Tekken5, JP, 0},
{0x1F88EE37, Tekken5, EU, 0},
{0x1F88BECD, Tekken5, EU, 0}, // language selector...
{0x652050D2, Tekken5, US, 0},
{0xEA64EF39, Tekken5, KO, 0},
{0x95CC86EF, GiTS, US, 0}, // same CRC also reported as EU
{0x2C5BF134, GiTS, US, 0}, // Demo
{0xA5768F53, GiTS, JP, 0},
{0xA3643EB1, GiTS, KO, 0},
{0x28557423, GiTS, RU, 0},
{0xBF6F101F, GiTS, EU, 0}, // same CRC as another US disc
{0xA616A6C2, TalesOfAbyss, US, 0},
{0x14FE77F7, TalesOfAbyss, US, 0},
{0xAA5EC3A3, TalesOfAbyss, JP, 0},
{0xFB236A46, SonicUnleashed, US, 0},
{0x8C913264, SonicUnleashed, EU, 0},
{0xE8FCF8EC, SMTNocturne, US, 0},
{0xF0A31EE3, SMTNocturne, EU, 0}, // SMTNocturne (Lucifers Call in EU)
{0xAE0DE7B7, SMTNocturne, EU, 0}, // SMTNocturne (Lucifers Call in EU)
{0xD60DA6D4, SMTNocturne, JP, 0}, // SMTNocturne
{0x0E762E8D, SMTNocturne, JP, 0}, // SMTNocturne Maniacs
{0x47BA9034, SMTNocturne, JP, 0}, // SMTNocturne Maniacs Chronicle
{0xD3FFC263, SMTNocturne, KO, 0},
{0x84D1A8DA, SMTNocturne, KO, 0},
{0x0B8AB37B, RozenMaidenGebetGarden, JP, 0},
{0x506644B3, BigMuthaTruckers, EU, 0},
{0x90F0D852, BigMuthaTruckers, US, 0},
{0x92624842, BigMuthaTruckers, US, 0},
{0xDD93DA88, BigMuthaTruckers, JP, 0}, // Bakusou Convoy Densetsu - Otoko Hanamichi America Roman
{0xE169BAF8, RedDeadRevolver, US, 0},
{0xE2E67E23, RedDeadRevolver, EU, 0},
{0xC5B75C7C, Oneechanbara2Special, JP, 0},
{0xC725CC6C, Oneechanbara2Special, JP, 0},
{0x07608CA2, Oneechanbara2Special, EU, 0}, // Zombie Hunters 2
{0xE0347841, XenosagaE3, JP, 0},
{0xA707236E, XenosagaE3, JP, 0}, // Demo
{0xA4E88698, XenosagaE3, CH, 0},
{0x2088950A, XenosagaE3, US, 0},
{0x694A998E, TombRaiderUnderworld, JP, 0},
{0x8E214549, TombRaiderUnderworld, EU, 0},
{0x618769D6, TombRaiderUnderworld, US, 0},
{0xB639EB17, TombRaiderAnniversary, US, 0}, // Also needed for automatic mipmapping
{0xB05805B6, TombRaiderAnniversary, JP, 0},
{0xA629A376, TombRaiderAnniversary, EU, 0},
{0xBC8B3F50, TombRaiderLegend, US, 0},
{0x365172A0, TombRaiderLegend, JP, 0},
{0x05177ECE, TombRaiderLegend, EU, 0},
{0xBEBF8793, BurnoutGames, US, 0}, // BurnoutTakedown
{0xBB2E845F, BurnoutGames, JP, 0}, // BurnoutTakedown
{0x5F060991, BurnoutGames, KO, 0}, // BurnoutTakedown
{0x75BECC18, BurnoutGames, EU, 0}, // BurnoutTakedown
{0xCE49B0DE, BurnoutGames, EU, 0}, // BurnoutTakedown
{0x381EE9EF, BurnoutGames, EU, 0}, // BurnoutTakedown E3 Demo
{0xD224D348, BurnoutGames, US, 0}, // BurnoutRevenge
{0x878E7A1D, BurnoutGames, JP, 0}, // BurnoutRevenge
{0xEEA60511, BurnoutGames, KO, 0}, // BurnoutRevenge
{0x7E83CC5B, BurnoutGames, EU, 0}, // BurnoutRevenge
{0x2CAC3DBC, BurnoutGames, EU, 0}, // BurnoutRevenge
{0x8C9576A1, BurnoutGames, US, 0}, // BurnoutDominator
{0xDDF76A98, BurnoutGames, JP, 0}, // BurnoutDominator
{0x8C9576B4, BurnoutGames, EU, 0}, // BurnoutDominator
{0x8C9C76B4, BurnoutGames, EU, 0}, // BurnoutDominator
{0x4A0E5B3A, MidnightClub3, US, 0}, // dub
{0xEBE1972D, MidnightClub3, EU, 0}, // dub
{0x60A42FF5, MidnightClub3, US, 0}, // remix
{0x43AB7214, TalesOfLegendia, US, 0},
{0x1F8640E0, TalesOfLegendia, JP, 0},
{0xE4F5DA2B, TalesOfLegendia, KO, 0},
{0x519E816B, Kunoichi, US, 0}, // Nightshade
{0x3FB419FD, Kunoichi, JP, 0},
{0x086D198E, Kunoichi, CH, 0},
{0x3B470BBD, Kunoichi, EU, 0},
{0x6BA65DD8, Kunoichi, KO, 0},
{0XD3F182A3, YakuzaGames, EU, 0}, // Yakuza
{0x6F9F99F8, YakuzaGames, EU, 0}, // Yakuza
{0x388F687B, YakuzaGames, US, 0}, // Yakuza
{0xC1B91FC5, YakuzaGames, US, 0}, // Yakuza Demo
{0xB7B3800A, YakuzaGames, JP, 0}, // Yakuza2
{0xA60C2E65, YakuzaGames, EU, 0}, // Yakuza2
{0x800E3E5A, YakuzaGames, EU, 0}, // Yakuza2
{0x97E9C87E, YakuzaGames, US, 0}, // Yakuza2
{0xB1EBD841, YakuzaGames, US, 0}, // Yakuza2
{0xC6B95C48, YakuzaGames, JP, 0}, // Yakuza2
{0x2905C5C6, ZettaiZetsumeiToshi2, US, 0}, // Raw Danger!
{0xC988ECBB, ZettaiZetsumeiToshi2, JP, 0},
{0x90F4B057, ZettaiZetsumeiToshi2, CH, 0},
{0xA98B5B22, ZettaiZetsumeiToshi2, EU, 0},
{0xBD17248E, ShinOnimusha, JP, 0},
{0xBE17248E, ShinOnimusha, JP, 0},
{0xB817248E, ShinOnimusha, JP, 0},
{0xC1C77637, ShinOnimusha, JP, 0}, // PlayStation 2 The Best, Disc 1
{0x5C1E5BEF, ShinOnimusha, JP, 0}, // PlayStation 2 The Best, Disc 2
{0x812C5A96, ShinOnimusha, EU, 0},
{0xFE44479E, ShinOnimusha, US, 0},
{0xFFDE85E9, ShinOnimusha, US, 0},
{0xE21404E2, GetawayGames, US, 0}, // Getaway
{0xE8249852, GetawayGames, JP, 0}, // Getaway
{0x458485EF, GetawayGames, EU, 0}, // Getaway
{0x5DFBE144, GetawayGames, EU, 0}, // Getaway
{0xE78971DF, GetawayGames, US, 0}, // GetawayBlackMonday
{0x342D97FA, GetawayGames, US, 0}, // GetawayBlackMonday Demo
{0xE8C0AD1A, GetawayGames, JP, 0}, // GetawayBlackMonday
{0x09C3DF79, GetawayGames, EU, 0}, // GetawayBlackMonday
{0x1130BF23, SakuraTaisen, CH, 0},
{0x4FAE8B83, SakuraTaisen, KO, 0},
{0xEF06DBD6, SakuraWarsSoLongMyLove, JP, 0},
{0xDD41054D, SakuraWarsSoLongMyLove, US, 0},
{0xC2E3A7A4, SakuraWarsSoLongMyLove, KO, 0},
{0x4A4B623A, FightingBeautyWulong, JP, 0},
{0xAEDAEE99, GodHand, JP, 0},
{0x6FB69282, GodHand, US, 0},
{0x924C4AA6, GodHand, KO, 0},
{0xDE9722A5, GodHand, EU, 0},
{0x9637D496, KnightsOfTheTemple2, NoRegion, 0}, // // EU and JP versions have the same CRC
{0x4E811100, UltramanFightingEvolution, JP, 0},
{0xF7F181C3, DeathByDegreesTekkenNinaWilliams, CH, 0},
{0xF088FA5B, DeathByDegreesTekkenNinaWilliams, KO, 0},
{0xE1D6F85E, DeathByDegreesTekkenNinaWilliams, US, 0},
{0x59683BB0, DeathByDegreesTekkenNinaWilliams, EU, 0},
{0x830B6FB1, TalesofSymphonia, JP, 0},
{0xFC0F8A5B, Simple2000Vol114, JP, 0},
{0xBDD9BAAD, UrbanReign, US, 0},
{0x0418486E, UrbanReign, RU, 0},
{0xAE4BEBD3, UrbanReign, EU, 0},
{0x48AC09BC, SteambotChronicles, EU, 0},
{0x9F391882, SteambotChronicles, US, 0},
{0xFEFCF9DE, SteambotChronicles, JP, 0}, // Ponkotsu Roman Daikatsugeki: Bumpy Trot
{0XE1BF5DCA, SuperManReturns, US, 0},
{0XE8F7BAB6, SuperManReturns, EU, 0},
{0x06A7506A, SacredBlaze, JP, 0},
{0x2479F4A9, Jak2, EU, 0},
{0xF41C1B29, Jak2, EU, 0}, // Demo
{0x9184AAF1, Jak2, US, 0},
{0xA2034C69, Jak2, US, 0}, // Demo
{0x25FE4D23, Jak2, KO, 0},
{0xB4976DAF, Jak2, JP, 0}, // Jak II: Jak x Daxter 2
{0x43D4FF3E, Jak2, JP, 0}, // Demo
{0x12804727, Jak3, EU, 0},
{0xE59E10BF, Jak3, EU, 0},
{0xCA68E4D5, Jak3, EU, 0}, // Demo
{0x644CFD03, Jak3, US, 0},
{0xD401BC20, Jak3, US, 0}, // Demo
{0xD1368EAE, Jak3, KO, 0},
{0xDF659E77, JakX, EU, 0}, // Jak X: Combat Racing
{0xC20596DB, JakX, EU, 0}, // Beta Trial Disc, v0.01
{0x3091E6FB, JakX, US, 0},
{0xC417D919, JakX, US, 0}, // Demo
{0xDA366A53, JakX, US, 0}, // Public Beta v.1
{0x7B564230, JakX, US, 0}, // Jak and Daxter Complete Trilogy Demo
{0xDBA28C59, JakX, US, 0}, // Greatest Hits
}; };
std::map<u32, const CRC::Game*> CRC::m_map;
std::string ToLower(std::string str)
{
transform(str.begin(), str.end(), str.begin(), ::tolower);
return str;
}
// The exclusions list is a comma separated list of: the word "all" and/or CRCs in standard hex notation (0x and 8 digits with leading 0's if required).
// The list is case insensitive and order insensitive.
// E.g. Disable all CRC hacks: CrcHacksExclusions=all
// E.g. Disable hacks for these CRCs: CrcHacksExclusions=0x0F0C4A9C, 0x0EE5646B, 0x7ACF7E03
bool IsCrcExcluded(std::string exclusionList, u32 crc)
{
std::string target = StringUtil::StdStringFromFormat("0x%08x", crc);
exclusionList = ToLower(exclusionList);
return exclusionList.find(target) != std::string::npos || exclusionList.find("all") != std::string::npos;
}
const CRC::Game& CRC::Lookup(u32 crc) const CRC::Game& CRC::Lookup(u32 crc)
{ {
printf("GS Lookup CRC:%08X\n", crc); for (const Game& game : m_games)
if (m_map.empty())
{ {
std::string exclusions = Host::GetStringSettingValue("EmuCore/GS", "CrcHacksExclusions"); if (game.crc == crc)
if (exclusions.length() != 0) return game;
printf("GS: CrcHacksExclusions: %s\n", exclusions.c_str());
int crcDups = 0;
for (const Game& game : m_games)
{
if (!IsCrcExcluded(exclusions, game.crc))
{
if (m_map[game.crc])
{
printf("[FIXME] GS: Duplicate CRC: 0x%08X: (game-id/region-id) %d/%d overrides %d/%d\n", game.crc, game.title, game.region, m_map[game.crc]->title, m_map[game.crc]->region);
crcDups++;
}
m_map[game.crc] = &game;
}
//else
// printf( "GS: excluding CRC hack for 0x%08x\n", game.crc );
}
if (crcDups)
printf("[FIXME] GS: Duplicate CRC: Overall: %d\n", crcDups);
}
auto i = m_map.find(crc);
if (i != m_map.end())
{
return *i->second;
} }
return m_games[0]; return m_games[0];

View File

@ -20,97 +20,29 @@
class CRC class CRC
{ {
public: public:
enum Title : u16 enum Title : u32
{ {
NoTitle, NoTitle,
AceCombat4,
ArTonelico2,
BigMuthaTruckers,
Black,
BurnoutGames,
CrashBandicootWoC,
DBZBT2,
DBZBT3,
DeathByDegreesTekkenNinaWilliams,
FFX,
FFX2, FFX2,
FFXII,
FightingBeautyWulong,
GetawayGames, GetawayGames,
GiTS,
GodHand,
HauntingGround, HauntingGround,
ICO, ICO,
Jak2,
Jak3,
JakX,
KnightsOfTheTemple2,
KOF2002, KOF2002,
Kunoichi,
Lamune,
Manhunt2,
MetalSlug6,
MidnightClub3,
Okami,
Oneechanbara2Special,
PolyphonyDigitalGames, PolyphonyDigitalGames,
RedDeadRevolver,
RozenMaidenGebetGarden,
SacredBlaze,
SakuraTaisen,
SakuraWarsSoLongMyLove,
SFEX3,
ShinOnimusha,
Simple2000Vol114,
SMTNocturne, SMTNocturne,
SonicUnleashed,
Spartan,
SteambotChronicles,
SuperManReturns,
SVCChaos, SVCChaos,
TalesOfAbyss,
TalesOfLegendia,
TalesofSymphonia,
Tekken5, Tekken5,
TombRaiderAnniversary,
TombRaiderLegend,
TombRaiderUnderworld,
UltramanFightingEvolution,
UrbanReign,
XenosagaE3,
YakuzaGames,
ZettaiZetsumeiToshi2,
TitleCount, TitleCount,
}; };
enum Region : u8
{
NoRegion,
US,
EU,
JP,
RU,
FR,
DE,
IT,
ES,
CH,
ASIA,
KO,
RegionCount,
};
struct Game struct Game
{ {
u32 crc; u32 crc;
Title title; Title title;
Region region;
u32 flags;
}; };
private: private:
static const Game m_games[]; static const Game m_games[];
static std::map<u32, const Game*> m_map;
public: public:
static const Game& Lookup(u32 crc); static const Game& Lookup(u32 crc);

View File

@ -2751,10 +2751,15 @@ int GSState::Defrost(const freezeData* fd)
return 0; return 0;
} }
void GSState::SetGameCRC(u32 crc, CRCHackLevel level) void GSState::SetGameCRC(u32 crc)
{ {
m_crc = crc; m_crc = crc;
m_game = CRC::Lookup((level != CRCHackLevel::Off) ? crc : 0); UpdateCRCHacks();
}
void GSState::UpdateCRCHacks()
{
m_game = CRC::Lookup((GSConfig.CRCHack != CRCHackLevel::Off) ? m_crc : 0);
} }
// //

View File

@ -365,7 +365,8 @@ public:
int Defrost(const freezeData* fd); int Defrost(const freezeData* fd);
u32 GetGameCRC() const { return m_crc; } u32 GetGameCRC() const { return m_crc; }
virtual void SetGameCRC(u32 crc, CRCHackLevel level); virtual void SetGameCRC(u32 crc);
virtual void UpdateCRCHacks();
u8* GetRegsMem() const { return reinterpret_cast<u8*>(m_regs); } u8* GetRegsMem() const { return reinterpret_cast<u8*>(m_regs); }
void SetRegsMem(u8* basemem) { m_regs = reinterpret_cast<GSPrivRegSet*>(basemem); } void SetRegsMem(u8* basemem) { m_regs = reinterpret_cast<GSPrivRegSet*>(basemem); }

View File

@ -145,11 +145,8 @@ bool GSUtil::HasCompatibleBits(u32 spsm, u32 dpsm)
return (s_maps.CompatibleBitsField[spsm][dpsm >> 5] & (1 << (dpsm & 0x1f))) != 0; return (s_maps.CompatibleBitsField[spsm][dpsm >> 5] & (1 << (dpsm & 0x1f))) != 0;
} }
CRCHackLevel GSUtil::GetEffectiveCRCHackLevel(GSRendererType type, CRCHackLevel level) CRCHackLevel GSUtil::GetRecommendedCRCHackLevel(GSRendererType type)
{ {
if (level != CRCHackLevel::Automatic)
return level;
return (type == GSRendererType::DX11 || type == GSRendererType::DX12) ? CRCHackLevel::Full : CRCHackLevel::Partial; return (type == GSRendererType::DX11 || type == GSRendererType::DX12) ? CRCHackLevel::Full : CRCHackLevel::Partial;
} }

View File

@ -33,7 +33,7 @@ public:
static bool HasSharedBits(u32 sbp, u32 spsm, u32 dbp, u32 dpsm); static bool HasSharedBits(u32 sbp, u32 spsm, u32 dbp, u32 dpsm);
static bool HasCompatibleBits(u32 spsm, u32 dpsm); static bool HasCompatibleBits(u32 spsm, u32 dpsm);
static CRCHackLevel GetEffectiveCRCHackLevel(GSRendererType type, CRCHackLevel level); static CRCHackLevel GetRecommendedCRCHackLevel(GSRendererType type);
static GSRendererType GetPreferredRenderer(); static GSRendererType GetPreferredRenderer();
}; };

View File

@ -780,6 +780,59 @@ bool GSHwHack::GSC_XenosagaE3(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
return true; return true;
} }
bool GSHwHack::OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
const size_t n_vertices = r.m_vertex.next;
const int w = r.m_r.width();
const int h = r.m_r.height();
const bool is_copy = !r.PRIM->ABE || (
r.m_context->ALPHA.A == r.m_context->ALPHA.B // (A - B) == 0 in blending equation, makes C value irrelevant.
&& r.m_context->ALPHA.D == 0 // Copy source RGB(A) color into frame buffer.
);
if (r.m_vt.m_primclass == GS_POINT_CLASS && w <= 64 // Small draws.
&& h <= 64 // Small draws.
&& n_vertices <= 256 // Small draws.
&& is_copy // Copy (no blending).
&& !r.PRIM->TME // No texturing please.
&& r.m_context->FRAME.PSM == PSM_PSMCT32 // Only 32-bit pixel format (CLUT format).
&& !r.PRIM->FGE // No FOG.
&& !r.PRIM->AA1 // No antialiasing.
&& !r.PRIM->FIX // Normal fragment value control.
&& !r.m_env.DTHE.DTHE // No dithering.
&& !r.m_context->TEST.ATE // No alpha test.
&& !r.m_context->TEST.DATE // No destination alpha test.
&& (!r.m_context->DepthRead() && !r.m_context->DepthWrite()) // No depth handling.
&& !r.m_context->TEX0.CSM // No CLUT usage.
&& !r.m_env.PABE.PABE // No PABE.
&& r.m_context->FBA.FBA == 0 // No Alpha Correction.
&& r.m_context->FRAME.FBMSK == 0 // No frame buffer masking.
)
{
const u32 FBP = r.m_context->FRAME.Block();
const u32 FBW = r.m_context->FRAME.FBW;
GL_INS("PointListPalette - m_r = <%d, %d => %d, %d>, n_vertices = %zu, FBP = 0x%x, FBW = %u", r.m_r.x, r.m_r.y, r.m_r.z, r.m_r.w, n_vertices, FBP, FBW);
const GSVertex* RESTRICT v = r.m_vertex.buff;
const int ox(r.m_context->XYOFFSET.OFX);
const int oy(r.m_context->XYOFFSET.OFY);
for (size_t i = 0; i < n_vertices; ++i)
{
const GSVertex& vi = v[i];
const GIFRegXYZ& xyz = vi.XYZ;
const int x = (int(xyz.X) - ox) / 16;
const int y = (int(xyz.Y) - oy) / 16;
if (x < r.m_r.x || x > r.m_r.z)
continue;
if (y < r.m_r.y || y > r.m_r.w)
continue;
const u32 c = vi.RGBAQ.U32[0];
r.m_mem.WritePixel32(x, y, c, FBP, FBW);
}
r.m_tc->InvalidateVideoMem(r.m_context->offset.fb, r.m_r);
return false;
}
return true;
}
bool GSHwHack::OI_BigMuthaTruckers(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t) bool GSHwHack::OI_BigMuthaTruckers(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{ {
// Rendering pattern: // Rendering pattern:
@ -1085,7 +1138,7 @@ bool GSHwHack::OI_JakGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSText
bool GSHwHack::OI_BurnoutGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t) bool GSHwHack::OI_BurnoutGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{ {
if (!r.OI_PointListPalette(r, rt, ds, t)) if (!OI_PointListPalette(r, rt, ds, t))
return false; // Render point list palette. return false; // Render point list palette.
if (t && t->m_from_target) // Avoid slow framebuffer readback if (t && t->m_from_target) // Avoid slow framebuffer readback
@ -1127,130 +1180,152 @@ void GSHwHack::OO_BurnoutGames(GSRendererHW& r)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_gsc_functions[] = { #define CRC_F(name, level) { #name, &GSHwHack::name, level }
{CRC::GodHand, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_GodHand},
{CRC::KnightsOfTheTemple2, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_KnightsOfTheTemple2}, const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_get_skip_count_functions[] = {
{CRC::Kunoichi, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_Kunoichi}, CRC_F(GSC_GodHand, CRCHackLevel::Partial),
{CRC::Manhunt2, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_Manhunt2}, CRC_F(GSC_KnightsOfTheTemple2, CRCHackLevel::Partial),
{CRC::MidnightClub3, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_MidnightClub3}, CRC_F(GSC_Kunoichi, CRCHackLevel::Partial),
{CRC::SacredBlaze, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_SacredBlaze}, CRC_F(GSC_Manhunt2, CRCHackLevel::Partial),
{CRC::SakuraTaisen, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_SakuraTaisen}, CRC_F(GSC_MidnightClub3, CRCHackLevel::Partial),
{CRC::SakuraWarsSoLongMyLove, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_SakuraWarsSoLongMyLove}, CRC_F(GSC_SacredBlaze, CRCHackLevel::Partial),
{CRC::Simple2000Vol114, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_Simple2000Vol114}, CRC_F(GSC_SakuraTaisen, CRCHackLevel::Partial),
{CRC::SFEX3, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_SFEX3}, CRC_F(GSC_SakuraWarsSoLongMyLove, CRCHackLevel::Partial),
{CRC::TalesOfLegendia, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_TalesOfLegendia}, CRC_F(GSC_Simple2000Vol114, CRCHackLevel::Partial),
{CRC::TalesofSymphonia, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_TalesofSymphonia}, CRC_F(GSC_SFEX3, CRCHackLevel::Partial),
{CRC::TombRaiderAnniversary, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_TombRaiderAnniversary}, CRC_F(GSC_TalesOfLegendia, CRCHackLevel::Partial),
{CRC::TombRaiderLegend, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_TombRaiderLegend}, CRC_F(GSC_TalesofSymphonia, CRCHackLevel::Partial),
{CRC::TombRaiderUnderworld, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_TombRaiderUnderWorld}, CRC_F(GSC_TombRaiderAnniversary, CRCHackLevel::Partial),
{CRC::UrbanReign, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_UrbanReign}, CRC_F(GSC_TombRaiderLegend, CRCHackLevel::Partial),
{CRC::ZettaiZetsumeiToshi2, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_ZettaiZetsumeiToshi2}, CRC_F(GSC_TombRaiderUnderWorld, CRCHackLevel::Partial),
CRC_F(GSC_UrbanReign, CRCHackLevel::Partial),
CRC_F(GSC_ZettaiZetsumeiToshi2, CRCHackLevel::Partial),
// Channel Effect // Channel Effect
{CRC::CrashBandicootWoC, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_CrashBandicootWoC}, CRC_F(GSC_CrashBandicootWoC, CRCHackLevel::Partial),
{CRC::GiTS, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_GiTS}, CRC_F(GSC_GiTS, CRCHackLevel::Partial),
{CRC::Spartan, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_Spartan}, CRC_F(GSC_Spartan, CRCHackLevel::Partial),
{CRC::SteambotChronicles, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_SteambotChronicles}, CRC_F(GSC_SteambotChronicles, CRCHackLevel::Partial),
// Depth Issue // Depth Issue
{CRC::BurnoutGames, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_BurnoutGames}, CRC_F(GSC_BurnoutGames, CRCHackLevel::Partial),
// Half Screen bottom issue // Half Screen bottom issue
{CRC::Tekken5, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_Tekken5}, CRC_F(GSC_Tekken5, CRCHackLevel::Partial),
// Texture shuffle // Texture shuffle
{CRC::BigMuthaTruckers, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_BigMuthaTruckers}, CRC_F(GSC_BigMuthaTruckers, CRCHackLevel::Partial),
{CRC::DeathByDegreesTekkenNinaWilliams, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_DeathByDegreesTekkenNinaWilliams}, // + Upscaling issues CRC_F(GSC_DeathByDegreesTekkenNinaWilliams, CRCHackLevel::Partial), // + Upscaling issues
// Upscaling hacks // Upscaling hacks
{CRC::FightingBeautyWulong, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_FightingBeautyWulong}, CRC_F(GSC_FightingBeautyWulong, CRCHackLevel::Partial),
{CRC::Oneechanbara2Special, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_Oneechanbara2Special}, CRC_F(GSC_Oneechanbara2Special, CRCHackLevel::Partial),
{CRC::UltramanFightingEvolution, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_UltramanFightingEvolution}, CRC_F(GSC_UltramanFightingEvolution, CRCHackLevel::Partial),
{CRC::YakuzaGames, CRC::RegionCount, CRCHackLevel::Partial, &GSHwHack::GSC_YakuzaGames}, CRC_F(GSC_YakuzaGames, CRCHackLevel::Partial),
// Accurate Blending // Accurate Blending
{CRC::GetawayGames, CRC::RegionCount, CRCHackLevel::Full, &GSHwHack::GSC_GetawayGames}, // Blending High CRC_F(GSC_GetawayGames, CRCHackLevel::Full), // Blending High
{CRC::AceCombat4, CRC::RegionCount, CRCHackLevel::Aggressive, &GSHwHack::GSC_AceCombat4}, CRC_F(GSC_AceCombat4, CRCHackLevel::Aggressive),
{CRC::FFX2, CRC::RegionCount, CRCHackLevel::Aggressive, &GSHwHack::GSC_FFXGames}, CRC_F(GSC_FFXGames, CRCHackLevel::Aggressive),
{CRC::FFX, CRC::RegionCount, CRCHackLevel::Aggressive, &GSHwHack::GSC_FFXGames}, CRC_F(GSC_FFXGames, CRCHackLevel::Aggressive),
{CRC::FFXII, CRC::RegionCount, CRCHackLevel::Aggressive, &GSHwHack::GSC_FFXGames}, CRC_F(GSC_FFXGames, CRCHackLevel::Aggressive),
{CRC::RedDeadRevolver, CRC::RegionCount, CRCHackLevel::Aggressive, &GSHwHack::GSC_RedDeadRevolver}, CRC_F(GSC_RedDeadRevolver, CRCHackLevel::Aggressive),
{CRC::ShinOnimusha, CRC::RegionCount, CRCHackLevel::Aggressive, &GSHwHack::GSC_ShinOnimusha}, CRC_F(GSC_ShinOnimusha, CRCHackLevel::Aggressive),
{CRC::XenosagaE3, CRC::RegionCount, CRCHackLevel::Aggressive, &GSHwHack::GSC_XenosagaE3}, CRC_F(GSC_XenosagaE3, CRCHackLevel::Aggressive),
// Upscaling issues // Upscaling issues
{CRC::Okami, CRC::RegionCount, CRCHackLevel::Aggressive, &GSHwHack::GSC_Okami}, CRC_F(GSC_Okami, CRCHackLevel::Aggressive),
}; };
const GSHwHack::Entry<GSRendererHW::OI_Ptr> GSHwHack::s_oi_functions[] = { const GSHwHack::Entry<GSRendererHW::OI_Ptr> GSHwHack::s_before_draw_functions[] = {
{CRC::BigMuthaTruckers, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_BigMuthaTruckers}, CRC_F(OI_PointListPalette, CRCHackLevel::Minimum),
{CRC::DBZBT2, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_DBZBTGames}, CRC_F(OI_BigMuthaTruckers, CRCHackLevel::Minimum),
{CRC::DBZBT3, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_DBZBTGames}, CRC_F(OI_DBZBTGames, CRCHackLevel::Minimum),
{CRC::FFXII, CRC::EU, CRCHackLevel::Minimum, &GSHwHack::OI_FFXII}, CRC_F(OI_FFXII, CRCHackLevel::Minimum),
{CRC::FFX, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_FFX}, CRC_F(OI_FFX, CRCHackLevel::Minimum),
{CRC::MetalSlug6, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_MetalSlug6}, CRC_F(OI_MetalSlug6, CRCHackLevel::Minimum),
{CRC::RozenMaidenGebetGarden, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_RozenMaidenGebetGarden}, CRC_F(OI_RozenMaidenGebetGarden, CRCHackLevel::Minimum),
{CRC::SonicUnleashed, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_SonicUnleashed}, CRC_F(OI_SonicUnleashed, CRCHackLevel::Minimum),
{CRC::ArTonelico2, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_ArTonelico2}, CRC_F(OI_ArTonelico2, CRCHackLevel::Minimum),
{CRC::Jak2, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_JakGames}, CRC_F(OI_JakGames, CRCHackLevel::Minimum),
{CRC::Jak3, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_JakGames}, CRC_F(OI_BurnoutGames, CRCHackLevel::Minimum),
{CRC::JakX, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_JakGames},
{CRC::BurnoutGames, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_BurnoutGames},
{CRC::Black, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OI_BurnoutGames}};
const GSHwHack::Entry<GSRendererHW::OO_Ptr> GSHwHack::s_oo_functions[] = {
{CRC::BurnoutGames, CRC::RegionCount, CRCHackLevel::Minimum, &GSHwHack::OO_BurnoutGames},
}; };
void GSRendererHW::SetupCrcHack(CRCHackLevel level) const GSHwHack::Entry<GSRendererHW::OO_Ptr> GSHwHack::s_after_draw_functions[] = {
CRC_F(OO_BurnoutGames, CRCHackLevel::Minimum),
};
#undef CRC_F
s16 GSLookupGetSkipCountFunctionId(const std::string_view& name)
{ {
for (u32 i = 0; i < std::size(GSHwHack::s_get_skip_count_functions); i++)
{
if (name == GSHwHack::s_get_skip_count_functions[i].name)
return static_cast<s16>(i);
}
return -1;
}
s16 GSLookupBeforeDrawFunctionId(const std::string_view& name)
{
for (u32 i = 0; i < std::size(GSHwHack::s_before_draw_functions); i++)
{
if (name == GSHwHack::s_before_draw_functions[i].name)
return static_cast<s16>(i);
}
return -1;
}
s16 GSLookupAfterDrawFunctionId(const std::string_view& name)
{
for (u32 i = 0; i < std::size(GSHwHack::s_after_draw_functions); i++)
{
if (name == GSHwHack::s_after_draw_functions[i].name)
return static_cast<s16>(i);
}
return -1;
}
void GSRendererHW::UpdateCRCHacks()
{
GSRenderer::UpdateCRCHacks();
const CRCHackLevel real_level = (GSConfig.CRCHack == CRCHackLevel::Automatic) ?
GSUtil::GetRecommendedCRCHackLevel(GSConfig.Renderer) : GSConfig.CRCHack;
s_nativeres = m_nativeres; s_nativeres = m_nativeres;
s_crc_hack_level = level; s_crc_hack_level = real_level;
m_gsc = nullptr; m_gsc = nullptr;
if (level != CRCHackLevel::Off)
{
for (const auto& entry : GSHwHack::s_gsc_functions)
{
if (entry.Test(m_game.title, m_game.region, level))
{
m_gsc = entry.ptr;
break;
}
}
}
m_oi = nullptr; m_oi = nullptr;
if (level != CRCHackLevel::Off)
{
for (const auto& entry : GSHwHack::s_oi_functions)
{
if (entry.Test(m_game.title, m_game.region, level))
{
m_oi = entry.ptr;
break;
}
}
}
if (GSConfig.PointListPalette)
{
if (m_oi)
Console.Warning("Overriding m_oi with PointListPalette");
m_oi = &GSRendererHW::OI_PointListPalette;
}
m_oo = nullptr; m_oo = nullptr;
if (level != CRCHackLevel::Off)
if (real_level != CRCHackLevel::Off)
{ {
for (const auto& entry : GSHwHack::s_oo_functions) if (GSConfig.GetSkipCountFunctionId >= 0 &&
static_cast<size_t>(GSConfig.GetSkipCountFunctionId) < std::size(GSHwHack::s_get_skip_count_functions) &&
real_level >= GSHwHack::s_get_skip_count_functions[GSConfig.GetSkipCountFunctionId].level)
{ {
if (entry.Test(m_game.title, m_game.region, level)) m_gsc = GSHwHack::s_get_skip_count_functions[GSConfig.GetSkipCountFunctionId].ptr;
{ }
m_oo = entry.ptr;
break; if (GSConfig.BeforeDrawFunctionId >= 0 &&
} static_cast<size_t>(GSConfig.BeforeDrawFunctionId) < std::size(GSHwHack::s_before_draw_functions) &&
real_level >= GSHwHack::s_before_draw_functions[GSConfig.BeforeDrawFunctionId].level)
{
m_oi = GSHwHack::s_before_draw_functions[GSConfig.BeforeDrawFunctionId].ptr;
}
if (GSConfig.AfterDrawFunctionId >= 0 &&
static_cast<size_t>(GSConfig.AfterDrawFunctionId) < std::size(GSHwHack::s_after_draw_functions) &&
real_level >= GSHwHack::s_after_draw_functions[GSConfig.AfterDrawFunctionId].level)
{
m_oo = GSHwHack::s_after_draw_functions[GSConfig.AfterDrawFunctionId].ptr;
} }
} }
} }

View File

@ -55,6 +55,7 @@ public:
static bool GSC_ShinOnimusha(GSRendererHW& r, const GSFrameInfo& fi, int& skip); static bool GSC_ShinOnimusha(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
static bool GSC_XenosagaE3(GSRendererHW& r, const GSFrameInfo& fi, int& skip); static bool GSC_XenosagaE3(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
static bool OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_BigMuthaTruckers(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t); static bool OI_BigMuthaTruckers(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_DBZBTGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t); static bool OI_DBZBTGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_FFXII(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t); static bool OI_FFXII(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
@ -71,18 +72,12 @@ public:
template <typename F> template <typename F>
struct Entry struct Entry
{ {
CRC::Title game; const char* name;
CRC::Region region;
CRCHackLevel level;
F ptr; F ptr;
CRCHackLevel level;
__fi bool Test(CRC::Title title_, CRC::Region region_, CRCHackLevel level_) const
{
return (game == title_ && (region == CRC::RegionCount || region == region_) && level_ >= level);
}
}; };
static const Entry<GSRendererHW::GSC_Ptr> s_gsc_functions[]; static const Entry<GSRendererHW::GSC_Ptr> s_get_skip_count_functions[];
static const Entry<GSRendererHW::OI_Ptr> s_oi_functions[]; static const Entry<GSRendererHW::OI_Ptr> s_before_draw_functions[];
static const Entry<GSRendererHW::OO_Ptr> s_oo_functions[]; static const Entry<GSRendererHW::OO_Ptr> s_after_draw_functions[];
}; };

View File

@ -159,11 +159,9 @@ bool GSRendererHW::IsPossibleTextureShuffle(GSTextureCache::Target* dst, const G
GSLocalMemory::m_psm[m_context->FRAME.PSM].bpp == 16); GSLocalMemory::m_psm[m_context->FRAME.PSM].bpp == 16);
} }
void GSRendererHW::SetGameCRC(u32 crc, CRCHackLevel level) void GSRendererHW::SetGameCRC(u32 crc)
{ {
GSRenderer::SetGameCRC(crc, level); GSRenderer::SetGameCRC(crc);
SetupCrcHack(level);
GSTextureReplacements::GameChanged(); GSTextureReplacements::GameChanged();
} }
@ -4369,56 +4367,3 @@ bool GSRendererHW::OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Sourc
// Nothing to see keep going // Nothing to see keep going
return true; return true;
} }
bool GSRendererHW::OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
const size_t n_vertices = r.m_vertex.next;
const int w = r.m_r.width();
const int h = r.m_r.height();
const bool is_copy = !r.PRIM->ABE || (
r.m_context->ALPHA.A == r.m_context->ALPHA.B // (A - B) == 0 in blending equation, makes C value irrelevant.
&& r.m_context->ALPHA.D == 0 // Copy source RGB(A) color into frame buffer.
);
if (r.m_vt.m_primclass == GS_POINT_CLASS && w <= 64 // Small draws.
&& h <= 64 // Small draws.
&& n_vertices <= 256 // Small draws.
&& is_copy // Copy (no blending).
&& !r.PRIM->TME // No texturing please.
&& r.m_context->FRAME.PSM == PSM_PSMCT32 // Only 32-bit pixel format (CLUT format).
&& !r.PRIM->FGE // No FOG.
&& !r.PRIM->AA1 // No antialiasing.
&& !r.PRIM->FIX // Normal fragment value control.
&& !r.m_env.DTHE.DTHE // No dithering.
&& !r.m_context->TEST.ATE // No alpha test.
&& !r.m_context->TEST.DATE // No destination alpha test.
&& (!r.m_context->DepthRead() && !r.m_context->DepthWrite()) // No depth handling.
&& !r.m_context->TEX0.CSM // No CLUT usage.
&& !r.m_env.PABE.PABE // No PABE.
&& r.m_context->FBA.FBA == 0 // No Alpha Correction.
&& r.m_context->FRAME.FBMSK == 0 // No frame buffer masking.
)
{
const u32 FBP = r.m_context->FRAME.Block();
const u32 FBW = r.m_context->FRAME.FBW;
GL_INS("PointListPalette - m_r = <%d, %d => %d, %d>, n_vertices = %zu, FBP = 0x%x, FBW = %u", r.m_r.x, r.m_r.y, r.m_r.z, r.m_r.w, n_vertices, FBP, FBW);
const GSVertex* RESTRICT v = r.m_vertex.buff;
const int ox(r.m_context->XYOFFSET.OFX);
const int oy(r.m_context->XYOFFSET.OFY);
for (size_t i = 0; i < n_vertices; ++i)
{
const GSVertex& vi = v[i];
const GIFRegXYZ& xyz = vi.XYZ;
const int x = (int(xyz.X) - ox) / 16;
const int y = (int(xyz.Y) - oy) / 16;
if (x < r.m_r.x || x > r.m_r.z)
continue;
if (y < r.m_r.y || y > r.m_r.w)
continue;
const u32 c = vi.RGBAQ.U32[0];
r.m_mem.WritePixel32(x, y, c, FBP, FBW);
}
r.m_tc->InvalidateVideoMem(r.m_context->offset.fb, r.m_r);
return false;
}
return true;
}

View File

@ -60,7 +60,6 @@ private:
bool OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Source* t, const GSVector4i& r_draw); bool OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Source* t, const GSVector4i& r_draw);
bool OI_GsMemClear(); // always on bool OI_GsMemClear(); // always on
void OI_DoubleHalfClear(GSTextureCache::Target*& rt, GSTextureCache::Target*& ds); // always on void OI_DoubleHalfClear(GSTextureCache::Target*& rt, GSTextureCache::Target*& ds); // always on
static bool OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
u16 Interpolate_UV(float alpha, int t0, int t1); u16 Interpolate_UV(float alpha, int t0, int t1);
float alpha0(int L, int X0, int X1); float alpha0(int L, int X0, int X1);
@ -95,8 +94,6 @@ private:
// CRC Hacks // CRC Hacks
bool IsBadFrame(); bool IsBadFrame();
void SetupCrcHack(CRCHackLevel level);
GSC_Ptr m_gsc = nullptr; GSC_Ptr m_gsc = nullptr;
OI_Ptr m_oi = nullptr; OI_Ptr m_oi = nullptr;
OO_Ptr m_oo = nullptr; OO_Ptr m_oo = nullptr;
@ -128,7 +125,9 @@ public:
void Destroy() override; void Destroy() override;
void SetGameCRC(u32 crc, CRCHackLevel level) override; void SetGameCRC(u32 crc) override;
void UpdateCRCHacks() override;
bool CanUpscale() override; bool CanUpscale() override;
float GetUpscaleMultiplier() override; float GetUpscaleMultiplier() override;
void Lines2Sprites(); void Lines2Sprites();

View File

@ -16,6 +16,7 @@
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "GameDatabase.h" #include "GameDatabase.h"
#include "GS/GS.h"
#include "Host.h" #include "Host.h"
#include "vtlb.h" #include "vtlb.h"
@ -240,12 +241,30 @@ void GameDatabase::parseAndInsert(const std::string_view& serial, const c4::yml:
{ {
const std::string_view id_name(n.key().data(), n.key().size()); const std::string_view id_name(n.key().data(), n.key().size());
std::optional<GameDatabaseSchema::GSHWFixId> id = GameDatabaseSchema::parseHWFixName(id_name); 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; std::optional<s32> value;
if (id.has_value() && (id.value() == GameDatabaseSchema::GSHWFixId::GetSkipCount || id.value() == GameDatabaseSchema::GSHWFixId::BeforeDraw || id.value() == GameDatabaseSchema::GSHWFixId::AfterDraw))
{
const std::string_view str_value(n.has_val() ? std::string_view(n.val().data(), n.val().size()) : std::string_view());
if (id.value() == GameDatabaseSchema::GSHWFixId::GetSkipCount)
value = GSLookupGetSkipCountFunctionId(str_value);
else if (id.value() == GameDatabaseSchema::GSHWFixId::BeforeDraw)
value = GSLookupBeforeDrawFunctionId(str_value);
else if (id.value() == GameDatabaseSchema::GSHWFixId::AfterDraw)
value = GSLookupAfterDrawFunctionId(str_value);
if (value.value_or(-1) < 0)
{
Console.Error(fmt::format("[GameDB] Invalid GS HW Fix Value for '{}' in '{}': '{}'", id_name, serial, str_value));
continue;
}
}
else
{
value = n.has_val() ? StringUtil::FromChars<s32>(std::string_view(n.val().data(), n.val().size())) : 1;
}
if (!id.has_value() || !value.has_value()) if (!id.has_value() || !value.has_value())
{ {
Console.Error("[GameDB] Invalid GS HW Fix: '%.*s' specified for serial '%.*s'. Dropping!", Console.Error(fmt::format("[GameDB] Invalid GS HW Fix: '{}' specified for serial '{}'. Dropping!", id_name, serial));
static_cast<int>(id_name.size()), id_name.data(),
static_cast<int>(serial.size()), serial.data());
continue; continue;
} }
@ -333,7 +352,6 @@ static const char* s_gs_hw_fix_names[] = {
"alignSprite", "alignSprite",
"mergeSprite", "mergeSprite",
"wildArmsHack", "wildArmsHack",
"pointListPalette",
"mipmap", "mipmap",
"trilinearFiltering", "trilinearFiltering",
"skipDrawStart", "skipDrawStart",
@ -346,6 +364,9 @@ static const char* s_gs_hw_fix_names[] = {
"cpuSpriteRenderBW", "cpuSpriteRenderBW",
"cpuCLUTRender", "cpuCLUTRender",
"gpuPaletteConversion", "gpuPaletteConversion",
"getSkipCount",
"beforeDraw",
"afterDraw"
}; };
static_assert(std::size(s_gs_hw_fix_names) == static_cast<u32>(GameDatabaseSchema::GSHWFixId::Count), "HW fix name lookup is correct size"); static_assert(std::size(s_gs_hw_fix_names) == static_cast<u32>(GameDatabaseSchema::GSHWFixId::Count), "HW fix name lookup is correct size");
@ -372,8 +393,10 @@ bool GameDatabaseSchema::isUserHackHWFix(GSHWFixId id)
case GSHWFixId::Deinterlace: case GSHWFixId::Deinterlace:
case GSHWFixId::Mipmap: case GSHWFixId::Mipmap:
case GSHWFixId::TexturePreloading: case GSHWFixId::TexturePreloading:
case GSHWFixId::PointListPalette:
case GSHWFixId::TrilinearFiltering: case GSHWFixId::TrilinearFiltering:
case GSHWFixId::GetSkipCount:
case GSHWFixId::BeforeDraw:
case GSHWFixId::AfterDraw:
return false; return false;
default: default:
return true; return true;
@ -552,9 +575,6 @@ bool GameDatabaseSchema::GameEntry::configMatchesHWFix(const Pcsx2Config::GSOpti
case GSHWFixId::WildArmsHack: case GSHWFixId::WildArmsHack:
return (config.UpscaleMultiplier <= 1.0f || static_cast<int>(config.UserHacks_WildHack) == value); return (config.UpscaleMultiplier <= 1.0f || static_cast<int>(config.UserHacks_WildHack) == value);
case GSHWFixId::PointListPalette:
return (static_cast<int>(config.PointListPalette) == value);
case GSHWFixId::Mipmap: case GSHWFixId::Mipmap:
return (config.HWMipmap == HWMipmapLevel::Automatic || static_cast<int>(config.HWMipmap) == value); return (config.HWMipmap == HWMipmapLevel::Automatic || static_cast<int>(config.HWMipmap) == value);
@ -591,6 +611,15 @@ bool GameDatabaseSchema::GameEntry::configMatchesHWFix(const Pcsx2Config::GSOpti
case GSHWFixId::GPUPaletteConversion: case GSHWFixId::GPUPaletteConversion:
return (config.GPUPaletteConversion == ((value > 1) ? (config.TexturePreloading == TexturePreloadingLevel::Full) : (value != 0))); return (config.GPUPaletteConversion == ((value > 1) ? (config.TexturePreloading == TexturePreloadingLevel::Full) : (value != 0)));
case GSHWFixId::GetSkipCount:
return (static_cast<int>(config.GetSkipCountFunctionId) == value);
case GSHWFixId::BeforeDraw:
return (static_cast<int>(config.BeforeDrawFunctionId) == value);
case GSHWFixId::AfterDraw:
return (static_cast<int>(config.AfterDrawFunctionId) == value);
default: default:
return false; return false;
} }
@ -660,10 +689,6 @@ u32 GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions&
config.UserHacks_WildHack = (value > 0); config.UserHacks_WildHack = (value > 0);
break; break;
case GSHWFixId::PointListPalette:
config.PointListPalette = (value > 0);
break;
case GSHWFixId::Mipmap: case GSHWFixId::Mipmap:
{ {
if (value >= 0 && value <= static_cast<int>(HWMipmapLevel::Full)) if (value >= 0 && value <= static_cast<int>(HWMipmapLevel::Full))
@ -745,6 +770,18 @@ u32 GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions&
} }
break; break;
case GSHWFixId::GetSkipCount:
config.GetSkipCountFunctionId = static_cast<s16>(value);
break;
case GSHWFixId::BeforeDraw:
config.BeforeDrawFunctionId = static_cast<s16>(value);
break;
case GSHWFixId::AfterDraw:
config.AfterDrawFunctionId = static_cast<s16>(value);
break;
default: default:
break; break;
} }

View File

@ -70,7 +70,6 @@ namespace GameDatabaseSchema
AlignSprite, AlignSprite,
MergeSprite, MergeSprite,
WildArmsHack, WildArmsHack,
PointListPalette,
// integer settings // integer settings
Mipmap, Mipmap,
@ -85,6 +84,9 @@ namespace GameDatabaseSchema
CPUSpriteRenderBW, CPUSpriteRenderBW,
CPUCLUTRender, CPUCLUTRender,
GPUPaletteConversion, GPUPaletteConversion,
GetSkipCount,
BeforeDraw,
AfterDraw,
Count Count
}; };

View File

@ -419,7 +419,6 @@ Pcsx2Config::GSOptions::GSOptions()
PreloadFrameWithGSData = false; PreloadFrameWithGSData = false;
WrapGSMem = false; WrapGSMem = false;
Mipmap = true; Mipmap = true;
PointListPalette = false;
ManualUserHacks = false; ManualUserHacks = false;
UserHacks_AlignSpriteX = false; UserHacks_AlignSpriteX = false;
@ -494,6 +493,9 @@ bool Pcsx2Config::GSOptions::OptionsAreEqual(const GSOptions& right) const
OpEqu(SWExtraThreadsHeight) && OpEqu(SWExtraThreadsHeight) &&
OpEqu(TriFilter) && OpEqu(TriFilter) &&
OpEqu(TVShader) && OpEqu(TVShader) &&
OpEqu(GetSkipCountFunctionId) &&
OpEqu(BeforeDrawFunctionId) &&
OpEqu(AfterDrawFunctionId) &&
OpEqu(SkipDrawEnd) && OpEqu(SkipDrawEnd) &&
OpEqu(SkipDrawStart) && OpEqu(SkipDrawStart) &&