Compare commits

..

3 Commits

Author SHA1 Message Date
Kravickas
13da5a82cc
protect (#4212)
Protection change may override page fault tracking set by the GPU buffer cache. Invalidate so the cache re-uploads and re-tracks.
2026-04-05 19:44:09 +03:00
rainmakerv2
eb429be1cc
FS: easy mods folder for games (#4216)
* Implement mods folder

* Add logging when modified files exist

* get mod folder entries in MntPoints::IterateDirectory

* support eboot from mods folder
2026-04-05 15:56:18 +03:00
Niram7777
304a2c7c78
Vulkan presenter reset CommandBuffer on *_scheduler (#4221)
[Render.Vulkan] <error> (shadPS4:Main) vk_platform.cpp:58 DebugUtilsCallback: VUID-vkDestroyImage-image-01000: vkDestroyImage(): can't be called on VkImage 0x850000000085[Frame image #2] that is currently in use by VkCommandBuffer 0x560ea08752c0[CommandPool: Command Buffer 2].
The Vulkan spec states: All submitted commands that refer to image, either directly or via a VkImageView, must have completed execution (https://docs.vulkan.org/spec/latest/chapters/resources.html#VUID-vkDestroyImage-image-01000)

Finish needed because of:

[Render.Vulkan] <error> (shadPS4:Main) vk_platform.cpp:58 DebugUtilsCallback: VUID-vkEndCommandBuffer-commandBuffer-00059: vkEndCommandBuffer(): Cannot be called for VkCommandBuffer 0x55c62bfbb580[CommandPool: Command Buffer 3] when it is not in a recording state, vkBeginCommandBuffer() must first be called.
The Vulkan spec states: commandBuffer must be in the recording state (https://docs.vulkan.org/spec/latest/chapters/cmdbuffers.html#VUID-vkEndCommandBuffer-commandBuffer-00059)
2026-04-05 07:44:50 +03:00
5 changed files with 82 additions and 18 deletions

View File

@ -42,7 +42,7 @@ void MntPoints::UnmountAll() {
}
std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_read_only,
bool force_base_path) {
HostPathType path_type) {
// Evil games like Turok2 pass double slashes e.g /app0//game.kpf
std::string corrected_path(path);
size_t pos = corrected_path.find("//");
@ -80,8 +80,24 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
}
patch_path /= rel_path;
std::filesystem::path mods_path = mount->host_path;
mods_path += "-mods";
mods_path /= rel_path;
if (path_type == HostPathType::Mod) {
return mods_path;
} else if (path_type == HostPathType::Patch) {
return patch_path;
}
if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) &&
!force_base_path && !ignore_game_patches && std::filesystem::exists(patch_path)) {
path_type != HostPathType::Base && std::filesystem::exists(mods_path)) {
return mods_path;
}
if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) &&
path_type != HostPathType::Base && !ignore_game_patches &&
std::filesystem::exists(patch_path)) {
return patch_path;
}
@ -143,7 +159,7 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
return std::optional<std::filesystem::path>(current_path);
};
if (!force_base_path && !ignore_game_patches) {
if (path_type != HostPathType::Base && !ignore_game_patches) {
if (const auto path = search(patch_path)) {
return *path;
}
@ -160,34 +176,54 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
// TODO: Does not handle mount points inside mount points.
void MntPoints::IterateDirectory(std::string_view guest_directory,
const IterateDirectoryCallback& callback) {
const auto base_path = GetHostPath(guest_directory, nullptr, true);
const auto patch_path = GetHostPath(guest_directory, nullptr, false);
// Only need to consider patch path if it exists and does not resolve to the same as base.
const auto apply_patch = base_path != patch_path && std::filesystem::exists(patch_path);
const auto base_path = GetHostPath(guest_directory, nullptr, HostPathType::Base);
// Forces path types so as not to resolve to base path
const auto patch_path = GetHostPath(guest_directory, nullptr, HostPathType::Patch);
const auto mod_path = GetHostPath(guest_directory, nullptr, HostPathType::Mod);
// Prepend entries for . and .., as both are treated as files on PS4.
callback(base_path / ".", false);
callback(base_path / "..", false);
// Pass 1: Any files that existed in the base directory, using patch directory if needed.
// Pass 1: Any files that existed in the base directory, using mod/patch directory if needed.
if (std::filesystem::exists(base_path)) {
for (const auto& entry : std::filesystem::directory_iterator(base_path)) {
if (apply_patch) {
const auto patch_entry_path = patch_path / entry.path().filename();
if (std::filesystem::exists(patch_entry_path)) {
callback(patch_entry_path, !std::filesystem::is_directory(patch_entry_path));
continue;
}
const auto mod_entry_path = mod_path / entry.path().filename();
const auto patch_entry_path = patch_path / entry.path().filename();
if (std::filesystem::exists(mod_entry_path)) {
callback(mod_entry_path, !std::filesystem::is_directory(mod_entry_path));
continue;
} else if (std::filesystem::exists(patch_entry_path)) {
callback(patch_entry_path, !std::filesystem::is_directory(patch_entry_path));
continue;
}
callback(entry.path(), !entry.is_directory());
}
}
// Pass 2: Any files that exist only in the patch directory.
if (apply_patch) {
if (std::filesystem::exists(patch_path)) {
for (const auto& entry : std::filesystem::directory_iterator(patch_path)) {
const auto base_entry_path = base_path / entry.path().filename();
if (!std::filesystem::exists(base_entry_path)) {
const auto mod_entry_path = mod_path / entry.path().filename();
if (std::filesystem::exists(mod_entry_path)) {
callback(mod_entry_path, !std::filesystem::is_directory(mod_entry_path));
continue;
}
callback(entry.path(), !entry.is_directory());
}
}
}
// Pass 3: Any files that exist only in the mod directory (confirmed this can be valid)
if (std::filesystem::exists(mod_path)) {
for (const auto& entry : std::filesystem::directory_iterator(mod_path)) {
const auto base_entry_path = base_path / entry.path().filename();
const auto patch_entry_path = patch_path / entry.path().filename();
if (!std::filesystem::exists(base_entry_path) &&
!std::filesystem::exists(patch_entry_path)) {
callback(entry.path(), !entry.is_directory());
}
}

View File

@ -36,6 +36,13 @@ public:
bool read_only;
};
enum class HostPathType {
Default, // Prioritizes Mod, then patch, then base
Base,
Patch,
Mod
};
explicit MntPoints() = default;
~MntPoints() = default;
@ -45,7 +52,8 @@ public:
void UnmountAll();
std::filesystem::path GetHostPath(std::string_view guest_directory,
bool* is_read_only = nullptr, bool force_base_path = false);
bool* is_read_only = nullptr,
HostPathType host_path = HostPathType::Default);
using IterateDirectoryCallback =
std::function<void(const std::filesystem::path& host_path, bool is_file)>;
void IterateDirectory(std::string_view guest_directory,

View File

@ -339,7 +339,11 @@ s32 PS4_SYSV_ABI sceKernelMprotect(const void* addr, u64 size, s32 prot) {
Core::MemoryManager* memory_manager = Core::Memory::Instance();
Core::MemoryProt protection_flags = static_cast<Core::MemoryProt>(prot);
return memory_manager->Protect(aligned_addr, aligned_size, protection_flags);
s32 result = memory_manager->Protect(aligned_addr, aligned_size, protection_flags);
if (result == ORBIS_OK) {
memory_manager->InvalidateMemory(aligned_addr, aligned_size);
}
return result;
}
s32 PS4_SYSV_ABI posix_mprotect(const void* addr, u64 size, s32 prot) {
@ -370,6 +374,7 @@ s32 PS4_SYSV_ABI sceKernelMtypeprotect(const void* addr, u64 size, s32 mtype, s3
s32 result = memory_manager->Protect(aligned_addr, aligned_size, protection_flags);
if (result == ORBIS_OK) {
memory_manager->SetDirectMemoryType(aligned_addr, aligned_size, mtype);
memory_manager->InvalidateMemory(aligned_addr, aligned_size);
}
return result;
}

View File

@ -109,7 +109,8 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
} else {
game_folder = file.parent_path();
if (const auto game_folder_name = game_folder.filename().string();
game_folder_name.ends_with("-UPDATE") || game_folder_name.ends_with("-patch")) {
game_folder_name.ends_with("-UPDATE") || game_folder_name.ends_with("-patch") ||
game_folder_name.ends_with("-mods")) {
// If an executable was launched from a separate update directory,
// use the base game directory as the game folder.
const std::string base_name = game_folder_name.substr(0, game_folder_name.rfind('-'));
@ -292,6 +293,13 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
}
}
std::filesystem::path mods_folder = game_folder;
mods_folder += "-mods";
if (std::filesystem::exists(mods_folder) && !std::filesystem::is_empty(mods_folder)) {
LOG_INFO(Loader, "Files found in game mods folder");
}
// Create stdin/stdout/stderr
Common::Singleton<FileSys::HandleTable>::Instance()->CreateStdHandles();

View File

@ -137,7 +137,14 @@ Presenter::Presenter(Frontend::WindowSDL& window_, AmdGpu::Liverpool* liverpool_
Presenter::~Presenter() {
ImGui::Layer::RemoveLayer(Common::Singleton<Core::Devtools::Layer>::Instance());
draw_scheduler.Finish();
present_scheduler.Finish();
flip_scheduler.Finish();
Check(draw_scheduler.CommandBuffer().reset());
Check(present_scheduler.CommandBuffer().reset());
Check(flip_scheduler.CommandBuffer().reset());
const vk::Device device = instance.GetDevice();
for (auto& frame : present_frames) {
vmaDestroyImage(instance.GetAllocator(), frame.image, frame.allocation);