mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-04-29 15:33:35 -06:00
Merge branch 'main' into fontlib
This commit is contained in:
commit
a254fe762f
@ -748,6 +748,8 @@ set(COMMON src/common/logging/backend.cpp
|
||||
src/common/memory_patcher.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/src/common/scm_rev.cpp
|
||||
src/common/scm_rev.h
|
||||
src/common/key_manager.cpp
|
||||
src/common/key_manager.h
|
||||
)
|
||||
|
||||
if (ENABLE_DISCORD_RPC)
|
||||
@ -791,6 +793,8 @@ set(CORE src/core/aerolib/stubs.cpp
|
||||
src/core/file_format/playgo_chunk.h
|
||||
src/core/file_format/trp.cpp
|
||||
src/core/file_format/trp.h
|
||||
src/core/file_format/npbind.cpp
|
||||
src/core/file_format/npbind.h
|
||||
src/core/file_sys/fs.cpp
|
||||
src/core/file_sys/fs.h
|
||||
src/core/ipc/ipc.cpp
|
||||
|
||||
161
src/common/key_manager.cpp
Normal file
161
src/common/key_manager.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadLauncher4 Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include "common/logging/log.h"
|
||||
#include "key_manager.h"
|
||||
#include "path_util.h"
|
||||
|
||||
std::shared_ptr<KeyManager> KeyManager::s_instance = nullptr;
|
||||
std::mutex KeyManager::s_mutex;
|
||||
|
||||
// ------------------- Constructor & Singleton -------------------
|
||||
KeyManager::KeyManager() {
|
||||
SetDefaultKeys();
|
||||
}
|
||||
KeyManager::~KeyManager() {
|
||||
SaveToFile();
|
||||
}
|
||||
|
||||
std::shared_ptr<KeyManager> KeyManager::GetInstance() {
|
||||
std::lock_guard<std::mutex> lock(s_mutex);
|
||||
if (!s_instance)
|
||||
s_instance = std::make_shared<KeyManager>();
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
void KeyManager::SetInstance(std::shared_ptr<KeyManager> instance) {
|
||||
std::lock_guard<std::mutex> lock(s_mutex);
|
||||
s_instance = instance;
|
||||
}
|
||||
|
||||
// ------------------- Load / Save -------------------
|
||||
bool KeyManager::LoadFromFile() {
|
||||
try {
|
||||
const auto userDir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
|
||||
const auto keysPath = userDir / "keys.json";
|
||||
|
||||
if (!std::filesystem::exists(keysPath)) {
|
||||
SetDefaultKeys();
|
||||
SaveToFile();
|
||||
LOG_DEBUG(KeyManager, "Created default key file: {}", keysPath.string());
|
||||
return true;
|
||||
}
|
||||
|
||||
std::ifstream file(keysPath);
|
||||
if (!file.is_open()) {
|
||||
LOG_ERROR(KeyManager, "Could not open key file: {}", keysPath.string());
|
||||
return false;
|
||||
}
|
||||
|
||||
json j;
|
||||
file >> j;
|
||||
|
||||
SetDefaultKeys(); // start from defaults
|
||||
|
||||
if (j.contains("TrophyKeySet"))
|
||||
j.at("TrophyKeySet").get_to(m_keys.TrophyKeySet);
|
||||
|
||||
LOG_DEBUG(KeyManager, "Successfully loaded keys from: {}", keysPath.string());
|
||||
return true;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(KeyManager, "Error loading keys, using defaults: {}", e.what());
|
||||
SetDefaultKeys();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool KeyManager::SaveToFile() {
|
||||
try {
|
||||
const auto userDir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
|
||||
const auto keysPath = userDir / "keys.json";
|
||||
|
||||
json j;
|
||||
KeysToJson(j);
|
||||
|
||||
std::ofstream file(keysPath);
|
||||
if (!file.is_open()) {
|
||||
LOG_ERROR(KeyManager, "Could not open key file for writing: {}", keysPath.string());
|
||||
return false;
|
||||
}
|
||||
|
||||
file << std::setw(4) << j;
|
||||
file.flush();
|
||||
|
||||
if (file.fail()) {
|
||||
LOG_ERROR(KeyManager, "Failed to write keys to: {}", keysPath.string());
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DEBUG(KeyManager, "Successfully saved keys to: {}", keysPath.string());
|
||||
return true;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(KeyManager, "Error saving keys: {}", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------- JSON conversion -------------------
|
||||
void KeyManager::KeysToJson(json& j) const {
|
||||
j = m_keys;
|
||||
}
|
||||
void KeyManager::JsonToKeys(const json& j) {
|
||||
json current = m_keys; // serialize current defaults
|
||||
current.update(j); // merge only fields present in file
|
||||
m_keys = current.get<AllKeys>(); // deserialize back
|
||||
}
|
||||
|
||||
// ------------------- Defaults / Checks -------------------
|
||||
void KeyManager::SetDefaultKeys() {
|
||||
m_keys = AllKeys{};
|
||||
}
|
||||
|
||||
bool KeyManager::HasKeys() const {
|
||||
return !m_keys.TrophyKeySet.ReleaseTrophyKey.empty();
|
||||
}
|
||||
|
||||
// ------------------- Hex conversion -------------------
|
||||
std::vector<u8> KeyManager::HexStringToBytes(const std::string& hexStr) {
|
||||
std::vector<u8> bytes;
|
||||
if (hexStr.empty())
|
||||
return bytes;
|
||||
|
||||
if (hexStr.size() % 2 != 0)
|
||||
throw std::runtime_error("Invalid hex string length");
|
||||
|
||||
bytes.reserve(hexStr.size() / 2);
|
||||
|
||||
auto hexCharToInt = [](char c) -> u8 {
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if (c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
if (c >= 'a' && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
throw std::runtime_error("Invalid hex character");
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < hexStr.size(); i += 2) {
|
||||
u8 high = hexCharToInt(hexStr[i]);
|
||||
u8 low = hexCharToInt(hexStr[i + 1]);
|
||||
bytes.push_back((high << 4) | low);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
std::string KeyManager::BytesToHexString(const std::vector<u8>& bytes) {
|
||||
static const char hexDigits[] = "0123456789ABCDEF";
|
||||
std::string hexStr;
|
||||
hexStr.reserve(bytes.size() * 2);
|
||||
for (u8 b : bytes) {
|
||||
hexStr.push_back(hexDigits[(b >> 4) & 0xF]);
|
||||
hexStr.push_back(hexDigits[b & 0xF]);
|
||||
}
|
||||
return hexStr;
|
||||
}
|
||||
79
src/common/key_manager.h
Normal file
79
src/common/key_manager.h
Normal file
@ -0,0 +1,79 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadLauncher4 Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/types.h"
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
class KeyManager {
|
||||
public:
|
||||
// ------------------- Nested keysets -------------------
|
||||
struct TrophyKeySet {
|
||||
std::vector<u8> ReleaseTrophyKey;
|
||||
};
|
||||
|
||||
struct AllKeys {
|
||||
KeyManager::TrophyKeySet TrophyKeySet;
|
||||
};
|
||||
|
||||
// ------------------- Construction -------------------
|
||||
KeyManager();
|
||||
~KeyManager();
|
||||
|
||||
// ------------------- Singleton -------------------
|
||||
static std::shared_ptr<KeyManager> GetInstance();
|
||||
static void SetInstance(std::shared_ptr<KeyManager> instance);
|
||||
|
||||
// ------------------- File operations -------------------
|
||||
bool LoadFromFile();
|
||||
bool SaveToFile();
|
||||
|
||||
// ------------------- Key operations -------------------
|
||||
void SetDefaultKeys();
|
||||
bool HasKeys() const;
|
||||
|
||||
// ------------------- Getters / Setters -------------------
|
||||
const AllKeys& GetAllKeys() const {
|
||||
return m_keys;
|
||||
}
|
||||
void SetAllKeys(const AllKeys& keys) {
|
||||
m_keys = keys;
|
||||
}
|
||||
|
||||
static std::vector<u8> HexStringToBytes(const std::string& hexStr);
|
||||
static std::string BytesToHexString(const std::vector<u8>& bytes);
|
||||
|
||||
private:
|
||||
void KeysToJson(json& j) const;
|
||||
void JsonToKeys(const json& j);
|
||||
|
||||
AllKeys m_keys{};
|
||||
|
||||
static std::shared_ptr<KeyManager> s_instance;
|
||||
static std::mutex s_mutex;
|
||||
};
|
||||
|
||||
// ------------------- NLOHMANN macros -------------------
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(KeyManager::TrophyKeySet, ReleaseTrophyKey)
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(KeyManager::AllKeys, TrophyKeySet)
|
||||
|
||||
namespace nlohmann {
|
||||
template <>
|
||||
struct adl_serializer<std::vector<u8>> {
|
||||
static void to_json(json& j, const std::vector<u8>& vec) {
|
||||
j = KeyManager::BytesToHexString(vec);
|
||||
}
|
||||
static void from_json(const json& j, std::vector<u8>& vec) {
|
||||
vec = KeyManager::HexStringToBytes(j.get<std::string>());
|
||||
}
|
||||
};
|
||||
} // namespace nlohmann
|
||||
@ -1,4 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2014 Citra Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
@ -159,6 +160,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
CLS(ImGui) \
|
||||
CLS(Input) \
|
||||
CLS(Tty) \
|
||||
CLS(KeyManager) \
|
||||
CLS(Loader)
|
||||
|
||||
// GetClassName is a macro defined by Windows.h, grrr...
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 Citra Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
@ -127,6 +128,7 @@ enum class Class : u8 {
|
||||
Loader, ///< ROM loader
|
||||
Input, ///< Input emulation
|
||||
Tty, ///< Debug output from emu
|
||||
KeyManager, ///< Key management system
|
||||
Count ///< Total number of logging classes
|
||||
};
|
||||
|
||||
|
||||
@ -93,7 +93,10 @@ static u64 BackingSize = ORBIS_KERNEL_TOTAL_MEM_DEV_PRO;
|
||||
|
||||
struct MemoryRegion {
|
||||
VAddr base;
|
||||
size_t size;
|
||||
PAddr phys_base;
|
||||
u64 size;
|
||||
u32 prot;
|
||||
s32 fd;
|
||||
bool is_mapped;
|
||||
};
|
||||
|
||||
@ -159,7 +162,8 @@ struct AddressSpace::Impl {
|
||||
// Restrict region size to avoid overly fragmenting the virtual memory space.
|
||||
if (info.State == MEM_FREE && info.RegionSize > 0x1000000) {
|
||||
VAddr addr = Common::AlignUp(reinterpret_cast<VAddr>(info.BaseAddress), alignment);
|
||||
regions.emplace(addr, MemoryRegion{addr, size, false});
|
||||
regions.emplace(addr,
|
||||
MemoryRegion{addr, PAddr(-1), size, PAGE_NOACCESS, -1, false});
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,29 +211,32 @@ struct AddressSpace::Impl {
|
||||
~Impl() {
|
||||
if (virtual_base) {
|
||||
if (!VirtualFree(virtual_base, 0, MEM_RELEASE)) {
|
||||
LOG_CRITICAL(Render, "Failed to free virtual memory");
|
||||
LOG_CRITICAL(Core, "Failed to free virtual memory");
|
||||
}
|
||||
}
|
||||
if (backing_base) {
|
||||
if (!UnmapViewOfFile2(process, backing_base, MEM_PRESERVE_PLACEHOLDER)) {
|
||||
LOG_CRITICAL(Render, "Failed to unmap backing memory placeholder");
|
||||
LOG_CRITICAL(Core, "Failed to unmap backing memory placeholder");
|
||||
}
|
||||
if (!VirtualFreeEx(process, backing_base, 0, MEM_RELEASE)) {
|
||||
LOG_CRITICAL(Render, "Failed to free backing memory");
|
||||
LOG_CRITICAL(Core, "Failed to free backing memory");
|
||||
}
|
||||
}
|
||||
if (!CloseHandle(backing_handle)) {
|
||||
LOG_CRITICAL(Render, "Failed to free backing memory file handle");
|
||||
LOG_CRITICAL(Core, "Failed to free backing memory file handle");
|
||||
}
|
||||
}
|
||||
|
||||
void* Map(VAddr virtual_addr, PAddr phys_addr, size_t size, ULONG prot, uintptr_t fd = 0) {
|
||||
// Before mapping we must carve a placeholder with the exact properties of our mapping.
|
||||
auto* region = EnsureSplitRegionForMapping(virtual_addr, size);
|
||||
region->is_mapped = true;
|
||||
void* MapRegion(MemoryRegion* region) {
|
||||
VAddr virtual_addr = region->base;
|
||||
PAddr phys_addr = region->phys_base;
|
||||
u64 size = region->size;
|
||||
ULONG prot = region->prot;
|
||||
s32 fd = region->fd;
|
||||
|
||||
void* ptr = nullptr;
|
||||
if (phys_addr != -1) {
|
||||
HANDLE backing = fd ? reinterpret_cast<HANDLE>(fd) : backing_handle;
|
||||
HANDLE backing = fd != -1 ? reinterpret_cast<HANDLE>(fd) : backing_handle;
|
||||
if (fd && prot == PAGE_READONLY) {
|
||||
DWORD resultvar;
|
||||
ptr = VirtualAlloc2(process, reinterpret_cast<PVOID>(virtual_addr), size,
|
||||
@ -257,110 +264,136 @@ struct AddressSpace::Impl {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void Unmap(VAddr virtual_addr, size_t size, bool has_backing) {
|
||||
bool ret;
|
||||
if (has_backing) {
|
||||
void UnmapRegion(MemoryRegion* region) {
|
||||
VAddr virtual_addr = region->base;
|
||||
PAddr phys_base = region->phys_base;
|
||||
u64 size = region->size;
|
||||
|
||||
bool ret = false;
|
||||
if (phys_base != -1) {
|
||||
ret = UnmapViewOfFile2(process, reinterpret_cast<PVOID>(virtual_addr),
|
||||
MEM_PRESERVE_PLACEHOLDER);
|
||||
} else {
|
||||
ret = VirtualFreeEx(process, reinterpret_cast<PVOID>(virtual_addr), size,
|
||||
MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER);
|
||||
}
|
||||
ASSERT_MSG(ret, "Unmap operation on virtual_addr={:#X} failed: {}", virtual_addr,
|
||||
ASSERT_MSG(ret, "Unmap on virtual_addr {:#x}, size {:#x} failed: {}", virtual_addr, size,
|
||||
Common::GetLastErrorMsg());
|
||||
|
||||
// The unmap call will create a new placeholder region. We need to see if we can coalesce it
|
||||
// with neighbors.
|
||||
JoinRegionsAfterUnmap(virtual_addr, size);
|
||||
}
|
||||
|
||||
// The following code is inspired from Dolphin's MemArena
|
||||
// https://github.com/dolphin-emu/dolphin/blob/deee3ee4/Source/Core/Common/MemArenaWin.cpp#L212
|
||||
MemoryRegion* EnsureSplitRegionForMapping(VAddr address, size_t size) {
|
||||
// Find closest region that is <= the given address by using upper bound and decrementing
|
||||
auto it = regions.upper_bound(address);
|
||||
ASSERT_MSG(it != regions.begin(), "Invalid address {:#x}", address);
|
||||
--it;
|
||||
ASSERT_MSG(!it->second.is_mapped,
|
||||
"Attempt to map {:#x} with size {:#x} which overlaps with {:#x} mapping",
|
||||
address, size, it->second.base);
|
||||
auto& [base, region] = *it;
|
||||
void SplitRegion(VAddr virtual_addr, u64 size) {
|
||||
// First, get the region this range covers
|
||||
auto it = std::prev(regions.upper_bound(virtual_addr));
|
||||
|
||||
const VAddr mapping_address = region.base;
|
||||
const size_t region_size = region.size;
|
||||
if (mapping_address == address) {
|
||||
// If this region is already split up correctly we don't have to do anything
|
||||
if (region_size == size) {
|
||||
return ®ion;
|
||||
// All unmapped areas will coalesce, so there should be a region
|
||||
// containing the full requested range. If not, then something is mapped here.
|
||||
ASSERT_MSG(it->second.base + it->second.size >= virtual_addr + size,
|
||||
"Cannot fit region into one placeholder");
|
||||
|
||||
// If the region is mapped, we need to unmap first before we can modify the placeholders.
|
||||
if (it->second.is_mapped) {
|
||||
ASSERT_MSG(it->second.phys_base != -1 || !it->second.is_mapped,
|
||||
"Cannot split unbacked mapping");
|
||||
UnmapRegion(&it->second);
|
||||
}
|
||||
|
||||
// We need to split this region to create a matching placeholder.
|
||||
if (it->second.base != virtual_addr) {
|
||||
// Requested address is not the start of the containing region,
|
||||
// create a new region to represent the memory before the requested range.
|
||||
auto& region = it->second;
|
||||
u64 base_offset = virtual_addr - region.base;
|
||||
u64 next_region_size = region.size - base_offset;
|
||||
PAddr next_region_phys_base = -1;
|
||||
if (region.is_mapped) {
|
||||
next_region_phys_base = region.phys_base + base_offset;
|
||||
}
|
||||
region.size = base_offset;
|
||||
|
||||
ASSERT_MSG(region_size >= size,
|
||||
"Region with address {:#x} and size {:#x} can't fit {:#x}", mapping_address,
|
||||
region_size, size);
|
||||
|
||||
// Split the placeholder.
|
||||
if (!VirtualFreeEx(process, LPVOID(address), size,
|
||||
// Use VirtualFreeEx to create the split.
|
||||
if (!VirtualFreeEx(process, LPVOID(region.base), region.size,
|
||||
MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) {
|
||||
UNREACHABLE_MSG("Region splitting failed: {}", Common::GetLastErrorMsg());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Update tracked mappings and return the first of the two
|
||||
// If the mapping was mapped, remap the region.
|
||||
if (region.is_mapped) {
|
||||
MapRegion(®ion);
|
||||
}
|
||||
|
||||
// Store a new region matching the removed area.
|
||||
it = regions.emplace_hint(std::next(it), virtual_addr,
|
||||
MemoryRegion(virtual_addr, next_region_phys_base,
|
||||
next_region_size, region.prot, region.fd,
|
||||
region.is_mapped));
|
||||
}
|
||||
|
||||
// At this point, the region's base will match virtual_addr.
|
||||
// Now check for a size difference.
|
||||
if (it->second.size != size) {
|
||||
// The requested size is smaller than the current region placeholder.
|
||||
// Update region to match the requested region,
|
||||
// then make a new region to represent the remaining space.
|
||||
auto& region = it->second;
|
||||
VAddr next_region_addr = region.base + size;
|
||||
u64 next_region_size = region.size - size;
|
||||
PAddr next_region_phys_base = -1;
|
||||
if (region.is_mapped) {
|
||||
next_region_phys_base = region.phys_base + size;
|
||||
}
|
||||
region.size = size;
|
||||
const VAddr new_mapping_start = address + size;
|
||||
regions.emplace_hint(std::next(it), new_mapping_start,
|
||||
MemoryRegion(new_mapping_start, region_size - size, false));
|
||||
return ®ion;
|
||||
|
||||
// Store the new region matching the remaining space
|
||||
regions.emplace_hint(std::next(it), next_region_addr,
|
||||
MemoryRegion(next_region_addr, next_region_phys_base,
|
||||
next_region_size, region.prot, region.fd,
|
||||
region.is_mapped));
|
||||
|
||||
// Use VirtualFreeEx to create the split.
|
||||
if (!VirtualFreeEx(process, LPVOID(region.base), region.size,
|
||||
MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) {
|
||||
UNREACHABLE_MSG("Region splitting failed: {}", Common::GetLastErrorMsg());
|
||||
}
|
||||
|
||||
// If these regions were mapped, then map the unmapped area beyond the requested range.
|
||||
if (region.is_mapped) {
|
||||
MapRegion(&std::next(it)->second);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(mapping_address < address);
|
||||
|
||||
// Is there enough space to map this?
|
||||
const size_t offset_in_region = address - mapping_address;
|
||||
const size_t minimum_size = size + offset_in_region;
|
||||
ASSERT(region_size >= minimum_size);
|
||||
|
||||
// Split the placeholder.
|
||||
if (!VirtualFreeEx(process, LPVOID(address), size,
|
||||
MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) {
|
||||
UNREACHABLE_MSG("Region splitting failed: {}", Common::GetLastErrorMsg());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Do we now have two regions or three regions?
|
||||
if (region_size == minimum_size) {
|
||||
// Split into two; update tracked mappings and return the second one
|
||||
region.size = offset_in_region;
|
||||
it = regions.emplace_hint(std::next(it), address, MemoryRegion(address, size, false));
|
||||
return &it->second;
|
||||
} else {
|
||||
// Split into three; update tracked mappings and return the middle one
|
||||
region.size = offset_in_region;
|
||||
const VAddr middle_mapping_start = address;
|
||||
const size_t middle_mapping_size = size;
|
||||
const VAddr after_mapping_start = address + size;
|
||||
const size_t after_mapping_size = region_size - minimum_size;
|
||||
it = regions.emplace_hint(std::next(it), after_mapping_start,
|
||||
MemoryRegion(after_mapping_start, after_mapping_size, false));
|
||||
it = regions.emplace_hint(
|
||||
it, middle_mapping_start,
|
||||
MemoryRegion(middle_mapping_start, middle_mapping_size, false));
|
||||
return &it->second;
|
||||
// If the requested region was mapped, remap it.
|
||||
if (it->second.is_mapped) {
|
||||
MapRegion(&it->second);
|
||||
}
|
||||
}
|
||||
|
||||
void JoinRegionsAfterUnmap(VAddr address, size_t size) {
|
||||
// There should be a mapping that matches the request exactly, find it
|
||||
auto it = regions.find(address);
|
||||
ASSERT_MSG(it != regions.end() && it->second.size == size,
|
||||
"Invalid address/size given to unmap.");
|
||||
void* Map(VAddr virtual_addr, PAddr phys_addr, u64 size, ULONG prot, s32 fd = -1) {
|
||||
// Split surrounding regions to create a placeholder
|
||||
SplitRegion(virtual_addr, size);
|
||||
|
||||
// Get the region this range covers
|
||||
auto it = std::prev(regions.upper_bound(virtual_addr));
|
||||
auto& [base, region] = *it;
|
||||
region.is_mapped = false;
|
||||
|
||||
ASSERT_MSG(!region.is_mapped, "Cannot overwrite mapped region");
|
||||
|
||||
// Now we have a region matching the requested region, perform the actual mapping.
|
||||
region.is_mapped = true;
|
||||
region.phys_base = phys_addr;
|
||||
region.prot = prot;
|
||||
region.fd = fd;
|
||||
return MapRegion(®ion);
|
||||
}
|
||||
|
||||
void CoalesceFreeRegions(VAddr virtual_addr) {
|
||||
// First, get the region to update
|
||||
auto it = std::prev(regions.upper_bound(virtual_addr));
|
||||
ASSERT_MSG(!it->second.is_mapped, "Cannot coalesce mapped regions");
|
||||
|
||||
// Check if a placeholder exists right before us.
|
||||
auto it_prev = it != regions.begin() ? std::prev(it) : regions.end();
|
||||
if (it_prev != regions.end() && !it_prev->second.is_mapped) {
|
||||
const size_t total_size = it_prev->second.size + size;
|
||||
const u64 total_size = it_prev->second.size + it->second.size;
|
||||
if (!VirtualFreeEx(process, LPVOID(it_prev->first), total_size,
|
||||
MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS)) {
|
||||
UNREACHABLE_MSG("Region coalescing failed: {}", Common::GetLastErrorMsg());
|
||||
@ -374,7 +407,7 @@ struct AddressSpace::Impl {
|
||||
// Check if a placeholder exists right after us.
|
||||
auto it_next = std::next(it);
|
||||
if (it_next != regions.end() && !it_next->second.is_mapped) {
|
||||
const size_t total_size = it->second.size + it_next->second.size;
|
||||
const u64 total_size = it->second.size + it_next->second.size;
|
||||
if (!VirtualFreeEx(process, LPVOID(it->first), total_size,
|
||||
MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS)) {
|
||||
UNREACHABLE_MSG("Region coalescing failed: {}", Common::GetLastErrorMsg());
|
||||
@ -385,7 +418,47 @@ struct AddressSpace::Impl {
|
||||
}
|
||||
}
|
||||
|
||||
void Protect(VAddr virtual_addr, size_t size, bool read, bool write, bool execute) {
|
||||
void Unmap(VAddr virtual_addr, u64 size) {
|
||||
// Loop through all regions in the requested range
|
||||
u64 remaining_size = size;
|
||||
VAddr current_addr = virtual_addr;
|
||||
while (remaining_size > 0) {
|
||||
// Get the region containing our current address.
|
||||
auto it = std::prev(regions.upper_bound(current_addr));
|
||||
|
||||
// If necessary, split regions to ensure a valid unmap.
|
||||
// To prevent complication, ensure size is within the bounds of the current region.
|
||||
u64 base_offset = current_addr - it->second.base;
|
||||
u64 size_to_unmap = std::min<u64>(it->second.size - base_offset, remaining_size);
|
||||
if (current_addr != it->second.base || size_to_unmap != it->second.size) {
|
||||
SplitRegion(current_addr, size_to_unmap);
|
||||
}
|
||||
|
||||
// Repair the region pointer, as SplitRegion modifies the regions map.
|
||||
it = std::prev(regions.upper_bound(current_addr));
|
||||
auto& [base, region] = *it;
|
||||
|
||||
// Unmap the region if it was previously mapped
|
||||
if (region.is_mapped) {
|
||||
UnmapRegion(®ion);
|
||||
}
|
||||
|
||||
// Update region data
|
||||
region.is_mapped = false;
|
||||
region.fd = -1;
|
||||
region.phys_base = -1;
|
||||
region.prot = PAGE_NOACCESS;
|
||||
|
||||
// Coalesce any free space
|
||||
CoalesceFreeRegions(current_addr);
|
||||
|
||||
// Update loop variables
|
||||
remaining_size -= size_to_unmap;
|
||||
current_addr += size_to_unmap;
|
||||
}
|
||||
}
|
||||
|
||||
void Protect(VAddr virtual_addr, u64 size, bool read, bool write, bool execute) {
|
||||
DWORD new_flags{};
|
||||
|
||||
if (write && !read) {
|
||||
@ -415,7 +488,7 @@ struct AddressSpace::Impl {
|
||||
|
||||
// If no flags are assigned, then something's gone wrong.
|
||||
if (new_flags == 0) {
|
||||
LOG_CRITICAL(Common_Memory,
|
||||
LOG_CRITICAL(Core,
|
||||
"Unsupported protection flag combination for address {:#x}, size {}, "
|
||||
"read={}, write={}, execute={}",
|
||||
virtual_addr, size, read, write, execute);
|
||||
@ -429,8 +502,8 @@ struct AddressSpace::Impl {
|
||||
continue;
|
||||
}
|
||||
const auto& region = it->second;
|
||||
const size_t range_addr = std::max(region.base, virtual_addr);
|
||||
const size_t range_size = std::min(region.base + region.size, virtual_end) - range_addr;
|
||||
const u64 range_addr = std::max(region.base, virtual_addr);
|
||||
const u64 range_size = std::min(region.base + region.size, virtual_end) - range_addr;
|
||||
DWORD old_flags{};
|
||||
if (!VirtualProtectEx(process, LPVOID(range_addr), range_size, new_flags, &old_flags)) {
|
||||
UNREACHABLE_MSG(
|
||||
@ -453,11 +526,11 @@ struct AddressSpace::Impl {
|
||||
u8* backing_base{};
|
||||
u8* virtual_base{};
|
||||
u8* system_managed_base{};
|
||||
size_t system_managed_size{};
|
||||
u64 system_managed_size{};
|
||||
u8* system_reserved_base{};
|
||||
size_t system_reserved_size{};
|
||||
u64 system_reserved_size{};
|
||||
u8* user_base{};
|
||||
size_t user_size{};
|
||||
u64 user_size{};
|
||||
std::map<VAddr, MemoryRegion> regions;
|
||||
};
|
||||
#else
|
||||
@ -601,7 +674,7 @@ struct AddressSpace::Impl {
|
||||
}
|
||||
}
|
||||
|
||||
void* Map(VAddr virtual_addr, PAddr phys_addr, size_t size, PosixPageProtection prot,
|
||||
void* Map(VAddr virtual_addr, PAddr phys_addr, u64 size, PosixPageProtection prot,
|
||||
int fd = -1) {
|
||||
m_free_regions.subtract({virtual_addr, virtual_addr + size});
|
||||
const int handle = phys_addr != -1 ? (fd == -1 ? backing_fd : fd) : -1;
|
||||
@ -613,10 +686,10 @@ struct AddressSpace::Impl {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Unmap(VAddr virtual_addr, size_t size, bool) {
|
||||
void Unmap(VAddr virtual_addr, u64 size, bool) {
|
||||
// Check to see if we are adjacent to any regions.
|
||||
auto start_address = virtual_addr;
|
||||
auto end_address = start_address + size;
|
||||
VAddr start_address = virtual_addr;
|
||||
VAddr end_address = start_address + size;
|
||||
auto it = m_free_regions.find({start_address - 1, end_address + 1});
|
||||
|
||||
// If we are, join with them, ensuring we stay in bounds.
|
||||
@ -634,7 +707,7 @@ struct AddressSpace::Impl {
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
|
||||
}
|
||||
|
||||
void Protect(VAddr virtual_addr, size_t size, bool read, bool write, bool execute) {
|
||||
void Protect(VAddr virtual_addr, u64 size, bool read, bool write, bool execute) {
|
||||
int flags = PROT_NONE;
|
||||
if (read) {
|
||||
flags |= PROT_READ;
|
||||
@ -654,11 +727,11 @@ struct AddressSpace::Impl {
|
||||
int backing_fd;
|
||||
u8* backing_base{};
|
||||
u8* system_managed_base{};
|
||||
size_t system_managed_size{};
|
||||
u64 system_managed_size{};
|
||||
u8* system_reserved_base{};
|
||||
size_t system_reserved_size{};
|
||||
u64 system_reserved_size{};
|
||||
u8* user_base{};
|
||||
size_t user_size{};
|
||||
u64 user_size{};
|
||||
boost::icl::interval_set<VAddr> m_free_regions;
|
||||
};
|
||||
#endif
|
||||
@ -675,8 +748,7 @@ AddressSpace::AddressSpace() : impl{std::make_unique<Impl>()} {
|
||||
|
||||
AddressSpace::~AddressSpace() = default;
|
||||
|
||||
void* AddressSpace::Map(VAddr virtual_addr, size_t size, u64 alignment, PAddr phys_addr,
|
||||
bool is_exec) {
|
||||
void* AddressSpace::Map(VAddr virtual_addr, u64 size, PAddr phys_addr, bool is_exec) {
|
||||
#if ARCH_X86_64
|
||||
const auto prot = is_exec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
|
||||
#else
|
||||
@ -687,8 +759,7 @@ void* AddressSpace::Map(VAddr virtual_addr, size_t size, u64 alignment, PAddr ph
|
||||
return impl->Map(virtual_addr, phys_addr, size, prot);
|
||||
}
|
||||
|
||||
void* AddressSpace::MapFile(VAddr virtual_addr, size_t size, size_t offset, u32 prot,
|
||||
uintptr_t fd) {
|
||||
void* AddressSpace::MapFile(VAddr virtual_addr, u64 size, u64 offset, u32 prot, uintptr_t fd) {
|
||||
#ifdef _WIN32
|
||||
return impl->Map(virtual_addr, offset, size,
|
||||
ToWindowsProt(std::bit_cast<Core::MemoryProt>(prot)), fd);
|
||||
@ -698,31 +769,15 @@ void* AddressSpace::MapFile(VAddr virtual_addr, size_t size, size_t offset, u32
|
||||
#endif
|
||||
}
|
||||
|
||||
void AddressSpace::Unmap(VAddr virtual_addr, size_t size, VAddr start_in_vma, VAddr end_in_vma,
|
||||
PAddr phys_base, bool is_exec, bool has_backing, bool readonly_file) {
|
||||
void AddressSpace::Unmap(VAddr virtual_addr, u64 size, bool has_backing) {
|
||||
#ifdef _WIN32
|
||||
// There does not appear to be comparable support for partial unmapping on Windows.
|
||||
// Unfortunately, a least one title was found to require this. The workaround is to unmap
|
||||
// the entire allocation and remap the portions outside of the requested unmapping range.
|
||||
impl->Unmap(virtual_addr, size, has_backing && !readonly_file);
|
||||
|
||||
// TODO: Determine if any titles require partial unmapping support for un-backed allocations.
|
||||
ASSERT_MSG(has_backing || (start_in_vma == 0 && end_in_vma == size),
|
||||
"Partial unmapping of un-backed allocations is not supported");
|
||||
|
||||
if (start_in_vma != 0) {
|
||||
Map(virtual_addr, start_in_vma, 0, phys_base, is_exec);
|
||||
}
|
||||
|
||||
if (end_in_vma != size) {
|
||||
Map(virtual_addr + end_in_vma, size - end_in_vma, 0, phys_base + end_in_vma, is_exec);
|
||||
}
|
||||
impl->Unmap(virtual_addr, size);
|
||||
#else
|
||||
impl->Unmap(virtual_addr + start_in_vma, end_in_vma - start_in_vma, has_backing);
|
||||
impl->Unmap(virtual_addr, size, has_backing);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AddressSpace::Protect(VAddr virtual_addr, size_t size, MemoryPermission perms) {
|
||||
void AddressSpace::Protect(VAddr virtual_addr, u64 size, MemoryPermission perms) {
|
||||
const bool read = True(perms & MemoryPermission::Read);
|
||||
const bool write = True(perms & MemoryPermission::Write);
|
||||
const bool execute = True(perms & MemoryPermission::Execute);
|
||||
|
||||
@ -39,7 +39,7 @@ public:
|
||||
[[nodiscard]] const u8* SystemManagedVirtualBase() const noexcept {
|
||||
return system_managed_base;
|
||||
}
|
||||
[[nodiscard]] size_t SystemManagedVirtualSize() const noexcept {
|
||||
[[nodiscard]] u64 SystemManagedVirtualSize() const noexcept {
|
||||
return system_managed_size;
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ public:
|
||||
[[nodiscard]] const u8* SystemReservedVirtualBase() const noexcept {
|
||||
return system_reserved_base;
|
||||
}
|
||||
[[nodiscard]] size_t SystemReservedVirtualSize() const noexcept {
|
||||
[[nodiscard]] u64 SystemReservedVirtualSize() const noexcept {
|
||||
return system_reserved_size;
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@ public:
|
||||
[[nodiscard]] const u8* UserVirtualBase() const noexcept {
|
||||
return user_base;
|
||||
}
|
||||
[[nodiscard]] size_t UserVirtualSize() const noexcept {
|
||||
[[nodiscard]] u64 UserVirtualSize() const noexcept {
|
||||
return user_size;
|
||||
}
|
||||
|
||||
@ -73,17 +73,15 @@ public:
|
||||
* If zero is provided the mapping is considered as private.
|
||||
* @return A pointer to the mapped memory.
|
||||
*/
|
||||
void* Map(VAddr virtual_addr, size_t size, u64 alignment = 0, PAddr phys_addr = -1,
|
||||
bool exec = false);
|
||||
void* Map(VAddr virtual_addr, u64 size, PAddr phys_addr = -1, bool exec = false);
|
||||
|
||||
/// Memory maps a specified file descriptor.
|
||||
void* MapFile(VAddr virtual_addr, size_t size, size_t offset, u32 prot, uintptr_t fd);
|
||||
void* MapFile(VAddr virtual_addr, u64 size, u64 offset, u32 prot, uintptr_t fd);
|
||||
|
||||
/// Unmaps specified virtual memory area.
|
||||
void Unmap(VAddr virtual_addr, size_t size, VAddr start_in_vma, VAddr end_in_vma,
|
||||
PAddr phys_base, bool is_exec, bool has_backing, bool readonly_file);
|
||||
void Unmap(VAddr virtual_addr, u64 size, bool has_backing);
|
||||
|
||||
void Protect(VAddr virtual_addr, size_t size, MemoryPermission perms);
|
||||
void Protect(VAddr virtual_addr, u64 size, MemoryPermission perms);
|
||||
|
||||
// Returns an interval set containing all usable regions.
|
||||
boost::icl::interval_set<VAddr> GetUsableRegions();
|
||||
@ -93,11 +91,11 @@ private:
|
||||
std::unique_ptr<Impl> impl;
|
||||
u8* backing_base{};
|
||||
u8* system_managed_base{};
|
||||
size_t system_managed_size{};
|
||||
u64 system_managed_size{};
|
||||
u8* system_reserved_base{};
|
||||
size_t system_reserved_size{};
|
||||
u64 system_reserved_size{};
|
||||
u8* user_base{};
|
||||
size_t user_size{};
|
||||
u64 user_size{};
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@ -32,7 +32,7 @@ bool MemoryMapViewer::Iterator::DrawLine() {
|
||||
TableNextColumn();
|
||||
Text("%s", magic_enum::enum_name(m.prot).data());
|
||||
TableNextColumn();
|
||||
if (m.is_exec) {
|
||||
if (True(m.prot & MemoryProt::CpuExec)) {
|
||||
Text("X");
|
||||
}
|
||||
TableNextColumn();
|
||||
@ -44,7 +44,7 @@ bool MemoryMapViewer::Iterator::DrawLine() {
|
||||
return false;
|
||||
}
|
||||
auto m = dmem.it->second;
|
||||
if (m.dma_type == DMAType::Free) {
|
||||
if (m.dma_type == PhysicalMemoryType::Free) {
|
||||
++dmem.it;
|
||||
return DrawLine();
|
||||
}
|
||||
@ -56,7 +56,8 @@ bool MemoryMapViewer::Iterator::DrawLine() {
|
||||
auto type = static_cast<::Libraries::Kernel::MemoryTypes>(m.memory_type);
|
||||
Text("%s", magic_enum::enum_name(type).data());
|
||||
TableNextColumn();
|
||||
Text("%d", m.dma_type == DMAType::Pooled || m.dma_type == DMAType::Committed);
|
||||
Text("%d",
|
||||
m.dma_type == PhysicalMemoryType::Pooled || m.dma_type == PhysicalMemoryType::Committed);
|
||||
++dmem.it;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -11,8 +11,8 @@ class MemoryMapViewer {
|
||||
struct Iterator {
|
||||
bool is_vma;
|
||||
struct {
|
||||
MemoryManager::DMemMap::iterator it;
|
||||
MemoryManager::DMemMap::iterator end;
|
||||
MemoryManager::PhysMap::iterator it;
|
||||
MemoryManager::PhysMap::iterator end;
|
||||
} dmem;
|
||||
struct {
|
||||
MemoryManager::VMAMap::iterator it;
|
||||
|
||||
114
src/core/file_format/npbind.cpp
Normal file
114
src/core/file_format/npbind.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadLauncher4 Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include "npbind.h"
|
||||
|
||||
bool NPBindFile::Load(const std::string& path) {
|
||||
Clear(); // Clear any existing data
|
||||
|
||||
std::ifstream f(path, std::ios::binary | std::ios::ate);
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
std::streamsize sz = f.tellg();
|
||||
if (sz <= 0)
|
||||
return false;
|
||||
|
||||
f.seekg(0, std::ios::beg);
|
||||
std::vector<u8> buf(static_cast<size_t>(sz));
|
||||
if (!f.read(reinterpret_cast<char*>(buf.data()), sz))
|
||||
return false;
|
||||
|
||||
const u64 size = buf.size();
|
||||
if (size < sizeof(NpBindHeader))
|
||||
return false;
|
||||
|
||||
// Read header
|
||||
memcpy(&m_header, buf.data(), sizeof(NpBindHeader));
|
||||
if (m_header.magic != NPBIND_MAGIC)
|
||||
return false;
|
||||
|
||||
// offset start of bodies
|
||||
size_t offset = sizeof(NpBindHeader);
|
||||
|
||||
m_bodies.reserve(static_cast<size_t>(m_header.num_entries));
|
||||
|
||||
// For each body: read 4 TLV entries then skip padding (0x98 = 152 bytes)
|
||||
const u64 body_padding = 0x98; // 152
|
||||
|
||||
for (u64 bi = 0; bi < m_header.num_entries; ++bi) {
|
||||
// Ensure we have room for 4 entries' headers at least
|
||||
if (offset + 4 * 4 > size)
|
||||
return false; // 4 entries x (type+size)
|
||||
|
||||
NPBindBody body;
|
||||
|
||||
// helper lambda to read one entry
|
||||
auto read_entry = [&](NPBindEntryRaw& e) -> bool {
|
||||
if (offset + 4 > size)
|
||||
return false;
|
||||
|
||||
memcpy(&e.type, &buf[offset], 2);
|
||||
memcpy(&e.size, &buf[offset + 2], 2);
|
||||
offset += 4;
|
||||
|
||||
if (offset + e.size > size)
|
||||
return false;
|
||||
|
||||
e.data.assign(buf.begin() + offset, buf.begin() + offset + e.size);
|
||||
offset += e.size;
|
||||
return true;
|
||||
};
|
||||
|
||||
// read 4 entries in order
|
||||
if (!read_entry(body.npcommid))
|
||||
return false;
|
||||
if (!read_entry(body.trophy))
|
||||
return false;
|
||||
if (!read_entry(body.unk1))
|
||||
return false;
|
||||
if (!read_entry(body.unk2))
|
||||
return false;
|
||||
|
||||
// skip fixed padding after body if present (but don't overrun)
|
||||
if (offset + body_padding <= size) {
|
||||
offset += body_padding;
|
||||
} else {
|
||||
// If padding not fully present, allow file to end (some variants may omit)
|
||||
offset = size;
|
||||
}
|
||||
|
||||
m_bodies.push_back(std::move(body));
|
||||
}
|
||||
|
||||
// Read digest if available
|
||||
if (size >= 20) {
|
||||
// Digest is typically the last 20 bytes, independent of offset
|
||||
memcpy(m_digest, &buf[size - 20], 20);
|
||||
} else {
|
||||
memset(m_digest, 0, 20);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> NPBindFile::GetNpCommIds() const {
|
||||
std::vector<std::string> npcommids;
|
||||
npcommids.reserve(m_bodies.size());
|
||||
|
||||
for (const auto& body : m_bodies) {
|
||||
// Convert binary data to string directly
|
||||
if (!body.npcommid.data.empty()) {
|
||||
std::string raw_string(reinterpret_cast<const char*>(body.npcommid.data.data()),
|
||||
body.npcommid.data.size());
|
||||
npcommids.push_back(raw_string);
|
||||
}
|
||||
}
|
||||
|
||||
return npcommids;
|
||||
}
|
||||
87
src/core/file_format/npbind.h
Normal file
87
src/core/file_format/npbind.h
Normal file
@ -0,0 +1,87 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadLauncher4 Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/endian.h"
|
||||
#include "common/types.h"
|
||||
|
||||
#define NPBIND_MAGIC 0xD294A018u
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct NpBindHeader {
|
||||
u32_be magic;
|
||||
u32_be version;
|
||||
u64_be file_size;
|
||||
u64_be entry_size;
|
||||
u64_be num_entries;
|
||||
char padding[0x60]; // 96 bytes
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct NPBindEntryRaw {
|
||||
u16_be type;
|
||||
u16_be size; // includes internal padding
|
||||
std::vector<u8> data;
|
||||
};
|
||||
|
||||
struct NPBindBody {
|
||||
NPBindEntryRaw npcommid; // expected type 0x0010, size 12
|
||||
NPBindEntryRaw trophy; // expected type 0x0011, size 12
|
||||
NPBindEntryRaw unk1; // expected type 0x0012, size 176
|
||||
NPBindEntryRaw unk2; // expected type 0x0013, size 16
|
||||
// The 0x98 padding after these entries is skipped while parsing
|
||||
};
|
||||
|
||||
class NPBindFile {
|
||||
private:
|
||||
NpBindHeader m_header;
|
||||
std::vector<NPBindBody> m_bodies;
|
||||
u8 m_digest[20]; // zeroed if absent
|
||||
|
||||
public:
|
||||
NPBindFile() {
|
||||
memset(m_digest, 0, sizeof(m_digest));
|
||||
}
|
||||
|
||||
// Load from file
|
||||
bool Load(const std::string& path);
|
||||
|
||||
// Accessors
|
||||
const NpBindHeader& Header() const {
|
||||
return m_header;
|
||||
}
|
||||
const std::vector<NPBindBody>& Bodies() const {
|
||||
return m_bodies;
|
||||
}
|
||||
const u8* Digest() const {
|
||||
return m_digest;
|
||||
}
|
||||
|
||||
// Get npcommid data
|
||||
std::vector<std::string> GetNpCommIds() const;
|
||||
|
||||
// Get specific body
|
||||
const NPBindBody& GetBody(size_t index) const {
|
||||
return m_bodies.at(index);
|
||||
}
|
||||
|
||||
// Get number of bodies
|
||||
u64 BodyCount() const {
|
||||
return m_bodies.size();
|
||||
}
|
||||
|
||||
// Check if file was loaded successfully
|
||||
bool IsValid() const {
|
||||
return m_header.magic == NPBIND_MAGIC;
|
||||
}
|
||||
|
||||
// Clear all data
|
||||
void Clear() {
|
||||
m_header = NpBindHeader{};
|
||||
m_bodies.clear();
|
||||
memset(m_digest, 0, sizeof(m_digest));
|
||||
}
|
||||
};
|
||||
@ -1,44 +1,31 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/aes.h"
|
||||
#include "common/config.h"
|
||||
#include "common/key_manager.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/path_util.h"
|
||||
#include "core/file_format/npbind.h"
|
||||
#include "core/file_format/trp.h"
|
||||
|
||||
static void DecryptEFSM(std::span<u8, 16> trophyKey, std::span<u8, 16> NPcommID,
|
||||
std::span<u8, 16> efsmIv, std::span<u8> ciphertext,
|
||||
static void DecryptEFSM(std::span<const u8, 16> trophyKey, std::span<const u8, 16> NPcommID,
|
||||
std::span<const u8, 16> efsmIv, std::span<const u8> ciphertext,
|
||||
std::span<u8> decrypted) {
|
||||
// Step 1: Encrypt NPcommID
|
||||
std::array<u8, 16> trophyIv{};
|
||||
std::array<u8, 16> trpKey;
|
||||
// Convert spans to pointers for the aes functions
|
||||
aes::encrypt_cbc(NPcommID.data(), NPcommID.size(), trophyKey.data(), trophyKey.size(),
|
||||
trophyIv.data(), trpKey.data(), trpKey.size(), false);
|
||||
|
||||
// Step 2: Decrypt EFSM
|
||||
aes::decrypt_cbc(ciphertext.data(), ciphertext.size(), trpKey.data(), trpKey.size(),
|
||||
efsmIv.data(), decrypted.data(), decrypted.size(), nullptr);
|
||||
const_cast<u8*>(efsmIv.data()), decrypted.data(), decrypted.size(), nullptr);
|
||||
}
|
||||
|
||||
TRP::TRP() = default;
|
||||
TRP::~TRP() = default;
|
||||
|
||||
void TRP::GetNPcommID(const std::filesystem::path& trophyPath, int index) {
|
||||
std::filesystem::path trpPath = trophyPath / "sce_sys/npbind.dat";
|
||||
Common::FS::IOFile npbindFile(trpPath, Common::FS::FileAccessMode::Read);
|
||||
if (!npbindFile.IsOpen()) {
|
||||
LOG_CRITICAL(Common_Filesystem, "Failed to open npbind.dat file");
|
||||
return;
|
||||
}
|
||||
if (!npbindFile.Seek(0x84 + (index * 0x180))) {
|
||||
LOG_CRITICAL(Common_Filesystem, "Failed to seek to NPbind offset");
|
||||
return;
|
||||
}
|
||||
npbindFile.ReadRaw<u8>(np_comm_id.data(), 12);
|
||||
std::fill(np_comm_id.begin() + 12, np_comm_id.end(), 0); // fill with 0, we need 16 bytes.
|
||||
}
|
||||
|
||||
static void removePadding(std::vector<u8>& vec) {
|
||||
for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
|
||||
if (*it == '>') {
|
||||
@ -63,91 +50,232 @@ bool TRP::Extract(const std::filesystem::path& trophyPath, const std::string tit
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto user_key_str = Config::getTrophyKey();
|
||||
if (user_key_str.size() != 32) {
|
||||
const auto& user_key_vec =
|
||||
KeyManager::GetInstance()->GetAllKeys().TrophyKeySet.ReleaseTrophyKey;
|
||||
|
||||
if (user_key_vec.size() != 16) {
|
||||
LOG_INFO(Common_Filesystem, "Trophy decryption key is not specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::array<u8, 16> user_key{};
|
||||
hexToBytes(user_key_str.c_str(), user_key.data());
|
||||
std::copy(user_key_vec.begin(), user_key_vec.end(), user_key.begin());
|
||||
|
||||
for (int index = 0; const auto& it : std::filesystem::directory_iterator(gameSysDir)) {
|
||||
if (it.is_regular_file()) {
|
||||
GetNPcommID(trophyPath, index);
|
||||
// Load npbind.dat using the new class
|
||||
std::filesystem::path npbindPath = trophyPath / "sce_sys/npbind.dat";
|
||||
NPBindFile npbind;
|
||||
if (!npbind.Load(npbindPath.string())) {
|
||||
LOG_WARNING(Common_Filesystem, "Failed to load npbind.dat file");
|
||||
}
|
||||
|
||||
auto npCommIds = npbind.GetNpCommIds();
|
||||
if (npCommIds.empty()) {
|
||||
LOG_WARNING(Common_Filesystem, "No NPComm IDs found in npbind.dat");
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
int trpFileIndex = 0;
|
||||
|
||||
try {
|
||||
// Process each TRP file in the trophy directory
|
||||
for (const auto& it : std::filesystem::directory_iterator(gameSysDir)) {
|
||||
if (!it.is_regular_file() || it.path().extension() != ".trp") {
|
||||
continue; // Skip non-TRP files
|
||||
}
|
||||
|
||||
// Get NPCommID for this TRP file (if available)
|
||||
std::string npCommId;
|
||||
if (trpFileIndex < static_cast<int>(npCommIds.size())) {
|
||||
npCommId = npCommIds[trpFileIndex];
|
||||
LOG_DEBUG(Common_Filesystem, "Using NPCommID: {} for {}", npCommId,
|
||||
it.path().filename().string());
|
||||
} else {
|
||||
LOG_WARNING(Common_Filesystem, "No NPCommID found for TRP file index {}",
|
||||
trpFileIndex);
|
||||
}
|
||||
|
||||
Common::FS::IOFile file(it.path(), Common::FS::FileAccessMode::Read);
|
||||
if (!file.IsOpen()) {
|
||||
LOG_CRITICAL(Common_Filesystem, "Unable to open trophy file for read");
|
||||
return false;
|
||||
LOG_ERROR(Common_Filesystem, "Unable to open trophy file: {}", it.path().string());
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
TrpHeader header;
|
||||
file.Read(header);
|
||||
if (header.magic != 0xDCA24D00) {
|
||||
LOG_CRITICAL(Common_Filesystem, "Wrong trophy magic number");
|
||||
return false;
|
||||
if (!file.Read(header)) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to read TRP header from {}",
|
||||
it.path().string());
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (header.magic != TRP_MAGIC) {
|
||||
LOG_ERROR(Common_Filesystem, "Wrong trophy magic number in {}", it.path().string());
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
s64 seekPos = sizeof(TrpHeader);
|
||||
std::filesystem::path trpFilesPath(
|
||||
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / titleId /
|
||||
"TrophyFiles" / it.path().stem());
|
||||
std::filesystem::create_directories(trpFilesPath / "Icons");
|
||||
std::filesystem::create_directory(trpFilesPath / "Xml");
|
||||
|
||||
// Create output directories
|
||||
if (!std::filesystem::create_directories(trpFilesPath / "Icons") ||
|
||||
!std::filesystem::create_directories(trpFilesPath / "Xml")) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to create output directories for {}", titleId);
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Process each entry in the TRP file
|
||||
for (int i = 0; i < header.entry_num; i++) {
|
||||
if (!file.Seek(seekPos)) {
|
||||
LOG_CRITICAL(Common_Filesystem, "Failed to seek to TRP entry offset");
|
||||
return false;
|
||||
LOG_ERROR(Common_Filesystem, "Failed to seek to TRP entry offset");
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
seekPos += (s64)header.entry_size;
|
||||
seekPos += static_cast<s64>(header.entry_size);
|
||||
|
||||
TrpEntry entry;
|
||||
file.Read(entry);
|
||||
std::string_view name(entry.entry_name);
|
||||
if (entry.flag == 0) { // PNG
|
||||
if (!file.Seek(entry.entry_pos)) {
|
||||
LOG_CRITICAL(Common_Filesystem, "Failed to seek to TRP entry offset");
|
||||
return false;
|
||||
}
|
||||
std::vector<u8> icon(entry.entry_len);
|
||||
file.Read(icon);
|
||||
Common::FS::IOFile::WriteBytes(trpFilesPath / "Icons" / name, icon);
|
||||
if (!file.Read(entry)) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to read TRP entry");
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
if (entry.flag == 3 && np_comm_id[0] == 'N' &&
|
||||
np_comm_id[1] == 'P') { // ESFM, encrypted.
|
||||
if (!file.Seek(entry.entry_pos)) {
|
||||
LOG_CRITICAL(Common_Filesystem, "Failed to seek to TRP entry offset");
|
||||
return false;
|
||||
|
||||
std::string_view name(entry.entry_name);
|
||||
|
||||
if (entry.flag == ENTRY_FLAG_PNG) {
|
||||
if (!ProcessPngEntry(file, entry, trpFilesPath, name)) {
|
||||
success = false;
|
||||
// Continue with next entry
|
||||
}
|
||||
file.Read(esfmIv); // get iv key.
|
||||
// Skip the first 16 bytes which are the iv key on every entry as we want a
|
||||
// clean xml file.
|
||||
std::vector<u8> ESFM(entry.entry_len - iv_len);
|
||||
std::vector<u8> XML(entry.entry_len - iv_len);
|
||||
if (!file.Seek(entry.entry_pos + iv_len)) {
|
||||
LOG_CRITICAL(Common_Filesystem, "Failed to seek to TRP entry + iv offset");
|
||||
return false;
|
||||
}
|
||||
file.Read(ESFM);
|
||||
DecryptEFSM(user_key, np_comm_id, esfmIv, ESFM, XML); // decrypt
|
||||
removePadding(XML);
|
||||
std::string xml_name = entry.entry_name;
|
||||
size_t pos = xml_name.find("ESFM");
|
||||
if (pos != std::string::npos)
|
||||
xml_name.replace(pos, xml_name.length(), "XML");
|
||||
std::filesystem::path path = trpFilesPath / "Xml" / xml_name;
|
||||
size_t written = Common::FS::IOFile::WriteBytes(path, XML);
|
||||
if (written != XML.size()) {
|
||||
LOG_CRITICAL(
|
||||
Common_Filesystem,
|
||||
"Trophy XML {} write failed, wanted to write {} bytes, wrote {}",
|
||||
fmt::UTF(path.u8string()), XML.size(), written);
|
||||
} else if (entry.flag == ENTRY_FLAG_ENCRYPTED_XML) {
|
||||
// Check if we have a valid NPCommID for decryption
|
||||
if (npCommId.size() >= 12 && npCommId[0] == 'N' && npCommId[1] == 'P') {
|
||||
if (!ProcessEncryptedXmlEntry(file, entry, trpFilesPath, name, user_key,
|
||||
npCommId)) {
|
||||
success = false;
|
||||
// Continue with next entry
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING(Common_Filesystem,
|
||||
"Skipping encrypted XML entry - invalid NPCommID");
|
||||
// Skip this entry but continue
|
||||
}
|
||||
} else {
|
||||
LOG_DEBUG(Common_Filesystem, "Unknown entry flag: {} for {}",
|
||||
static_cast<unsigned int>(entry.flag), name);
|
||||
}
|
||||
}
|
||||
|
||||
trpFileIndex++;
|
||||
}
|
||||
index++;
|
||||
} catch (const std::filesystem::filesystem_error& e) {
|
||||
LOG_CRITICAL(Common_Filesystem, "Filesystem error during trophy extraction: {}", e.what());
|
||||
return false;
|
||||
} catch (const std::exception& e) {
|
||||
LOG_CRITICAL(Common_Filesystem, "Error during trophy extraction: {}", e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
LOG_INFO(Common_Filesystem, "Successfully extracted {} trophy files for {}", trpFileIndex,
|
||||
titleId);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool TRP::ProcessPngEntry(Common::FS::IOFile& file, const TrpEntry& entry,
|
||||
const std::filesystem::path& outputPath, std::string_view name) {
|
||||
if (!file.Seek(entry.entry_pos)) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to seek to PNG entry offset");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<u8> icon(entry.entry_len);
|
||||
if (!file.Read(icon)) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to read PNG data");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto outputFile = outputPath / "Icons" / name;
|
||||
size_t written = Common::FS::IOFile::WriteBytes(outputFile, icon);
|
||||
if (written != icon.size()) {
|
||||
LOG_ERROR(Common_Filesystem, "PNG write failed: wanted {} bytes, wrote {}", icon.size(),
|
||||
written);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TRP::ProcessEncryptedXmlEntry(Common::FS::IOFile& file, const TrpEntry& entry,
|
||||
const std::filesystem::path& outputPath, std::string_view name,
|
||||
const std::array<u8, 16>& user_key,
|
||||
const std::string& npCommId) {
|
||||
constexpr size_t IV_LEN = 16;
|
||||
|
||||
if (!file.Seek(entry.entry_pos)) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to seek to encrypted XML entry offset");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::array<u8, IV_LEN> esfmIv;
|
||||
if (!file.Read(esfmIv)) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to read IV for encrypted XML");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry.entry_len <= IV_LEN) {
|
||||
LOG_ERROR(Common_Filesystem, "Encrypted XML entry too small");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip to the encrypted data (after IV)
|
||||
if (!file.Seek(entry.entry_pos + IV_LEN)) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to seek to encrypted data");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<u8> ESFM(entry.entry_len - IV_LEN);
|
||||
std::vector<u8> XML(entry.entry_len - IV_LEN);
|
||||
|
||||
if (!file.Read(ESFM)) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to read encrypted XML data");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Decrypt the data - FIX: Don't check return value since DecryptEFSM returns void
|
||||
std::span<const u8, 16> key_span(user_key);
|
||||
|
||||
// Convert npCommId string to span (pad or truncate to 16 bytes)
|
||||
std::array<u8, 16> npcommid_array{};
|
||||
size_t copy_len = std::min(npCommId.size(), npcommid_array.size());
|
||||
std::memcpy(npcommid_array.data(), npCommId.data(), copy_len);
|
||||
std::span<const u8, 16> npcommid_span(npcommid_array);
|
||||
|
||||
DecryptEFSM(key_span, npcommid_span, esfmIv, ESFM, XML);
|
||||
|
||||
// Remove padding
|
||||
removePadding(XML);
|
||||
|
||||
// Create output filename (replace ESFM with XML)
|
||||
std::string xml_name(entry.entry_name);
|
||||
size_t pos = xml_name.find("ESFM");
|
||||
if (pos != std::string::npos) {
|
||||
xml_name.replace(pos, 4, "XML");
|
||||
}
|
||||
|
||||
auto outputFile = outputPath / "Xml" / xml_name;
|
||||
size_t written = Common::FS::IOFile::WriteBytes(outputFile, XML);
|
||||
if (written != XML.size()) {
|
||||
LOG_ERROR(Common_Filesystem, "XML write failed: wanted {} bytes, wrote {}", XML.size(),
|
||||
written);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -8,6 +8,10 @@
|
||||
#include "common/io_file.h"
|
||||
#include "common/types.h"
|
||||
|
||||
static constexpr u32 TRP_MAGIC = 0xDCA24D00;
|
||||
static constexpr u8 ENTRY_FLAG_PNG = 0;
|
||||
static constexpr u8 ENTRY_FLAG_ENCRYPTED_XML = 3;
|
||||
|
||||
struct TrpHeader {
|
||||
u32_be magic; // (0xDCA24D00)
|
||||
u32_be version;
|
||||
@ -33,9 +37,14 @@ public:
|
||||
TRP();
|
||||
~TRP();
|
||||
bool Extract(const std::filesystem::path& trophyPath, const std::string titleId);
|
||||
void GetNPcommID(const std::filesystem::path& trophyPath, int index);
|
||||
|
||||
private:
|
||||
bool ProcessPngEntry(Common::FS::IOFile& file, const TrpEntry& entry,
|
||||
const std::filesystem::path& outputPath, std::string_view name);
|
||||
bool ProcessEncryptedXmlEntry(Common::FS::IOFile& file, const TrpEntry& entry,
|
||||
const std::filesystem::path& outputPath, std::string_view name,
|
||||
const std::array<u8, 16>& user_key, const std::string& npCommId);
|
||||
|
||||
std::vector<u8> NPcommID = std::vector<u8>(12);
|
||||
std::array<u8, 16> np_comm_id{};
|
||||
std::array<u8, 16> esfmIv{};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cmath>
|
||||
@ -345,7 +345,7 @@ int PS4_SYSV_ABI sceAppContentInitialize(const OrbisAppContentInitParam* initPar
|
||||
if (addcont_count > 0) {
|
||||
SystemService::OrbisSystemServiceEvent event{};
|
||||
event.event_type = SystemService::OrbisSystemServiceEventType::EntitlementUpdate;
|
||||
event.service_entitlement_update.user_id = 0;
|
||||
event.service_entitlement_update.userId = 0;
|
||||
event.service_entitlement_update.np_service_label = 0;
|
||||
SystemService::PushSystemServiceEvent(event);
|
||||
}
|
||||
|
||||
@ -29,10 +29,10 @@ s32 PS4_SYSV_ABI sceAudio3dAudioOutClose(const s32 handle) {
|
||||
return AudioOut::sceAudioOutClose(handle);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI
|
||||
sceAudio3dAudioOutOpen(const OrbisAudio3dPortId port_id, const OrbisUserServiceUserId user_id,
|
||||
s32 type, const s32 index, const u32 len, const u32 freq,
|
||||
const AudioOut::OrbisAudioOutParamExtendedInformation param) {
|
||||
s32 PS4_SYSV_ABI sceAudio3dAudioOutOpen(
|
||||
const OrbisAudio3dPortId port_id, const Libraries::UserService::OrbisUserServiceUserId user_id,
|
||||
s32 type, const s32 index, const u32 len, const u32 freq,
|
||||
const AudioOut::OrbisAudioOutParamExtendedInformation param) {
|
||||
LOG_INFO(Lib_Audio3d,
|
||||
"called, port_id = {}, user_id = {}, type = {}, index = {}, len = {}, freq = {}",
|
||||
port_id, user_id, type, index, len, freq);
|
||||
@ -422,7 +422,7 @@ s32 PS4_SYSV_ABI sceAudio3dPortGetStatus() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortOpen(const OrbisUserServiceUserId user_id,
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortOpen(const Libraries::UserService::OrbisUserServiceUserId user_id,
|
||||
const OrbisAudio3dOpenParameters* parameters,
|
||||
OrbisAudio3dPortId* port_id) {
|
||||
LOG_INFO(Lib_Audio3d, "called, user_id = {}, parameters = {}, id = {}", user_id,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
@ -15,8 +15,6 @@ class SymbolsResolver;
|
||||
|
||||
namespace Libraries::Audio3d {
|
||||
|
||||
using OrbisUserServiceUserId = s32;
|
||||
|
||||
enum class OrbisAudio3dRate : u32 {
|
||||
ORBIS_AUDIO3D_RATE_48000 = 0,
|
||||
};
|
||||
@ -91,7 +89,8 @@ struct Audio3dState {
|
||||
};
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudio3dAudioOutClose(s32 handle);
|
||||
s32 PS4_SYSV_ABI sceAudio3dAudioOutOpen(OrbisAudio3dPortId port_id, OrbisUserServiceUserId user_id,
|
||||
s32 PS4_SYSV_ABI sceAudio3dAudioOutOpen(OrbisAudio3dPortId port_id,
|
||||
Libraries::UserService::OrbisUserServiceUserId user_id,
|
||||
s32 type, s32 index, u32 len, u32 freq,
|
||||
AudioOut::OrbisAudioOutParamExtendedInformation param);
|
||||
s32 PS4_SYSV_ABI sceAudio3dAudioOutOutput(s32 handle, void* ptr);
|
||||
@ -127,7 +126,7 @@ s32 PS4_SYSV_ABI sceAudio3dPortGetQueueLevel(OrbisAudio3dPortId port_id, u32* qu
|
||||
u32* queue_available);
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortGetState();
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortGetStatus();
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortOpen(OrbisUserServiceUserId user_id,
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortOpen(Libraries::UserService::OrbisUserServiceUserId user_id,
|
||||
const OrbisAudio3dOpenParameters* parameters,
|
||||
OrbisAudio3dPortId* port_id);
|
||||
s32 PS4_SYSV_ABI sceAudio3dPortPush(OrbisAudio3dPortId port_id, OrbisAudio3dBlocking blocking);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
@ -16,7 +16,7 @@ s32 PS4_SYSV_ABI sceCompanionHttpdAddHeader(const char* key, const char* value,
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI
|
||||
sceCompanionHttpdGet2ndScreenStatus(Libraries::UserService::OrbisUserServiceUserId) {
|
||||
sceCompanionHttpdGet2ndScreenStatus(Libraries::UserService::OrbisUserServiceUserId userId) {
|
||||
LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
@ -15,11 +16,11 @@ struct OrbisGameLiveStreamingStatus {
|
||||
bool isOnAir;
|
||||
u8 align[3];
|
||||
u32 spectatorCounts;
|
||||
s32 userId;
|
||||
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||
u8 reserved[60];
|
||||
};
|
||||
struct OrbisGameLiveStreamingStatus2 {
|
||||
s32 userId;
|
||||
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||
bool isOnAir;
|
||||
u8 align[3];
|
||||
u32 spectatorCounts;
|
||||
|
||||
@ -115,9 +115,199 @@ Error PS4_SYSV_ABI sceImeDialogGetPanelSize(const OrbisImeDialogParam* param, u3
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended() {
|
||||
LOG_ERROR(Lib_ImeDialog, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended(const OrbisImeDialogParam* param,
|
||||
const OrbisImeParamExtended* extended,
|
||||
u32* width, u32* height) {
|
||||
if (!param || !width || !height) {
|
||||
return Error::INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
// Check parameter bounds
|
||||
if (static_cast<uint32_t>(param->type) > 4) {
|
||||
return Error::INVALID_ARG;
|
||||
}
|
||||
|
||||
if (extended) {
|
||||
// Check panel priority for full panel mode (Accent = 3)
|
||||
if (extended->priority == OrbisImePanelPriority::Accent) {
|
||||
// Full panel mode - return maximum size
|
||||
if ((param->option & OrbisImeOption::USE_OVER_2K_COORDINATES) !=
|
||||
OrbisImeOption::DEFAULT) {
|
||||
*width = 2560; // For 4K/5K displays
|
||||
*height = 1440;
|
||||
} else {
|
||||
*width = 1920;
|
||||
*height = 1080;
|
||||
}
|
||||
LOG_DEBUG(Lib_ImeDialog, "Full panel mode: width={}, height={}", *width, *height);
|
||||
return Error::OK;
|
||||
}
|
||||
}
|
||||
|
||||
// First get the base panel size from the basic function
|
||||
Error result = sceImeDialogGetPanelSize(param, width, height);
|
||||
if (result != Error::OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Adjust based on IME type
|
||||
switch (param->type) {
|
||||
case OrbisImeType::Default:
|
||||
case OrbisImeType::BasicLatin:
|
||||
case OrbisImeType::Url:
|
||||
case OrbisImeType::Mail:
|
||||
// Standard IME types
|
||||
if ((param->option & OrbisImeOption::PASSWORD) != OrbisImeOption::DEFAULT) {
|
||||
*height = *height + 20;
|
||||
}
|
||||
if ((param->option & OrbisImeOption::MULTILINE) != OrbisImeOption::DEFAULT) {
|
||||
*height = *height * 3 / 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case OrbisImeType::Number:
|
||||
*width = *width * 3 / 4;
|
||||
*height = *height * 2 / 3;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Unknown type, use default size
|
||||
break;
|
||||
}
|
||||
|
||||
// Apply extended options if provided
|
||||
if (extended) {
|
||||
// Handle extended option flags
|
||||
if ((extended->option & OrbisImeExtOption::PRIORITY_FULL_WIDTH) !=
|
||||
OrbisImeExtOption::DEFAULT) {
|
||||
// Full width priority
|
||||
bool use_2k = (param->option & OrbisImeOption::USE_OVER_2K_COORDINATES) !=
|
||||
OrbisImeOption::DEFAULT;
|
||||
*width = use_2k ? 1200 : 800;
|
||||
LOG_DEBUG(Lib_ImeDialog, "Full width priority: width={}", *width);
|
||||
}
|
||||
|
||||
if ((extended->option & OrbisImeExtOption::PRIORITY_FIXED_PANEL) !=
|
||||
OrbisImeExtOption::DEFAULT) {
|
||||
// Fixed panel size
|
||||
*width = 600;
|
||||
*height = 400;
|
||||
LOG_DEBUG(Lib_ImeDialog, "Fixed panel: width={}, height={}", *width, *height);
|
||||
}
|
||||
|
||||
switch (extended->priority) {
|
||||
case OrbisImePanelPriority::Alphabet:
|
||||
*width = 600;
|
||||
*height = 400;
|
||||
break;
|
||||
|
||||
case OrbisImePanelPriority::Symbol:
|
||||
*width = 500;
|
||||
*height = 300;
|
||||
break;
|
||||
|
||||
case OrbisImePanelPriority::Accent:
|
||||
// Already handled
|
||||
break;
|
||||
|
||||
case OrbisImePanelPriority::Default:
|
||||
default:
|
||||
// Use calculated sizes
|
||||
break;
|
||||
}
|
||||
|
||||
if ((extended->option & OrbisImeExtOption::INIT_EXT_KEYBOARD_MODE) !=
|
||||
OrbisImeExtOption::DEFAULT) {
|
||||
if (extended->ext_keyboard_mode != 0) {
|
||||
// Check for high-res mode flags
|
||||
if ((extended->ext_keyboard_mode &
|
||||
static_cast<uint32_t>(
|
||||
OrbisImeInitExtKeyboardMode::INPUT_METHOD_STATE_FULL_WIDTH)) != 0) {
|
||||
*width = *width * 5 / 4;
|
||||
}
|
||||
|
||||
// Check for format characters enabled
|
||||
if ((extended->ext_keyboard_mode &
|
||||
static_cast<uint32_t>(
|
||||
OrbisImeInitExtKeyboardMode::ENABLE_FORMAT_CHARACTERS)) != 0) {
|
||||
*height = *height + 30;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for accessibility mode
|
||||
if ((extended->option & OrbisImeExtOption::ENABLE_ACCESSIBILITY) !=
|
||||
OrbisImeExtOption::DEFAULT) {
|
||||
*width = *width * 5 / 4; // 25% larger for accessibility
|
||||
*height = *height * 5 / 4;
|
||||
LOG_DEBUG(Lib_ImeDialog, "Accessibility mode: width={}, height={}", *width, *height);
|
||||
}
|
||||
|
||||
// Check for forced accessibility panel
|
||||
if ((extended->option & OrbisImeExtOption::ACCESSIBILITY_PANEL_FORCED) !=
|
||||
OrbisImeExtOption::DEFAULT) {
|
||||
*width = 800;
|
||||
*height = 600;
|
||||
LOG_DEBUG(Lib_ImeDialog, "Forced accessibility panel: width={}, height={}", *width,
|
||||
*height);
|
||||
}
|
||||
}
|
||||
|
||||
if ((param->option & static_cast<OrbisImeOption>(0x8)) != OrbisImeOption::DEFAULT) { //?
|
||||
*width *= 2;
|
||||
*height *= 2;
|
||||
LOG_DEBUG(Lib_ImeDialog, "Size mode: width={}, height={}", *width, *height);
|
||||
}
|
||||
|
||||
// Adjust for supported languages if specified
|
||||
if (param->supported_languages != static_cast<OrbisImeLanguage>(0)) {
|
||||
// Check if CJK languages are supported (need larger panel)
|
||||
OrbisImeLanguage cjk_mask = OrbisImeLanguage::JAPANESE | OrbisImeLanguage::KOREAN |
|
||||
OrbisImeLanguage::SIMPLIFIED_CHINESE |
|
||||
OrbisImeLanguage::TRADITIONAL_CHINESE;
|
||||
|
||||
if ((param->supported_languages & cjk_mask) != static_cast<OrbisImeLanguage>(0)) {
|
||||
*width = *width * 5 / 4; // 25% wider for CJK input
|
||||
*height = *height * 6 / 5; // 20% taller
|
||||
LOG_DEBUG(Lib_ImeDialog, "CJK language support: width={}, height={}", *width, *height);
|
||||
}
|
||||
|
||||
// Check if Arabic is supported (right-to-left layout)
|
||||
if ((param->supported_languages & OrbisImeLanguage::ARABIC) !=
|
||||
static_cast<OrbisImeLanguage>(0)) {
|
||||
*width = *width * 11 / 10; // 10% wider for Arabic
|
||||
LOG_DEBUG(Lib_ImeDialog, "Arabic language support: width={}", *width);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure minimum sizes
|
||||
const uint32_t min_width = 200;
|
||||
const uint32_t min_height = 100;
|
||||
if (*width < min_width)
|
||||
*width = min_width;
|
||||
if (*height < min_height)
|
||||
*height = min_height;
|
||||
|
||||
// Ensure maximum sizes (don't exceed screen bounds)
|
||||
bool use_2k_coords =
|
||||
(param->option & OrbisImeOption::USE_OVER_2K_COORDINATES) != OrbisImeOption::DEFAULT;
|
||||
const uint32_t max_width = use_2k_coords ? 2560 : 1920;
|
||||
const uint32_t max_height = use_2k_coords ? 1440 : 1080;
|
||||
if (*width > max_width)
|
||||
*width = max_width;
|
||||
if (*height > max_height)
|
||||
*height = max_height;
|
||||
|
||||
// Check for fixed position option
|
||||
if ((param->option & OrbisImeOption::FIXED_POSITION) != OrbisImeOption::DEFAULT) {
|
||||
if (*width > 800)
|
||||
*width = 800;
|
||||
if (*height > 600)
|
||||
*height = 600;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Lib_ImeDialog, "Final panel size: width={}, height={}", *width, *height);
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result) {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
@ -37,7 +37,9 @@ int PS4_SYSV_ABI sceImeDialogGetCurrentStarState();
|
||||
int PS4_SYSV_ABI sceImeDialogGetPanelPositionAndForm();
|
||||
Error PS4_SYSV_ABI sceImeDialogGetPanelSize(const OrbisImeDialogParam* param, u32* width,
|
||||
u32* height);
|
||||
int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended();
|
||||
Error PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended(const OrbisImeDialogParam* param,
|
||||
const OrbisImeParamExtended* extended,
|
||||
u32* width, u32* height);
|
||||
Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result);
|
||||
OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus();
|
||||
Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended);
|
||||
|
||||
@ -102,6 +102,7 @@ s32 PS4_SYSV_ABI sceKernelReleaseDirectMemory(u64 start, u64 len) {
|
||||
if (len == 0) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
LOG_INFO(Kernel_Vmm, "called start = {:#x}, len = {:#x}", start, len);
|
||||
auto* memory = Core::Memory::Instance();
|
||||
memory->Free(start, len);
|
||||
return ORBIS_OK;
|
||||
|
||||
@ -655,10 +655,17 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or
|
||||
|
||||
switch (file->type) {
|
||||
case Core::FileSys::FileType::Socket: {
|
||||
auto native_handle = file->socket->Native();
|
||||
if (!native_handle) {
|
||||
// P2P socket, cannot be added to epoll
|
||||
LOG_ERROR(Lib_Net, "P2P socket cannot be added to epoll (unimplemented)");
|
||||
*sceNetErrnoLoc() = ORBIS_NET_EBADF;
|
||||
return ORBIS_NET_ERROR_EBADF;
|
||||
}
|
||||
|
||||
epoll_event native_event = {.events = ConvertEpollEventsIn(event->events),
|
||||
.data = {.fd = id}};
|
||||
ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_ADD, *file->socket->Native(),
|
||||
&native_event) == 0);
|
||||
ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_ADD, *native_handle, &native_event) == 0);
|
||||
epoll->events.emplace_back(id, *event);
|
||||
break;
|
||||
}
|
||||
@ -696,10 +703,17 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or
|
||||
|
||||
switch (file->type) {
|
||||
case Core::FileSys::FileType::Socket: {
|
||||
auto native_handle = file->socket->Native();
|
||||
if (!native_handle) {
|
||||
// P2P socket, cannot be modified in epoll
|
||||
LOG_ERROR(Lib_Net, "P2P socket cannot be modified in epoll (unimplemented)");
|
||||
*sceNetErrnoLoc() = ORBIS_NET_EBADF;
|
||||
return ORBIS_NET_ERROR_EBADF;
|
||||
}
|
||||
|
||||
epoll_event native_event = {.events = ConvertEpollEventsIn(event->events),
|
||||
.data = {.fd = id}};
|
||||
ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_MOD, *file->socket->Native(),
|
||||
&native_event) == 0);
|
||||
ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_MOD, *native_handle, &native_event) == 0);
|
||||
*it = {id, *event};
|
||||
break;
|
||||
}
|
||||
@ -731,8 +745,15 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or
|
||||
|
||||
switch (file->type) {
|
||||
case Core::FileSys::FileType::Socket: {
|
||||
ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_DEL, *file->socket->Native(), nullptr) ==
|
||||
0);
|
||||
auto native_handle = file->socket->Native();
|
||||
if (!native_handle) {
|
||||
// P2P socket, cannot be removed from epoll
|
||||
LOG_ERROR(Lib_Net, "P2P socket cannot be removed from epoll (unimplemented)");
|
||||
*sceNetErrnoLoc() = ORBIS_NET_EBADF;
|
||||
return ORBIS_NET_ERROR_EBADF;
|
||||
}
|
||||
|
||||
ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_DEL, *native_handle, nullptr) == 0);
|
||||
epoll->events.erase(it);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/np/np_types.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
@ -31,7 +32,7 @@ struct OrbisNpAuthGetAuthorizationCodeParameter {
|
||||
|
||||
struct OrbisNpAuthGetAuthorizationCodeParameterA {
|
||||
u64 size;
|
||||
s32 user_id;
|
||||
Libraries::UserService::OrbisUserServiceUserId user_id;
|
||||
u8 padding[4];
|
||||
const OrbisNpClientId* client_id;
|
||||
const char* scope;
|
||||
@ -47,7 +48,7 @@ struct OrbisNpAuthGetIdTokenParameter {
|
||||
|
||||
struct OrbisNpAuthGetIdTokenParameterA {
|
||||
u64 size;
|
||||
s32 user_id;
|
||||
Libraries::UserService::OrbisUserServiceUserId user_id;
|
||||
u8 padding[4];
|
||||
const OrbisNpClientId* client_id;
|
||||
const OrbisNpClientSecret* client_secret;
|
||||
|
||||
@ -23,8 +23,8 @@ enum class OrbisNpState : u32 {
|
||||
SignedIn = 2,
|
||||
};
|
||||
|
||||
using OrbisNpStateCallbackForNpToolkit = PS4_SYSV_ABI void (*)(s32 userId, OrbisNpState state,
|
||||
void* userdata);
|
||||
using OrbisNpStateCallbackForNpToolkit = PS4_SYSV_ABI void (*)(
|
||||
Libraries::UserService::OrbisUserServiceUserId userId, OrbisNpState state, void* userdata);
|
||||
|
||||
enum class OrbisNpGamePresenseStatus {
|
||||
Offline = 0,
|
||||
|
||||
@ -149,7 +149,8 @@ int PS4_SYSV_ABI sceNpTrophyConfigHasGroupFeature() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTrophyCreateContext(OrbisNpTrophyContext* context, s32 user_id,
|
||||
s32 PS4_SYSV_ABI sceNpTrophyCreateContext(OrbisNpTrophyContext* context,
|
||||
Libraries::UserService::OrbisUserServiceUserId user_id,
|
||||
uint32_t service_label, u64 options) {
|
||||
ASSERT(options == 0ull);
|
||||
if (!context) {
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <core/libraries/system/userservice.h>
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/rtc/rtc.h"
|
||||
|
||||
@ -132,7 +133,8 @@ int PS4_SYSV_ABI sceNpTrophyConfigGetTrophySetInfoInGroup();
|
||||
int PS4_SYSV_ABI sceNpTrophyConfigGetTrophySetVersion();
|
||||
int PS4_SYSV_ABI sceNpTrophyConfigGetTrophyTitleDetails();
|
||||
int PS4_SYSV_ABI sceNpTrophyConfigHasGroupFeature();
|
||||
s32 PS4_SYSV_ABI sceNpTrophyCreateContext(OrbisNpTrophyContext* context, s32 user_id,
|
||||
s32 PS4_SYSV_ABI sceNpTrophyCreateContext(OrbisNpTrophyContext* context,
|
||||
Libraries::UserService::OrbisUserServiceUserId user_id,
|
||||
u32 service_label, u64 options);
|
||||
s32 PS4_SYSV_ABI sceNpTrophyCreateHandle(OrbisNpTrophyHandle* handle);
|
||||
int PS4_SYSV_ABI sceNpTrophyDestroyContext(OrbisNpTrophyContext context);
|
||||
|
||||
@ -159,7 +159,8 @@ int PS4_SYSV_ABI scePadGetFeatureReport() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePadGetHandle(s32 userId, s32 type, s32 index) {
|
||||
int PS4_SYSV_ABI scePadGetHandle(Libraries::UserService::OrbisUserServiceUserId userId, s32 type,
|
||||
s32 index) {
|
||||
if (!g_initialized) {
|
||||
return ORBIS_PAD_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
@ -256,7 +257,8 @@ int PS4_SYSV_ABI scePadMbusTerm() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePadOpen(s32 userId, s32 type, s32 index, const OrbisPadOpenParam* pParam) {
|
||||
int PS4_SYSV_ABI scePadOpen(Libraries::UserService::OrbisUserServiceUserId userId, s32 type,
|
||||
s32 index, const OrbisPadOpenParam* pParam) {
|
||||
if (!g_initialized) {
|
||||
return ORBIS_PAD_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
@ -277,8 +279,8 @@ int PS4_SYSV_ABI scePadOpen(s32 userId, s32 type, s32 index, const OrbisPadOpenP
|
||||
return 1; // dummy
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePadOpenExt(s32 userId, s32 type, s32 index,
|
||||
const OrbisPadOpenExtParam* pParam) {
|
||||
int PS4_SYSV_ABI scePadOpenExt(Libraries::UserService::OrbisUserServiceUserId userId, s32 type,
|
||||
s32 index, const OrbisPadOpenExtParam* pParam) {
|
||||
LOG_ERROR(Lib_Pad, "(STUBBED) called");
|
||||
if (Config::getUseSpecialPad()) {
|
||||
if (type != ORBIS_PAD_PORT_TYPE_SPECIAL)
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <core/libraries/system/userservice.h>
|
||||
#include "common/enum.h"
|
||||
#include "common/types.h"
|
||||
|
||||
@ -276,7 +277,8 @@ int PS4_SYSV_ABI scePadGetExtControllerInformation(s32 handle,
|
||||
OrbisPadExtendedControllerInformation* pInfo);
|
||||
int PS4_SYSV_ABI scePadGetExtensionUnitInfo();
|
||||
int PS4_SYSV_ABI scePadGetFeatureReport();
|
||||
int PS4_SYSV_ABI scePadGetHandle(s32 userId, s32 type, s32 index);
|
||||
int PS4_SYSV_ABI scePadGetHandle(Libraries::UserService::OrbisUserServiceUserId userId, s32 type,
|
||||
s32 index);
|
||||
int PS4_SYSV_ABI scePadGetIdleCount();
|
||||
int PS4_SYSV_ABI scePadGetInfo();
|
||||
int PS4_SYSV_ABI scePadGetInfoByPortType();
|
||||
@ -294,8 +296,10 @@ int PS4_SYSV_ABI scePadIsMoveReproductionModel();
|
||||
int PS4_SYSV_ABI scePadIsValidHandle();
|
||||
int PS4_SYSV_ABI scePadMbusInit();
|
||||
int PS4_SYSV_ABI scePadMbusTerm();
|
||||
int PS4_SYSV_ABI scePadOpen(s32 userId, s32 type, s32 index, const OrbisPadOpenParam* pParam);
|
||||
int PS4_SYSV_ABI scePadOpenExt(s32 userId, s32 type, s32 index, const OrbisPadOpenExtParam* pParam);
|
||||
int PS4_SYSV_ABI scePadOpen(Libraries::UserService::OrbisUserServiceUserId userId, s32 type,
|
||||
s32 index, const OrbisPadOpenParam* pParam);
|
||||
int PS4_SYSV_ABI scePadOpenExt(Libraries::UserService::OrbisUserServiceUserId userId, s32 type,
|
||||
s32 index, const OrbisPadOpenExtParam* pParam);
|
||||
int PS4_SYSV_ABI scePadOpenExt2();
|
||||
int PS4_SYSV_ABI scePadOutputReport();
|
||||
int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num);
|
||||
|
||||
@ -54,7 +54,8 @@ int PS4_SYSV_ABI sceRemoteplayGetConnectHistory() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayGetConnectionStatus(s32 userId, int* pStatus) {
|
||||
int PS4_SYSV_ABI sceRemoteplayGetConnectionStatus(
|
||||
Libraries::UserService::OrbisUserServiceUserId userId, int* pStatus) {
|
||||
*pStatus = ORBIS_REMOTEPLAY_CONNECTION_STATUS_DISCONNECT;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <core/libraries/system/userservice.h>
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
@ -24,7 +25,8 @@ int PS4_SYSV_ABI sceRemoteplayDisconnect();
|
||||
int PS4_SYSV_ABI sceRemoteplayGeneratePinCode();
|
||||
int PS4_SYSV_ABI sceRemoteplayGetApMode();
|
||||
int PS4_SYSV_ABI sceRemoteplayGetConnectHistory();
|
||||
int PS4_SYSV_ABI sceRemoteplayGetConnectionStatus(s32 userId, int* pStatus);
|
||||
int PS4_SYSV_ABI sceRemoteplayGetConnectionStatus(
|
||||
Libraries::UserService::OrbisUserServiceUserId userId, int* pStatus);
|
||||
int PS4_SYSV_ABI sceRemoteplayGetConnectUserId();
|
||||
int PS4_SYSV_ABI sceRemoteplayGetMbusDeviceInfo();
|
||||
int PS4_SYSV_ABI sceRemoteplayGetOperationStatus();
|
||||
|
||||
@ -167,7 +167,7 @@ void StopThread() {
|
||||
g_backup_thread_semaphore.release();
|
||||
}
|
||||
|
||||
bool NewRequest(OrbisUserServiceUserId user_id, std::string_view title_id,
|
||||
bool NewRequest(Libraries::UserService::OrbisUserServiceUserId user_id, std::string_view title_id,
|
||||
std::string_view dir_name, OrbisSaveDataEventType origin) {
|
||||
auto save_path = SaveInstance::MakeDirSavePath(user_id, title_id, dir_name);
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
@ -6,12 +6,11 @@
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
|
||||
#include <core/libraries/system/userservice.h>
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Libraries::SaveData {
|
||||
|
||||
using OrbisUserServiceUserId = s32;
|
||||
|
||||
namespace Backup {
|
||||
|
||||
enum class WorkerStatus {
|
||||
@ -32,7 +31,7 @@ enum class OrbisSaveDataEventType : u32 {
|
||||
struct BackupRequest {
|
||||
bool done{};
|
||||
|
||||
OrbisUserServiceUserId user_id{};
|
||||
Libraries::UserService::OrbisUserServiceUserId user_id{};
|
||||
std::string title_id{};
|
||||
std::string dir_name{};
|
||||
OrbisSaveDataEventType origin{};
|
||||
@ -45,7 +44,7 @@ void StartThread();
|
||||
|
||||
void StopThread();
|
||||
|
||||
bool NewRequest(OrbisUserServiceUserId user_id, std::string_view title_id,
|
||||
bool NewRequest(Libraries::UserService::OrbisUserServiceUserId user_id, std::string_view title_id,
|
||||
std::string_view dir_name, OrbisSaveDataEventType origin);
|
||||
|
||||
bool Restore(const std::filesystem::path& save_path);
|
||||
|
||||
@ -46,13 +46,13 @@ static const std::unordered_map<int, std::string> default_title = {
|
||||
|
||||
namespace Libraries::SaveData {
|
||||
|
||||
fs::path SaveInstance::MakeTitleSavePath(OrbisUserServiceUserId user_id,
|
||||
fs::path SaveInstance::MakeTitleSavePath(Libraries::UserService::OrbisUserServiceUserId user_id,
|
||||
std::string_view game_serial) {
|
||||
return Config::GetSaveDataPath() / std::to_string(user_id) / game_serial;
|
||||
}
|
||||
|
||||
fs::path SaveInstance::MakeDirSavePath(OrbisUserServiceUserId user_id, std::string_view game_serial,
|
||||
std::string_view dir_name) {
|
||||
fs::path SaveInstance::MakeDirSavePath(Libraries::UserService::OrbisUserServiceUserId user_id,
|
||||
std::string_view game_serial, std::string_view dir_name) {
|
||||
return Config::GetSaveDataPath() / std::to_string(user_id) / game_serial / dir_name;
|
||||
}
|
||||
|
||||
@ -89,8 +89,8 @@ void SaveInstance::SetupDefaultParamSFO(PSF& param_sfo, std::string dir_name,
|
||||
#undef P
|
||||
}
|
||||
|
||||
SaveInstance::SaveInstance(int slot_num, OrbisUserServiceUserId user_id, std::string _game_serial,
|
||||
std::string_view _dir_name, int max_blocks)
|
||||
SaveInstance::SaveInstance(int slot_num, Libraries::UserService::OrbisUserServiceUserId user_id,
|
||||
std::string _game_serial, std::string_view _dir_name, int max_blocks)
|
||||
: slot_num(slot_num), user_id(user_id), game_serial(std::move(_game_serial)),
|
||||
dir_name(_dir_name),
|
||||
max_blocks(std::clamp(max_blocks, OrbisSaveDataBlocksMin2, OrbisSaveDataBlocksMax)) {
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include <core/libraries/system/userservice.h>
|
||||
#include "common/io_file.h"
|
||||
#include "core/file_format/psf.h"
|
||||
|
||||
@ -52,13 +53,13 @@ class SaveInstance {
|
||||
|
||||
public:
|
||||
// Location of all save data for a title
|
||||
static std::filesystem::path MakeTitleSavePath(OrbisUserServiceUserId user_id,
|
||||
std::string_view game_serial);
|
||||
static std::filesystem::path MakeTitleSavePath(
|
||||
Libraries::UserService::OrbisUserServiceUserId user_id, std::string_view game_serial);
|
||||
|
||||
// Location of a specific save data directory
|
||||
static std::filesystem::path MakeDirSavePath(OrbisUserServiceUserId user_id,
|
||||
std::string_view game_serial,
|
||||
std::string_view dir_name);
|
||||
static std::filesystem::path MakeDirSavePath(
|
||||
Libraries::UserService::OrbisUserServiceUserId user_id, std::string_view game_serial,
|
||||
std::string_view dir_name);
|
||||
|
||||
static uint64_t GetMaxBlockFromSFO(const PSF& psf);
|
||||
|
||||
@ -67,8 +68,8 @@ public:
|
||||
|
||||
static void SetupDefaultParamSFO(PSF& param_sfo, std::string dir_name, std::string game_serial);
|
||||
|
||||
explicit SaveInstance(int slot_num, OrbisUserServiceUserId user_id, std::string game_serial,
|
||||
std::string_view dir_name, int max_blocks = 0);
|
||||
explicit SaveInstance(int slot_num, Libraries::UserService::OrbisUserServiceUserId user_id,
|
||||
std::string game_serial, std::string_view dir_name, int max_blocks = 0);
|
||||
|
||||
~SaveInstance();
|
||||
|
||||
|
||||
@ -88,8 +88,8 @@ std::string GetSaveDir(u32 slot_id) {
|
||||
return dir;
|
||||
}
|
||||
|
||||
std::filesystem::path GetSavePath(OrbisUserServiceUserId user_id, u32 slot_id,
|
||||
std::string_view game_serial) {
|
||||
std::filesystem::path GetSavePath(Libraries::UserService::OrbisUserServiceUserId user_id,
|
||||
u32 slot_id, std::string_view game_serial) {
|
||||
std::string dir(StandardDirnameSaveDataMemory);
|
||||
if (slot_id > 0) {
|
||||
dir += std::to_string(slot_id);
|
||||
@ -97,8 +97,8 @@ std::filesystem::path GetSavePath(OrbisUserServiceUserId user_id, u32 slot_id,
|
||||
return SaveInstance::MakeDirSavePath(user_id, game_serial, dir);
|
||||
}
|
||||
|
||||
size_t SetupSaveMemory(OrbisUserServiceUserId user_id, u32 slot_id, std::string_view game_serial,
|
||||
size_t memory_size) {
|
||||
size_t SetupSaveMemory(Libraries::UserService::OrbisUserServiceUserId user_id, u32 slot_id,
|
||||
std::string_view game_serial, size_t memory_size) {
|
||||
std::lock_guard lck{g_slot_mtx};
|
||||
|
||||
const auto save_dir = GetSavePath(user_id, slot_id, game_serial);
|
||||
|
||||
@ -4,26 +4,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <core/libraries/system/userservice.h>
|
||||
#include "core/libraries/save_data/save_backup.h"
|
||||
|
||||
class PSF;
|
||||
|
||||
namespace Libraries::SaveData {
|
||||
using OrbisUserServiceUserId = s32;
|
||||
} // namespace Libraries::SaveData
|
||||
|
||||
namespace Libraries::SaveData::SaveMemory {
|
||||
|
||||
void PersistMemory(u32 slot_id, bool lock = true);
|
||||
|
||||
[[nodiscard]] std::string GetSaveDir(u32 slot_id);
|
||||
|
||||
[[nodiscard]] std::filesystem::path GetSavePath(OrbisUserServiceUserId user_id, u32 slot_id,
|
||||
std::string_view game_serial);
|
||||
[[nodiscard]] std::filesystem::path GetSavePath(
|
||||
Libraries::UserService::OrbisUserServiceUserId user_id, u32 slot_id,
|
||||
std::string_view game_serial);
|
||||
|
||||
// returns the size of the save memory if exists
|
||||
size_t SetupSaveMemory(OrbisUserServiceUserId user_id, u32 slot_id, std::string_view game_serial,
|
||||
size_t memory_size);
|
||||
size_t SetupSaveMemory(Libraries::UserService::OrbisUserServiceUserId user_id, u32 slot_id,
|
||||
std::string_view game_serial, size_t memory_size);
|
||||
|
||||
// Write the icon. Set buf to null to read the standard icon.
|
||||
void SetIcon(u32 slot_id, void* buf = nullptr, size_t buf_size = 0);
|
||||
|
||||
@ -42,7 +42,6 @@ enum class OrbisSaveDataSaveDataMemoryOption : u32 {
|
||||
UNLOCK_LIMITATIONS = 1 << 2,
|
||||
};
|
||||
|
||||
using OrbisUserServiceUserId = s32;
|
||||
using OrbisSaveDataBlocks = u64;
|
||||
|
||||
constexpr u32 OrbisSaveDataBlockSize = 32768; // 32 KiB
|
||||
@ -97,7 +96,7 @@ struct OrbisSaveDataFingerprint {
|
||||
};
|
||||
|
||||
struct OrbisSaveDataBackup {
|
||||
OrbisUserServiceUserId userId;
|
||||
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||
s32 : 32;
|
||||
const OrbisSaveDataTitleId* titleId;
|
||||
const OrbisSaveDataDirName* dirName;
|
||||
@ -106,7 +105,7 @@ struct OrbisSaveDataBackup {
|
||||
};
|
||||
|
||||
struct OrbisSaveDataCheckBackupData {
|
||||
OrbisUserServiceUserId userId;
|
||||
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||
s32 : 32;
|
||||
const OrbisSaveDataTitleId* titleId;
|
||||
const OrbisSaveDataDirName* dirName;
|
||||
@ -116,7 +115,7 @@ struct OrbisSaveDataCheckBackupData {
|
||||
};
|
||||
|
||||
struct OrbisSaveDataDelete {
|
||||
OrbisUserServiceUserId userId;
|
||||
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||
s32 : 32;
|
||||
const OrbisSaveDataTitleId* titleId;
|
||||
const OrbisSaveDataDirName* dirName;
|
||||
@ -153,7 +152,7 @@ struct OrbisSaveDataMemoryData {
|
||||
};
|
||||
|
||||
struct OrbisSaveDataMemoryGet2 {
|
||||
OrbisUserServiceUserId userId;
|
||||
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||
std::array<u8, 4> _pad;
|
||||
OrbisSaveDataMemoryData* data;
|
||||
OrbisSaveDataParam* param;
|
||||
@ -163,7 +162,7 @@ struct OrbisSaveDataMemoryGet2 {
|
||||
};
|
||||
|
||||
struct OrbisSaveDataMemorySet2 {
|
||||
OrbisUserServiceUserId userId;
|
||||
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||
std::array<u8, 4> _pad;
|
||||
const OrbisSaveDataMemoryData* data;
|
||||
const OrbisSaveDataParam* param;
|
||||
@ -175,7 +174,7 @@ struct OrbisSaveDataMemorySet2 {
|
||||
|
||||
struct OrbisSaveDataMemorySetup2 {
|
||||
OrbisSaveDataSaveDataMemoryOption option;
|
||||
OrbisUserServiceUserId userId;
|
||||
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||
size_t memorySize;
|
||||
size_t iconMemorySize;
|
||||
// +4.5
|
||||
@ -197,14 +196,14 @@ enum OrbisSaveDataMemorySyncOption : u32 {
|
||||
};
|
||||
|
||||
struct OrbisSaveDataMemorySync {
|
||||
OrbisUserServiceUserId userId;
|
||||
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||
u32 slotId;
|
||||
OrbisSaveDataMemorySyncOption option;
|
||||
std::array<u8, 28> _reserved;
|
||||
};
|
||||
|
||||
struct OrbisSaveDataMount2 {
|
||||
OrbisUserServiceUserId userId;
|
||||
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||
s32 : 32;
|
||||
const OrbisSaveDataDirName* dirName;
|
||||
OrbisSaveDataBlocks blocks;
|
||||
@ -214,7 +213,7 @@ struct OrbisSaveDataMount2 {
|
||||
};
|
||||
|
||||
struct OrbisSaveDataMount {
|
||||
OrbisUserServiceUserId userId;
|
||||
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||
s32 : 32;
|
||||
const OrbisSaveDataTitleId* titleId;
|
||||
const OrbisSaveDataDirName* dirName;
|
||||
@ -245,7 +244,7 @@ struct OrbisSaveDataMountResult {
|
||||
};
|
||||
|
||||
struct OrbisSaveDataRestoreBackupData {
|
||||
OrbisUserServiceUserId userId;
|
||||
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||
s32 : 32;
|
||||
const OrbisSaveDataTitleId* titleId;
|
||||
const OrbisSaveDataDirName* dirName;
|
||||
@ -256,7 +255,7 @@ struct OrbisSaveDataRestoreBackupData {
|
||||
};
|
||||
|
||||
struct OrbisSaveDataTransferringMount {
|
||||
OrbisUserServiceUserId userId;
|
||||
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||
const OrbisSaveDataTitleId* titleId;
|
||||
const OrbisSaveDataDirName* dirName;
|
||||
const OrbisSaveDataFingerprint* fingerprint;
|
||||
@ -264,7 +263,7 @@ struct OrbisSaveDataTransferringMount {
|
||||
};
|
||||
|
||||
struct OrbisSaveDataDirNameSearchCond {
|
||||
OrbisUserServiceUserId userId;
|
||||
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||
int : 32;
|
||||
const OrbisSaveDataTitleId* titleId;
|
||||
const OrbisSaveDataDirName* dirName;
|
||||
@ -303,7 +302,7 @@ using OrbisSaveDataEventType = Backup::OrbisSaveDataEventType;
|
||||
struct OrbisSaveDataEvent {
|
||||
OrbisSaveDataEventType type;
|
||||
s32 errorCode;
|
||||
OrbisUserServiceUserId userId;
|
||||
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||
std::array<u8, 4> _pad;
|
||||
OrbisSaveDataTitleId titleId;
|
||||
OrbisSaveDataDirName dirName;
|
||||
@ -1106,8 +1105,9 @@ int PS4_SYSV_ABI sceSaveDataGetSaveDataCount() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
Error PS4_SYSV_ABI sceSaveDataGetSaveDataMemory(const OrbisUserServiceUserId userId, void* buf,
|
||||
const size_t bufSize, const int64_t offset) {
|
||||
Error PS4_SYSV_ABI
|
||||
sceSaveDataGetSaveDataMemory(const Libraries::UserService::OrbisUserServiceUserId userId, void* buf,
|
||||
const size_t bufSize, const int64_t offset) {
|
||||
LOG_DEBUG(Lib_SaveData, "Redirecting to sceSaveDataGetSaveDataMemory2");
|
||||
OrbisSaveDataMemoryData data{};
|
||||
data.buf = buf;
|
||||
@ -1469,8 +1469,9 @@ int PS4_SYSV_ABI sceSaveDataSetSaveDataLibraryUser() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
Error PS4_SYSV_ABI sceSaveDataSetSaveDataMemory(OrbisUserServiceUserId userId, void* buf,
|
||||
size_t bufSize, int64_t offset) {
|
||||
Error PS4_SYSV_ABI
|
||||
sceSaveDataSetSaveDataMemory(Libraries::UserService::OrbisUserServiceUserId userId, void* buf,
|
||||
size_t bufSize, int64_t offset) {
|
||||
LOG_DEBUG(Lib_SaveData, "Redirecting to sceSaveDataSetSaveDataMemory2");
|
||||
OrbisSaveDataMemoryData data{};
|
||||
data.buf = buf;
|
||||
@ -1527,8 +1528,9 @@ Error PS4_SYSV_ABI sceSaveDataSetSaveDataMemory2(const OrbisSaveDataMemorySet2*
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
Error PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory(OrbisUserServiceUserId userId, size_t memorySize,
|
||||
OrbisSaveDataParam* param) {
|
||||
Error PS4_SYSV_ABI
|
||||
sceSaveDataSetupSaveDataMemory(Libraries::UserService::OrbisUserServiceUserId userId,
|
||||
size_t memorySize, OrbisSaveDataParam* param) {
|
||||
LOG_DEBUG(Lib_SaveData, "called: userId = {}, memorySize = {}", userId, memorySize);
|
||||
OrbisSaveDataMemorySetup2 setupParam{};
|
||||
setupParam.userId = userId;
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <core/libraries/system/userservice.h>
|
||||
#include "common/cstring.h"
|
||||
#include "common/types.h"
|
||||
|
||||
@ -21,8 +22,6 @@ constexpr size_t OrbisSaveDataDetailMaxsize = 1024; // Maximum detail name size
|
||||
enum class Error : u32;
|
||||
enum class OrbisSaveDataParamType : u32;
|
||||
|
||||
using OrbisUserServiceUserId = s32;
|
||||
|
||||
// Maximum size for a title ID (4 uppercase letters + 5 digits)
|
||||
constexpr int OrbisSaveDataTitleIdDataSize = 10;
|
||||
// Maximum save directory name size
|
||||
@ -126,8 +125,9 @@ Error PS4_SYSV_ABI sceSaveDataGetParam(const OrbisSaveDataMountPoint* mountPoint
|
||||
size_t paramBufSize, size_t* gotSize);
|
||||
Error PS4_SYSV_ABI sceSaveDataGetProgress(float* progress);
|
||||
int PS4_SYSV_ABI sceSaveDataGetSaveDataCount();
|
||||
Error PS4_SYSV_ABI sceSaveDataGetSaveDataMemory(OrbisUserServiceUserId userId, void* buf,
|
||||
size_t bufSize, int64_t offset);
|
||||
Error PS4_SYSV_ABI
|
||||
sceSaveDataGetSaveDataMemory(Libraries::UserService::OrbisUserServiceUserId userId, void* buf,
|
||||
size_t bufSize, int64_t offset);
|
||||
Error PS4_SYSV_ABI sceSaveDataGetSaveDataMemory2(OrbisSaveDataMemoryGet2* getParam);
|
||||
int PS4_SYSV_ABI sceSaveDataGetSaveDataRootDir();
|
||||
int PS4_SYSV_ABI sceSaveDataGetSaveDataRootPath();
|
||||
@ -163,11 +163,13 @@ Error PS4_SYSV_ABI sceSaveDataSetParam(const OrbisSaveDataMountPoint* mountPoint
|
||||
OrbisSaveDataParamType paramType, const void* paramBuf,
|
||||
size_t paramBufSize);
|
||||
int PS4_SYSV_ABI sceSaveDataSetSaveDataLibraryUser();
|
||||
Error PS4_SYSV_ABI sceSaveDataSetSaveDataMemory(OrbisUserServiceUserId userId, void* buf,
|
||||
size_t bufSize, int64_t offset);
|
||||
Error PS4_SYSV_ABI
|
||||
sceSaveDataSetSaveDataMemory(Libraries::UserService::OrbisUserServiceUserId userId, void* buf,
|
||||
size_t bufSize, int64_t offset);
|
||||
Error PS4_SYSV_ABI sceSaveDataSetSaveDataMemory2(const OrbisSaveDataMemorySet2* setParam);
|
||||
Error PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory(OrbisUserServiceUserId userId, size_t memorySize,
|
||||
OrbisSaveDataParam* param);
|
||||
Error PS4_SYSV_ABI
|
||||
sceSaveDataSetupSaveDataMemory(Libraries::UserService::OrbisUserServiceUserId userId,
|
||||
size_t memorySize, OrbisSaveDataParam* param);
|
||||
Error PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory2(const OrbisSaveDataMemorySetup2* setupParam,
|
||||
OrbisSaveDataMemorySetupResult* result);
|
||||
int PS4_SYSV_ABI sceSaveDataShutdownStart();
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <core/libraries/system/userservice.h>
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/np/np_types.h"
|
||||
|
||||
@ -21,8 +22,8 @@ struct OrbisSharePlayConnectionInfo {
|
||||
int mode;
|
||||
Libraries::Np::OrbisNpOnlineId hostOnlineId;
|
||||
Libraries::Np::OrbisNpOnlineId visitorOnlineId;
|
||||
s32 hostUserId;
|
||||
s32 visitorUserId;
|
||||
Libraries::UserService::OrbisUserServiceUserId hostUserId;
|
||||
Libraries::UserService::OrbisUserServiceUserId visitorUserId;
|
||||
};
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlayCrashDaemon();
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// reference
|
||||
// https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/master/include/orbis/_types/sys_service.h
|
||||
@ -7,6 +7,7 @@
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include "common/types.h"
|
||||
#include "userservice.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
@ -119,12 +120,12 @@ struct OrbisSystemServiceEvent {
|
||||
char boot_argument[7169];
|
||||
} join_event;
|
||||
struct {
|
||||
s32 user_id;
|
||||
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||
u32 np_service_label;
|
||||
u8 reserved[8184];
|
||||
} service_entitlement_update;
|
||||
struct {
|
||||
s32 user_id;
|
||||
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||
u32 np_service_label;
|
||||
u8 reserved[8184];
|
||||
} unified_entitlement_update;
|
||||
|
||||
@ -291,8 +291,8 @@ s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutio
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 index,
|
||||
const void* param) {
|
||||
s32 PS4_SYSV_ABI sceVideoOutOpen(Libraries::UserService::OrbisUserServiceUserId userId, s32 busType,
|
||||
s32 index, const void* param) {
|
||||
LOG_INFO(Lib_VideoOut, "called");
|
||||
ASSERT(busType == SCE_VIDEO_OUT_BUS_TYPE_MAIN);
|
||||
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <core/libraries/system/userservice.h>
|
||||
#include "core/libraries/kernel/equeue.h"
|
||||
#include "core/libraries/videoout/buffer.h"
|
||||
|
||||
@ -12,8 +13,6 @@ class SymbolsResolver;
|
||||
|
||||
namespace Libraries::VideoOut {
|
||||
|
||||
using SceUserServiceUserId = s32; // TODO move it to proper place
|
||||
|
||||
// SceVideoOutBusType
|
||||
constexpr int SCE_VIDEO_OUT_BUS_TYPE_MAIN = 0; // Main output
|
||||
constexpr int SCE_VIDEO_OUT_BUS_TYPE_AUX_SOCIAL_SCREEN = 5; // Aux output for social
|
||||
@ -131,8 +130,8 @@ s32 PS4_SYSV_ABI sceVideoOutWaitVblank(s32 handle);
|
||||
s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode, s64 flipArg);
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, FlipStatus* status);
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutionStatus* status);
|
||||
s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 index,
|
||||
const void* param);
|
||||
s32 PS4_SYSV_ABI sceVideoOutOpen(Libraries::UserService::OrbisUserServiceUserId userId, s32 busType,
|
||||
s32 index, const void* param);
|
||||
s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle);
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev);
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, s64* data);
|
||||
|
||||
@ -5,9 +5,12 @@
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/web_browser_dialog/webbrowserdialog.h"
|
||||
#include "magic_enum/magic_enum.hpp"
|
||||
|
||||
namespace Libraries::WebBrowserDialog {
|
||||
|
||||
static auto g_status = Libraries::CommonDialog::Status::NONE;
|
||||
|
||||
s32 PS4_SYSV_ABI sceWebBrowserDialogClose() {
|
||||
LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
@ -23,14 +26,19 @@ s32 PS4_SYSV_ABI sceWebBrowserDialogGetResult() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceWebBrowserDialogGetStatus() {
|
||||
LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Libraries::CommonDialog::Status PS4_SYSV_ABI sceWebBrowserDialogGetStatus() {
|
||||
LOG_TRACE(Lib_MsgDlg, "called status={}", magic_enum::enum_name(g_status));
|
||||
return g_status;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceWebBrowserDialogInitialize() {
|
||||
LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Libraries::CommonDialog::Error PS4_SYSV_ABI sceWebBrowserDialogInitialize() {
|
||||
if (CommonDialog::g_isInitialized) {
|
||||
LOG_INFO(Lib_WebBrowserDialog, "already initialized");
|
||||
return Libraries::CommonDialog::Error::ALREADY_SYSTEM_INITIALIZED;
|
||||
}
|
||||
LOG_DEBUG(Lib_WebBrowserDialog, "initialized");
|
||||
CommonDialog::g_isInitialized = true;
|
||||
return Libraries::CommonDialog::Error::OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceWebBrowserDialogNavigate() {
|
||||
@ -63,14 +71,22 @@ s32 PS4_SYSV_ABI sceWebBrowserDialogSetZoom() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceWebBrowserDialogTerminate() {
|
||||
LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Libraries::CommonDialog::Error PS4_SYSV_ABI sceWebBrowserDialogTerminate() {
|
||||
if (g_status == Libraries::CommonDialog::Status::RUNNING) {
|
||||
LOG_ERROR(Lib_WebBrowserDialog,
|
||||
"CloseWebBrowser Dialog unimplemented"); // sceWebBrowserDialogClose();
|
||||
}
|
||||
if (g_status == Libraries::CommonDialog::Status::NONE) {
|
||||
return Libraries::CommonDialog::Error::NOT_INITIALIZED;
|
||||
}
|
||||
g_status = Libraries::CommonDialog::Status::NONE;
|
||||
CommonDialog::g_isUsed = false;
|
||||
return Libraries::CommonDialog::Error::OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceWebBrowserDialogUpdateStatus() {
|
||||
LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Libraries::CommonDialog::Status PS4_SYSV_ABI sceWebBrowserDialogUpdateStatus() {
|
||||
LOG_TRACE(Lib_MsgDlg, "called status={}", magic_enum::enum_name(g_status));
|
||||
return g_status;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI Func_F2BE042771625F8C() {
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <core/libraries/system/commondialog.h>
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
@ -14,16 +15,16 @@ namespace Libraries::WebBrowserDialog {
|
||||
s32 PS4_SYSV_ABI sceWebBrowserDialogClose();
|
||||
s32 PS4_SYSV_ABI sceWebBrowserDialogGetEvent();
|
||||
s32 PS4_SYSV_ABI sceWebBrowserDialogGetResult();
|
||||
s32 PS4_SYSV_ABI sceWebBrowserDialogGetStatus();
|
||||
s32 PS4_SYSV_ABI sceWebBrowserDialogInitialize();
|
||||
Libraries::CommonDialog::Status PS4_SYSV_ABI sceWebBrowserDialogGetStatus();
|
||||
Libraries::CommonDialog::Error PS4_SYSV_ABI sceWebBrowserDialogInitialize();
|
||||
s32 PS4_SYSV_ABI sceWebBrowserDialogNavigate();
|
||||
s32 PS4_SYSV_ABI sceWebBrowserDialogOpen();
|
||||
s32 PS4_SYSV_ABI sceWebBrowserDialogOpenForPredeterminedContent();
|
||||
s32 PS4_SYSV_ABI sceWebBrowserDialogResetCookie();
|
||||
s32 PS4_SYSV_ABI sceWebBrowserDialogSetCookie();
|
||||
s32 PS4_SYSV_ABI sceWebBrowserDialogSetZoom();
|
||||
s32 PS4_SYSV_ABI sceWebBrowserDialogTerminate();
|
||||
s32 PS4_SYSV_ABI sceWebBrowserDialogUpdateStatus();
|
||||
Libraries::CommonDialog::Error PS4_SYSV_ABI sceWebBrowserDialogTerminate();
|
||||
Libraries::CommonDialog::Status PS4_SYSV_ABI sceWebBrowserDialogUpdateStatus();
|
||||
s32 PS4_SYSV_ABI Func_F2BE042771625F8C();
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -8,6 +8,7 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include "common/enum.h"
|
||||
#include "common/shared_first_mutex.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/types.h"
|
||||
#include "core/address_space.h"
|
||||
@ -54,12 +55,37 @@ enum class MemoryMapFlags : u32 {
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(MemoryMapFlags)
|
||||
|
||||
enum class DMAType : u32 {
|
||||
enum class PhysicalMemoryType : u32 {
|
||||
Free = 0,
|
||||
Allocated = 1,
|
||||
Mapped = 2,
|
||||
Pooled = 3,
|
||||
Committed = 4,
|
||||
Flexible = 5,
|
||||
};
|
||||
|
||||
struct PhysicalMemoryArea {
|
||||
PAddr base = 0;
|
||||
u64 size = 0;
|
||||
s32 memory_type = 0;
|
||||
PhysicalMemoryType dma_type = PhysicalMemoryType::Free;
|
||||
|
||||
PAddr GetEnd() const {
|
||||
return base + size;
|
||||
}
|
||||
|
||||
bool CanMergeWith(const PhysicalMemoryArea& next) const {
|
||||
if (base + size != next.base) {
|
||||
return false;
|
||||
}
|
||||
if (memory_type != next.memory_type) {
|
||||
return false;
|
||||
}
|
||||
if (dma_type != next.dma_type) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
enum class VMAType : u32 {
|
||||
@ -74,60 +100,15 @@ enum class VMAType : u32 {
|
||||
File = 8,
|
||||
};
|
||||
|
||||
struct DirectMemoryArea {
|
||||
PAddr base = 0;
|
||||
u64 size = 0;
|
||||
s32 memory_type = 0;
|
||||
DMAType dma_type = DMAType::Free;
|
||||
|
||||
PAddr GetEnd() const {
|
||||
return base + size;
|
||||
}
|
||||
|
||||
bool CanMergeWith(const DirectMemoryArea& next) const {
|
||||
if (base + size != next.base) {
|
||||
return false;
|
||||
}
|
||||
if (memory_type != next.memory_type) {
|
||||
return false;
|
||||
}
|
||||
if (dma_type != next.dma_type) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct FlexibleMemoryArea {
|
||||
PAddr base = 0;
|
||||
u64 size = 0;
|
||||
bool is_free = true;
|
||||
|
||||
PAddr GetEnd() const {
|
||||
return base + size;
|
||||
}
|
||||
|
||||
bool CanMergeWith(const FlexibleMemoryArea& next) const {
|
||||
if (base + size != next.base) {
|
||||
return false;
|
||||
}
|
||||
if (is_free != next.is_free) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct VirtualMemoryArea {
|
||||
VAddr base = 0;
|
||||
u64 size = 0;
|
||||
PAddr phys_base = 0;
|
||||
std::map<uintptr_t, PhysicalMemoryArea> phys_areas;
|
||||
VMAType type = VMAType::Free;
|
||||
MemoryProt prot = MemoryProt::NoAccess;
|
||||
bool disallow_merge = false;
|
||||
std::string name = "";
|
||||
uintptr_t fd = 0;
|
||||
bool is_exec = false;
|
||||
s32 fd = 0;
|
||||
bool disallow_merge = false;
|
||||
|
||||
bool Contains(VAddr addr, u64 size) const {
|
||||
return addr >= base && (addr + size) <= (base + this->size);
|
||||
@ -141,30 +122,32 @@ struct VirtualMemoryArea {
|
||||
return type != VMAType::Free && type != VMAType::Reserved && type != VMAType::PoolReserved;
|
||||
}
|
||||
|
||||
bool CanMergeWith(const VirtualMemoryArea& next) const {
|
||||
bool CanMergeWith(VirtualMemoryArea& next) {
|
||||
if (disallow_merge || next.disallow_merge) {
|
||||
return false;
|
||||
}
|
||||
if (base + size != next.base) {
|
||||
return false;
|
||||
}
|
||||
if ((type == VMAType::Direct || type == VMAType::Flexible || type == VMAType::Pooled) &&
|
||||
phys_base + size != next.phys_base) {
|
||||
return false;
|
||||
if (type == VMAType::Direct && next.type == VMAType::Direct) {
|
||||
auto& last_phys = std::prev(phys_areas.end())->second;
|
||||
auto& first_next_phys = next.phys_areas.begin()->second;
|
||||
if (last_phys.base + last_phys.size != first_next_phys.base ||
|
||||
last_phys.memory_type != first_next_phys.memory_type) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (prot != next.prot || type != next.type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class MemoryManager {
|
||||
using DMemMap = std::map<PAddr, DirectMemoryArea>;
|
||||
using DMemHandle = DMemMap::iterator;
|
||||
|
||||
using FMemMap = std::map<PAddr, FlexibleMemoryArea>;
|
||||
using FMemHandle = FMemMap::iterator;
|
||||
using PhysMap = std::map<PAddr, PhysicalMemoryArea>;
|
||||
using PhysHandle = PhysMap::iterator;
|
||||
|
||||
using VMAMap = std::map<VAddr, VirtualMemoryArea>;
|
||||
using VMAHandle = VMAMap::iterator;
|
||||
@ -220,10 +203,11 @@ public:
|
||||
// Now make sure the full address range is contained in vma_map.
|
||||
auto vma_handle = FindVMA(virtual_addr);
|
||||
auto addr_to_check = virtual_addr;
|
||||
s64 size_to_validate = size;
|
||||
u64 size_to_validate = size;
|
||||
while (vma_handle != vma_map.end() && size_to_validate > 0) {
|
||||
const auto offset_in_vma = addr_to_check - vma_handle->second.base;
|
||||
const auto size_in_vma = vma_handle->second.size - offset_in_vma;
|
||||
const auto size_in_vma =
|
||||
std::min<u64>(vma_handle->second.size - offset_in_vma, size_to_validate);
|
||||
size_to_validate -= size_in_vma;
|
||||
addr_to_check += size_in_vma;
|
||||
vma_handle++;
|
||||
@ -245,7 +229,7 @@ public:
|
||||
|
||||
void CopySparseMemory(VAddr source, u8* dest, u64 size);
|
||||
|
||||
bool TryWriteBacking(void* address, const void* data, u32 num_bytes);
|
||||
bool TryWriteBacking(void* address, const void* data, u64 size);
|
||||
|
||||
void SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1, bool use_extended_mem2);
|
||||
|
||||
@ -300,34 +284,14 @@ private:
|
||||
return std::prev(vma_map.upper_bound(target));
|
||||
}
|
||||
|
||||
DMemHandle FindDmemArea(PAddr target) {
|
||||
PhysHandle FindDmemArea(PAddr target) {
|
||||
return std::prev(dmem_map.upper_bound(target));
|
||||
}
|
||||
|
||||
FMemHandle FindFmemArea(PAddr target) {
|
||||
PhysHandle FindFmemArea(PAddr target) {
|
||||
return std::prev(fmem_map.upper_bound(target));
|
||||
}
|
||||
|
||||
template <typename Handle>
|
||||
Handle MergeAdjacent(auto& handle_map, Handle iter) {
|
||||
const auto next_vma = std::next(iter);
|
||||
if (next_vma != handle_map.end() && iter->second.CanMergeWith(next_vma->second)) {
|
||||
iter->second.size += next_vma->second.size;
|
||||
handle_map.erase(next_vma);
|
||||
}
|
||||
|
||||
if (iter != handle_map.begin()) {
|
||||
auto prev_vma = std::prev(iter);
|
||||
if (prev_vma->second.CanMergeWith(iter->second)) {
|
||||
prev_vma->second.size += iter->second.size;
|
||||
handle_map.erase(iter);
|
||||
iter = prev_vma;
|
||||
}
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
bool HasPhysicalBacking(VirtualMemoryArea vma) {
|
||||
return vma.type == VMAType::Direct || vma.type == VMAType::Flexible ||
|
||||
vma.type == VMAType::Pooled;
|
||||
@ -335,17 +299,17 @@ private:
|
||||
|
||||
VAddr SearchFree(VAddr virtual_addr, u64 size, u32 alignment);
|
||||
|
||||
VMAHandle MergeAdjacent(VMAMap& map, VMAHandle iter);
|
||||
|
||||
PhysHandle MergeAdjacent(PhysMap& map, PhysHandle iter);
|
||||
|
||||
VMAHandle CarveVMA(VAddr virtual_addr, u64 size);
|
||||
|
||||
DMemHandle CarveDmemArea(PAddr addr, u64 size);
|
||||
|
||||
FMemHandle CarveFmemArea(PAddr addr, u64 size);
|
||||
PhysHandle CarvePhysArea(PhysMap& map, PAddr addr, u64 size);
|
||||
|
||||
VMAHandle Split(VMAHandle vma_handle, u64 offset_in_vma);
|
||||
|
||||
DMemHandle Split(DMemHandle dmem_handle, u64 offset_in_area);
|
||||
|
||||
FMemHandle Split(FMemHandle fmem_handle, u64 offset_in_area);
|
||||
PhysHandle Split(PhysMap& map, PhysHandle dmem_handle, u64 offset_in_area);
|
||||
|
||||
u64 UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma_base, u64 size);
|
||||
|
||||
@ -353,14 +317,15 @@ private:
|
||||
|
||||
private:
|
||||
AddressSpace impl;
|
||||
DMemMap dmem_map;
|
||||
FMemMap fmem_map;
|
||||
PhysMap dmem_map;
|
||||
PhysMap fmem_map;
|
||||
VMAMap vma_map;
|
||||
std::mutex mutex;
|
||||
Common::SharedFirstMutex mutex{};
|
||||
u64 total_direct_size{};
|
||||
u64 total_flexible_size{};
|
||||
u64 flexible_usage{};
|
||||
u64 pool_budget{};
|
||||
s32 sdk_version{};
|
||||
Vulkan::Rasterizer* rasterizer{};
|
||||
|
||||
struct PrtArea {
|
||||
|
||||
11
src/main.cpp
11
src/main.cpp
@ -23,6 +23,7 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <core/emulator_settings.h>
|
||||
#include <common/key_manager.h>
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
#ifdef _WIN32
|
||||
@ -39,7 +40,17 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
|
||||
Config::load(user_dir / "config.toml");
|
||||
// temp copy the trophy key from old config to key manager if exists
|
||||
auto key_manager = KeyManager::GetInstance();
|
||||
if (key_manager->GetAllKeys().TrophyKeySet.ReleaseTrophyKey.empty()) {
|
||||
if (!Config::getTrophyKey().empty()) {
|
||||
|
||||
key_manager->SetAllKeys(
|
||||
{.TrophyKeySet = {.ReleaseTrophyKey =
|
||||
KeyManager::HexStringToBytes(Config::getTrophyKey())}});
|
||||
key_manager->SaveToFile();
|
||||
}
|
||||
}
|
||||
bool has_game_argument = false;
|
||||
std::string game_path;
|
||||
std::vector<std::string> game_args{};
|
||||
|
||||
@ -660,6 +660,7 @@ void PatchGlobalDataShareAccess(IR::Block& block, IR::Inst& inst, Info& info,
|
||||
inst.SetArg(1, ir.Imm32(binding));
|
||||
} else {
|
||||
// Convert shared memory opcode to storage buffer atomic to GDS buffer.
|
||||
auto& buffer = info.buffers[binding];
|
||||
const IR::U32 offset = IR::U32{inst.Arg(0)};
|
||||
const IR::U32 address_words = ir.ShiftRightLogical(offset, ir.Imm32(1));
|
||||
const IR::U32 address_dwords = ir.ShiftRightLogical(offset, ir.Imm32(2));
|
||||
@ -705,27 +706,35 @@ void PatchGlobalDataShareAccess(IR::Block& block, IR::Inst& inst, Info& info,
|
||||
case IR::Opcode::SharedAtomicXor32:
|
||||
inst.ReplaceUsesWith(ir.BufferAtomicXor(handle, address_dwords, inst.Arg(1), {}));
|
||||
break;
|
||||
case IR::Opcode::LoadSharedU16:
|
||||
case IR::Opcode::LoadSharedU16: {
|
||||
inst.ReplaceUsesWith(ir.LoadBufferU16(handle, address_words, {}));
|
||||
buffer.used_types |= IR::Type::U16;
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::LoadSharedU32:
|
||||
inst.ReplaceUsesWith(ir.LoadBufferU32(1, handle, address_dwords, {}));
|
||||
break;
|
||||
case IR::Opcode::LoadSharedU64:
|
||||
case IR::Opcode::LoadSharedU64: {
|
||||
inst.ReplaceUsesWith(ir.LoadBufferU64(handle, address_qwords, {}));
|
||||
buffer.used_types |= IR::Type::U64;
|
||||
break;
|
||||
case IR::Opcode::WriteSharedU16:
|
||||
}
|
||||
case IR::Opcode::WriteSharedU16: {
|
||||
ir.StoreBufferU16(handle, address_words, IR::U16{inst.Arg(1)}, {});
|
||||
inst.Invalidate();
|
||||
buffer.used_types |= IR::Type::U16;
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::WriteSharedU32:
|
||||
ir.StoreBufferU32(1, handle, address_dwords, inst.Arg(1), {});
|
||||
inst.Invalidate();
|
||||
break;
|
||||
case IR::Opcode::WriteSharedU64:
|
||||
case IR::Opcode::WriteSharedU64: {
|
||||
ir.StoreBufferU64(handle, address_qwords, IR::U64{inst.Arg(1)}, {});
|
||||
inst.Invalidate();
|
||||
buffer.used_types |= IR::Type::U64;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@ -830,7 +830,14 @@ Liverpool::Task Liverpool::ProcessCompute(std::span<const u32> acb, u32 vqid) {
|
||||
FIBER_ENTER(acb_task_name[vqid]);
|
||||
auto& queue = asc_queues[{vqid}];
|
||||
|
||||
struct IndirectPatch {
|
||||
const PM4Header* header;
|
||||
VAddr indirect_addr;
|
||||
};
|
||||
boost::container::small_vector<IndirectPatch, 4> indirect_patches;
|
||||
|
||||
auto base_addr = reinterpret_cast<VAddr>(acb.data());
|
||||
size_t acb_size = acb.size_bytes();
|
||||
while (!acb.empty()) {
|
||||
ProcessCommands();
|
||||
|
||||
@ -919,8 +926,18 @@ Liverpool::Task Liverpool::ProcessCompute(std::span<const u32> acb, u32 vqid) {
|
||||
dma_data->src_sel == DmaDataSrc::MemoryUsingL2) &&
|
||||
(dma_data->dst_sel == DmaDataDst::Memory ||
|
||||
dma_data->dst_sel == DmaDataDst::MemoryUsingL2)) {
|
||||
rasterizer->CopyBuffer(dma_data->DstAddress<VAddr>(), dma_data->SrcAddress<VAddr>(),
|
||||
dma_data->NumBytes(), false, false);
|
||||
const u32 num_bytes = dma_data->NumBytes();
|
||||
const VAddr src_addr = dma_data->SrcAddress<VAddr>();
|
||||
const VAddr dst_addr = dma_data->DstAddress<VAddr>();
|
||||
const PM4Header* header =
|
||||
reinterpret_cast<const PM4Header*>(dst_addr - sizeof(PM4Header));
|
||||
if (dst_addr >= base_addr && dst_addr < base_addr + acb_size &&
|
||||
num_bytes == sizeof(PM4CmdDispatchIndirect::GroupDimensions) &&
|
||||
header->type == 3 && header->type3.opcode == PM4ItOpcode::DispatchDirect) {
|
||||
indirect_patches.emplace_back(header, src_addr);
|
||||
} else {
|
||||
rasterizer->CopyBuffer(dst_addr, src_addr, num_bytes, false, false);
|
||||
}
|
||||
} else {
|
||||
UNREACHABLE_MSG("WriteData src_sel = {}, dst_sel = {}",
|
||||
u32(dma_data->src_sel.Value()), u32(dma_data->dst_sel.Value()));
|
||||
@ -964,6 +981,12 @@ Liverpool::Task Liverpool::ProcessCompute(std::span<const u32> acb, u32 vqid) {
|
||||
}
|
||||
case PM4ItOpcode::DispatchDirect: {
|
||||
const auto* dispatch_direct = reinterpret_cast<const PM4CmdDispatchDirect*>(header);
|
||||
if (auto it = std::ranges::find(indirect_patches, header, &IndirectPatch::header);
|
||||
it != indirect_patches.end()) {
|
||||
const auto size = sizeof(PM4CmdDispatchIndirect::GroupDimensions);
|
||||
rasterizer->DispatchIndirect(it->indirect_addr, 0, size);
|
||||
break;
|
||||
}
|
||||
auto& cs_program = GetCsRegs();
|
||||
cs_program.dim_x = dispatch_direct->dim_x;
|
||||
cs_program.dim_y = dispatch_direct->dim_y;
|
||||
@ -1034,9 +1057,13 @@ Liverpool::Task Liverpool::ProcessCompute(std::span<const u32> acb, u32 vqid) {
|
||||
}
|
||||
case PM4ItOpcode::ReleaseMem: {
|
||||
const auto* release_mem = reinterpret_cast<const PM4CmdReleaseMem*>(header);
|
||||
release_mem->SignalFence([pipe_id = queue.pipe_id] {
|
||||
Platform::IrqC::Instance()->Signal(static_cast<Platform::InterruptId>(pipe_id));
|
||||
});
|
||||
release_mem->SignalFence(
|
||||
[pipe_id = queue.pipe_id] {
|
||||
Platform::IrqC::Instance()->Signal(static_cast<Platform::InterruptId>(pipe_id));
|
||||
},
|
||||
[this](VAddr dst, u16 gds_index, u16 num_dwords) {
|
||||
rasterizer->CopyBuffer(dst, gds_index, num_dwords * sizeof(u32), false, true);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case PM4ItOpcode::EventWrite: {
|
||||
|
||||
@ -50,7 +50,7 @@ union PM4Type3Header {
|
||||
}
|
||||
|
||||
u32 NumWords() const {
|
||||
return count + 1;
|
||||
return (count + 1) & 0x3fff;
|
||||
}
|
||||
|
||||
u32 raw;
|
||||
@ -327,6 +327,7 @@ enum class DataSelect : u32 {
|
||||
Data64 = 2,
|
||||
GpuClock64 = 3,
|
||||
PerfCounter = 4,
|
||||
GdsMemStore = 5,
|
||||
};
|
||||
|
||||
enum class InterruptSelect : u32 {
|
||||
@ -920,8 +921,9 @@ struct PM4CmdReleaseMem {
|
||||
u32 data_hi;
|
||||
|
||||
template <typename T>
|
||||
T* Address() const {
|
||||
return reinterpret_cast<T*>(address_lo | u64(address_hi) << 32);
|
||||
T Address() const {
|
||||
u64 full_address = address_lo | (u64(address_hi) << 32);
|
||||
return std::bit_cast<T>(full_address);
|
||||
}
|
||||
|
||||
u32 DataDWord() const {
|
||||
@ -932,22 +934,26 @@ struct PM4CmdReleaseMem {
|
||||
return data_lo | u64(data_hi) << 32;
|
||||
}
|
||||
|
||||
void SignalFence(auto&& signal_irq) const {
|
||||
void SignalFence(auto&& signal_irq, auto&& gds_to_mem) const {
|
||||
switch (data_sel.Value()) {
|
||||
case DataSelect::Data32Low: {
|
||||
*Address<u32>() = DataDWord();
|
||||
*Address<u32*>() = DataDWord();
|
||||
break;
|
||||
}
|
||||
case DataSelect::Data64: {
|
||||
*Address<u64>() = DataQWord();
|
||||
*Address<u64*>() = DataQWord();
|
||||
break;
|
||||
}
|
||||
case DataSelect::GpuClock64: {
|
||||
*Address<u64>() = GetGpuClock64();
|
||||
*Address<u64*>() = GetGpuClock64();
|
||||
break;
|
||||
}
|
||||
case DataSelect::PerfCounter: {
|
||||
*Address<u64>() = GetGpuPerfCounter();
|
||||
*Address<u64*>() = GetGpuPerfCounter();
|
||||
break;
|
||||
}
|
||||
case DataSelect::GdsMemStore: {
|
||||
gds_to_mem(Address<VAddr>(), gds_index, num_dw);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user