Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 46 additions & 5 deletions src/common/memory_patcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <string>
#include <nlohmann/json.hpp>
#include <pugixml.hpp>
#include "common/config.h"
#include "common/elf_info.h"
#include "common/logging/log.h"
#include "common/path_util.h"
Expand Down Expand Up @@ -193,16 +194,56 @@ void OnGameLoaded() {
ApplyPatchesFromXML(file_path);
}
} else if (EmulatorState::GetInstance()->IsAutoPatchesLoadEnabled()) {
if (g_game_serial.empty()) {
ApplyPendingPatches();
return;
}
for (auto const& repo : std::filesystem::directory_iterator(patch_dir)) {
if (!repo.is_directory()) {
continue;
}
std::ifstream json_file{repo.path() / "files.json"};
nlohmann::json available_patches = nlohmann::json::parse(json_file);
const auto json_path = repo.path() / "files.json";
if (!std::filesystem::exists(json_path)) {
continue;
}

std::ifstream json_file{json_path};
if (!json_file.is_open()) {
LOG_ERROR(Loader, "Failed to open patch index: {}", json_path.string());
continue;
}

nlohmann::json available_patches;
try {
available_patches = nlohmann::json::parse(json_file);
} catch (const std::exception& e) {
LOG_ERROR(Loader, "Failed to parse patch index {}: {}", json_path.string(),
e.what());
continue;
}

if (!available_patches.is_object()) {
LOG_ERROR(Loader, "Invalid patch index (expected object): {}", json_path.string());
continue;
}

std::filesystem::path game_patch_file;
for (auto const& [filename, serials] : available_patches.items()) {
if (std::find(serials.begin(), serials.end(), g_game_serial) != serials.end()) {
game_patch_file = repo.path() / filename;
for (auto it = available_patches.begin(); it != available_patches.end(); ++it) {
const auto& serials = it.value();
if (!serials.is_array()) {
continue;
}

bool matches = false;
for (const auto& serial : serials) {
if (serial.is_string() &&
serial.get_ref<const std::string&>() == g_game_serial) {
matches = true;
break;
}
}
if (matches) {
game_patch_file = repo.path() / it.key();
break;
}
}
Expand Down
48 changes: 34 additions & 14 deletions src/emulator.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <charconv>
#include <filesystem>
#include <fstream>
#include <iostream>
Expand Down Expand Up @@ -132,7 +133,7 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
std::string id;
std::string title;
std::string app_version;
u32 sdk_version;
u32 sdk_version{};
u32 fw_version;
Common::PSFAttributes psf_attributes{};
if (param_sfo_exists) {
Expand All @@ -141,10 +142,17 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,

const auto content_id = param_sfo->GetString("CONTENT_ID");
const auto title_id = param_sfo->GetString("TITLE_ID");
if (content_id.has_value() && !content_id->empty()) {
id = std::string(*content_id, 7, 9);
} else if (title_id.has_value()) {
id = *title_id;
if (content_id.has_value() && content_id->size() >= 16) {
id = content_id->substr(7, 9);
} else {
if (content_id.has_value()) {
LOG_WARNING(Loader, "CONTENT_ID too short to derive game id: {}", *content_id);
} else {
LOG_WARNING(Loader, "CONTENT_ID is missing");
}
if (title_id.has_value() && !title_id->empty()) {
id = *title_id;
}
}
title = param_sfo->GetString("TITLE").value_or("Unknown title");
fw_version = param_sfo->GetInteger("SYSTEM_VER").value_or(0x4700000);
Expand All @@ -164,16 +172,28 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
} else {
// Increment offset to account for sdk_ver= part of string.
sdk_ver_offset += 8;
u64 sdk_ver_len = pubtool_info.find(",", sdk_ver_offset);
if (sdk_ver_len == pubtool_info.npos) {
// If there's no more commas, this is likely the last entry of pubtool info.
// Use string length instead.
sdk_ver_len = pubtool_info.size();
if (sdk_ver_offset > pubtool_info.size()) {
LOG_WARNING(Loader, "PUBTOOLINFO sdk_ver is malformed");
sdk_version = fw_version;
} else {
u64 sdk_ver_len = pubtool_info.find(",", sdk_ver_offset);
if (sdk_ver_len == pubtool_info.npos) {
// If there's no more commas, this is likely the last entry of pubtool info.
// Use string length instead.
sdk_ver_len = pubtool_info.size();
}
sdk_ver_len -= sdk_ver_offset;
std::string_view sdk_ver_string = pubtool_info.substr(sdk_ver_offset, sdk_ver_len);
// Number is stored in base 16.
auto result =
std::from_chars(sdk_ver_string.data(),
sdk_ver_string.data() + sdk_ver_string.size(), sdk_version, 16);
if (result.ec != std::errc()) {
LOG_WARNING(Loader, "Failed to parse sdk_ver '{}' from PUBTOOLINFO",
sdk_ver_string);
sdk_version = fw_version;
}
}
sdk_ver_len -= sdk_ver_offset;
std::string sdk_ver_string = pubtool_info.substr(sdk_ver_offset, sdk_ver_len).data();
// Number is stored in base 16.
sdk_version = std::stoi(sdk_ver_string, nullptr, 16);
}
}

Expand Down