diff --git a/android/app/src/cpp/android_host_interface.cpp b/android/app/src/cpp/android_host_interface.cpp index 682411a85..8c5e18936 100644 --- a/android/app/src/cpp/android_host_interface.cpp +++ b/android/app/src/cpp/android_host_interface.cpp @@ -5,10 +5,10 @@ #include "common/string.h" #include "common/timestamp.h" #include "core/controller.h" -#include "core/game_list.h" #include "core/gpu.h" #include "core/host_display.h" #include "core/system.h" +#include "frontend-common/game_list.h" #include "frontend-common/imgui_styles.h" #include "frontend-common/opengl_host_display.h" #include "frontend-common/vulkan_host_display.h" diff --git a/dep/CMakeLists.txt b/dep/CMakeLists.txt index 79acdde2b..5b00dbbfa 100644 --- a/dep/CMakeLists.txt +++ b/dep/CMakeLists.txt @@ -2,9 +2,7 @@ add_subdirectory(cubeb) add_subdirectory(glad) add_subdirectory(googletest) add_subdirectory(libcue) -add_subdirectory(simpleini) add_subdirectory(stb) -add_subdirectory(tinyxml2) add_subdirectory(zlib) add_subdirectory(minizip) add_subdirectory(lzma) @@ -17,6 +15,8 @@ add_subdirectory(vulkan-loader) if(NOT BUILD_LIBRETRO_CORE) add_subdirectory(imgui) + add_subdirectory(simpleini) + add_subdirectory(tinyxml2) endif() if(ENABLE_DISCORD_PRESENCE) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 0517bc810..c8e2f074a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -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) diff --git a/src/core/cdrom.cpp b/src/core/cdrom.cpp index 673e186d8..033d5d9b5 100644 --- a/src/core/cdrom.cpp +++ b/src/core/cdrom.cpp @@ -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 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())); diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index 85d116da7..cd02f72f2 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -59,8 +59,6 @@ - - @@ -107,8 +105,6 @@ - - @@ -147,14 +143,14 @@ {bb08260f-6fbc-46af-8924-090ee71360c6} - - {3773f4cc-614e-4028-8595-22e08ca649e3} - {ed601289-ac1a-46b8-a8ed-17db9eb73423} - - {933118a9-68c5-47b4-b151-b03c93961623} + + {9c8ddeb0-2b8f-4f5f-ba86-127cdf27f035} + + + {7ff9fdb9-d504-47db-a16a-b08071999620} {ee054e08-3799-4a59-a422-18259c105ffd} @@ -307,7 +303,7 @@ WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(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) + $(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) true false stdcpp17 @@ -333,7 +329,7 @@ WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(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) + $(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) true false stdcpp17 @@ -359,7 +355,7 @@ WITH_IMGUI=1;WITH_RECOMPILER=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(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) + $(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) Default true false @@ -388,7 +384,7 @@ WITH_IMGUI=1;WITH_RECOMPILER=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(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) + $(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) Default true false @@ -416,7 +412,7 @@ MaxSpeed true WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(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) + $(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) true false stdcpp17 @@ -443,7 +439,7 @@ MaxSpeed true WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(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) + $(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) true true stdcpp17 @@ -471,7 +467,7 @@ MaxSpeed true WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(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) + $(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) true false stdcpp17 @@ -498,7 +494,7 @@ MaxSpeed true WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(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) + $(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) true true stdcpp17 diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index 0d86f8170..96b2c050b 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -31,7 +31,6 @@ - @@ -47,7 +46,6 @@ - @@ -82,7 +80,6 @@ - @@ -98,6 +95,5 @@ - \ No newline at end of file diff --git a/src/core/system.cpp b/src/core/system.cpp index 3f24ea4ea..3d4630663 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -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 +#include #include 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 ParseM3UFile(const char* path) +{ + std::ifstream ifs(path); + if (!ifs.is_open()) + { + Log_ErrorPrintf("Failed to open %s", path); + return {}; + } + + std::vector 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(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 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 system_cnf_data; + if (!iso.ReadFile("SYSTEM.CNF", &system_cnf_data)) + return {}; + + // Parse lines + std::vector> lines; + std::pair current_line; + bool reading_value = false; + for (size_t pos = 0; pos < system_cnf_data.size(); pos++) + { + const char ch = static_cast(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(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(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 GetRegionForPath(const char* image_path) +{ + std::unique_ptr 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()) diff --git a/src/core/system.h b/src/core/system.h index eee1f6973..a6fde2ac5 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -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 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 GetRegionForPath(const char* image_path); +std::string_view GetTitleForPath(const char* path); + State GetState(); void SetState(State new_state); bool IsRunning(); diff --git a/src/duckstation-libretro/libretro_host_interface.cpp b/src/duckstation-libretro/libretro_host_interface.cpp index 8388ae868..09feaa9c5 100644 --- a/src/duckstation-libretro/libretro_host_interface.cpp +++ b/src/duckstation-libretro/libretro_host_interface.cpp @@ -7,7 +7,6 @@ #include "core/analog_controller.h" #include "core/bus.h" #include "core/digital_controller.h" -#include "core/game_list.h" #include "core/gpu.h" #include "core/system.h" #include "libretro_audio_stream.h" @@ -132,7 +131,7 @@ bool LibretroHostInterface::ConfirmMessage(const char* message) void LibretroHostInterface::GetGameInfo(const char* path, CDImage* image, std::string* code, std::string* title) { // Just use the filename for now... we don't have the game list. Unless we can pull this from the frontend somehow? - *title = GameList::GetTitleForPath(path); + *title = System::GetTitleForPath(path); code->clear(); } @@ -1158,7 +1157,7 @@ bool LibretroHostInterface::DiskControlGetImageLabel(unsigned index, char* label if (image_path.empty()) return false; - const std::string_view title = GameList::GetTitleForPath(label); + const std::string_view title = System::GetTitleForPath(label); StringUtil::Strlcpy(label, title, len); Log_DevPrintf("DiskControlGetImagePath(%u) -> %s", index, label); return true; diff --git a/src/duckstation-qt/gamelistmodel.cpp b/src/duckstation-qt/gamelistmodel.cpp index c3e422bb8..7946dbeba 100644 --- a/src/duckstation-qt/gamelistmodel.cpp +++ b/src/duckstation-qt/gamelistmodel.cpp @@ -1,5 +1,6 @@ #include "gamelistmodel.h" #include "common/string_util.h" +#include "core/system.h" static constexpr std::array s_column_names = { {"Type", "Code", "Title", "File Title", "Size", "Region", "Compatibility"}}; @@ -69,7 +70,7 @@ QVariant GameListModel::data(const QModelIndex& index, int role) const case Column_FileTitle: { - const std::string_view file_title(GameList::GetTitleForPath(ge.path.c_str())); + const std::string_view file_title(System::GetTitleForPath(ge.path.c_str())); return QString::fromUtf8(file_title.data(), static_cast(file_title.length())); } @@ -96,7 +97,7 @@ QVariant GameListModel::data(const QModelIndex& index, int role) const case Column_FileTitle: { - const std::string_view file_title(GameList::GetTitleForPath(ge.path.c_str())); + const std::string_view file_title(System::GetTitleForPath(ge.path.c_str())); return QString::fromUtf8(file_title.data(), static_cast(file_title.length())); } @@ -234,8 +235,8 @@ bool GameListModel::lessThan(const QModelIndex& left_index, const QModelIndex& r case Column_FileTitle: { - const std::string_view file_title_left(GameList::GetTitleForPath(left.path.c_str())); - const std::string_view file_title_right(GameList::GetTitleForPath(right.path.c_str())); + const std::string_view file_title_left(System::GetTitleForPath(left.path.c_str())); + const std::string_view file_title_right(System::GetTitleForPath(right.path.c_str())); if (file_title_left == file_title_right) return titlesLessThan(left_row, right_row, ascending); diff --git a/src/duckstation-qt/gamelistmodel.h b/src/duckstation-qt/gamelistmodel.h index 547555951..edf04892e 100644 --- a/src/duckstation-qt/gamelistmodel.h +++ b/src/duckstation-qt/gamelistmodel.h @@ -1,6 +1,6 @@ #pragma once -#include "core/game_list.h" #include "core/types.h" +#include "frontend-common/game_list.h" #include #include #include diff --git a/src/duckstation-qt/gamelistsettingswidget.cpp b/src/duckstation-qt/gamelistsettingswidget.cpp index c298d3527..268e2490f 100644 --- a/src/duckstation-qt/gamelistsettingswidget.cpp +++ b/src/duckstation-qt/gamelistsettingswidget.cpp @@ -2,7 +2,7 @@ #include "common/assert.h" #include "common/minizip_helpers.h" #include "common/string_util.h" -#include "core/game_list.h" +#include "frontend-common/game_list.h" #include "gamelistsearchdirectoriesmodel.h" #include "qthostinterface.h" #include "qtutils.h" diff --git a/src/duckstation-qt/gamelistwidget.cpp b/src/duckstation-qt/gamelistwidget.cpp index e06aa3f7f..2f4325dcd 100644 --- a/src/duckstation-qt/gamelistwidget.cpp +++ b/src/duckstation-qt/gamelistwidget.cpp @@ -1,7 +1,7 @@ #include "gamelistwidget.h" #include "common/string_util.h" -#include "core/game_list.h" #include "core/settings.h" +#include "frontend-common/game_list.h" #include "gamelistmodel.h" #include "qthostinterface.h" #include "qtutils.h" diff --git a/src/duckstation-qt/gamepropertiesdialog.cpp b/src/duckstation-qt/gamepropertiesdialog.cpp index 1cae9feb7..1564b54ca 100644 --- a/src/duckstation-qt/gamepropertiesdialog.cpp +++ b/src/duckstation-qt/gamepropertiesdialog.cpp @@ -1,8 +1,8 @@ #include "gamepropertiesdialog.h" #include "common/cd_image.h" #include "common/cd_image_hasher.h" -#include "core/game_list.h" #include "core/settings.h" +#include "frontend-common/game_list.h" #include "qthostinterface.h" #include "qtprogresscallback.h" #include "qtutils.h" diff --git a/src/duckstation-qt/gamepropertiesdialog.h b/src/duckstation-qt/gamepropertiesdialog.h index 74c0def22..65f74c22e 100644 --- a/src/duckstation-qt/gamepropertiesdialog.h +++ b/src/duckstation-qt/gamepropertiesdialog.h @@ -1,5 +1,5 @@ #pragma once -#include "core/game_settings.h" +#include "frontend-common/game_settings.h" #include "ui_gamepropertiesdialog.h" #include #include diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index c600ec749..756951b0f 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -2,10 +2,10 @@ #include "aboutdialog.h" #include "autoupdaterdialog.h" #include "common/assert.h" -#include "core/game_list.h" #include "core/host_display.h" #include "core/settings.h" #include "core/system.h" +#include "frontend-common/game_list.h" #include "gamelistsettingswidget.h" #include "gamelistwidget.h" #include "gamepropertiesdialog.h" diff --git a/src/duckstation-qt/qthostinterface.cpp b/src/duckstation-qt/qthostinterface.cpp index a1b60a00b..ddacf57e3 100644 --- a/src/duckstation-qt/qthostinterface.cpp +++ b/src/duckstation-qt/qthostinterface.cpp @@ -6,9 +6,9 @@ #include "common/log.h" #include "common/string_util.h" #include "core/controller.h" -#include "core/game_list.h" #include "core/gpu.h" #include "core/system.h" +#include "frontend-common/game_list.h" #include "frontend-common/imgui_styles.h" #include "frontend-common/ini_settings_interface.h" #include "frontend-common/opengl_host_display.h" diff --git a/src/frontend-common/CMakeLists.txt b/src/frontend-common/CMakeLists.txt index ddae45acc..a27a9bf2a 100644 --- a/src/frontend-common/CMakeLists.txt +++ b/src/frontend-common/CMakeLists.txt @@ -3,6 +3,10 @@ add_library(frontend-common common_host_interface.h controller_interface.cpp controller_interface.h + game_list.cpp + game_list.h + game_settings.cpp + game_settings.h icon.cpp icon.h imgui_styles.cpp @@ -17,7 +21,7 @@ add_library(frontend-common vulkan_host_display.h ) -target_link_libraries(frontend-common PUBLIC core common imgui simpleini scmversion glad vulkan-loader) +target_link_libraries(frontend-common PUBLIC core common imgui simpleini tinyxml2 scmversion glad vulkan-loader) if(WIN32) target_sources(frontend-common PRIVATE diff --git a/src/frontend-common/common_host_interface.cpp b/src/frontend-common/common_host_interface.cpp index 84f9a13fc..93a332774 100644 --- a/src/frontend-common/common_host_interface.cpp +++ b/src/frontend-common/common_host_interface.cpp @@ -9,7 +9,6 @@ #include "core/cdrom.h" #include "core/cpu_code_cache.h" #include "core/dma.h" -#include "core/game_list.h" #include "core/gpu.h" #include "core/host_display.h" #include "core/mdec.h" @@ -18,6 +17,7 @@ #include "core/spu.h" #include "core/system.h" #include "core/timers.h" +#include "game_list.h" #include "imgui.h" #include "ini_settings_interface.h" #include "save_state_selector_ui.h" @@ -368,7 +368,7 @@ bool CommonHostInterface::ParseCommandLineParameters(int argc, char* argv[], else { // find the game id, and get its save state path - std::string game_code = m_game_list->GetGameCodeForPath(boot_filename.c_str()); + std::string game_code = System::GetGameCodeForPath(boot_filename.c_str()); if (game_code.empty()) { Log_WarningPrintf("Could not identify game code for '%s', cannot load save state %d.", boot_filename.c_str(), @@ -2032,13 +2032,13 @@ void CommonHostInterface::GetGameInfo(const char* path, CDImage* image, std::str else { if (image) - *code = GameList::GetGameCodeForImage(image); + *code = System::GetGameCodeForImage(image); const GameListDatabaseEntry* db_entry = (!code->empty()) ? m_game_list->GetDatabaseEntryForCode(*code) : nullptr; if (db_entry) *title = db_entry->title; else - *title = GameList::GetTitleForPath(path); + *title = System::GetTitleForPath(path); } } diff --git a/src/frontend-common/frontend-common.vcxproj b/src/frontend-common/frontend-common.vcxproj index ebcae7ee0..e291fa0e3 100644 --- a/src/frontend-common/frontend-common.vcxproj +++ b/src/frontend-common/frontend-common.vcxproj @@ -53,6 +53,9 @@ {3773f4cc-614e-4028-8595-22e08ca649e3} + + {933118a9-68c5-47b4-b151-b03c93961623} + {9c8ddeb0-2b8f-4f5f-ba86-127cdf27f035} @@ -67,6 +70,8 @@ + + @@ -82,6 +87,8 @@ + + @@ -241,7 +248,7 @@ WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true stdcpp17 true @@ -269,7 +276,7 @@ WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;_ITERATOR_DEBUG_LEVEL=1;WIN32;_DEBUGFAST;_DEBUG;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) Default false true @@ -300,7 +307,7 @@ WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true stdcpp17 true @@ -328,7 +335,7 @@ WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;_ITERATOR_DEBUG_LEVEL=1;WIN32;_DEBUGFAST;_DEBUG;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) Default false true @@ -360,7 +367,7 @@ true WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true - $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true stdcpp17 false @@ -390,7 +397,7 @@ true WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true - $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true true stdcpp17 @@ -422,7 +429,7 @@ true WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true - $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true stdcpp17 false @@ -452,7 +459,7 @@ true WITH_SDL2=1;WITH_DISCORD_PRESENCE=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true - $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\discord-rpc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true true stdcpp17 diff --git a/src/frontend-common/frontend-common.vcxproj.filters b/src/frontend-common/frontend-common.vcxproj.filters index d84627ceb..5efdfa632 100644 --- a/src/frontend-common/frontend-common.vcxproj.filters +++ b/src/frontend-common/frontend-common.vcxproj.filters @@ -14,6 +14,8 @@ + + @@ -29,6 +31,8 @@ + + diff --git a/src/core/game_list.cpp b/src/frontend-common/game_list.cpp similarity index 81% rename from src/core/game_list.cpp rename to src/frontend-common/game_list.cpp index fef61d38b..58db28b40 100644 --- a/src/core/game_list.cpp +++ b/src/frontend-common/game_list.cpp @@ -1,5 +1,4 @@ #include "game_list.h" -#include "bios.h" #include "common/assert.h" #include "common/byte_stream.h" #include "common/cd_image.h" @@ -8,12 +7,13 @@ #include "common/log.h" #include "common/progress_callback.h" #include "common/string_util.h" -#include "host_interface.h" -#include "settings.h" +#include "core/bios.h" +#include "core/host_interface.h" +#include "core/settings.h" +#include "core/system.h" #include #include #include -#include #include #include #include @@ -36,238 +36,6 @@ const char* GameList::EntryCompatibilityRatingToString(GameListCompatibilityRati return names[static_cast(rating)]; } -std::string GameList::GetGameCodeForPath(const char* image_path) -{ - std::unique_ptr cdi = CDImage::Open(image_path); - if (!cdi) - return {}; - - return GetGameCodeForImage(cdi.get()); -} - -std::string GameList::GetGameCodeForImage(CDImage* cdi) -{ - ISOReader iso; - if (!iso.Open(cdi, 1)) - return {}; - - // Read SYSTEM.CNF - std::vector system_cnf_data; - if (!iso.ReadFile("SYSTEM.CNF", &system_cnf_data)) - return {}; - - // Parse lines - std::vector> lines; - std::pair current_line; - bool reading_value = false; - for (size_t pos = 0; pos < system_cnf_data.size(); pos++) - { - const char ch = static_cast(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(std::toupper(code[pos])); - - pos++; - } - - return code; -} - -DiscRegion GameList::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(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 GameList::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 GameList::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 GameList::GetRegionForPath(const char* image_path) -{ - std::unique_ptr cdi = CDImage::Open(image_path); - if (!cdi) - return {}; - - return GetRegionForImage(cdi.get()); -} - -bool GameList::IsExeFileName(const char* path) -{ - const char* extension = std::strrchr(path, '.'); - return (extension && - (StringUtil::Strcasecmp(extension, ".exe") == 0 || StringUtil::Strcasecmp(extension, ".psexe") == 0)); -} - -bool GameList::IsPsfFileName(const char* path) -{ - const char* extension = std::strrchr(path, '.'); - return (extension && StringUtil::Strcasecmp(extension, ".psf") == 0); -} - -bool GameList::IsM3UFileName(const char* path) -{ - const char* extension = std::strrchr(path, '.'); - return (extension && StringUtil::Strcasecmp(extension, ".m3u") == 0); -} - -std::vector GameList::ParseM3UFile(const char* path) -{ - std::ifstream ifs(path); - if (!ifs.is_open()) - { - Log_ErrorPrintf("Failed to open %s", path); - return {}; - } - - std::vector 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(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; -} - const char* GameList::GetGameListCompatibilityRatingString(GameListCompatibilityRating rating) { static constexpr std::array(GameListCompatibilityRating::Count)> names = { @@ -292,21 +60,6 @@ static std::string_view GetFileNameFromPath(const char* path) return std::string_view(filename_start + 1, filename_end - filename_start); } -std::string_view GameList::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); -} - bool GameList::GetExeListEntry(const char* path, GameListEntry* entry) { FILESYSTEM_STAT_DATA ffd; @@ -360,12 +113,12 @@ bool GameList::GetM3UListEntry(const char* path, GameListEntry* entry) if (!FileSystem::StatFile(path, &ffd)) return false; - std::vector entries = ParseM3UFile(path); + std::vector entries = System::ParseM3UFile(path); if (entries.empty()) return false; entry->code.clear(); - entry->title = GetTitleForPath(path); + entry->title = System::GetTitleForPath(path); entry->path = path; entry->region = DiscRegion::Other; entry->total_size = 0; @@ -385,11 +138,11 @@ bool GameList::GetM3UListEntry(const char* path, GameListEntry* entry) entry->total_size += static_cast(CDImage::RAW_SECTOR_SIZE) * static_cast(entry_image->GetLBACount()); if (entry->region == DiscRegion::Other) - entry->region = GetRegionForImage(entry_image.get()); + entry->region = System::GetRegionForImage(entry_image.get()); if (entry->compatibility_rating == GameListCompatibilityRating::Unknown) { - std::string code = GetGameCodeForImage(entry_image.get()); + std::string code = System::GetGameCodeForImage(entry_image.get()); const GameListCompatibilityEntry* compatibility_entry = GetCompatibilityEntryForCode(entry->code); if (compatibility_entry) entry->compatibility_rating = compatibility_entry->compatibility_rating; @@ -403,19 +156,19 @@ bool GameList::GetM3UListEntry(const char* path, GameListEntry* entry) bool GameList::GetGameListEntry(const std::string& path, GameListEntry* entry) { - if (IsExeFileName(path.c_str())) + if (System::IsExeFileName(path.c_str())) return GetExeListEntry(path.c_str(), entry); - if (IsM3UFileName(path.c_str())) + if (System::IsM3UFileName(path.c_str())) return GetM3UListEntry(path.c_str(), entry); std::unique_ptr cdi = CDImage::Open(path.c_str()); if (!cdi) return false; - std::string code = GetGameCodeForImage(cdi.get()); - DiscRegion region = GetRegionFromSystemArea(cdi.get()); + std::string code = System::GetGameCodeForImage(cdi.get()); + DiscRegion region = System::GetRegionFromSystemArea(cdi.get()); if (region == DiscRegion::Other) - region = GetRegionForCode(code); + region = System::GetRegionForCode(code); entry->path = path; entry->code = std::move(code); @@ -428,7 +181,7 @@ bool GameList::GetGameListEntry(const std::string& path, GameListEntry* entry) if (entry->code.empty()) { // no game code, so use the filename title - entry->title = GetTitleForPath(path.c_str()); + entry->title = System::GetTitleForPath(path.c_str()); entry->compatibility_rating = GameListCompatibilityRating::Unknown; } else @@ -444,7 +197,7 @@ bool GameList::GetGameListEntry(const std::string& path, GameListEntry* entry) else { Log_WarningPrintf("'%s' not found in database", entry->code.c_str()); - entry->title = GetTitleForPath(path.c_str()); + entry->title = System::GetTitleForPath(path.c_str()); } const GameListCompatibilityEntry* compatibility_entry = GetCompatibilityEntryForCode(entry->code); @@ -825,7 +578,7 @@ public: { GameListDatabaseEntry gde; gde.code = std::move(code); - gde.region = GameList::GetRegionForCode(gde.code); + gde.region = System::GetRegionForCode(gde.code); gde.title = name; m_database.emplace(gde.code, std::move(gde)); } diff --git a/src/core/game_list.h b/src/frontend-common/game_list.h similarity index 85% rename from src/core/game_list.h rename to src/frontend-common/game_list.h index 7b4f3fc80..22503f306 100644 --- a/src/core/game_list.h +++ b/src/frontend-common/game_list.h @@ -1,6 +1,6 @@ #pragma once +#include "core/types.h" #include "game_settings.h" -#include "types.h" #include #include #include @@ -74,29 +74,9 @@ public: 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 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 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(m_entries.size()); } diff --git a/src/core/game_settings.cpp b/src/frontend-common/game_settings.cpp similarity index 99% rename from src/core/game_settings.cpp rename to src/frontend-common/game_settings.cpp index 4e347f9e4..f390398d2 100644 --- a/src/core/game_settings.cpp +++ b/src/frontend-common/game_settings.cpp @@ -5,8 +5,8 @@ #include "common/log.h" #include "common/string.h" #include "common/string_util.h" -#include "host_interface.h" -#include "settings.h" +#include "core/host_interface.h" +#include "core/settings.h" #include #include Log_SetChannel(GameSettings); diff --git a/src/core/game_settings.h b/src/frontend-common/game_settings.h similarity index 98% rename from src/core/game_settings.h rename to src/frontend-common/game_settings.h index 030e957e1..918210ab1 100644 --- a/src/core/game_settings.h +++ b/src/frontend-common/game_settings.h @@ -1,5 +1,5 @@ #pragma once -#include "types.h" +#include "core/types.h" #include #include #include