mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-25 15:15:40 +00:00
System: Generate hash serials when running PS-EXE
Allows for per-game settings.
This commit is contained in:
parent
25bf2b3adc
commit
7682cd2c10
|
@ -155,9 +155,10 @@ bool GameList::GetExeListEntry(const std::string& path, GameList::Entry* entry)
|
|||
return false;
|
||||
}
|
||||
|
||||
const std::string display_name(FileSystem::GetDisplayNameFromPath(path));
|
||||
entry->serial.clear();
|
||||
entry->title = Path::GetFileTitle(display_name);
|
||||
const System::GameHash hash = System::GetGameHashFromFile(path.c_str());
|
||||
|
||||
entry->serial = hash ? System::GetGameHashId(hash) : std::string();
|
||||
entry->title = Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path));
|
||||
entry->region = BIOS::GetPSExeDiscRegion(header);
|
||||
entry->file_size = ZeroExtend64(file_size);
|
||||
entry->uncompressed_size = entry->file_size;
|
||||
|
|
|
@ -114,6 +114,8 @@ static bool LoadEXE(const char* filename);
|
|||
static std::string GetExecutableNameForImage(IsoReader& iso, bool strip_subdirectories);
|
||||
static bool ReadExecutableFromImage(IsoReader& iso, std::string* out_executable_name,
|
||||
std::vector<u8>* out_executable_data);
|
||||
static GameHash GetGameHashFromBuffer(std::string_view exe_name, std::span<const u8> exe_buffer,
|
||||
const IsoReader::ISOPrimaryVolumeDescriptor& iso_pvd, u32 track_1_length);
|
||||
|
||||
static bool LoadBIOS(Error* error);
|
||||
static void InternalReset();
|
||||
|
@ -700,15 +702,7 @@ bool System::GetGameDetailsFromImage(CDImage* cdi, std::string* out_id, GameHash
|
|||
}
|
||||
|
||||
// Always compute the hash.
|
||||
const u32 track_1_length = cdi->GetTrackLength(1);
|
||||
XXH64_state_t* state = XXH64_createState();
|
||||
XXH64_reset(state, 0x4242D00C);
|
||||
XXH64_update(state, exe_name.c_str(), exe_name.size());
|
||||
XXH64_update(state, exe_buffer.data(), exe_buffer.size());
|
||||
XXH64_update(state, &iso.GetPVD(), sizeof(IsoReader::ISOPrimaryVolumeDescriptor));
|
||||
XXH64_update(state, &track_1_length, sizeof(track_1_length));
|
||||
const GameHash hash = XXH64_digest(state);
|
||||
XXH64_freeState(state);
|
||||
const GameHash hash = GetGameHashFromBuffer(exe_name, exe_buffer, iso.GetPVD(), cdi->GetTrackLength(1));
|
||||
DEV_LOG("Hash for '{}' - {:016X}", exe_name, hash);
|
||||
|
||||
if (exe_name != FALLBACK_EXE_NAME)
|
||||
|
@ -752,6 +746,16 @@ bool System::GetGameDetailsFromImage(CDImage* cdi, std::string* out_id, GameHash
|
|||
return true;
|
||||
}
|
||||
|
||||
System::GameHash System::GetGameHashFromFile(const char* path)
|
||||
{
|
||||
const std::optional<std::vector<u8>> data = FileSystem::ReadBinaryFile(path);
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
const std::string display_name = FileSystem::GetDisplayNameFromPath(path);
|
||||
return GetGameHashFromBuffer(display_name, data.value(), IsoReader::ISOPrimaryVolumeDescriptor{}, 0);
|
||||
}
|
||||
|
||||
std::string System::GetExecutableNameForImage(IsoReader& iso, bool strip_subdirectories)
|
||||
{
|
||||
// Read SYSTEM.CNF
|
||||
|
@ -881,6 +885,20 @@ bool System::ReadExecutableFromImage(IsoReader& iso, std::string* out_executable
|
|||
return true;
|
||||
}
|
||||
|
||||
System::GameHash System::GetGameHashFromBuffer(std::string_view exe_name, std::span<const u8> exe_buffer,
|
||||
const IsoReader::ISOPrimaryVolumeDescriptor& iso_pvd, u32 track_1_length)
|
||||
{
|
||||
XXH64_state_t* state = XXH64_createState();
|
||||
XXH64_reset(state, 0x4242D00C);
|
||||
XXH64_update(state, exe_name.data(), exe_name.size());
|
||||
XXH64_update(state, exe_buffer.data(), exe_buffer.size());
|
||||
XXH64_update(state, &iso_pvd, sizeof(IsoReader::ISOPrimaryVolumeDescriptor));
|
||||
XXH64_update(state, &track_1_length, sizeof(track_1_length));
|
||||
const GameHash hash = XXH64_digest(state);
|
||||
XXH64_freeState(state);
|
||||
return hash;
|
||||
}
|
||||
|
||||
DiscRegion System::GetRegionForSerial(std::string_view serial)
|
||||
{
|
||||
std::string prefix;
|
||||
|
@ -1470,12 +1488,12 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
// Load CD image up and detect region.
|
||||
std::unique_ptr<CDImage> disc;
|
||||
DiscRegion disc_region = DiscRegion::NonPS1;
|
||||
std::string exe_boot;
|
||||
std::string psf_boot;
|
||||
bool do_exe_boot = false;
|
||||
bool do_psf_boot = false;
|
||||
if (!parameters.filename.empty())
|
||||
{
|
||||
const bool do_exe_boot = IsExeFileName(parameters.filename);
|
||||
const bool do_psf_boot = (!do_exe_boot && IsPsfFileName(parameters.filename));
|
||||
do_exe_boot = IsExeFileName(parameters.filename);
|
||||
do_psf_boot = (!do_exe_boot && IsPsfFileName(parameters.filename));
|
||||
if (do_exe_boot || do_psf_boot)
|
||||
{
|
||||
if (s_region == ConsoleRegion::Auto)
|
||||
|
@ -1485,10 +1503,6 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
INFO_LOG("EXE/PSF Region: {}", Settings::GetDiscRegionDisplayName(file_region));
|
||||
s_region = GetConsoleRegionForDiscRegion(file_region);
|
||||
}
|
||||
if (do_psf_boot)
|
||||
psf_boot = std::move(parameters.filename);
|
||||
else
|
||||
exe_boot = std::move(parameters.filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1544,6 +1558,8 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
// Update running game, this will apply settings as well.
|
||||
UpdateRunningGame(disc ? disc->GetFileName().c_str() : parameters.filename.c_str(), disc.get(), true);
|
||||
|
||||
// Get boot EXE override.
|
||||
std::string exe_boot;
|
||||
if (!parameters.override_exe.empty())
|
||||
{
|
||||
if (!FileSystem::FileExists(parameters.override_exe.c_str()) || !IsExeFileName(parameters.override_exe))
|
||||
|
@ -1559,6 +1575,10 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
INFO_LOG("Overriding boot executable: '{}'", parameters.override_exe);
|
||||
exe_boot = std::move(parameters.override_exe);
|
||||
}
|
||||
else if (do_exe_boot)
|
||||
{
|
||||
exe_boot = std::move(parameters.filename);
|
||||
}
|
||||
|
||||
// Check for SBI.
|
||||
if (!CheckForSBIFile(disc.get(), error))
|
||||
|
@ -1639,9 +1659,9 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
DestroySystem();
|
||||
return false;
|
||||
}
|
||||
else if (!psf_boot.empty() && !PSFLoader::Load(psf_boot.c_str()))
|
||||
else if (do_psf_boot && !PSFLoader::Load(parameters.filename.c_str()))
|
||||
{
|
||||
Error::SetStringFmt(error, "Failed to load PSF file '{}'", Path::GetFileName(psf_boot));
|
||||
Error::SetStringFmt(error, "Failed to load PSF file '{}'", Path::GetFileName(parameters.filename));
|
||||
DestroySystem();
|
||||
return false;
|
||||
}
|
||||
|
@ -3658,7 +3678,14 @@ void System::UpdateRunningGame(const char* path, CDImage* image, bool booting)
|
|||
{
|
||||
s_running_game_path = path;
|
||||
|
||||
if (IsExeFileName(path) || IsPsfFileName(path))
|
||||
if (IsExeFileName(path))
|
||||
{
|
||||
s_running_game_title = Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path));
|
||||
s_running_game_hash = GetGameHashFromFile(path);
|
||||
if (s_running_game_hash != 0)
|
||||
s_running_game_serial = GetGameHashId(s_running_game_hash);
|
||||
}
|
||||
else if (IsPsfFileName(path))
|
||||
{
|
||||
// TODO: We could pull the title from the PSF.
|
||||
s_running_game_title = Path::GetFileTitle(path);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
|
@ -133,6 +134,7 @@ bool ReadExecutableFromImage(CDImage* cdi, std::string* out_executable_name, std
|
|||
|
||||
std::string GetGameHashId(GameHash hash);
|
||||
bool GetGameDetailsFromImage(CDImage* cdi, std::string* out_id, GameHash* out_hash);
|
||||
GameHash GetGameHashFromFile(const char* path);
|
||||
DiscRegion GetRegionForSerial(std::string_view serial);
|
||||
DiscRegion GetRegionFromSystemArea(CDImage* cdi);
|
||||
DiscRegion GetRegionForImage(CDImage* cdi);
|
||||
|
|
|
@ -1347,12 +1347,16 @@ void MainWindow::onViewGamePropertiesActionTriggered()
|
|||
if (!s_system_valid)
|
||||
return;
|
||||
|
||||
const std::string& path = System::GetDiscPath();
|
||||
const std::string& serial = System::GetGameSerial();
|
||||
if (path.empty() || serial.empty())
|
||||
return;
|
||||
Host::RunOnCPUThread([]() {
|
||||
const std::string& path = System::GetDiscPath();
|
||||
const std::string& serial = System::GetGameSerial();
|
||||
if (path.empty() || serial.empty())
|
||||
return;
|
||||
|
||||
SettingsWindow::openGamePropertiesDialog(path, serial, System::GetDiscRegion());
|
||||
QtHost::RunOnUIThread([path = path, serial = serial]() {
|
||||
SettingsWindow::openGamePropertiesDialog(path, System::GetGameTitle(), serial, System::GetDiscRegion());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::onGitHubRepositoryActionTriggered()
|
||||
|
@ -1448,8 +1452,9 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
|
|||
{
|
||||
if (!entry->IsDiscSet())
|
||||
{
|
||||
connect(menu.addAction(tr("Properties...")), &QAction::triggered,
|
||||
[entry]() { SettingsWindow::openGamePropertiesDialog(entry->path, entry->serial, entry->region); });
|
||||
connect(menu.addAction(tr("Properties...")), &QAction::triggered, [entry]() {
|
||||
SettingsWindow::openGamePropertiesDialog(entry->path, entry->title, entry->serial, entry->region);
|
||||
});
|
||||
|
||||
connect(menu.addAction(tr("Open Containing Directory...")), &QAction::triggered, [this, entry]() {
|
||||
const QFileInfo fi(QString::fromStdString(entry->path));
|
||||
|
@ -1516,7 +1521,10 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
|
|||
auto lock = GameList::GetLock();
|
||||
const GameList::Entry* first_disc = GameList::GetFirstDiscSetMember(disc_set_name);
|
||||
if (first_disc)
|
||||
SettingsWindow::openGamePropertiesDialog(first_disc->path, first_disc->serial, first_disc->region);
|
||||
{
|
||||
SettingsWindow::openGamePropertiesDialog(first_disc->path, first_disc->title, first_disc->serial,
|
||||
first_disc->region);
|
||||
}
|
||||
});
|
||||
|
||||
connect(menu.addAction(tr("Set Cover Image...")), &QAction::triggered,
|
||||
|
|
|
@ -611,7 +611,8 @@ void SettingsWindow::saveAndReloadGameSettings()
|
|||
g_emu_thread->reloadGameSettings(false);
|
||||
}
|
||||
|
||||
void SettingsWindow::openGamePropertiesDialog(const std::string& path, const std::string& serial, DiscRegion region)
|
||||
void SettingsWindow::openGamePropertiesDialog(const std::string& path, const std::string& title,
|
||||
const std::string& serial, DiscRegion region)
|
||||
{
|
||||
const GameDatabase::Entry* dentry = nullptr;
|
||||
if (!System::IsExeFileName(path) && !System::IsPsfFileName(path))
|
||||
|
@ -652,9 +653,8 @@ void SettingsWindow::openGamePropertiesDialog(const std::string& path, const std
|
|||
if (FileSystem::FileExists(sif->GetFileName().c_str()))
|
||||
sif->Load();
|
||||
|
||||
const QString window_title(tr("%1 [%2]")
|
||||
.arg(dentry ? QtUtils::StringViewToQString(dentry->title) : QStringLiteral("<UNKNOWN>"))
|
||||
.arg(QtUtils::StringViewToQString(real_serial)));
|
||||
const QString window_title(
|
||||
tr("%1 [%2]").arg(QString::fromStdString(dentry ? dentry->title : title)).arg(QString::fromStdString(real_serial)));
|
||||
|
||||
SettingsWindow* dialog = new SettingsWindow(path, real_serial, region, dentry, std::move(sif));
|
||||
dialog->setWindowTitle(window_title);
|
||||
|
|
|
@ -44,7 +44,8 @@ public:
|
|||
const GameDatabase::Entry* entry, std::unique_ptr<INISettingsInterface> sif);
|
||||
~SettingsWindow();
|
||||
|
||||
static void openGamePropertiesDialog(const std::string& path, const std::string& serial, DiscRegion region);
|
||||
static void openGamePropertiesDialog(const std::string& path, const std::string& title, const std::string& serial,
|
||||
DiscRegion region);
|
||||
static void closeGamePropertiesDialogs();
|
||||
|
||||
// Helper for externally setting fields in game settings ini.
|
||||
|
|
Loading…
Reference in a new issue