mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-18 05:05:46 -04:00
Move GameList to FrontendCommon
Reduces libretro core dependencies further.
This commit is contained in:
@ -24,10 +24,6 @@ add_library(core
|
||||
digital_controller.h
|
||||
dma.cpp
|
||||
dma.h
|
||||
game_list.cpp
|
||||
game_list.h
|
||||
game_settings.cpp
|
||||
game_settings.h
|
||||
gpu.cpp
|
||||
gpu.h
|
||||
gpu_commands.cpp
|
||||
@ -98,7 +94,7 @@ set(RECOMPILER_SRCS
|
||||
|
||||
target_include_directories(core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||
target_include_directories(core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||
target_link_libraries(core PUBLIC Threads::Threads common tinyxml2 zlib vulkan-loader simpleini)
|
||||
target_link_libraries(core PUBLIC Threads::Threads common zlib vulkan-loader)
|
||||
target_link_libraries(core PRIVATE glad stb)
|
||||
|
||||
if(WIN32)
|
||||
|
@ -3,7 +3,6 @@
|
||||
#include "common/log.h"
|
||||
#include "common/state_wrapper.h"
|
||||
#include "dma.h"
|
||||
#include "game_list.h"
|
||||
#include "interrupt_controller.h"
|
||||
#include "settings.h"
|
||||
#include "spu.h"
|
||||
@ -446,7 +445,7 @@ void CDROM::InsertMedia(std::unique_ptr<CDImage> media)
|
||||
RemoveMedia();
|
||||
|
||||
// set the region from the system area of the disc
|
||||
m_disc_region = GameList::GetRegionForImage(media.get());
|
||||
m_disc_region = System::GetRegionForImage(media.get());
|
||||
Log_InfoPrintf("Inserting new media, disc region: %s, console region: %s", Settings::GetDiscRegionName(m_disc_region),
|
||||
Settings::GetConsoleRegionName(System::GetRegion()));
|
||||
|
||||
|
@ -59,8 +59,6 @@
|
||||
<ClCompile Include="cpu_recompiler_register_cache.cpp" />
|
||||
<ClCompile Include="cpu_types.cpp" />
|
||||
<ClCompile Include="digital_controller.cpp" />
|
||||
<ClCompile Include="game_list.cpp" />
|
||||
<ClCompile Include="game_settings.cpp" />
|
||||
<ClCompile Include="gpu_commands.cpp" />
|
||||
<ClCompile Include="gpu_hw_d3d11.cpp" />
|
||||
<ClCompile Include="gpu_hw_shadergen.cpp" />
|
||||
@ -107,8 +105,6 @@
|
||||
<ClInclude Include="cpu_recompiler_thunks.h" />
|
||||
<ClInclude Include="cpu_recompiler_types.h" />
|
||||
<ClInclude Include="digital_controller.h" />
|
||||
<ClInclude Include="game_list.h" />
|
||||
<ClInclude Include="game_settings.h" />
|
||||
<ClInclude Include="gpu_hw_d3d11.h" />
|
||||
<ClInclude Include="gpu_hw_shadergen.h" />
|
||||
<ClInclude Include="gpu_hw_vulkan.h" />
|
||||
@ -147,14 +143,14 @@
|
||||
<ProjectReference Include="..\..\dep\imgui\imgui.vcxproj">
|
||||
<Project>{bb08260f-6fbc-46af-8924-090ee71360c6}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\dep\simpleini\simpleini.vcxproj">
|
||||
<Project>{3773f4cc-614e-4028-8595-22e08ca649e3}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\dep\stb\stb.vcxproj">
|
||||
<Project>{ed601289-ac1a-46b8-a8ed-17db9eb73423}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\dep\tinyxml2\tinyxml2.vcxproj">
|
||||
<Project>{933118a9-68c5-47b4-b151-b03c93961623}</Project>
|
||||
<ProjectReference Include="..\..\dep\vulkan-loader\vulkan-loader.vcxproj">
|
||||
<Project>{9c8ddeb0-2b8f-4f5f-ba86-127cdf27f035}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\dep\zlib\zlib.vcxproj">
|
||||
<Project>{7ff9fdb9-d504-47db-a16a-b08071999620}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\common\common.vcxproj">
|
||||
<Project>{ee054e08-3799-4a59-a422-18259c105ffd}</Project>
|
||||
@ -307,7 +303,7 @@
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
@ -333,7 +329,7 @@
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
@ -359,7 +355,7 @@
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
@ -388,7 +384,7 @@
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
@ -416,7 +412,7 @@
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
@ -443,7 +439,7 @@
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
@ -471,7 +467,7 @@
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
@ -498,7 +494,7 @@
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -31,7 +31,6 @@
|
||||
<ClCompile Include="cpu_recompiler_code_generator.cpp" />
|
||||
<ClCompile Include="cpu_recompiler_code_generator_generic.cpp" />
|
||||
<ClCompile Include="cpu_types.cpp" />
|
||||
<ClCompile Include="game_list.cpp" />
|
||||
<ClCompile Include="cpu_recompiler_code_generator_aarch64.cpp" />
|
||||
<ClCompile Include="sio.cpp" />
|
||||
<ClCompile Include="controller.cpp" />
|
||||
@ -47,7 +46,6 @@
|
||||
<ClCompile Include="resources.cpp" />
|
||||
<ClCompile Include="host_interface_progress_callback.cpp" />
|
||||
<ClCompile Include="pgxp.cpp" />
|
||||
<ClCompile Include="game_settings.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="types.h" />
|
||||
@ -82,7 +80,6 @@
|
||||
<ClInclude Include="cpu_recompiler_register_cache.h" />
|
||||
<ClInclude Include="cpu_recompiler_thunks.h" />
|
||||
<ClInclude Include="cpu_recompiler_code_generator.h" />
|
||||
<ClInclude Include="game_list.h" />
|
||||
<ClInclude Include="sio.h" />
|
||||
<ClInclude Include="controller.h" />
|
||||
<ClInclude Include="analog_controller.h" />
|
||||
@ -98,6 +95,5 @@
|
||||
<ClInclude Include="gte_types.h" />
|
||||
<ClInclude Include="pgxp.h" />
|
||||
<ClInclude Include="cpu_core_private.h" />
|
||||
<ClInclude Include="game_settings.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
File diff suppressed because it is too large
Load Diff
@ -1,194 +0,0 @@
|
||||
#pragma once
|
||||
#include "game_settings.h"
|
||||
#include "types.h"
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
class CDImage;
|
||||
class ByteStream;
|
||||
class ProgressCallback;
|
||||
|
||||
class SettingsInterface;
|
||||
|
||||
enum class GameListEntryType
|
||||
{
|
||||
Disc,
|
||||
PSExe,
|
||||
Playlist
|
||||
};
|
||||
|
||||
enum class GameListCompatibilityRating
|
||||
{
|
||||
Unknown = 0,
|
||||
DoesntBoot = 1,
|
||||
CrashesInIntro = 2,
|
||||
CrashesInGame = 3,
|
||||
GraphicalAudioIssues = 4,
|
||||
NoIssues = 5,
|
||||
Count,
|
||||
};
|
||||
|
||||
struct GameListDatabaseEntry
|
||||
{
|
||||
std::string code;
|
||||
std::string title;
|
||||
DiscRegion region;
|
||||
};
|
||||
|
||||
struct GameListEntry
|
||||
{
|
||||
std::string path;
|
||||
std::string code;
|
||||
std::string title;
|
||||
u64 total_size;
|
||||
u64 last_modified_time;
|
||||
DiscRegion region;
|
||||
GameListEntryType type;
|
||||
GameListCompatibilityRating compatibility_rating;
|
||||
GameSettings::Entry settings;
|
||||
};
|
||||
|
||||
struct GameListCompatibilityEntry
|
||||
{
|
||||
std::string code;
|
||||
std::string title;
|
||||
std::string version_tested;
|
||||
std::string upscaling_issues;
|
||||
std::string comments;
|
||||
DiscRegion region;
|
||||
GameListCompatibilityRating compatibility_rating;
|
||||
};
|
||||
|
||||
class GameList
|
||||
{
|
||||
public:
|
||||
using EntryList = std::vector<GameListEntry>;
|
||||
|
||||
GameList();
|
||||
~GameList();
|
||||
|
||||
static const char* EntryTypeToString(GameListEntryType type);
|
||||
static const char* EntryCompatibilityRatingToString(GameListCompatibilityRating rating);
|
||||
|
||||
/// Returns true if the filename is a PlayStation executable we can inject.
|
||||
static bool IsExeFileName(const char* path);
|
||||
|
||||
/// Returns true if the filename is a Portable Sound Format file we can uncompress/load.
|
||||
static bool IsPsfFileName(const char* path);
|
||||
|
||||
/// Returns true if the filename is a M3U Playlist we can handle.
|
||||
static bool IsM3UFileName(const char* path);
|
||||
|
||||
/// Parses an M3U playlist, returning the entries.
|
||||
static std::vector<std::string> ParseM3UFile(const char* path);
|
||||
|
||||
/// Returns a string representation of a compatibility level.
|
||||
static const char* GetGameListCompatibilityRatingString(GameListCompatibilityRating rating);
|
||||
|
||||
static std::string GetGameCodeForImage(CDImage* cdi);
|
||||
static std::string GetGameCodeForPath(const char* image_path);
|
||||
static DiscRegion GetRegionForCode(std::string_view code);
|
||||
static DiscRegion GetRegionFromSystemArea(CDImage* cdi);
|
||||
static DiscRegion GetRegionForImage(CDImage* cdi);
|
||||
static std::optional<DiscRegion> GetRegionForPath(const char* image_path);
|
||||
static std::string_view GetTitleForPath(const char* path);
|
||||
|
||||
const EntryList& GetEntries() const { return m_entries; }
|
||||
const u32 GetEntryCount() const { return static_cast<u32>(m_entries.size()); }
|
||||
|
||||
const GameListEntry* GetEntryForPath(const char* path) const;
|
||||
const GameListDatabaseEntry* GetDatabaseEntryForCode(const std::string& code) const;
|
||||
const GameListCompatibilityEntry* GetCompatibilityEntryForCode(const std::string& code) const;
|
||||
|
||||
const std::string& GetCacheFilename() const { return m_cache_filename; }
|
||||
const std::string& GetDatabaseFilename() const { return m_database_filename; }
|
||||
const std::string& GetCompatibilityFilename() const { return m_database_filename; }
|
||||
|
||||
void SetCacheFilename(std::string filename) { m_cache_filename = std::move(filename); }
|
||||
void SetDatabaseFilename(std::string filename) { m_database_filename = std::move(filename); }
|
||||
void SetCompatibilityFilename(std::string filename) { m_compatibility_list_filename = std::move(filename); }
|
||||
void SetGameSettingsFilename(std::string filename) { m_game_settings_filename = std::move(filename); }
|
||||
void SetUserGameSettingsFilename(std::string filename) { m_user_game_settings_filename = std::move(filename); }
|
||||
void SetSearchDirectoriesFromSettings(SettingsInterface& si);
|
||||
|
||||
bool IsDatabasePresent() const;
|
||||
|
||||
void AddDirectory(std::string path, bool recursive);
|
||||
void Refresh(bool invalidate_cache, bool invalidate_database, ProgressCallback* progress = nullptr);
|
||||
|
||||
void UpdateCompatibilityEntry(GameListCompatibilityEntry new_entry, bool save_to_list = true);
|
||||
|
||||
static std::string ExportCompatibilityEntry(const GameListCompatibilityEntry* entry);
|
||||
|
||||
const GameSettings::Entry* GetGameSettings(const std::string& filename, const std::string& game_code);
|
||||
void UpdateGameSettings(const std::string& filename, const std::string& game_code, const std::string& game_title,
|
||||
const GameSettings::Entry& new_entry, bool save_to_list = true, bool save_to_user = true);
|
||||
|
||||
private:
|
||||
enum : u32
|
||||
{
|
||||
GAME_LIST_CACHE_SIGNATURE = 0x45434C47,
|
||||
GAME_LIST_CACHE_VERSION = 6
|
||||
};
|
||||
|
||||
using DatabaseMap = std::unordered_map<std::string, GameListDatabaseEntry>;
|
||||
using CacheMap = std::unordered_map<std::string, GameListEntry>;
|
||||
using CompatibilityMap = std::unordered_map<std::string, GameListCompatibilityEntry>;
|
||||
|
||||
struct DirectoryEntry
|
||||
{
|
||||
std::string path;
|
||||
bool recursive;
|
||||
};
|
||||
|
||||
class RedumpDatVisitor;
|
||||
class CompatibilityListVisitor;
|
||||
|
||||
GameListEntry* GetMutableEntryForPath(const char* path);
|
||||
|
||||
static bool GetExeListEntry(const char* path, GameListEntry* entry);
|
||||
bool GetM3UListEntry(const char* path, GameListEntry* entry);
|
||||
|
||||
bool GetGameListEntry(const std::string& path, GameListEntry* entry);
|
||||
bool GetGameListEntryFromCache(const std::string& path, GameListEntry* entry);
|
||||
void ScanDirectory(const char* path, bool recursive, ProgressCallback* progress);
|
||||
|
||||
void LoadCache();
|
||||
bool LoadEntriesFromCache(ByteStream* stream);
|
||||
bool OpenCacheForWriting();
|
||||
bool WriteEntryToCache(const GameListEntry* entry, ByteStream* stream);
|
||||
void FlushCacheFileStream();
|
||||
void CloseCacheFileStream();
|
||||
void RewriteCacheFile();
|
||||
void DeleteCacheFile();
|
||||
|
||||
void LoadDatabase();
|
||||
void ClearDatabase();
|
||||
|
||||
void LoadCompatibilityList();
|
||||
bool SaveCompatibilityDatabase();
|
||||
bool SaveCompatibilityDatabaseForEntry(const GameListCompatibilityEntry* entry);
|
||||
|
||||
void LoadGameSettings();
|
||||
|
||||
DatabaseMap m_database;
|
||||
EntryList m_entries;
|
||||
CacheMap m_cache_map;
|
||||
CompatibilityMap m_compatibility_list;
|
||||
GameSettings::Database m_game_settings;
|
||||
std::unique_ptr<ByteStream> m_cache_write_stream;
|
||||
|
||||
std::vector<DirectoryEntry> m_search_directories;
|
||||
std::string m_cache_filename;
|
||||
std::string m_database_filename;
|
||||
std::string m_compatibility_list_filename;
|
||||
std::string m_game_settings_filename;
|
||||
std::string m_user_game_settings_filename;
|
||||
bool m_database_load_tried = false;
|
||||
bool m_compatibility_list_load_tried = false;
|
||||
bool m_game_settings_load_tried = false;
|
||||
};
|
@ -1,477 +0,0 @@
|
||||
#include "game_settings.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/byte_stream.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "common/string.h"
|
||||
#include "common/string_util.h"
|
||||
#include "host_interface.h"
|
||||
#include "settings.h"
|
||||
#include <array>
|
||||
#include <utility>
|
||||
Log_SetChannel(GameSettings);
|
||||
|
||||
#ifdef WIN32
|
||||
#include "common/windows_headers.h"
|
||||
#endif
|
||||
#include "SimpleIni.h"
|
||||
|
||||
namespace GameSettings {
|
||||
|
||||
std::array<std::pair<const char*, const char*>, static_cast<u32>(Trait::Count)> s_trait_names = {{
|
||||
{"ForceInterpreter", TRANSLATABLE("GameSettingsTrait", "Force Interpreter")},
|
||||
{"ForceSoftwareRenderer", TRANSLATABLE("GameSettingsTrait", "Force Software Renderer")},
|
||||
{"ForceInterlacing", TRANSLATABLE("GameSettingsTrait", "Force Interlacing")},
|
||||
{"DisableTrueColor", TRANSLATABLE("GameSettingsTrait", "Disable True Color")},
|
||||
{"DisableUpscaling", TRANSLATABLE("GameSettingsTrait", "Disable Upscaling")},
|
||||
{"DisableScaledDithering", TRANSLATABLE("GameSettingsTrait", "Disable Scaled Dithering")},
|
||||
{"DisableWidescreen", TRANSLATABLE("GameSettingsTrait", "Disable Widescreen")},
|
||||
{"DisablePGXP", TRANSLATABLE("GameSettingsTrait", "Disable PGXP")},
|
||||
{"DisablePGXPCulling", TRANSLATABLE("GameSettingsTrait", "Disable PGXP Culling")},
|
||||
{"ForcePGXPVertexCache", TRANSLATABLE("GameSettingsTrait", "Force PGXP Vertex Cache")},
|
||||
{"ForcePGXPCPUMode", TRANSLATABLE("GameSettingsTrait", "Force PGXP CPU Mode")},
|
||||
{"ForceDigitalController", TRANSLATABLE("GameSettingsTrait", "Force Digital Controller")},
|
||||
{"ForceRecompilerMemoryExceptions", TRANSLATABLE("GameSettingsTrait", "Force Recompiler Memory Exceptions")},
|
||||
{"ForceRecompilerICache", TRANSLATABLE("GameSettingsTrait", "Force Recompiler ICache")},
|
||||
}};
|
||||
|
||||
const char* GetTraitName(Trait trait)
|
||||
{
|
||||
DebugAssert(trait < Trait::Count);
|
||||
return s_trait_names[static_cast<u32>(trait)].first;
|
||||
}
|
||||
|
||||
const char* GetTraitDisplayName(Trait trait)
|
||||
{
|
||||
DebugAssert(trait < Trait::Count);
|
||||
return s_trait_names[static_cast<u32>(trait)].second;
|
||||
}
|
||||
|
||||
bool Entry::HasAnySettings() const
|
||||
{
|
||||
return traits.any() || display_active_start_offset >= 0 || display_active_end_offset > 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ReadOptionalFromStream(ByteStream* stream, std::optional<T>* dest)
|
||||
{
|
||||
bool has_value;
|
||||
if (!stream->Read2(&has_value, sizeof(has_value)))
|
||||
return false;
|
||||
|
||||
if (!has_value)
|
||||
return true;
|
||||
|
||||
T value;
|
||||
if (!stream->Read2(&value, sizeof(T)))
|
||||
return false;
|
||||
|
||||
*dest = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool WriteOptionalToStream(ByteStream* stream, const std::optional<T>& src)
|
||||
{
|
||||
const bool has_value = src.has_value();
|
||||
if (!stream->Write2(&has_value, sizeof(has_value)))
|
||||
return false;
|
||||
|
||||
if (!has_value)
|
||||
return true;
|
||||
|
||||
return stream->Write2(&src.value(), sizeof(T));
|
||||
}
|
||||
|
||||
bool Entry::LoadFromStream(ByteStream* stream)
|
||||
{
|
||||
constexpr u32 num_bytes = (static_cast<u32>(Trait::Count) + 7) / 8;
|
||||
std::array<u8, num_bytes> bits;
|
||||
|
||||
if (!stream->Read2(bits.data(), num_bytes) || !ReadOptionalFromStream(stream, &display_active_start_offset) ||
|
||||
!ReadOptionalFromStream(stream, &display_active_end_offset) ||
|
||||
!ReadOptionalFromStream(stream, &display_crop_mode) || !ReadOptionalFromStream(stream, &display_aspect_ratio) ||
|
||||
!ReadOptionalFromStream(stream, &controller_1_type) || !ReadOptionalFromStream(stream, &controller_2_type))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
traits.reset();
|
||||
for (u32 i = 0; i < static_cast<int>(Trait::Count); i++)
|
||||
{
|
||||
if ((bits[i / 8] & (1u << (i % 8))) != 0)
|
||||
AddTrait(static_cast<Trait>(i));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Entry::SaveToStream(ByteStream* stream) const
|
||||
{
|
||||
constexpr u32 num_bytes = (static_cast<u32>(Trait::Count) + 7) / 8;
|
||||
std::array<u8, num_bytes> bits;
|
||||
bits.fill(0);
|
||||
for (u32 i = 0; i < static_cast<int>(Trait::Count); i++)
|
||||
{
|
||||
if (HasTrait(static_cast<Trait>(i)))
|
||||
bits[i / 8] |= (1u << (i % 8));
|
||||
}
|
||||
|
||||
return stream->Write2(bits.data(), num_bytes) && WriteOptionalToStream(stream, display_active_start_offset) &&
|
||||
WriteOptionalToStream(stream, display_active_end_offset) && WriteOptionalToStream(stream, display_crop_mode) &&
|
||||
WriteOptionalToStream(stream, display_aspect_ratio) && WriteOptionalToStream(stream, controller_1_type) &&
|
||||
WriteOptionalToStream(stream, controller_2_type);
|
||||
}
|
||||
|
||||
static void ParseIniSection(Entry* entry, const char* section, const CSimpleIniA& ini)
|
||||
{
|
||||
for (u32 trait = 0; trait < static_cast<u32>(Trait::Count); trait++)
|
||||
{
|
||||
if (ini.GetBoolValue(section, s_trait_names[trait].first, false))
|
||||
entry->AddTrait(static_cast<Trait>(trait));
|
||||
}
|
||||
|
||||
long lvalue = ini.GetLongValue(section, "DisplayActiveStartOffset", 0);
|
||||
if (lvalue != 0)
|
||||
entry->display_active_start_offset = static_cast<s16>(lvalue);
|
||||
lvalue = ini.GetLongValue(section, "DisplayActiveEndOffset", 0);
|
||||
if (lvalue != 0)
|
||||
entry->display_active_end_offset = static_cast<s16>(lvalue);
|
||||
|
||||
const char* cvalue = ini.GetValue(section, "DisplayCropMode", nullptr);
|
||||
if (cvalue)
|
||||
entry->display_crop_mode = Settings::ParseDisplayCropMode(cvalue);
|
||||
cvalue = ini.GetValue(section, "DisplayAspectRatio", nullptr);
|
||||
if (cvalue)
|
||||
entry->display_aspect_ratio = Settings::ParseDisplayAspectRatio(cvalue);
|
||||
|
||||
cvalue = ini.GetValue(section, "Controller1Type", nullptr);
|
||||
if (cvalue)
|
||||
entry->controller_1_type = Settings::ParseControllerTypeName(cvalue);
|
||||
cvalue = ini.GetValue(section, "Controller2Type", nullptr);
|
||||
if (cvalue)
|
||||
entry->controller_2_type = Settings::ParseControllerTypeName(cvalue);
|
||||
|
||||
cvalue = ini.GetValue(section, "GPUWidescreenHack", nullptr);
|
||||
if (cvalue)
|
||||
entry->gpu_widescreen_hack = StringUtil::FromChars<bool>(cvalue);
|
||||
}
|
||||
|
||||
static void StoreIniSection(const Entry& entry, const char* section, CSimpleIniA& ini)
|
||||
{
|
||||
for (u32 trait = 0; trait < static_cast<u32>(Trait::Count); trait++)
|
||||
{
|
||||
if (entry.HasTrait(static_cast<Trait>(trait)))
|
||||
ini.SetBoolValue(section, s_trait_names[trait].first, true);
|
||||
}
|
||||
|
||||
if (entry.display_active_start_offset.has_value())
|
||||
ini.SetLongValue(section, "DisplayActiveStartOffset", entry.display_active_start_offset.value());
|
||||
|
||||
if (entry.display_active_end_offset.has_value())
|
||||
ini.SetLongValue(section, "DisplayActiveEndOffset", entry.display_active_end_offset.value());
|
||||
|
||||
if (entry.display_crop_mode.has_value())
|
||||
ini.SetValue(section, "DisplayCropMode", Settings::GetDisplayCropModeName(entry.display_crop_mode.value()));
|
||||
if (entry.display_aspect_ratio.has_value())
|
||||
{
|
||||
ini.SetValue(section, "DisplayAspectRatio",
|
||||
Settings::GetDisplayAspectRatioName(entry.display_aspect_ratio.value()));
|
||||
}
|
||||
|
||||
if (entry.controller_1_type.has_value())
|
||||
ini.SetValue(section, "Controller1Type", Settings::GetControllerTypeName(entry.controller_1_type.value()));
|
||||
if (entry.controller_2_type.has_value())
|
||||
ini.SetValue(section, "Controller2Type", Settings::GetControllerTypeName(entry.controller_2_type.value()));
|
||||
|
||||
if (entry.gpu_widescreen_hack.has_value())
|
||||
ini.SetValue(section, "GPUWidescreenHack", entry.gpu_widescreen_hack.value() ? "true" : "false");
|
||||
}
|
||||
|
||||
Database::Database() = default;
|
||||
|
||||
Database::~Database() = default;
|
||||
|
||||
const GameSettings::Entry* Database::GetEntry(const std::string& code) const
|
||||
{
|
||||
auto it = m_entries.find(code);
|
||||
return (it != m_entries.end()) ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
bool Database::Load(const char* path)
|
||||
{
|
||||
auto fp = FileSystem::OpenManagedCFile(path, "rb");
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
CSimpleIniA ini;
|
||||
SI_Error err = ini.LoadFile(fp.get());
|
||||
if (err != SI_OK)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to parse game settings ini: %d", static_cast<int>(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
std::list<CSimpleIniA::Entry> sections;
|
||||
ini.GetAllSections(sections);
|
||||
for (const CSimpleIniA::Entry& section_entry : sections)
|
||||
{
|
||||
std::string code(section_entry.pItem);
|
||||
auto it = m_entries.find(code);
|
||||
if (it != m_entries.end())
|
||||
{
|
||||
ParseIniSection(&it->second, code.c_str(), ini);
|
||||
continue;
|
||||
}
|
||||
|
||||
Entry entry;
|
||||
ParseIniSection(&entry, code.c_str(), ini);
|
||||
m_entries.emplace(std::move(code), std::move(entry));
|
||||
}
|
||||
|
||||
Log_InfoPrintf("Loaded settings for %zu games from '%s'", sections.size(), path);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Database::SetEntry(const std::string& code, const std::string& name, const Entry& entry, const char* save_path)
|
||||
{
|
||||
if (save_path)
|
||||
{
|
||||
CSimpleIniA ini;
|
||||
if (FileSystem::FileExists(save_path))
|
||||
{
|
||||
auto fp = FileSystem::OpenManagedCFile(save_path, "rb");
|
||||
if (fp)
|
||||
{
|
||||
SI_Error err = ini.LoadFile(fp.get());
|
||||
if (err != SI_OK)
|
||||
Log_ErrorPrintf("Failed to parse game settings ini: %d. Contents will be lost.", static_cast<int>(err));
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_ErrorPrintf("Failed to open existing settings ini: '%s'", save_path);
|
||||
}
|
||||
}
|
||||
|
||||
ini.Delete(code.c_str(), nullptr, false);
|
||||
ini.SetValue(code.c_str(), nullptr, nullptr, SmallString::FromFormat("# %s (%s)", code.c_str(), name.c_str()),
|
||||
false);
|
||||
StoreIniSection(entry, code.c_str(), ini);
|
||||
|
||||
const bool did_exist = FileSystem::FileExists(save_path);
|
||||
auto fp = FileSystem::OpenManagedCFile(save_path, "wb");
|
||||
if (fp)
|
||||
{
|
||||
// write file comment so simpleini doesn't get confused
|
||||
if (!did_exist)
|
||||
std::fputs("# DuckStation Game Settings\n\n", fp.get());
|
||||
|
||||
SI_Error err = ini.SaveFile(fp.get());
|
||||
if (err != SI_OK)
|
||||
Log_ErrorPrintf("Failed to save game settings ini: %d", static_cast<int>(err));
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_ErrorPrintf("Failed to open settings ini for saving: '%s'", save_path);
|
||||
}
|
||||
}
|
||||
|
||||
auto it = m_entries.find(code);
|
||||
if (it != m_entries.end())
|
||||
it->second = entry;
|
||||
else
|
||||
m_entries.emplace(code, entry);
|
||||
}
|
||||
|
||||
void Entry::ApplySettings(bool display_osd_messages) const
|
||||
{
|
||||
constexpr float osd_duration = 10.0f;
|
||||
|
||||
if (display_active_start_offset.has_value())
|
||||
g_settings.display_active_start_offset = display_active_start_offset.value();
|
||||
if (display_active_end_offset.has_value())
|
||||
g_settings.display_active_end_offset = display_active_end_offset.value();
|
||||
|
||||
if (display_crop_mode.has_value())
|
||||
g_settings.display_crop_mode = display_crop_mode.value();
|
||||
if (display_aspect_ratio.has_value())
|
||||
g_settings.display_aspect_ratio = display_aspect_ratio.value();
|
||||
if (controller_1_type.has_value())
|
||||
g_settings.controller_types[0] = controller_1_type.value();
|
||||
if (controller_2_type.has_value())
|
||||
g_settings.controller_types[1] = controller_2_type.value();
|
||||
if (gpu_widescreen_hack.has_value())
|
||||
g_settings.gpu_widescreen_hack = gpu_widescreen_hack.value();
|
||||
|
||||
if (HasTrait(Trait::ForceInterpreter))
|
||||
{
|
||||
if (display_osd_messages && g_settings.cpu_execution_mode != CPUExecutionMode::Interpreter)
|
||||
{
|
||||
g_host_interface->AddOSDMessage(
|
||||
g_host_interface->TranslateStdString("OSDMessage", "CPU interpreter forced by game settings."), osd_duration);
|
||||
}
|
||||
|
||||
g_settings.cpu_execution_mode = CPUExecutionMode::Interpreter;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::ForceSoftwareRenderer))
|
||||
{
|
||||
if (display_osd_messages && g_settings.gpu_renderer != GPURenderer::Software)
|
||||
{
|
||||
g_host_interface->AddOSDMessage(
|
||||
g_host_interface->TranslateStdString("OSDMessage", "Software renderer forced by game settings."), osd_duration);
|
||||
}
|
||||
|
||||
g_settings.gpu_renderer = GPURenderer::Software;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::ForceInterlacing))
|
||||
{
|
||||
if (display_osd_messages && g_settings.gpu_disable_interlacing)
|
||||
{
|
||||
g_host_interface->AddOSDMessage(
|
||||
g_host_interface->TranslateStdString("OSDMessage", "Interlacing forced by game settings."), osd_duration);
|
||||
}
|
||||
|
||||
g_settings.gpu_disable_interlacing = false;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::DisableTrueColor))
|
||||
{
|
||||
if (display_osd_messages && g_settings.gpu_true_color)
|
||||
{
|
||||
g_host_interface->AddOSDMessage(
|
||||
g_host_interface->TranslateStdString("OSDMessage", "True color disabled by game settings."), osd_duration);
|
||||
}
|
||||
|
||||
g_settings.gpu_true_color = false;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::DisableUpscaling))
|
||||
{
|
||||
if (display_osd_messages && g_settings.gpu_resolution_scale > 1)
|
||||
{
|
||||
g_host_interface->AddOSDMessage(
|
||||
g_host_interface->TranslateStdString("OSDMessage", "Upscaling disabled by game settings."), osd_duration);
|
||||
}
|
||||
|
||||
g_settings.gpu_resolution_scale = 1;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::DisableScaledDithering))
|
||||
{
|
||||
if (display_osd_messages && g_settings.gpu_scaled_dithering)
|
||||
{
|
||||
g_host_interface->AddOSDMessage(
|
||||
g_host_interface->TranslateStdString("OSDMessage", "Scaled dithering disabled by game settings."),
|
||||
osd_duration);
|
||||
}
|
||||
|
||||
g_settings.gpu_scaled_dithering = false;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::DisableWidescreen))
|
||||
{
|
||||
if (display_osd_messages &&
|
||||
(g_settings.display_aspect_ratio == DisplayAspectRatio::R16_9 || g_settings.gpu_widescreen_hack))
|
||||
{
|
||||
g_host_interface->AddOSDMessage(
|
||||
g_host_interface->TranslateStdString("OSDMessage", "Widescreen disabled by game settings."), osd_duration);
|
||||
}
|
||||
|
||||
g_settings.display_aspect_ratio = DisplayAspectRatio::R4_3;
|
||||
g_settings.gpu_widescreen_hack = false;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::DisablePGXP))
|
||||
{
|
||||
if (display_osd_messages && g_settings.gpu_pgxp_enable)
|
||||
{
|
||||
g_host_interface->AddOSDMessage(
|
||||
g_host_interface->TranslateStdString("OSDMessage", "PGXP geometry correction disabled by game settings."),
|
||||
osd_duration);
|
||||
}
|
||||
|
||||
g_settings.gpu_pgxp_enable = false;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::DisablePGXPCulling))
|
||||
{
|
||||
if (display_osd_messages && g_settings.gpu_pgxp_culling)
|
||||
{
|
||||
g_host_interface->AddOSDMessage(
|
||||
g_host_interface->TranslateStdString("OSDMessage", "PGXP culling disabled by game settings."), osd_duration);
|
||||
}
|
||||
|
||||
g_settings.gpu_pgxp_culling = false;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::ForcePGXPVertexCache))
|
||||
{
|
||||
if (display_osd_messages && g_settings.gpu_pgxp_enable && !g_settings.gpu_pgxp_vertex_cache)
|
||||
{
|
||||
g_host_interface->AddOSDMessage(
|
||||
g_host_interface->TranslateStdString("OSDMessage", "PGXP vertex cache forced by game settings."), osd_duration);
|
||||
}
|
||||
|
||||
g_settings.gpu_pgxp_vertex_cache = true;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::ForcePGXPCPUMode))
|
||||
{
|
||||
if (display_osd_messages && g_settings.gpu_pgxp_enable && !g_settings.gpu_pgxp_cpu)
|
||||
{
|
||||
g_host_interface->AddOSDMessage(
|
||||
g_host_interface->TranslateStdString("OSDMessage", "PGXP CPU mode forced by game settings."), osd_duration);
|
||||
}
|
||||
|
||||
g_settings.gpu_pgxp_cpu = true;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::ForceDigitalController))
|
||||
{
|
||||
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
|
||||
{
|
||||
if (g_settings.controller_types[i] != ControllerType::None &&
|
||||
g_settings.controller_types[i] != ControllerType::DigitalController)
|
||||
{
|
||||
if (display_osd_messages)
|
||||
{
|
||||
g_host_interface->AddFormattedOSDMessage(
|
||||
osd_duration,
|
||||
g_host_interface->TranslateString("OSDMessage", "Controller %u changed to digital by game settings."),
|
||||
i + 1u);
|
||||
}
|
||||
|
||||
g_settings.controller_types[i] = ControllerType::DigitalController;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::ForceRecompilerMemoryExceptions))
|
||||
{
|
||||
if (display_osd_messages && g_settings.cpu_execution_mode == CPUExecutionMode::Recompiler &&
|
||||
!g_settings.cpu_recompiler_memory_exceptions)
|
||||
{
|
||||
g_host_interface->AddOSDMessage(
|
||||
g_host_interface->TranslateStdString("OSDMessage", "Recompiler memory exceptions forced by game settings."),
|
||||
osd_duration);
|
||||
}
|
||||
|
||||
g_settings.cpu_recompiler_memory_exceptions = true;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::ForceRecompilerICache))
|
||||
{
|
||||
if (display_osd_messages && g_settings.cpu_execution_mode != CPUExecutionMode::Interpreter &&
|
||||
!g_settings.cpu_recompiler_icache)
|
||||
{
|
||||
g_host_interface->AddOSDMessage(
|
||||
g_host_interface->TranslateStdString("OSDMessage", "Recompiler ICache forced by game settings."), osd_duration);
|
||||
}
|
||||
|
||||
g_settings.cpu_recompiler_icache = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace GameSettings
|
@ -1,75 +0,0 @@
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
#include <bitset>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
class ByteStream;
|
||||
|
||||
namespace GameSettings {
|
||||
enum class Trait : u32
|
||||
{
|
||||
ForceInterpreter,
|
||||
ForceSoftwareRenderer,
|
||||
ForceInterlacing,
|
||||
DisableTrueColor,
|
||||
DisableUpscaling,
|
||||
DisableScaledDithering,
|
||||
DisableWidescreen,
|
||||
DisablePGXP,
|
||||
DisablePGXPCulling,
|
||||
ForcePGXPVertexCache,
|
||||
ForcePGXPCPUMode,
|
||||
ForceDigitalController,
|
||||
ForceRecompilerMemoryExceptions,
|
||||
ForceRecompilerICache,
|
||||
|
||||
Count
|
||||
};
|
||||
|
||||
const char* GetTraitName(Trait trait);
|
||||
const char* GetTraitDisplayName(Trait trait);
|
||||
|
||||
struct Entry
|
||||
{
|
||||
std::bitset<static_cast<int>(Trait::Count)> traits{};
|
||||
std::optional<s16> display_active_start_offset;
|
||||
std::optional<s16> display_active_end_offset;
|
||||
|
||||
// user settings
|
||||
std::optional<DisplayCropMode> display_crop_mode;
|
||||
std::optional<DisplayAspectRatio> display_aspect_ratio;
|
||||
std::optional<ControllerType> controller_1_type;
|
||||
std::optional<ControllerType> controller_2_type;
|
||||
std::optional<bool> gpu_widescreen_hack;
|
||||
|
||||
ALWAYS_INLINE bool HasTrait(Trait trait) const { return traits[static_cast<int>(trait)]; }
|
||||
ALWAYS_INLINE void AddTrait(Trait trait) { traits[static_cast<int>(trait)] = true; }
|
||||
ALWAYS_INLINE void RemoveTrait(Trait trait) { traits[static_cast<int>(trait)] = false; }
|
||||
ALWAYS_INLINE void SetTrait(Trait trait, bool enabled) { traits[static_cast<int>(trait)] = enabled; }
|
||||
|
||||
bool HasAnySettings() const;
|
||||
|
||||
bool LoadFromStream(ByteStream* stream);
|
||||
bool SaveToStream(ByteStream* stream) const;
|
||||
|
||||
void ApplySettings(bool display_osd_messages) const;
|
||||
};
|
||||
|
||||
class Database
|
||||
{
|
||||
public:
|
||||
Database();
|
||||
~Database();
|
||||
|
||||
const Entry* GetEntry(const std::string& code) const;
|
||||
void SetEntry(const std::string& code, const std::string& name, const Entry& entry, const char* save_path);
|
||||
|
||||
bool Load(const char* path);
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, Entry> m_entries;
|
||||
};
|
||||
|
||||
}; // namespace GameSettings
|
@ -4,6 +4,7 @@
|
||||
#include "cdrom.h"
|
||||
#include "common/audio_stream.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/iso_reader.h"
|
||||
#include "common/log.h"
|
||||
#include "common/state_wrapper.h"
|
||||
#include "common/string_util.h"
|
||||
@ -11,7 +12,6 @@
|
||||
#include "cpu_code_cache.h"
|
||||
#include "cpu_core.h"
|
||||
#include "dma.h"
|
||||
#include "game_list.h"
|
||||
#include "gpu.h"
|
||||
#include "gte.h"
|
||||
#include "host_display.h"
|
||||
@ -27,6 +27,7 @@
|
||||
#include "spu.h"
|
||||
#include "timers.h"
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
Log_SetChannel(System);
|
||||
|
||||
@ -203,6 +204,71 @@ float GetThrottleFrequency()
|
||||
return s_throttle_frequency;
|
||||
}
|
||||
|
||||
bool IsExeFileName(const char* path)
|
||||
{
|
||||
const char* extension = std::strrchr(path, '.');
|
||||
return (extension &&
|
||||
(StringUtil::Strcasecmp(extension, ".exe") == 0 || StringUtil::Strcasecmp(extension, ".psexe") == 0));
|
||||
}
|
||||
|
||||
bool IsPsfFileName(const char* path)
|
||||
{
|
||||
const char* extension = std::strrchr(path, '.');
|
||||
return (extension && StringUtil::Strcasecmp(extension, ".psf") == 0);
|
||||
}
|
||||
|
||||
bool IsM3UFileName(const char* path)
|
||||
{
|
||||
const char* extension = std::strrchr(path, '.');
|
||||
return (extension && StringUtil::Strcasecmp(extension, ".m3u") == 0);
|
||||
}
|
||||
|
||||
std::vector<std::string> ParseM3UFile(const char* path)
|
||||
{
|
||||
std::ifstream ifs(path);
|
||||
if (!ifs.is_open())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to open %s", path);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::string> entries;
|
||||
std::string line;
|
||||
while (std::getline(ifs, line))
|
||||
{
|
||||
u32 start_offset = 0;
|
||||
while (start_offset < line.size() && std::isspace(line[start_offset]))
|
||||
start_offset++;
|
||||
|
||||
// skip comments
|
||||
if (start_offset == line.size() || line[start_offset] == '#')
|
||||
continue;
|
||||
|
||||
// strip ending whitespace
|
||||
u32 end_offset = static_cast<u32>(line.size()) - 1;
|
||||
while (std::isspace(line[end_offset]) && end_offset > start_offset)
|
||||
end_offset--;
|
||||
|
||||
// anything?
|
||||
if (start_offset == end_offset)
|
||||
continue;
|
||||
|
||||
std::string entry_path(line.begin() + start_offset, line.begin() + end_offset + 1);
|
||||
if (!FileSystem::IsAbsolutePath(entry_path))
|
||||
{
|
||||
SmallString absolute_path;
|
||||
FileSystem::BuildPathRelativeToFile(absolute_path, path, entry_path.c_str());
|
||||
entry_path = absolute_path;
|
||||
}
|
||||
|
||||
Log_DevPrintf("Read path from m3u: '%s'", entry_path.c_str());
|
||||
entries.push_back(std::move(entry_path));
|
||||
}
|
||||
|
||||
Log_InfoPrintf("Loaded %zu paths from m3u '%s'", entries.size(), path);
|
||||
return entries;
|
||||
}
|
||||
|
||||
ConsoleRegion GetConsoleRegionForDiscRegion(DiscRegion region)
|
||||
{
|
||||
switch (region)
|
||||
@ -220,6 +286,188 @@ ConsoleRegion GetConsoleRegionForDiscRegion(DiscRegion region)
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view GetTitleForPath(const char* path)
|
||||
{
|
||||
const char* extension = std::strrchr(path, '.');
|
||||
if (path == extension)
|
||||
return path;
|
||||
|
||||
const char* path_end = path + std::strlen(path);
|
||||
const char* title_end = extension ? (extension - 1) : (path_end);
|
||||
const char* title_start = std::max(std::strrchr(path, '/'), std::strrchr(path, '\\'));
|
||||
if (!title_start || title_start == path)
|
||||
return std::string_view(path, title_end - title_start);
|
||||
else
|
||||
return std::string_view(title_start + 1, title_end - title_start);
|
||||
}
|
||||
|
||||
std::string GetGameCodeForPath(const char* image_path)
|
||||
{
|
||||
std::unique_ptr<CDImage> cdi = CDImage::Open(image_path);
|
||||
if (!cdi)
|
||||
return {};
|
||||
|
||||
return GetGameCodeForImage(cdi.get());
|
||||
}
|
||||
|
||||
std::string GetGameCodeForImage(CDImage* cdi)
|
||||
{
|
||||
ISOReader iso;
|
||||
if (!iso.Open(cdi, 1))
|
||||
return {};
|
||||
|
||||
// Read SYSTEM.CNF
|
||||
std::vector<u8> system_cnf_data;
|
||||
if (!iso.ReadFile("SYSTEM.CNF", &system_cnf_data))
|
||||
return {};
|
||||
|
||||
// Parse lines
|
||||
std::vector<std::pair<std::string, std::string>> lines;
|
||||
std::pair<std::string, std::string> current_line;
|
||||
bool reading_value = false;
|
||||
for (size_t pos = 0; pos < system_cnf_data.size(); pos++)
|
||||
{
|
||||
const char ch = static_cast<char>(system_cnf_data[pos]);
|
||||
if (ch == '\r' || ch == '\n')
|
||||
{
|
||||
if (!current_line.first.empty())
|
||||
{
|
||||
lines.push_back(std::move(current_line));
|
||||
current_line = {};
|
||||
reading_value = false;
|
||||
}
|
||||
}
|
||||
else if (ch == ' ' || (ch >= 0x09 && ch <= 0x0D))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (ch == '=' && !reading_value)
|
||||
{
|
||||
reading_value = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (reading_value)
|
||||
current_line.second.push_back(ch);
|
||||
else
|
||||
current_line.first.push_back(ch);
|
||||
}
|
||||
}
|
||||
|
||||
if (!current_line.first.empty())
|
||||
lines.push_back(std::move(current_line));
|
||||
|
||||
// Find the BOOT line
|
||||
auto iter = std::find_if(lines.begin(), lines.end(),
|
||||
[](const auto& it) { return StringUtil::Strcasecmp(it.first.c_str(), "boot") == 0; });
|
||||
if (iter == lines.end())
|
||||
return {};
|
||||
|
||||
// cdrom:\SCES_123.45;1
|
||||
std::string code = iter->second;
|
||||
std::string::size_type pos = code.rfind('\\');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
code.erase(0, pos + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// cdrom:SCES_123.45;1
|
||||
pos = code.rfind(':');
|
||||
if (pos != std::string::npos)
|
||||
code.erase(0, pos + 1);
|
||||
}
|
||||
|
||||
pos = code.find(';');
|
||||
if (pos != std::string::npos)
|
||||
code.erase(pos);
|
||||
|
||||
// SCES_123.45 -> SCES-12345
|
||||
for (pos = 0; pos < code.size();)
|
||||
{
|
||||
if (code[pos] == '.')
|
||||
{
|
||||
code.erase(pos, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (code[pos] == '_')
|
||||
code[pos] = '-';
|
||||
else
|
||||
code[pos] = static_cast<char>(std::toupper(code[pos]));
|
||||
|
||||
pos++;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
DiscRegion GetRegionForCode(std::string_view code)
|
||||
{
|
||||
std::string prefix;
|
||||
for (size_t pos = 0; pos < code.length(); pos++)
|
||||
{
|
||||
const int ch = std::tolower(code[pos]);
|
||||
if (ch < 'a' || ch > 'z')
|
||||
break;
|
||||
|
||||
prefix.push_back(static_cast<char>(ch));
|
||||
}
|
||||
|
||||
if (prefix == "sces" || prefix == "sced" || prefix == "sles" || prefix == "sled")
|
||||
return DiscRegion::PAL;
|
||||
else if (prefix == "scps" || prefix == "slps" || prefix == "slpm" || prefix == "sczs" || prefix == "papx")
|
||||
return DiscRegion::NTSC_J;
|
||||
else if (prefix == "scus" || prefix == "slus")
|
||||
return DiscRegion::NTSC_U;
|
||||
else
|
||||
return DiscRegion::Other;
|
||||
}
|
||||
|
||||
DiscRegion GetRegionFromSystemArea(CDImage* cdi)
|
||||
{
|
||||
// The license code is on sector 4 of the disc.
|
||||
u8 sector[CDImage::DATA_SECTOR_SIZE];
|
||||
if (!cdi->Seek(1, 4) || cdi->Read(CDImage::ReadMode::DataOnly, 1, sector) != 1)
|
||||
return DiscRegion::Other;
|
||||
|
||||
static constexpr char ntsc_u_string[] = " Licensed by Sony Computer Entertainment Amer ica ";
|
||||
static constexpr char ntsc_j_string[] = " Licensed by Sony Computer Entertainment Inc.";
|
||||
static constexpr char pal_string[] = " Licensed by Sony Computer Entertainment Euro pe";
|
||||
|
||||
// subtract one for the terminating null
|
||||
if (std::equal(ntsc_u_string, ntsc_u_string + countof(ntsc_u_string) - 1, sector))
|
||||
return DiscRegion::NTSC_U;
|
||||
else if (std::equal(ntsc_j_string, ntsc_j_string + countof(ntsc_j_string) - 1, sector))
|
||||
return DiscRegion::NTSC_J;
|
||||
else if (std::equal(pal_string, pal_string + countof(pal_string) - 1, sector))
|
||||
return DiscRegion::PAL;
|
||||
else
|
||||
return DiscRegion::Other;
|
||||
}
|
||||
|
||||
DiscRegion GetRegionForImage(CDImage* cdi)
|
||||
{
|
||||
DiscRegion system_area_region = GetRegionFromSystemArea(cdi);
|
||||
if (system_area_region != DiscRegion::Other)
|
||||
return system_area_region;
|
||||
|
||||
std::string code = GetGameCodeForImage(cdi);
|
||||
if (code.empty())
|
||||
return DiscRegion::Other;
|
||||
|
||||
return GetRegionForCode(code);
|
||||
}
|
||||
|
||||
std::optional<DiscRegion> GetRegionForPath(const char* image_path)
|
||||
{
|
||||
std::unique_ptr<CDImage> cdi = CDImage::Open(image_path);
|
||||
if (!cdi)
|
||||
return {};
|
||||
|
||||
return GetRegionForImage(cdi.get());
|
||||
}
|
||||
|
||||
bool RecreateGPU(GPURenderer renderer)
|
||||
{
|
||||
g_gpu->RestoreGraphicsAPIState();
|
||||
@ -297,8 +545,8 @@ bool Boot(const SystemBootParameters& params)
|
||||
bool psf_boot = false;
|
||||
if (!params.filename.empty())
|
||||
{
|
||||
exe_boot = GameList::IsExeFileName(params.filename.c_str());
|
||||
psf_boot = (!exe_boot && GameList::IsPsfFileName(params.filename.c_str()));
|
||||
exe_boot = IsExeFileName(params.filename.c_str());
|
||||
psf_boot = (!exe_boot && IsPsfFileName(params.filename.c_str()));
|
||||
if (exe_boot || psf_boot)
|
||||
{
|
||||
// TODO: Pull region from PSF
|
||||
@ -311,9 +559,9 @@ bool Boot(const SystemBootParameters& params)
|
||||
else
|
||||
{
|
||||
u32 playlist_index;
|
||||
if (GameList::IsM3UFileName(params.filename.c_str()))
|
||||
if (IsM3UFileName(params.filename.c_str()))
|
||||
{
|
||||
s_media_playlist = GameList::ParseM3UFile(params.filename.c_str());
|
||||
s_media_playlist = ParseM3UFile(params.filename.c_str());
|
||||
s_media_playlist_filename = params.filename;
|
||||
if (s_media_playlist.empty())
|
||||
{
|
||||
@ -350,7 +598,7 @@ bool Boot(const SystemBootParameters& params)
|
||||
|
||||
if (s_region == ConsoleRegion::Auto)
|
||||
{
|
||||
const DiscRegion disc_region = GameList::GetRegionForImage(media.get());
|
||||
const DiscRegion disc_region = GetRegionForImage(media.get());
|
||||
if (disc_region != DiscRegion::Other)
|
||||
{
|
||||
s_region = GetConsoleRegionForDiscRegion(disc_region);
|
||||
@ -689,7 +937,7 @@ bool DoLoadState(ByteStream* state, bool force_software_renderer)
|
||||
return false;
|
||||
}
|
||||
|
||||
playlist_entries = GameList::ParseM3UFile(playlist_filename.c_str());
|
||||
playlist_entries = ParseM3UFile(playlist_filename.c_str());
|
||||
if (playlist_entries.empty())
|
||||
{
|
||||
g_host_interface->ReportFormattedError("Failed to load save state playlist entries from '%s'",
|
||||
@ -1191,7 +1439,7 @@ void UpdateMemoryCards()
|
||||
{
|
||||
if (!s_media_playlist_filename.empty() && g_settings.memory_card_use_playlist_title)
|
||||
{
|
||||
const std::string playlist_title(GameList::GetTitleForPath(s_media_playlist_filename.c_str()));
|
||||
const std::string playlist_title(GetTitleForPath(s_media_playlist_filename.c_str()));
|
||||
card = MemoryCard::Open(g_host_interface->GetGameMemoryCardPath(playlist_title.c_str(), i));
|
||||
}
|
||||
else if (s_running_game_title.empty())
|
||||
|
@ -45,9 +45,29 @@ enum class State
|
||||
Paused
|
||||
};
|
||||
|
||||
/// Returns true if the filename is a PlayStation executable we can inject.
|
||||
bool IsExeFileName(const char* path);
|
||||
|
||||
/// Returns true if the filename is a Portable Sound Format file we can uncompress/load.
|
||||
bool IsPsfFileName(const char* path);
|
||||
|
||||
/// Returns true if the filename is a M3U Playlist we can handle.
|
||||
bool IsM3UFileName(const char* path);
|
||||
|
||||
/// Parses an M3U playlist, returning the entries.
|
||||
std::vector<std::string> ParseM3UFile(const char* path);
|
||||
|
||||
/// Returns the preferred console type for a disc.
|
||||
ConsoleRegion GetConsoleRegionForDiscRegion(DiscRegion region);
|
||||
|
||||
std::string GetGameCodeForImage(CDImage* cdi);
|
||||
std::string GetGameCodeForPath(const char* image_path);
|
||||
DiscRegion GetRegionForCode(std::string_view code);
|
||||
DiscRegion GetRegionFromSystemArea(CDImage* cdi);
|
||||
DiscRegion GetRegionForImage(CDImage* cdi);
|
||||
std::optional<DiscRegion> GetRegionForPath(const char* image_path);
|
||||
std::string_view GetTitleForPath(const char* path);
|
||||
|
||||
State GetState();
|
||||
void SetState(State new_state);
|
||||
bool IsRunning();
|
||||
|
Reference in New Issue
Block a user