Add initial game list class implementation
This commit is contained in:
parent
3b11d936df
commit
04c70b3118
|
@ -19,6 +19,8 @@ add_library(core
|
||||||
digital_controller.h
|
digital_controller.h
|
||||||
dma.cpp
|
dma.cpp
|
||||||
dma.h
|
dma.h
|
||||||
|
game_list.cpp
|
||||||
|
game_list.h
|
||||||
gpu.cpp
|
gpu.cpp
|
||||||
gpu.h
|
gpu.h
|
||||||
gpu_commands.cpp
|
gpu_commands.cpp
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
<ClCompile Include="cpu_recompiler_thunks.cpp" />
|
<ClCompile Include="cpu_recompiler_thunks.cpp" />
|
||||||
<ClCompile Include="cpu_types.cpp" />
|
<ClCompile Include="cpu_types.cpp" />
|
||||||
<ClCompile Include="digital_controller.cpp" />
|
<ClCompile Include="digital_controller.cpp" />
|
||||||
|
<ClCompile Include="game_list.cpp" />
|
||||||
<ClCompile Include="gpu_commands.cpp" />
|
<ClCompile Include="gpu_commands.cpp" />
|
||||||
<ClCompile Include="gpu_hw_d3d11.cpp" />
|
<ClCompile Include="gpu_hw_d3d11.cpp" />
|
||||||
<ClCompile Include="gpu_hw_shadergen.cpp" />
|
<ClCompile Include="gpu_hw_shadergen.cpp" />
|
||||||
|
@ -80,6 +81,7 @@
|
||||||
<ClInclude Include="cpu_recompiler_thunks.h" />
|
<ClInclude Include="cpu_recompiler_thunks.h" />
|
||||||
<ClInclude Include="cpu_recompiler_types.h" />
|
<ClInclude Include="cpu_recompiler_types.h" />
|
||||||
<ClInclude Include="digital_controller.h" />
|
<ClInclude Include="digital_controller.h" />
|
||||||
|
<ClInclude Include="game_list.h" />
|
||||||
<ClInclude Include="gpu_hw_d3d11.h" />
|
<ClInclude Include="gpu_hw_d3d11.h" />
|
||||||
<ClInclude Include="gpu_hw_shadergen.h" />
|
<ClInclude Include="gpu_hw_shadergen.h" />
|
||||||
<ClInclude Include="gpu_sw.h" />
|
<ClInclude Include="gpu_sw.h" />
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
<ClCompile Include="cpu_recompiler_code_generator.cpp" />
|
<ClCompile Include="cpu_recompiler_code_generator.cpp" />
|
||||||
<ClCompile Include="cpu_recompiler_code_generator_generic.cpp" />
|
<ClCompile Include="cpu_recompiler_code_generator_generic.cpp" />
|
||||||
<ClCompile Include="cpu_types.cpp" />
|
<ClCompile Include="cpu_types.cpp" />
|
||||||
|
<ClCompile Include="game_list.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="types.h" />
|
<ClInclude Include="types.h" />
|
||||||
|
@ -69,6 +70,7 @@
|
||||||
<ClInclude Include="cpu_recompiler_register_cache.h" />
|
<ClInclude Include="cpu_recompiler_register_cache.h" />
|
||||||
<ClInclude Include="cpu_recompiler_thunks.h" />
|
<ClInclude Include="cpu_recompiler_thunks.h" />
|
||||||
<ClInclude Include="cpu_recompiler_code_generator.h" />
|
<ClInclude Include="cpu_recompiler_code_generator.h" />
|
||||||
|
<ClInclude Include="game_list.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="cpu_core.inl" />
|
<None Include="cpu_core.inl" />
|
||||||
|
|
|
@ -0,0 +1,185 @@
|
||||||
|
#include "game_list.h"
|
||||||
|
#include "YBaseLib/CString.h"
|
||||||
|
#include "YBaseLib/FileSystem.h"
|
||||||
|
#include "YBaseLib/Log.h"
|
||||||
|
#include "common/cd_image.h"
|
||||||
|
#include "common/iso_reader.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <utility>
|
||||||
|
Log_SetChannel(GameList);
|
||||||
|
|
||||||
|
GameList::GameList() = default;
|
||||||
|
|
||||||
|
GameList::~GameList() = default;
|
||||||
|
|
||||||
|
std::string GameList::GetGameCodeForPath(const char* image_path)
|
||||||
|
{
|
||||||
|
std::unique_ptr<CDImage> 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<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 == ' ')
|
||||||
|
{
|
||||||
|
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 Y_stricmp(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)
|
||||||
|
return {};
|
||||||
|
code.erase(0, pos + 1);
|
||||||
|
pos = code.find(';');
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
code.erase(pos);
|
||||||
|
|
||||||
|
// SCES_123.45 -> SCES-12345
|
||||||
|
for (pos = 0; pos < code.size();)
|
||||||
|
{
|
||||||
|
if (code[pos] == '_')
|
||||||
|
code[pos++] = '-';
|
||||||
|
else if (code[pos] == '.')
|
||||||
|
code.erase(pos, 1);
|
||||||
|
else
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ConsoleRegion> 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<char>(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: PAPX?
|
||||||
|
|
||||||
|
if (prefix == "sces" || prefix == "sced" || prefix == "sles" || prefix == "sled")
|
||||||
|
return ConsoleRegion::PAL;
|
||||||
|
else if (prefix == "scps" || prefix == "scpd" || prefix == "slps" || prefix == "slpd")
|
||||||
|
return ConsoleRegion::NTSC_J;
|
||||||
|
else if (prefix == "scus" || prefix == "slus")
|
||||||
|
return ConsoleRegion::NTSC_U;
|
||||||
|
else
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameList::AddDirectory(const char* path, bool recursive)
|
||||||
|
{
|
||||||
|
ScanDirectory(path, recursive);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GameList::GetGameListEntry(const char* path, GameListEntry* entry)
|
||||||
|
{
|
||||||
|
std::unique_ptr<CDImage> cdi = CDImage::Open(path);
|
||||||
|
if (!cdi)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string game_code = GetGameCodeForImage(cdi.get());
|
||||||
|
|
||||||
|
entry->path = path;
|
||||||
|
entry->title = game_code;
|
||||||
|
entry->code = game_code;
|
||||||
|
entry->region = GetRegionForCode(game_code).value_or(ConsoleRegion::NTSC_U);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameList::ScanDirectory(const char* path, bool recursive)
|
||||||
|
{
|
||||||
|
Log_DevPrintf("Scanning %s%s", path, recursive ? " (recursively)" : "");
|
||||||
|
|
||||||
|
FileSystem::FindResultsArray files;
|
||||||
|
FileSystem::FindFiles(path, "*", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_RECURSIVE, &files);
|
||||||
|
|
||||||
|
GameListEntry entry;
|
||||||
|
for (const FILESYSTEM_FIND_DATA& ffd : files)
|
||||||
|
{
|
||||||
|
Log_DebugPrintf("Trying '%s'...", ffd.FileName);
|
||||||
|
|
||||||
|
// if this is a .bin, check if we have a .cue. if there is one, skip it
|
||||||
|
const char* extension = std::strrchr(ffd.FileName, '.');
|
||||||
|
if (extension && Y_stricmp(extension, ".bin") == 0)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
std::string temp(ffd.FileName, extension - ffd.FileName);
|
||||||
|
temp += ".cue";
|
||||||
|
if (std::any_of(files.begin(), files.end(),
|
||||||
|
[&temp](const FILESYSTEM_FIND_DATA& it) { return Y_stricmp(it.FileName, temp.c_str()) == 0; }))
|
||||||
|
{
|
||||||
|
Log_DebugPrintf("Skipping due to '%s' existing", temp.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
continue;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// try opening the image
|
||||||
|
if (GetGameListEntry(ffd.FileName, &entry))
|
||||||
|
{
|
||||||
|
m_entries.push_back(std::move(entry));
|
||||||
|
entry = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
#pragma once
|
||||||
|
#include "types.h"
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
class CDImage;
|
||||||
|
|
||||||
|
class GameList
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GameList();
|
||||||
|
~GameList();
|
||||||
|
|
||||||
|
static std::string GetGameCodeForImage(CDImage* cdi);
|
||||||
|
static std::string GetGameCodeForPath(const char* image_path);
|
||||||
|
static std::optional<ConsoleRegion> GetRegionForCode(std::string_view code);
|
||||||
|
|
||||||
|
void AddDirectory(const char* path, bool recursive);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct GameListEntry
|
||||||
|
{
|
||||||
|
std::string path;
|
||||||
|
std::string code;
|
||||||
|
std::string title;
|
||||||
|
ConsoleRegion region;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool GetGameListEntry(const char* path, GameListEntry* entry);
|
||||||
|
|
||||||
|
void ScanDirectory(const char* path, bool recursive);
|
||||||
|
|
||||||
|
std::vector<GameListEntry> m_entries;
|
||||||
|
};
|
Loading…
Reference in New Issue