mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-05 08:52:36 +00:00
Merge branch 'shadps4-emu:main' into main
This commit is contained in:
commit
613ae4fc83
72
.github/workflows/build.yml
vendored
72
.github/workflows/build.yml
vendored
@ -376,6 +376,78 @@ jobs:
|
|||||||
name: shadps4-linux-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
|
name: shadps4-linux-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
|
||||||
path: Shadps4-qt.AppImage
|
path: Shadps4-qt.AppImage
|
||||||
|
|
||||||
|
linux-sdl-gcc:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
needs: get-info
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev
|
||||||
|
|
||||||
|
- name: Cache CMake Configuration
|
||||||
|
uses: actions/cache@v4
|
||||||
|
env:
|
||||||
|
cache-name: ${{ runner.os }}-sdl-cache-cmake-configuration
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
${{github.workspace}}/build
|
||||||
|
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ env.cache-name }}-
|
||||||
|
|
||||||
|
- name: Cache CMake Build
|
||||||
|
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||||
|
env:
|
||||||
|
cache-name: ${{ runner.os }}-sdl-cache-cmake-build
|
||||||
|
with:
|
||||||
|
append-timestamp: false
|
||||||
|
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||||
|
|
||||||
|
- name: Configure CMake
|
||||||
|
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
|
||||||
|
|
||||||
|
linux-qt-gcc:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
needs: get-info
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev
|
||||||
|
|
||||||
|
- name: Cache CMake Configuration
|
||||||
|
uses: actions/cache@v4
|
||||||
|
env:
|
||||||
|
cache-name: ${{ runner.os }}-qt-cache-cmake-configuration
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
${{github.workspace}}/build
|
||||||
|
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ env.cache-name }}-
|
||||||
|
|
||||||
|
- name: Cache CMake Build
|
||||||
|
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||||
|
env:
|
||||||
|
cache-name: ${{ runner.os }}-qt-cache-cmake-build
|
||||||
|
with:
|
||||||
|
append-timestamp: false
|
||||||
|
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||||
|
|
||||||
|
- name: Configure CMake
|
||||||
|
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
|
||||||
|
|
||||||
pre-release:
|
pre-release:
|
||||||
if: github.ref == 'refs/heads/main' && github.repository == 'shadps4-emu/shadPS4' && github.event_name == 'push'
|
if: github.ref == 'refs/heads/main' && github.repository == 'shadps4-emu/shadPS4' && github.event_name == 'push'
|
||||||
needs: [get-info, windows-sdl, windows-qt, macos-sdl, macos-qt, linux-sdl, linux-qt]
|
needs: [get-info, windows-sdl, windows-qt, macos-sdl, macos-qt, linux-sdl, linux-qt]
|
||||||
|
2
externals/CMakeLists.txt
vendored
2
externals/CMakeLists.txt
vendored
@ -213,9 +213,7 @@ endif()
|
|||||||
|
|
||||||
# Discord RPC
|
# Discord RPC
|
||||||
if (ENABLE_DISCORD_RPC)
|
if (ENABLE_DISCORD_RPC)
|
||||||
set(BUILD_EXAMPLES OFF)
|
|
||||||
add_subdirectory(discord-rpc)
|
add_subdirectory(discord-rpc)
|
||||||
target_include_directories(discord-rpc INTERFACE discord-rpc/include)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# GCN Headers
|
# GCN Headers
|
||||||
|
2
externals/discord-rpc
vendored
2
externals/discord-rpc
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 4ec218155d73bcb8022f8f7ca72305d801f84beb
|
Subproject commit 51b09d426a4a1bcfa6ee6d4894e57d669f4a2e65
|
@ -40,7 +40,8 @@ void MntPoints::UnmountAll() {
|
|||||||
m_mnt_pairs.clear();
|
m_mnt_pairs.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_read_only) {
|
std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_read_only,
|
||||||
|
bool force_base_path) {
|
||||||
// Evil games like Turok2 pass double slashes e.g /app0//game.kpf
|
// Evil games like Turok2 pass double slashes e.g /app0//game.kpf
|
||||||
std::string corrected_path(path);
|
std::string corrected_path(path);
|
||||||
size_t pos = corrected_path.find("//");
|
size_t pos = corrected_path.find("//");
|
||||||
@ -72,7 +73,7 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
|
|||||||
patch_path /= rel_path;
|
patch_path /= rel_path;
|
||||||
|
|
||||||
if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) &&
|
if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) &&
|
||||||
std::filesystem::exists(patch_path)) {
|
!force_base_path && std::filesystem::exists(patch_path)) {
|
||||||
return patch_path;
|
return patch_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,9 +133,11 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
|
|||||||
return std::optional<std::filesystem::path>(current_path);
|
return std::optional<std::filesystem::path>(current_path);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!force_base_path) {
|
||||||
if (const auto path = search(patch_path)) {
|
if (const auto path = search(patch_path)) {
|
||||||
return *path;
|
return *path;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (const auto path = search(host_path)) {
|
if (const auto path = search(host_path)) {
|
||||||
return *path;
|
return *path;
|
||||||
}
|
}
|
||||||
@ -144,6 +147,39 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
|
|||||||
return host_path;
|
return host_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Pass 1: Any files that existed in the base directory, using 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback(entry.path(), !entry.is_directory());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass 2: Any files that exist only in the patch directory.
|
||||||
|
if (apply_patch) {
|
||||||
|
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)) {
|
||||||
|
callback(entry.path(), !entry.is_directory());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int HandleTable::CreateHandle() {
|
int HandleTable::CreateHandle() {
|
||||||
std::scoped_lock lock{m_mutex};
|
std::scoped_lock lock{m_mutex};
|
||||||
|
|
||||||
|
@ -36,7 +36,11 @@ public:
|
|||||||
void UnmountAll();
|
void UnmountAll();
|
||||||
|
|
||||||
std::filesystem::path GetHostPath(std::string_view guest_directory,
|
std::filesystem::path GetHostPath(std::string_view guest_directory,
|
||||||
bool* is_read_only = nullptr);
|
bool* is_read_only = nullptr, bool force_base_path = false);
|
||||||
|
using IterateDirectoryCallback =
|
||||||
|
std::function<void(const std::filesystem::path& host_path, bool is_file)>;
|
||||||
|
void IterateDirectory(std::string_view guest_directory,
|
||||||
|
const IterateDirectoryCallback& callback);
|
||||||
|
|
||||||
const MntPair* GetMountFromHostPath(const std::string& host_path) {
|
const MntPair* GetMountFromHostPath(const std::string& host_path) {
|
||||||
std::scoped_lock lock{m_mutex};
|
std::scoped_lock lock{m_mutex};
|
||||||
|
@ -46,17 +46,6 @@ static std::map<std::string, FactoryDevice> available_device = {
|
|||||||
|
|
||||||
namespace Libraries::Kernel {
|
namespace Libraries::Kernel {
|
||||||
|
|
||||||
auto GetDirectoryEntries(const std::filesystem::path& path) {
|
|
||||||
std::vector<Core::FileSys::DirEntry> files;
|
|
||||||
for (const auto& entry : std::filesystem::directory_iterator(path)) {
|
|
||||||
auto& dir_entry = files.emplace_back();
|
|
||||||
dir_entry.name = entry.path().filename().string();
|
|
||||||
dir_entry.isFile = !std::filesystem::is_directory(entry.path().string());
|
|
||||||
}
|
|
||||||
|
|
||||||
return files;
|
|
||||||
}
|
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) {
|
int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) {
|
||||||
LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {}", raw_path, flags, mode);
|
LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {}", raw_path, flags, mode);
|
||||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||||
@ -115,7 +104,12 @@ int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) {
|
|||||||
if (create) {
|
if (create) {
|
||||||
return handle; // dir already exists
|
return handle; // dir already exists
|
||||||
} else {
|
} else {
|
||||||
file->dirents = GetDirectoryEntries(file->m_host_name);
|
mnt->IterateDirectory(file->m_guest_name,
|
||||||
|
[&file](const auto& ent_path, const auto ent_is_file) {
|
||||||
|
auto& dir_entry = file->dirents.emplace_back();
|
||||||
|
dir_entry.name = ent_path.filename().string();
|
||||||
|
dir_entry.isFile = ent_is_file;
|
||||||
|
});
|
||||||
file->dirents_index = 0;
|
file->dirents_index = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -695,66 +689,12 @@ static int GetDents(int fd, char* buf, int nbytes, s64* basep) {
|
|||||||
return sizeof(OrbisKernelDirent);
|
return sizeof(OrbisKernelDirent);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int HandleSeparateUpdateDents(int fd, char* buf, int nbytes, s64* basep) {
|
|
||||||
int dir_entries = 0;
|
|
||||||
|
|
||||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
|
||||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
|
||||||
auto* file = h->GetFile(fd);
|
|
||||||
auto update_dir_name = std::string{fmt::UTF(file->m_host_name.u8string()).data};
|
|
||||||
auto mount = mnt->GetMountFromHostPath(update_dir_name);
|
|
||||||
auto suffix = std::string{fmt::UTF(mount->host_path.u8string()).data};
|
|
||||||
|
|
||||||
size_t pos = update_dir_name.find("-UPDATE");
|
|
||||||
if (pos != std::string::npos) {
|
|
||||||
update_dir_name.erase(pos, 7);
|
|
||||||
auto guest_name = mount->mount + "/" + update_dir_name.substr(suffix.size() + 1);
|
|
||||||
int descriptor;
|
|
||||||
|
|
||||||
auto existent_folder = h->GetFile(update_dir_name);
|
|
||||||
if (!existent_folder) {
|
|
||||||
u32 handle = h->CreateHandle();
|
|
||||||
auto* new_file = h->GetFile(handle);
|
|
||||||
new_file->type = Core::FileSys::FileType::Directory;
|
|
||||||
new_file->m_guest_name = guest_name;
|
|
||||||
new_file->m_host_name = update_dir_name;
|
|
||||||
if (!std::filesystem::is_directory(new_file->m_host_name)) {
|
|
||||||
h->DeleteHandle(handle);
|
|
||||||
return dir_entries;
|
|
||||||
} else {
|
|
||||||
new_file->dirents = GetDirectoryEntries(new_file->m_host_name);
|
|
||||||
new_file->dirents_index = 0;
|
|
||||||
}
|
|
||||||
new_file->is_opened = true;
|
|
||||||
descriptor = h->GetFileDescriptor(new_file);
|
|
||||||
} else {
|
|
||||||
descriptor = h->GetFileDescriptor(existent_folder);
|
|
||||||
}
|
|
||||||
|
|
||||||
dir_entries = GetDents(descriptor, buf, nbytes, basep);
|
|
||||||
if (dir_entries == ORBIS_OK && existent_folder) {
|
|
||||||
existent_folder->dirents_index = 0;
|
|
||||||
file->dirents_index = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dir_entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelGetdents(int fd, char* buf, int nbytes) {
|
int PS4_SYSV_ABI sceKernelGetdents(int fd, char* buf, int nbytes) {
|
||||||
int a = GetDents(fd, buf, nbytes, nullptr);
|
return GetDents(fd, buf, nbytes, nullptr);
|
||||||
if (a == ORBIS_OK) {
|
|
||||||
return HandleSeparateUpdateDents(fd, buf, nbytes, nullptr);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelGetdirentries(int fd, char* buf, int nbytes, s64* basep) {
|
int PS4_SYSV_ABI sceKernelGetdirentries(int fd, char* buf, int nbytes, s64* basep) {
|
||||||
int a = GetDents(fd, buf, nbytes, basep);
|
return GetDents(fd, buf, nbytes, basep);
|
||||||
if (a == ORBIS_OK) {
|
|
||||||
return HandleSeparateUpdateDents(fd, buf, nbytes, basep);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset) {
|
s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset) {
|
||||||
|
@ -171,10 +171,11 @@ int MemoryManager::PoolReserve(void** out_addr, VAddr virtual_addr, size_t size,
|
|||||||
|
|
||||||
// Fixed mapping means the virtual address must exactly match the provided one.
|
// Fixed mapping means the virtual address must exactly match the provided one.
|
||||||
if (True(flags & MemoryMapFlags::Fixed)) {
|
if (True(flags & MemoryMapFlags::Fixed)) {
|
||||||
const auto& vma = FindVMA(mapped_addr)->second;
|
auto& vma = FindVMA(mapped_addr)->second;
|
||||||
// If the VMA is mapped, unmap the region first.
|
// If the VMA is mapped, unmap the region first.
|
||||||
if (vma.IsMapped()) {
|
if (vma.IsMapped()) {
|
||||||
UnmapMemoryImpl(mapped_addr, size);
|
UnmapMemoryImpl(mapped_addr, size);
|
||||||
|
vma = FindVMA(mapped_addr)->second;
|
||||||
}
|
}
|
||||||
const size_t remaining_size = vma.base + vma.size - mapped_addr;
|
const size_t remaining_size = vma.base + vma.size - mapped_addr;
|
||||||
ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size);
|
ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size);
|
||||||
@ -208,10 +209,11 @@ int MemoryManager::Reserve(void** out_addr, VAddr virtual_addr, size_t size, Mem
|
|||||||
|
|
||||||
// Fixed mapping means the virtual address must exactly match the provided one.
|
// Fixed mapping means the virtual address must exactly match the provided one.
|
||||||
if (True(flags & MemoryMapFlags::Fixed)) {
|
if (True(flags & MemoryMapFlags::Fixed)) {
|
||||||
const auto& vma = FindVMA(mapped_addr)->second;
|
auto& vma = FindVMA(mapped_addr)->second;
|
||||||
// If the VMA is mapped, unmap the region first.
|
// If the VMA is mapped, unmap the region first.
|
||||||
if (vma.IsMapped()) {
|
if (vma.IsMapped()) {
|
||||||
UnmapMemoryImpl(mapped_addr, size);
|
UnmapMemoryImpl(mapped_addr, size);
|
||||||
|
vma = FindVMA(mapped_addr)->second;
|
||||||
}
|
}
|
||||||
const size_t remaining_size = vma.base + vma.size - mapped_addr;
|
const size_t remaining_size = vma.base + vma.size - mapped_addr;
|
||||||
ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size);
|
ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size);
|
||||||
@ -393,14 +395,18 @@ s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) {
|
|||||||
ASSERT_MSG(vma_base.Contains(virtual_addr, size),
|
ASSERT_MSG(vma_base.Contains(virtual_addr, size),
|
||||||
"Existing mapping does not contain requested unmap range");
|
"Existing mapping does not contain requested unmap range");
|
||||||
|
|
||||||
|
const auto type = vma_base.type;
|
||||||
|
if (type == VMAType::Free) {
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
const auto vma_base_addr = vma_base.base;
|
const auto vma_base_addr = vma_base.base;
|
||||||
const auto vma_base_size = vma_base.size;
|
const auto vma_base_size = vma_base.size;
|
||||||
const auto phys_base = vma_base.phys_base;
|
const auto phys_base = vma_base.phys_base;
|
||||||
const bool is_exec = vma_base.is_exec;
|
const bool is_exec = vma_base.is_exec;
|
||||||
const auto start_in_vma = virtual_addr - vma_base_addr;
|
const auto start_in_vma = virtual_addr - vma_base_addr;
|
||||||
const auto type = vma_base.type;
|
|
||||||
const bool has_backing = type == VMAType::Direct || type == VMAType::File;
|
const bool has_backing = type == VMAType::Direct || type == VMAType::File;
|
||||||
if (type == VMAType::Direct) {
|
if (type == VMAType::Direct || type == VMAType::Pooled) {
|
||||||
rasterizer->UnmapMemory(virtual_addr, size);
|
rasterizer->UnmapMemory(virtual_addr, size);
|
||||||
}
|
}
|
||||||
if (type == VMAType::Flexible) {
|
if (type == VMAType::Flexible) {
|
||||||
@ -418,10 +424,12 @@ s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) {
|
|||||||
MergeAdjacent(vma_map, new_it);
|
MergeAdjacent(vma_map, new_it);
|
||||||
bool readonly_file = vma.prot == MemoryProt::CpuRead && type == VMAType::File;
|
bool readonly_file = vma.prot == MemoryProt::CpuRead && type == VMAType::File;
|
||||||
|
|
||||||
|
if (type != VMAType::Reserved && type != VMAType::PoolReserved) {
|
||||||
// Unmap the memory region.
|
// Unmap the memory region.
|
||||||
impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base, is_exec,
|
impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base,
|
||||||
has_backing, readonly_file);
|
is_exec, has_backing, readonly_file);
|
||||||
TRACK_FREE(virtual_addr, "VMEM");
|
TRACK_FREE(virtual_addr, "VMEM");
|
||||||
|
}
|
||||||
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
@ -217,41 +217,15 @@ void Emulator::Run(const std::filesystem::path& file) {
|
|||||||
linker->LoadModule(eboot_path);
|
linker->LoadModule(eboot_path);
|
||||||
|
|
||||||
// check if we have system modules to load
|
// check if we have system modules to load
|
||||||
LoadSystemModules(eboot_path, game_info.game_serial);
|
LoadSystemModules(game_info.game_serial);
|
||||||
|
|
||||||
// Load all prx from game's sce_module folder
|
// Load all prx from game's sce_module folder
|
||||||
std::vector<std::filesystem::path> modules_to_load;
|
mnt->IterateDirectory("/app0/sce_module", [this](const auto& path, const auto is_file) {
|
||||||
std::filesystem::path game_module_folder = file.parent_path() / "sce_module";
|
if (is_file) {
|
||||||
if (std::filesystem::is_directory(game_module_folder)) {
|
LOG_INFO(Loader, "Loading {}", fmt::UTF(path.u8string()));
|
||||||
for (const auto& entry : std::filesystem::directory_iterator(game_module_folder)) {
|
linker->LoadModule(path);
|
||||||
if (entry.is_regular_file()) {
|
|
||||||
modules_to_load.push_back(entry.path());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load all prx from separate update's sce_module folder
|
|
||||||
std::filesystem::path game_patch_folder = game_folder;
|
|
||||||
game_patch_folder += "-UPDATE";
|
|
||||||
std::filesystem::path update_module_folder = game_patch_folder / "sce_module";
|
|
||||||
if (std::filesystem::is_directory(update_module_folder)) {
|
|
||||||
for (const auto& entry : std::filesystem::directory_iterator(update_module_folder)) {
|
|
||||||
auto it = std::find_if(modules_to_load.begin(), modules_to_load.end(),
|
|
||||||
[&entry](const std::filesystem::path& p) {
|
|
||||||
return p.filename() == entry.path().filename();
|
|
||||||
});
|
});
|
||||||
if (it != modules_to_load.end()) {
|
|
||||||
*it = entry.path();
|
|
||||||
} else {
|
|
||||||
modules_to_load.push_back(entry.path());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& module_path : modules_to_load) {
|
|
||||||
LOG_INFO(Loader, "Loading {}", fmt::UTF(module_path.u8string()));
|
|
||||||
linker->LoadModule(module_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ENABLE_DISCORD_RPC
|
#ifdef ENABLE_DISCORD_RPC
|
||||||
// Discord RPC
|
// Discord RPC
|
||||||
@ -278,7 +252,7 @@ void Emulator::Run(const std::filesystem::path& file) {
|
|||||||
std::exit(0);
|
std::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string game_serial) {
|
void Emulator::LoadSystemModules(const std::string& game_serial) {
|
||||||
constexpr std::array<SysModules, 11> ModulesToLoad{
|
constexpr std::array<SysModules, 11> ModulesToLoad{
|
||||||
{{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2},
|
{{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2},
|
||||||
{"libSceUlt.sprx", nullptr},
|
{"libSceUlt.sprx", nullptr},
|
||||||
|
@ -29,7 +29,7 @@ public:
|
|||||||
void UpdatePlayTime(const std::string& serial);
|
void UpdatePlayTime(const std::string& serial);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void LoadSystemModules(const std::filesystem::path& file, std::string game_serial);
|
void LoadSystemModules(const std::string& game_serial);
|
||||||
|
|
||||||
Core::MemoryManager* memory;
|
Core::MemoryManager* memory;
|
||||||
Input::GameController* controller;
|
Input::GameController* controller;
|
||||||
|
@ -47,13 +47,26 @@ static IR::Condition MakeCondition(const GcnInst& inst) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IgnoresExecMask(Opcode opcode) {
|
static bool IgnoresExecMask(const GcnInst& inst) {
|
||||||
switch (opcode) {
|
// EXEC mask does not affect scalar instructions or branches.
|
||||||
case Opcode::V_WRITELANE_B32:
|
switch (inst.category) {
|
||||||
|
case InstCategory::ScalarALU:
|
||||||
|
case InstCategory::ScalarMemory:
|
||||||
|
case InstCategory::FlowControl:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
break;
|
||||||
}
|
}
|
||||||
|
// Read/Write Lane instructions are not affected either.
|
||||||
|
switch (inst.opcode) {
|
||||||
|
case Opcode::V_READLANE_B32:
|
||||||
|
case Opcode::V_WRITELANE_B32:
|
||||||
|
case Opcode::V_READFIRSTLANE_B32:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr size_t LabelReserveSize = 32;
|
static constexpr size_t LabelReserveSize = 32;
|
||||||
@ -147,8 +160,7 @@ void CFG::EmitDivergenceLabels() {
|
|||||||
// If all instructions in the scope ignore exec masking, we shouldn't insert a
|
// If all instructions in the scope ignore exec masking, we shouldn't insert a
|
||||||
// scope.
|
// scope.
|
||||||
const auto start = inst_list.begin() + curr_begin + 1;
|
const auto start = inst_list.begin() + curr_begin + 1;
|
||||||
if (!std::ranges::all_of(start, inst_list.begin() + index, IgnoresExecMask,
|
if (!std::ranges::all_of(start, inst_list.begin() + index, IgnoresExecMask)) {
|
||||||
&GcnInst::opcode)) {
|
|
||||||
// Add a label to the instruction right after the open scope call.
|
// Add a label to the instruction right after the open scope call.
|
||||||
// It is the start of a new basic block.
|
// It is the start of a new basic block.
|
||||||
const auto& save_inst = inst_list[curr_begin];
|
const auto& save_inst = inst_list[curr_begin];
|
||||||
|
Loading…
Reference in New Issue
Block a user