diff --git a/.gitignore b/.gitignore
index 683f6f0a6..7ace5e4f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -418,3 +418,6 @@ FodyWeavers.xsd
# JetBrains
.idea
cmake-build-*
+
+# Nix Result symlink
+result
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b2c9fe48d..8bdcf88e2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -245,7 +245,7 @@ find_package(VulkanMemoryAllocator 3.1.0 CONFIG)
find_package(xbyak 7.07 CONFIG)
find_package(xxHash 0.8.2 MODULE)
find_package(ZLIB 1.3 MODULE)
-find_package(Zydis 5.0.0 CONFIG)
+find_package(Zydis 5.0.0 MODULE)
find_package(pugixml 1.14 CONFIG)
if (APPLE)
find_package(date 3.0.1 CONFIG)
@@ -1093,6 +1093,8 @@ set(IMGUI src/imgui/imgui_config.h
src/imgui/imgui_layer.h
src/imgui/imgui_std.h
src/imgui/imgui_texture.h
+ src/imgui/imgui_translations.cpp
+ src/imgui/imgui_translations.h
src/imgui/renderer/imgui_core.cpp
src/imgui/renderer/imgui_core.h
src/imgui/renderer/imgui_impl_sdl3.cpp
@@ -1139,7 +1141,14 @@ create_target_directory_groups(shadps4)
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG)
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 SDL3_mixer::SDL3_mixer pugixml::pugixml)
-target_link_libraries(shadps4 PRIVATE stb::headers libusb::usb lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json miniz::miniz fdk-aac CLI11::CLI11 OpenAL::OpenAL Cpp_Httplib)
+target_link_libraries(shadps4 PRIVATE stb::headers lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json miniz::miniz fdk-aac CLI11::CLI11 OpenAL::OpenAL Cpp_Httplib)
+
+if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
+ target_link_libraries(shadps4 PRIVATE "/usr/lib/libusb.so")
+ target_link_libraries(shadps4 PRIVATE "/usr/local/lib/libuuid.so")
+else()
+ target_link_libraries(shadps4 PRIVATE libusb::usb)
+endif()
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h")
@@ -1185,6 +1194,8 @@ if (APPLE)
# Replacement for std::chrono::time_zone
target_link_libraries(shadps4 PRIVATE date::date-tz epoll-shim)
+elseif (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
+ target_link_libraries(shadps4 PRIVATE date::date-tz epoll-shim)
endif()
if (WIN32)
@@ -1276,4 +1287,4 @@ install(TARGETS shadps4 BUNDLE DESTINATION .)
else()
enable_testing()
add_subdirectory(tests)
-endif()
\ No newline at end of file
+endif()
diff --git a/README.md b/README.md
index 0fb5c26ed..2a2aa9f34 100644
--- a/README.md
+++ b/README.md
@@ -150,12 +150,12 @@ The following firmware modules are supported and must be placed in shadPS4's `sy
-| Modules | Modules | Modules | Modules |
-|-------------------------|-------------------------|-------------------------|-------------------------|
-| libSceCesCs.sprx | libSceFont.sprx | libSceFontFt.sprx | libSceFreeTypeOt.sprx |
-| libSceJpegDec.sprx | libSceJpegEnc.sprx | libSceJson.sprx | libSceJson2.sprx |
-| libSceLibcInternal.sprx | libSceNgs2.sprx | libScePngEnc.sprx | libSceRtc.sprx |
-| libSceUlt.sprx | libSceAudiodec.sprx | | |
+| Modules | Modules | Modules | Modules |
+|--------------------------|--------------------------|--------------------------|--------------------------|
+| libSceAudiodec.sprx | libSceCesCs.sprx | libSceFont.sprx | libSceFontFt.sprx |
+| libSceFreeTypeOt.sprx | libSceJpegDec.sprx | libSceJpegEnc.sprx | libSceJson.sprx |
+| libSceJson2.sprx | libSceLibcInternal.sprx | libSceNgs2.sprx | libScePngEnc.sprx |
+| libSceRtc.sprx | libSceSystemGesture.sprx | libSceUlt.sprx | |
> [!Caution]
diff --git a/REUSE.toml b/REUSE.toml
index 22bed2a50..e8997f007 100644
--- a/REUSE.toml
+++ b/REUSE.toml
@@ -22,6 +22,7 @@ path = [
"documents/Screenshots/Linux/*",
"documents/Screenshots/Windows/*",
"externals/MoltenVK/MoltenVK_icd.json",
+ "flake.lock",
"scripts/ps4_names.txt",
"src/images/bronze.png",
"src/images/gold.png",
@@ -130,4 +131,4 @@ SPDX-License-Identifier = "MIT"
[[annotations]]
path = "src/video_core/host_shaders/fsr/*"
SPDX-FileCopyrightText = "Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved."
-SPDX-License-Identifier = "MIT"
\ No newline at end of file
+SPDX-License-Identifier = "MIT"
diff --git a/documents/building-linux.md b/documents/building-linux.md
index 49aacdfc9..9495a226b 100644
--- a/documents/building-linux.md
+++ b/documents/building-linux.md
@@ -53,6 +53,23 @@ sudo zypper install clang git cmake libasound2 libpulse-devel \
nix-shell shell.nix
```
+#### Nix Flake Development Shell
+```bash
+nix develop
+cmake -S . -B build/ -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
+ln -s ./build/compile_commands.json .
+```
+
+#### Nix Flake Build
+```bash
+nix build .?submodules=1#linux.debug
+```
+```bash
+nix build .?submodules=1#linux.release
+```
+```bash
+nix build .?submodules=1#linux.releaseWithDebugInfo
+```
#### Other Linux distributions
You can try one of two methods:
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index 41a0f71c7..9e19e1404 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -210,8 +210,15 @@ endif()
# libusb
if (NOT TARGET libusb::usb)
- add_subdirectory(ext-libusb)
- add_library(libusb::usb ALIAS usb-1.0)
+ if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
+ # YOU MUST USE NATIVE LIBUSB
+ # using anything else will crash instantly, also freebsd will NOT like it
+ # no you cant vendor this libusb, its builtin on freebsd
+ find_package(libusb)
+ else()
+ add_subdirectory(ext-libusb)
+ add_library(libusb::usb ALIAS usb-1.0)
+ endif()
endif()
# Discord RPC
@@ -233,25 +240,26 @@ endif()
set(HWINFO_STATIC ON)
add_subdirectory(hwinfo)
-# Apple-only dependencies
-if (APPLE)
+if (APPLE OR ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
# date
if (NOT TARGET date::date-tz)
option(BUILD_TZ_LIB "" ON)
option(USE_SYSTEM_TZ_DB "" ON)
add_subdirectory(date)
endif()
+ if (NOT TARGET epoll-shim)
+ add_subdirectory(epoll-shim)
+ endif()
+endif()
+# Apple-only dependencies
+if (APPLE)
# MoltenVK
if (NOT TARGET MoltenVK)
set(MVK_EXCLUDE_SPIRV_TOOLS ON)
set(MVK_USE_METAL_PRIVATE_API ON)
add_subdirectory(MoltenVK)
endif()
-
- if (NOT TARGET epoll-shim)
- add_subdirectory(epoll-shim)
- endif()
endif()
#windows only
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 000000000..246cfd4e7
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,27 @@
+{
+ "nodes": {
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1774386573,
+ "narHash": "sha256-4hAV26quOxdC6iyG7kYaZcM3VOskcPUrdCQd/nx8obc=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "46db2e09e1d3f113a13c0d7b81e2f221c63b8ce9",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "nixpkgs": "nixpkgs"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 000000000..9042105c5
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,160 @@
+## SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
+## SPDX-License-Identifier: GPL-2.0-or-later
+
+{
+ description = "shadPS4 Nix Flake";
+
+ inputs = {
+ nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
+ };
+
+ outputs =
+ { self, nixpkgs }:
+ let
+ pkgsLinux = nixpkgs.legacyPackages.x86_64-linux;
+ in
+ {
+ devShells.x86_64-linux.default = pkgsLinux.mkShell.override { stdenv = pkgsLinux.clangStdenv; } {
+ packages = with pkgsLinux; [
+ clang-tools
+ cmake
+ pkg-config
+ vulkan-tools
+
+ renderdoc
+ gef
+ strace
+
+ openal
+ zlib.dev
+ libedit.dev
+ vulkan-headers
+ vulkan-utility-libraries
+ ffmpeg.dev
+ fmt.dev
+ glslang.dev
+ wayland.dev
+ stb
+ libpng.dev
+ libuuid
+
+ sdl3.dev
+ alsa-lib
+ hidapi
+ ibus.dev
+ jack2.dev
+ libdecor.dev
+ libthai.dev
+ fribidi.dev
+ libxcb.dev
+ libGL.dev
+ libpulseaudio.dev
+ libusb1.dev
+ libx11.dev
+ libxcursor.dev
+ libxext
+ libxfixes.dev
+ libxi.dev
+ libxinerama.dev
+ libxkbcommon
+ libxrandr.dev
+ libxrender.dev
+ libxtst
+ pipewire.dev
+ libxscrnsaver
+ sndio
+ ];
+ shellHook = ''
+ echo "Entering shadPS4 development shell!"
+ '';
+ };
+
+ linux =
+ let
+ execName = "shadps4";
+ nativeInputs = with pkgsLinux; [
+ cmake
+ ninja
+ pkg-config
+ magic-enum
+ fmt
+ eudev
+ ];
+ buildInputs = with pkgsLinux; [
+ boost
+ cli11
+ openal
+ nlohmann_json
+ vulkan-loader
+ vulkan-headers
+ vulkan-memory-allocator
+ toml11
+ zlib
+ zydis
+ pugixml
+ ffmpeg
+ libpulseaudio
+ pipewire
+ vulkan-loader
+ wayland
+ wayland-scanner
+ libX11
+ libxrandr
+ libxext
+ libxcursor
+ libxi
+ libxscrnsaver
+ libxtst
+ libxcb
+ libdecor
+ libxkbcommon
+ libGL
+ libuuid
+ ];
+
+ defaultFlags = [
+ "-DCMAKE_INSTALL_PREFIX=$out"
+ ];
+ in
+ {
+ debug = pkgsLinux.stdenv.mkDerivation {
+ pname = "${execName}";
+ version = "git";
+ system = "x86_64-linux";
+ src = ./.;
+ dontStrip = true;
+
+ nativeBuildInputs = nativeInputs;
+ buildInputs = buildInputs;
+ cmakeFlags = [
+ "-DCMAKE_BUILD_TYPE=Debug"
+ ] ++ [defaultFlags];
+ };
+ release = pkgsLinux.stdenv.mkDerivation {
+ pname = "${execName}";
+ version = "git";
+ system = "x86_64-linux";
+ src = ./.;
+
+ nativeBuildInputs = nativeInputs;
+ buildInputs = buildInputs;
+ cmakeFlags = [
+ "-DCMAKE_BUILD_TYPE=Release"
+ ] ++ [defaultFlags];
+ };
+ releaseWithDebugInfo = pkgsLinux.stdenv.mkDerivation {
+ pname = "${execName}";
+ version = "git";
+ system = "x86_64-linux";
+ src = ./.;
+ dontStrip = true;
+
+ nativeBuildInputs = nativeInputs;
+ buildInputs = buildInputs;
+ cmakeFlags = [
+ "-DCMAKE_BUILD_TYPE=Release"
+ ] ++ [defaultFlags];
+ };
+ };
+ };
+}
diff --git a/src/common/adaptive_mutex.h b/src/common/adaptive_mutex.h
index 2ab385bdb..247d4c1ec 100644
--- a/src/common/adaptive_mutex.h
+++ b/src/common/adaptive_mutex.h
@@ -3,7 +3,7 @@
#pragma once
-#ifdef __linux__
+#if __unix__
#include
#endif
diff --git a/src/common/serdes.h b/src/common/serdes.h
index f91a0ace8..a8152e95a 100644
--- a/src/common/serdes.h
+++ b/src/common/serdes.h
@@ -42,7 +42,8 @@ struct Archive {
}
void Advance(size_t size) {
- ASSERT(offset + size <= container.size());
+ ASSERT_MSG(offset + size <= container.size(),
+ "Invalid or corrupted deserialization container/shader cache");
offset += size;
}
@@ -104,7 +105,8 @@ struct Writer {
struct Reader {
template
void Read(T* ptr, size_t size) {
- ASSERT(ar.offset + size <= ar.container.size());
+ ASSERT_MSG(ar.offset + size <= ar.container.size(),
+ "Invalid or corrupted deserialization container/shader cache");
std::memcpy(reinterpret_cast(ptr), ar.CurrPtr(), size);
ar.Advance(size);
}
diff --git a/src/common/signal_context.cpp b/src/common/signal_context.cpp
index 112160bc8..b1ac8d96f 100644
--- a/src/common/signal_context.cpp
+++ b/src/common/signal_context.cpp
@@ -7,6 +7,9 @@
#ifdef _WIN32
#include
+#elif defined(__FreeBSD__)
+#include
+#include
#else
#include
#endif
@@ -22,6 +25,16 @@ void* GetXmmPointer(void* ctx, u8 index) {
#define CASE(index) \
case index: \
return (void*)(&((ucontext_t*)ctx)->uc_mcontext->__fs.__fpu_xmm##index);
+#elif defined(__FreeBSD__)
+ // In mc_fpstate
+ // See for the internals of mc_fpstate[].
+#define CASE(index) \
+ case index: { \
+ auto& mctx = ((ucontext_t*)ctx)->uc_mcontext; \
+ ASSERT(mctx.mc_fpformat == _MC_FPFMT_XMM); \
+ auto* s_fpu = (struct savefpu*)(&mctx.mc_fpstate[0]); \
+ return (void*)(&(s_fpu->sv_xmm[0])); \
+ }
#else
#define CASE(index) \
case index: \
@@ -57,6 +70,8 @@ void* GetRip(void* ctx) {
return (void*)((EXCEPTION_POINTERS*)ctx)->ContextRecord->Rip;
#elif defined(__APPLE__)
return (void*)((ucontext_t*)ctx)->uc_mcontext->__ss.__rip;
+#elif defined(__FreeBSD__)
+ return (void*)((ucontext_t*)ctx)->uc_mcontext.mc_rip;
#else
return (void*)((ucontext_t*)ctx)->uc_mcontext.gregs[REG_RIP];
#endif
@@ -67,6 +82,8 @@ void IncrementRip(void* ctx, u64 length) {
((EXCEPTION_POINTERS*)ctx)->ContextRecord->Rip += length;
#elif defined(__APPLE__)
((ucontext_t*)ctx)->uc_mcontext->__ss.__rip += length;
+#elif defined(__FreeBSD__)
+ ((ucontext_t*)ctx)->uc_mcontext.mc_rip += length;
#else
((ucontext_t*)ctx)->uc_mcontext.gregs[REG_RIP] += length;
#endif
@@ -75,18 +92,16 @@ void IncrementRip(void* ctx, u64 length) {
bool IsWriteError(void* ctx) {
#if defined(_WIN32)
return ((EXCEPTION_POINTERS*)ctx)->ExceptionRecord->ExceptionInformation[0] == 1;
-#elif defined(__APPLE__)
-#if defined(ARCH_X86_64)
+#elif defined(__APPLE__) && defined(ARCH_X86_64)
return ((ucontext_t*)ctx)->uc_mcontext->__es.__err & 0x2;
-#elif defined(ARCH_ARM64)
+#elif defined(__APPLE__) && defined(ARCH_ARM64)
return ((ucontext_t*)ctx)->uc_mcontext->__es.__esr & 0x40;
-#endif
-#else
-#if defined(ARCH_X86_64)
+#elif defined(__FreeBSD__) && defined(ARCH_X86_64)
+ return ((ucontext_t*)ctx)->uc_mcontext.mc_err & 0x2;
+#elif defined(ARCH_X86_64)
return ((ucontext_t*)ctx)->uc_mcontext.gregs[REG_ERR] & 0x2;
#else
#error "Unsupported architecture"
#endif
-#endif
}
-} // namespace Common
\ No newline at end of file
+} // namespace Common
diff --git a/src/core/address_space.cpp b/src/core/address_space.cpp
index ca3d52042..4830f65a5 100644
--- a/src/core/address_space.cpp
+++ b/src/core/address_space.cpp
@@ -613,7 +613,11 @@ struct AddressSpace::Impl {
user_size = UserSize;
constexpr int protection_flags = PROT_READ | PROT_WRITE;
- constexpr int map_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED;
+ int map_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED; // compiler knows its constexpr
+#if !defined(__FreeBSD__)
+ map_flags |= MAP_NORESERVE;
+#endif
+
#if defined(__APPLE__) && defined(ARCH_X86_64)
// On ARM64 Macs, we run into limitations due to the commpage from 0xFC0000000 - 0xFFFFFFFFF
// and the GPU carveout region from 0x1000000000 - 0x6FFFFFFFFF. Because this creates gaps
@@ -628,7 +632,7 @@ struct AddressSpace::Impl {
mmap(reinterpret_cast(USER_MIN), user_size, protection_flags, map_flags, -1, 0));
#else
const auto virtual_size = system_managed_size + system_reserved_size + user_size;
-#if defined(ARCH_X86_64)
+#if defined(ARCH_X86_64) && !defined(__FreeBSD__)
const auto virtual_base =
reinterpret_cast(mmap(reinterpret_cast(SYSTEM_MANAGED_MIN), virtual_size,
protection_flags, map_flags, -1, 0));
@@ -636,8 +640,10 @@ struct AddressSpace::Impl {
system_reserved_base = reinterpret_cast(SYSTEM_RESERVED_MIN);
user_base = reinterpret_cast(USER_MIN);
#else
+ // FreeBSD can't stand MAP_FIXED or it may overwrite mmap() itself!
// Map memory wherever possible and instruction translation can handle offsetting to the
// base.
+ map_flags &= ~MAP_FIXED;
const auto virtual_base =
reinterpret_cast(mmap(nullptr, virtual_size, protection_flags, map_flags, -1, 0));
system_managed_base = virtual_base;
@@ -676,8 +682,13 @@ struct AddressSpace::Impl {
}
shm_unlink(shm_path.c_str());
#else
+#ifndef __FreeBSD__
madvise(virtual_base, virtual_size, MADV_HUGEPAGE);
-
+#endif
+ // NOTE: If you add MFD_HUGETLB or whatever, remember that FBSD will break (libc bug)
+ // so please, do not, add MFD_* whatever unless you ifdef it away (must be 0 for FBSD)
+ // using sized pages as well causes incessant vm_reclaim calls in kernel, do not use on FBSD
+ // under any circumstances.
backing_fd = memfd_create("BackingDmem", 0);
if (backing_fd < 0) {
LOG_CRITICAL(Kernel_Vmm, "memfd_create failed: {}", strerror(errno));
diff --git a/src/core/debugger.cpp b/src/core/debugger.cpp
index 16071ee69..b396a3ba5 100644
--- a/src/core/debugger.cpp
+++ b/src/core/debugger.cpp
@@ -12,7 +12,7 @@
#elif defined(__linux__)
#include
#include
-#elif defined(__APPLE__)
+#elif defined(__APPLE__) || defined(__FreeBSD__)
#include
#include
#include
@@ -48,6 +48,8 @@ bool Core::Debugger::IsDebuggerAttached() {
return (info.kp_proc.p_flag & P_TRACED) != 0;
}
return false;
+#elif defined(__FreeBSD__)
+ return false;
#else
#error "Unsupported platform"
#endif
@@ -66,7 +68,7 @@ void Core::Debugger::WaitForDebuggerAttach() {
int Core::Debugger::GetCurrentPid() {
#if defined(_WIN32)
return GetCurrentProcessId();
-#elif defined(__APPLE__) || defined(__linux__)
+#elif defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__)
return getpid();
#else
#error "Unsupported platform"
@@ -88,7 +90,7 @@ void Core::Debugger::WaitForPid(int pid) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::cerr << "Waiting for process " << pid << " to exit..." << std::endl;
}
-#elif defined(__APPLE__)
+#elif defined(__APPLE__) || defined(__FreeBSD__)
while (kill(pid, 0) == 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::cerr << "Waiting for process " << pid << " to exit..." << std::endl;
diff --git a/src/core/file_format/trp.cpp b/src/core/file_format/trp.cpp
index f0a258c12..6269fc6c7 100644
--- a/src/core/file_format/trp.cpp
+++ b/src/core/file_format/trp.cpp
@@ -5,7 +5,6 @@
#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 trophyKey, std::span NPcommID,
@@ -43,8 +42,10 @@ static void hexToBytes(const char* hex, unsigned char* dst) {
}
}
-bool TRP::Extract(const std::filesystem::path& trophyPath, const std::string titleId) {
- std::filesystem::path gameSysDir = trophyPath / "sce_sys/trophy/";
+bool TRP::Extract(const std::filesystem::path& trophyPath, int index, std::string npCommId,
+ const std::filesystem::path& outputPath) {
+ std::filesystem::path gameSysDir =
+ trophyPath / "sce_sys/trophy/" / std::format("trophy{:02d}.trp", index);
if (!std::filesystem::exists(gameSysDir)) {
LOG_WARNING(Common_Filesystem, "Game trophy directory doesn't exist");
return false;
@@ -61,117 +62,82 @@ bool TRP::Extract(const std::filesystem::path& trophyPath, const std::string tit
std::array user_key{};
std::copy(user_key_vec.begin(), user_key_vec.end(), user_key.begin());
- // 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
- }
+ const auto& it = gameSysDir;
+ if (it.extension() != ".trp") {
+ return false;
+ }
+ Common::FS::IOFile file(it, Common::FS::FileAccessMode::Read);
+ if (!file.IsOpen()) {
+ LOG_ERROR(Common_Filesystem, "Unable to open trophy file: {}", it.string());
+ return false;
+ }
- // Get NPCommID for this TRP file (if available)
- std::string npCommId;
- if (trpFileIndex < static_cast(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);
- }
+ TrpHeader header;
+ if (!file.Read(header)) {
+ LOG_ERROR(Common_Filesystem, "Failed to read TRP header from {}", it.string());
+ return false;
+ }
- Common::FS::IOFile file(it.path(), Common::FS::FileAccessMode::Read);
- if (!file.IsOpen()) {
- LOG_ERROR(Common_Filesystem, "Unable to open trophy file: {}", it.path().string());
+ if (header.magic != TRP_MAGIC) {
+ LOG_ERROR(Common_Filesystem, "Wrong trophy magic number in {}", it.string());
+ return false;
+ }
+
+ s64 seekPos = sizeof(TrpHeader);
+ // Create output directories
+ if (!std::filesystem::create_directories(outputPath / "Icons") ||
+ !std::filesystem::create_directories(outputPath / "Xml")) {
+ LOG_ERROR(Common_Filesystem, "Failed to create output directories for {}", npCommId);
+ return false;
+ }
+
+ // Process each entry in the TRP file
+ for (int i = 0; i < header.entry_num; i++) {
+ if (!file.Seek(seekPos)) {
+ LOG_ERROR(Common_Filesystem, "Failed to seek to TRP entry offset");
success = false;
- continue;
+ break;
}
+ seekPos += static_cast(header.entry_size);
- TrpHeader header;
- if (!file.Read(header)) {
- LOG_ERROR(Common_Filesystem, "Failed to read TRP header from {}",
- it.path().string());
+ TrpEntry entry;
+ if (!file.Read(entry)) {
+ LOG_ERROR(Common_Filesystem, "Failed to read TRP entry");
success = false;
- continue;
+ break;
}
- if (header.magic != TRP_MAGIC) {
- LOG_ERROR(Common_Filesystem, "Wrong trophy magic number in {}", it.path().string());
- success = false;
- continue;
- }
+ std::string_view name(entry.entry_name);
- s64 seekPos = sizeof(TrpHeader);
- std::filesystem::path trpFilesPath(
- Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / titleId /
- "TrophyFiles" / it.path().stem());
-
- // 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_ERROR(Common_Filesystem, "Failed to seek to TRP entry offset");
+ if (entry.flag == ENTRY_FLAG_PNG) {
+ if (!ProcessPngEntry(file, entry, outputPath, name)) {
success = false;
- break;
+ // Continue with next entry
}
- seekPos += static_cast(header.entry_size);
-
- TrpEntry entry;
- if (!file.Read(entry)) {
- LOG_ERROR(Common_Filesystem, "Failed to read TRP entry");
- success = false;
- break;
- }
-
- std::string_view name(entry.entry_name);
-
- if (entry.flag == ENTRY_FLAG_PNG) {
- if (!ProcessPngEntry(file, entry, trpFilesPath, name)) {
+ } 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, outputPath, name, user_key,
+ npCommId)) {
success = false;
// Continue with next entry
}
- } 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(entry.flag), name);
+ 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(entry.flag), name);
}
-
trpFileIndex++;
}
+
} catch (const std::filesystem::filesystem_error& e) {
LOG_CRITICAL(Common_Filesystem, "Filesystem error during trophy extraction: {}", e.what());
return false;
@@ -182,7 +148,7 @@ bool TRP::Extract(const std::filesystem::path& trophyPath, const std::string tit
if (success) {
LOG_INFO(Common_Filesystem, "Successfully extracted {} trophy files for {}", trpFileIndex,
- titleId);
+ npCommId);
}
return success;
diff --git a/src/core/file_format/trp.h b/src/core/file_format/trp.h
index 2b52a4d57..df0ea6eaf 100644
--- a/src/core/file_format/trp.h
+++ b/src/core/file_format/trp.h
@@ -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
@@ -36,7 +36,8 @@ class TRP {
public:
TRP();
~TRP();
- bool Extract(const std::filesystem::path& trophyPath, const std::string titleId);
+ bool Extract(const std::filesystem::path& trophyPath, int index, std::string npCommId,
+ const std::filesystem::path& outputPath);
private:
bool ProcessPngEntry(Common::FS::IOFile& file, const TrpEntry& entry,
@@ -45,9 +46,6 @@ private:
const std::filesystem::path& outputPath, std::string_view name,
const std::array& user_key, const std::string& npCommId);
- std::vector NPcommID = std::vector(12);
- std::array np_comm_id{};
std::array esfmIv{};
- std::filesystem::path trpFilesPath;
static constexpr int iv_len = 16;
};
diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp
index 2fdf8c10b..8327b51de 100644
--- a/src/core/file_sys/fs.cpp
+++ b/src/core/file_sys/fs.cpp
@@ -42,7 +42,7 @@ void MntPoints::UnmountAll() {
}
std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_read_only,
- bool force_base_path) {
+ HostPathType path_type) {
// Evil games like Turok2 pass double slashes e.g /app0//game.kpf
std::string corrected_path(path);
size_t pos = corrected_path.find("//");
@@ -80,8 +80,24 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
}
patch_path /= rel_path;
+ std::filesystem::path mods_path = mount->host_path;
+ mods_path += "-mods";
+ mods_path /= rel_path;
+
+ if (path_type == HostPathType::Mod) {
+ return mods_path;
+ } else if (path_type == HostPathType::Patch) {
+ return patch_path;
+ }
+
if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) &&
- !force_base_path && !ignore_game_patches && std::filesystem::exists(patch_path)) {
+ path_type != HostPathType::Base && std::filesystem::exists(mods_path)) {
+ return mods_path;
+ }
+
+ if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) &&
+ path_type != HostPathType::Base && !ignore_game_patches &&
+ std::filesystem::exists(patch_path)) {
return patch_path;
}
@@ -95,7 +111,7 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
std::scoped_lock lk{m_mutex};
path_parts.clear();
auto current_path = host_path;
- while (!std::filesystem::exists(current_path)) {
+ while (!current_path.empty() && !std::filesystem::exists(current_path)) {
// We have probably cached this if it's a folder.
if (auto it = path_cache.find(current_path); it != path_cache.end()) {
current_path = it->second;
@@ -104,44 +120,46 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
path_parts.emplace_back(current_path.filename());
current_path = current_path.parent_path();
}
- // We have found an anchor. Traverse parts we recoded and see if they
- // exist in filesystem but in different case.
- auto guest_path = current_path;
- while (!path_parts.empty()) {
- const auto part = path_parts.back();
- const auto add_match = [&](const auto& host_part) {
- current_path /= host_part;
- guest_path /= part;
- path_cache[guest_path] = current_path;
- path_parts.pop_back();
- };
- // Can happen when the mismatch is in upper folder.
- if (std::filesystem::exists(current_path / part)) {
- add_match(part);
- continue;
- }
- const auto part_low = Common::ToLower(part.string());
- bool found_match = false;
- for (const auto& path : std::filesystem::directory_iterator(current_path)) {
- const auto candidate = path.path().filename();
- const auto filename = Common::ToLower(candidate.string());
- // Check if a filename matches in case insensitive manner.
- if (filename != part_low) {
+ if (!current_path.empty()) {
+ // We have found an anchor. Traverse parts we recoded and see if they
+ // exist in filesystem but in different case.
+ auto guest_path = current_path;
+ while (!path_parts.empty()) {
+ const auto part = path_parts.back();
+ const auto add_match = [&](const auto& host_part) {
+ current_path /= host_part;
+ guest_path /= part;
+ path_cache[guest_path] = current_path;
+ path_parts.pop_back();
+ };
+ // Can happen when the mismatch is in upper folder.
+ if (std::filesystem::exists(current_path / part)) {
+ add_match(part);
continue;
}
- // We found a match, record the actual path in the cache.
- add_match(candidate);
- found_match = true;
- break;
- }
- if (!found_match) {
- return std::optional({});
+ const auto part_low = Common::ToLower(part.string());
+ bool found_match = false;
+ for (const auto& path : std::filesystem::directory_iterator(current_path)) {
+ const auto candidate = path.path().filename();
+ const auto filename = Common::ToLower(candidate.string());
+ // Check if a filename matches in case insensitive manner.
+ if (filename != part_low) {
+ continue;
+ }
+ // We found a match, record the actual path in the cache.
+ add_match(candidate);
+ found_match = true;
+ break;
+ }
+ if (!found_match) {
+ return std::optional({});
+ }
}
}
return std::optional(current_path);
};
- if (!force_base_path && !ignore_game_patches) {
+ if (path_type != HostPathType::Base && !ignore_game_patches) {
if (const auto path = search(patch_path)) {
return *path;
}
@@ -158,34 +176,54 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
// TODO: Does not handle mount points inside mount points.
void MntPoints::IterateDirectory(std::string_view guest_directory,
const IterateDirectoryCallback& callback) {
- const auto base_path = GetHostPath(guest_directory, nullptr, true);
- const auto patch_path = GetHostPath(guest_directory, nullptr, false);
- // Only need to consider patch path if it exists and does not resolve to the same as base.
- const auto apply_patch = base_path != patch_path && std::filesystem::exists(patch_path);
+ const auto base_path = GetHostPath(guest_directory, nullptr, HostPathType::Base);
+
+ // Forces path types so as not to resolve to base path
+ const auto patch_path = GetHostPath(guest_directory, nullptr, HostPathType::Patch);
+ const auto mod_path = GetHostPath(guest_directory, nullptr, HostPathType::Mod);
// Prepend entries for . and .., as both are treated as files on PS4.
callback(base_path / ".", false);
callback(base_path / "..", false);
- // Pass 1: Any files that existed in the base directory, using patch directory if needed.
+ // Pass 1: Any files that existed in the base directory, using mod/patch directory if needed.
if (std::filesystem::exists(base_path)) {
for (const auto& entry : std::filesystem::directory_iterator(base_path)) {
- if (apply_patch) {
- const auto patch_entry_path = patch_path / entry.path().filename();
- if (std::filesystem::exists(patch_entry_path)) {
- callback(patch_entry_path, !std::filesystem::is_directory(patch_entry_path));
- continue;
- }
+ const auto mod_entry_path = mod_path / entry.path().filename();
+ const auto patch_entry_path = patch_path / entry.path().filename();
+ if (std::filesystem::exists(mod_entry_path)) {
+ callback(mod_entry_path, !std::filesystem::is_directory(mod_entry_path));
+ continue;
+ } else if (std::filesystem::exists(patch_entry_path)) {
+ callback(patch_entry_path, !std::filesystem::is_directory(patch_entry_path));
+ continue;
}
callback(entry.path(), !entry.is_directory());
}
}
// Pass 2: Any files that exist only in the patch directory.
- if (apply_patch) {
+ if (std::filesystem::exists(patch_path)) {
for (const auto& entry : std::filesystem::directory_iterator(patch_path)) {
const auto base_entry_path = base_path / entry.path().filename();
if (!std::filesystem::exists(base_entry_path)) {
+ const auto mod_entry_path = mod_path / entry.path().filename();
+ if (std::filesystem::exists(mod_entry_path)) {
+ callback(mod_entry_path, !std::filesystem::is_directory(mod_entry_path));
+ continue;
+ }
+ callback(entry.path(), !entry.is_directory());
+ }
+ }
+ }
+
+ // Pass 3: Any files that exist only in the mod directory (confirmed this can be valid)
+ if (std::filesystem::exists(mod_path)) {
+ for (const auto& entry : std::filesystem::directory_iterator(mod_path)) {
+ const auto base_entry_path = base_path / entry.path().filename();
+ const auto patch_entry_path = patch_path / entry.path().filename();
+ if (!std::filesystem::exists(base_entry_path) &&
+ !std::filesystem::exists(patch_entry_path)) {
callback(entry.path(), !entry.is_directory());
}
}
diff --git a/src/core/file_sys/fs.h b/src/core/file_sys/fs.h
index 0522c3d8a..9a782b19e 100644
--- a/src/core/file_sys/fs.h
+++ b/src/core/file_sys/fs.h
@@ -36,6 +36,13 @@ public:
bool read_only;
};
+ enum class HostPathType {
+ Default, // Prioritizes Mod, then patch, then base
+ Base,
+ Patch,
+ Mod
+ };
+
explicit MntPoints() = default;
~MntPoints() = default;
@@ -45,7 +52,8 @@ public:
void UnmountAll();
std::filesystem::path GetHostPath(std::string_view guest_directory,
- bool* is_read_only = nullptr, bool force_base_path = false);
+ bool* is_read_only = nullptr,
+ HostPathType host_path = HostPathType::Default);
using IterateDirectoryCallback =
std::function;
void IterateDirectory(std::string_view guest_directory,
diff --git a/src/core/ipc/ipc.cpp b/src/core/ipc/ipc.cpp
index 489c34646..70180d3bf 100644
--- a/src/core/ipc/ipc.cpp
+++ b/src/core/ipc/ipc.cpp
@@ -211,13 +211,6 @@ void IPC::InputLoop() {
} else if (cmd == "RELOAD_INPUTS") {
std::string config = next_str();
Input::ParseInputConfig(config);
- } else if (cmd == "SET_ACTIVE_CONTROLLER") {
- std::string active_controller = next_str();
- GamepadSelect::SetSelectedGamepad(active_controller);
- SDL_Event checkGamepad;
- SDL_memset(&checkGamepad, 0, sizeof(checkGamepad));
- checkGamepad.type = SDL_EVENT_CHANGE_CONTROLLER;
- SDL_PushEvent(&checkGamepad);
} else {
std::cerr << ";UNKNOWN CMD: " << cmd << std::endl;
}
diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp
index 6594bfab2..4d74e731b 100644
--- a/src/core/libraries/kernel/kernel.cpp
+++ b/src/core/libraries/kernel/kernel.cpp
@@ -298,6 +298,126 @@ s32 PS4_SYSV_ABI sceKernelGetAppInfo(s32 pid, OrbisKernelAppInfo* app_info) {
return ORBIS_OK;
}
+// Nominally: long sysconf(int name);
+u64 PS4_SYSV_ABI posix_sysconf(s32 name) {
+ switch (name) {
+ case 0:
+ return 0x20000;
+ case POSIX_SC_ARG_MAX:
+ return 0x588bc000;
+ case POSIX_SC_CHILD_MAX:
+ return 0x64;
+ case POSIX_SC_CLK_TCK:
+ return 0x20;
+ case POSIX_SC_NGROUPS_MAX:
+ return 0x644;
+ case POSIX_SC_OPEN_MAX:
+ return -0x1;
+ case POSIX_SC_JOB_CONTROL:
+ return 0x6;
+ case POSIX_SC_SAVED_IDS:
+ return 0x1;
+ case POSIX_SC_VERSION:
+ return 0x1;
+ case POSIX_SC_BC_BASE_MAX:
+ return 0x31069;
+ case POSIX_SC_BC_DIM_MAX:
+ return -0x1;
+ case POSIX_SC_BC_SCALE_MAX:
+ return 0x31069;
+ case POSIX_SC_BC_STRING_MAX:
+ return 0x31069;
+ case POSIX_SC_COLL_WEIGHTS_MAX:
+ return -0x1;
+ case POSIX_SC_EXPR_NEST_MAX:
+ return -0x1;
+ case POSIX_SC_LINE_MAX:
+ return 0x31069;
+ case POSIX_SC_RE_DUP_MAX:
+ return 0x31069;
+ case POSIX_SC_2_VERSION:
+ return 0x31069;
+ case POSIX_SC_2_C_BIND:
+ return 0x31069;
+ case POSIX_SC_2_C_DEV:
+ return 0x31069;
+ case POSIX_SC_2_CHAR_TERM:
+ return 0x31069;
+ case POSIX_SC_2_FORT_DEV:
+ return 0x31069;
+ case POSIX_SC_2_FORT_RUN:
+ return 0x31069;
+ case POSIX_SC_2_LOCALEDEF:
+ return -0x1;
+ case POSIX_SC_2_SW_DEV:
+ return -0x1;
+ case POSIX_SC_2_UPE:
+ return 0x0;
+ case POSIX_SC_STREAM_MAX:
+ return 0x7fffffff;
+ case POSIX_SC_TZNAME_MAX:
+ return -0x1;
+ case POSIX_SC_ASYNCHRONOUS_IO:
+ return 0x8000;
+ case POSIX_SC_MAPPED_FILES:
+ return 0x31069;
+ case POSIX_SC_MEMLOCK:
+ return 0x4000;
+ case POSIX_SC_MEMLOCK_RANGE:
+ return 0x1e;
+ case POSIX_SC_MEMORY_PROTECTION:
+ return 0x100;
+ case POSIX_SC_MESSAGE_PASSING:
+ return 0x7fffffff;
+ case POSIX_SC_PRIORITIZED_IO:
+ return -0x1;
+ case POSIX_SC_PRIORITY_SCHEDULING:
+ return -0x1;
+ case POSIX_SC_REALTIME_SIGNALS:
+ return 0x63;
+ case POSIX_SC_SEMAPHORES:
+ return 0x800;
+ case POSIX_SC_FSYNC:
+ return 0x63;
+ case POSIX_SC_SHARED_MEMORY_OBJECTS:
+ return 0x3e8;
+ case POSIX_SC_SYNCHRONIZED_IO:
+ return 0x2;
+ case POSIX_SC_THREAD_ATTR_STACKSIZE:
+ return 0x1;
+ case POSIX_SC_THREAD_CPUTIME:
+ return 0x1;
+ case POSIX_SC_THREAD_DESTRUCTOR_ITERATIONS:
+ return 0x48000;
+ case POSIX_SC_THREAD_KEYS_MAX:
+ return 0x1a078630b2dd7;
+ case POSIX_SC_THREAD_PRIO_INHERIT:
+ return -0x1;
+ case POSIX_SC_THREAD_PRIO_PROTECT:
+ return -0x1;
+ case POSIX_SC_THREAD_PRIORITY_SCHEDULING:
+ return 0x2bc;
+ case POSIX_SC_THREAD_PROCESS_SHARED:
+ return 0x2bc;
+ case POSIX_SC_THREAD_SAFE_FUNCTIONS:
+ return 0x1;
+ case POSIX_SC_THREAD_SPORADIC_SERVER:
+ return -0x1;
+ case POSIX_SC_THREAD_STACK_MIN:
+ return 0x1;
+ case POSIX_SC_THREAD_THREADS_MAX:
+ return 0x1;
+ case POSIX_SC_TIMEOUTS:
+ return -0x1;
+ // Manually specified
+ case POSIX_SC_PAGESIZE:
+ return posix_getpagesize();
+ default:
+ LOG_ERROR(Lib_Kernel, "unhandled {}", name);
+ return 0;
+ }
+}
+
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
service_thread = std::jthread{KernelServiceThread};
@@ -327,6 +447,10 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("9BcDykPmo1I", "libkernel", 1, "libkernel", __Error);
LIB_FUNCTION("k+AXqu2-eBc", "libkernel", 1, "libkernel", posix_getpagesize);
LIB_FUNCTION("k+AXqu2-eBc", "libScePosix", 1, "libkernel", posix_getpagesize);
+
+ LIB_FUNCTION("mkawd0NA9ts", "libkernel", 1, "libkernel", posix_sysconf);
+ LIB_FUNCTION("mkawd0NA9ts", "libScePosix", 1, "libkernel", posix_sysconf);
+
LIB_FUNCTION("NWtTN10cJzE", "libSceLibcInternalExt", 1, "libSceLibcInternal",
sceLibcHeapGetTraceInfo);
diff --git a/src/core/libraries/kernel/kernel.h b/src/core/libraries/kernel/kernel.h
index 022ec749d..017749543 100644
--- a/src/core/libraries/kernel/kernel.h
+++ b/src/core/libraries/kernel/kernel.h
@@ -82,4 +82,126 @@ struct OrbisKernelAppInfo {
void RegisterLib(Core::Loader::SymbolsResolver* sym);
+constexpr u32 POSIX_SC_ARG_MAX = 1;
+constexpr u32 POSIX_SC_CHILD_MAX = 2;
+constexpr u32 POSIX_SC_CLK_TCK = 3;
+constexpr u32 POSIX_SC_NGROUPS_MAX = 4;
+constexpr u32 POSIX_SC_OPEN_MAX = 5;
+constexpr u32 POSIX_SC_JOB_CONTROL = 6;
+constexpr u32 POSIX_SC_SAVED_IDS = 7;
+constexpr u32 POSIX_SC_VERSION = 8;
+constexpr u32 POSIX_SC_BC_BASE_MAX = 9;
+constexpr u32 POSIX_SC_BC_DIM_MAX = 10;
+constexpr u32 POSIX_SC_BC_SCALE_MAX = 11;
+constexpr u32 POSIX_SC_BC_STRING_MAX = 12;
+constexpr u32 POSIX_SC_COLL_WEIGHTS_MAX = 13;
+constexpr u32 POSIX_SC_EXPR_NEST_MAX = 14;
+constexpr u32 POSIX_SC_LINE_MAX = 15;
+constexpr u32 POSIX_SC_RE_DUP_MAX = 16;
+constexpr u32 POSIX_SC_2_VERSION = 17;
+constexpr u32 POSIX_SC_2_C_BIND = 18;
+constexpr u32 POSIX_SC_2_C_DEV = 19;
+constexpr u32 POSIX_SC_2_CHAR_TERM = 20;
+constexpr u32 POSIX_SC_2_FORT_DEV = 21;
+constexpr u32 POSIX_SC_2_FORT_RUN = 22;
+constexpr u32 POSIX_SC_2_LOCALEDEF = 23;
+constexpr u32 POSIX_SC_2_SW_DEV = 24;
+constexpr u32 POSIX_SC_2_UPE = 25;
+constexpr u32 POSIX_SC_STREAM_MAX = 26;
+constexpr u32 POSIX_SC_TZNAME_MAX = 27;
+constexpr u32 POSIX_SC_ASYNCHRONOUS_IO = 28;
+constexpr u32 POSIX_SC_MAPPED_FILES = 29;
+constexpr u32 POSIX_SC_MEMLOCK = 30;
+constexpr u32 POSIX_SC_MEMLOCK_RANGE = 31;
+constexpr u32 POSIX_SC_MEMORY_PROTECTION = 32;
+constexpr u32 POSIX_SC_MESSAGE_PASSING = 33;
+constexpr u32 POSIX_SC_PRIORITIZED_IO = 34;
+constexpr u32 POSIX_SC_PRIORITY_SCHEDULING = 35;
+constexpr u32 POSIX_SC_REALTIME_SIGNALS = 36;
+constexpr u32 POSIX_SC_SEMAPHORES = 37;
+constexpr u32 POSIX_SC_FSYNC = 38;
+constexpr u32 POSIX_SC_SHARED_MEMORY_OBJECTS = 39;
+constexpr u32 POSIX_SC_SYNCHRONIZED_IO = 40;
+constexpr u32 POSIX_SC_TIMERS = 41;
+constexpr u32 POSIX_SC_AIO_LISTIO_MAX = 42;
+constexpr u32 POSIX_SC_AIO_MAX = 43;
+constexpr u32 POSIX_SC_AIO_PRIO_DELTA_MAX = 44;
+constexpr u32 POSIX_SC_DELAYTIMER_MAX = 45;
+constexpr u32 POSIX_SC_MQ_OPEN_MAX = 46;
+constexpr u32 POSIX_SC_PAGESIZE = 47;
+constexpr u32 POSIX_SC_RTSIG_MAX = 48;
+constexpr u32 POSIX_SC_SEM_NSEMS_MAX = 49;
+constexpr u32 POSIX_SC_SEM_VALUE_MAX = 50;
+constexpr u32 POSIX_SC_SIGQUEUE_MAX = 51;
+constexpr u32 POSIX_SC_TIMER_MAX = 52;
+constexpr u32 POSIX_SC_2_PBS = 59;
+constexpr u32 POSIX_SC_2_PBS_ACCOUNTING = 60;
+constexpr u32 POSIX_SC_2_PBS_CHECKPOINT = 61;
+constexpr u32 POSIX_SC_2_PBS_LOCATE = 62;
+constexpr u32 POSIX_SC_2_PBS_MESSAGE = 63;
+constexpr u32 POSIX_SC_2_PBS_TRACK = 64;
+constexpr u32 POSIX_SC_ADVISORY_INFO = 65;
+constexpr u32 POSIX_SC_BARRIERS = 66;
+constexpr u32 POSIX_SC_CLOCK_SELECTION = 67;
+constexpr u32 POSIX_SC_CPUTIME = 68;
+constexpr u32 POSIX_SC_FILE_LOCKING = 69;
+constexpr u32 POSIX_SC_GETGR_R_SIZE_MAX = 70;
+constexpr u32 POSIX_SC_GETPW_R_SIZE_MAX = 71;
+constexpr u32 POSIX_SC_HOST_NAME_MAX = 72;
+constexpr u32 POSIX_SC_LOGIN_NAME_MAX = 73;
+constexpr u32 POSIX_SC_MONOTONIC_CLOCK = 74;
+constexpr u32 POSIX_SC_MQ_PRIO_MAX = 75;
+constexpr u32 POSIX_SC_READER_WRITER_LOCKS = 76;
+constexpr u32 POSIX_SC_REGEXP = 77;
+constexpr u32 POSIX_SC_SHELL = 78;
+constexpr u32 POSIX_SC_SPAWN = 79;
+constexpr u32 POSIX_SC_SPIN_LOCKS = 80;
+constexpr u32 POSIX_SC_SPORADIC_SERVER = 81;
+constexpr u32 POSIX_SC_THREAD_ATTR_STACKADDR = 82;
+constexpr u32 POSIX_SC_THREAD_ATTR_STACKSIZE = 83;
+constexpr u32 POSIX_SC_THREAD_CPUTIME = 84;
+constexpr u32 POSIX_SC_THREAD_DESTRUCTOR_ITERATIONS = 85;
+constexpr u32 POSIX_SC_THREAD_KEYS_MAX = 86;
+constexpr u32 POSIX_SC_THREAD_PRIO_INHERIT = 87;
+constexpr u32 POSIX_SC_THREAD_PRIO_PROTECT = 88;
+constexpr u32 POSIX_SC_THREAD_PRIORITY_SCHEDULING = 89;
+constexpr u32 POSIX_SC_THREAD_PROCESS_SHARED = 90;
+constexpr u32 POSIX_SC_THREAD_SAFE_FUNCTIONS = 91;
+constexpr u32 POSIX_SC_THREAD_SPORADIC_SERVER = 92;
+constexpr u32 POSIX_SC_THREAD_STACK_MIN = 93;
+constexpr u32 POSIX_SC_THREAD_THREADS_MAX = 94;
+constexpr u32 POSIX_SC_TIMEOUTS = 95;
+constexpr u32 POSIX_SC_THREADS = 96;
+constexpr u32 POSIX_SC_TRACE = 97;
+constexpr u32 POSIX_SC_TRACE_EVENT_FILTER = 98;
+constexpr u32 POSIX_SC_TRACE_INHERIT = 99;
+constexpr u32 POSIX_SC_TRACE_LOG = 100;
+constexpr u32 POSIX_SC_TTY_NAME_MAX = 101;
+constexpr u32 POSIX_SC_TYPED_MEMORY_OBJECTS = 102;
+constexpr u32 POSIX_SC_V6_ILP32_OFF32 = 103;
+constexpr u32 POSIX_SC_V6_ILP32_OFFBIG = 104;
+constexpr u32 POSIX_SC_V6_LP64_OFF64 = 105;
+constexpr u32 POSIX_SC_V6_LPBIG_OFFBIG = 106;
+constexpr u32 POSIX_SC_IPV6 = 118;
+constexpr u32 POSIX_SC_RAW_SOCKETS = 119;
+constexpr u32 POSIX_SC_SYMLOOP_MAX = 120;
+constexpr u32 POSIX_SC_ATEXIT_MAX = 107;
+constexpr u32 POSIX_SC_IOV_MAX = 56;
+constexpr u32 POSIX_SC_XOPEN_CRYPT = 108;
+constexpr u32 POSIX_SC_XOPEN_ENH_I18N = 109;
+constexpr u32 POSIX_SC_XOPEN_LEGACY = 110;
+constexpr u32 POSIX_SC_XOPEN_REALTIME = 111;
+constexpr u32 POSIX_SC_XOPEN_REALTIME_THREADS = 112;
+constexpr u32 POSIX_SC_XOPEN_SHM = 113;
+constexpr u32 POSIX_SC_XOPEN_STREAMS = 114;
+constexpr u32 POSIX_SC_XOPEN_UNIX = 115;
+constexpr u32 POSIX_SC_XOPEN_VERSION = 116;
+constexpr u32 POSIX_SC_XOPEN_XCU_VERSION = 117;
+constexpr u32 POSIX_SC_NPROCESSORS_CONF = 57;
+constexpr u32 POSIX_SC_NPROCESSORS_ONLN = 58;
+constexpr u32 POSIX_SC_CPUSET_SIZE = 122;
+constexpr u32 POSIX_SC_UEXTERR_MAXLEN = 123;
+constexpr u32 POSIX_SC_NSIG = 124;
+constexpr u32 POSIX_SC_PHYS_PAGES = 121;
+
} // namespace Libraries::Kernel
diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp
index 378064e44..f0b7df4c9 100644
--- a/src/core/libraries/kernel/memory.cpp
+++ b/src/core/libraries/kernel/memory.cpp
@@ -339,7 +339,11 @@ s32 PS4_SYSV_ABI sceKernelMprotect(const void* addr, u64 size, s32 prot) {
Core::MemoryManager* memory_manager = Core::Memory::Instance();
Core::MemoryProt protection_flags = static_cast(prot);
- return memory_manager->Protect(aligned_addr, aligned_size, protection_flags);
+ s32 result = memory_manager->Protect(aligned_addr, aligned_size, protection_flags);
+ if (result == ORBIS_OK) {
+ memory_manager->InvalidateMemory(aligned_addr, aligned_size);
+ }
+ return result;
}
s32 PS4_SYSV_ABI posix_mprotect(const void* addr, u64 size, s32 prot) {
@@ -370,6 +374,7 @@ s32 PS4_SYSV_ABI sceKernelMtypeprotect(const void* addr, u64 size, s32 mtype, s3
s32 result = memory_manager->Protect(aligned_addr, aligned_size, protection_flags);
if (result == ORBIS_OK) {
memory_manager->SetDirectMemoryType(aligned_addr, aligned_size, mtype);
+ memory_manager->InvalidateMemory(aligned_addr, aligned_size);
}
return result;
}
diff --git a/src/core/libraries/kernel/threads/exception.cpp b/src/core/libraries/kernel/threads/exception.cpp
index e2fd032f5..f9a062da7 100644
--- a/src/core/libraries/kernel/threads/exception.cpp
+++ b/src/core/libraries/kernel/threads/exception.cpp
@@ -215,6 +215,28 @@ void SigactionHandler(int native_signum, siginfo_t* inf, ucontext_t* raw_context
ctx.uc_mcontext.mc_gs = regs.__gs;
ctx.uc_mcontext.mc_rip = regs.__rip;
ctx.uc_mcontext.mc_addr = reinterpret_cast(inf->si_addr);
+#elif defined(__FreeBSD__)
+ const auto& regs = raw_context->uc_mcontext;
+ ctx.uc_mcontext.mc_r8 = regs.mc_r8;
+ ctx.uc_mcontext.mc_r9 = regs.mc_r9;
+ ctx.uc_mcontext.mc_r10 = regs.mc_r10;
+ ctx.uc_mcontext.mc_r11 = regs.mc_r11;
+ ctx.uc_mcontext.mc_r12 = regs.mc_r12;
+ ctx.uc_mcontext.mc_r13 = regs.mc_r13;
+ ctx.uc_mcontext.mc_r14 = regs.mc_r14;
+ ctx.uc_mcontext.mc_r15 = regs.mc_r15;
+ ctx.uc_mcontext.mc_rdi = regs.mc_rdi;
+ ctx.uc_mcontext.mc_rsi = regs.mc_rsi;
+ ctx.uc_mcontext.mc_rbp = regs.mc_rbp;
+ ctx.uc_mcontext.mc_rbx = regs.mc_rbx;
+ ctx.uc_mcontext.mc_rdx = regs.mc_rdx;
+ ctx.uc_mcontext.mc_rax = regs.mc_rax;
+ ctx.uc_mcontext.mc_rcx = regs.mc_rcx;
+ ctx.uc_mcontext.mc_rsp = regs.mc_rsp;
+ ctx.uc_mcontext.mc_fs = regs.mc_fs;
+ ctx.uc_mcontext.mc_gs = regs.mc_gs;
+ ctx.uc_mcontext.mc_rip = regs.mc_rip;
+ ctx.uc_mcontext.mc_addr = uint64_t(regs.mc_addr);
#else
const auto& regs = raw_context->uc_mcontext.gregs;
ctx.uc_mcontext.mc_r8 = regs[REG_R8];
@@ -286,6 +308,28 @@ bool PS4_SYSV_ABI posix_sigisemptyset(Sigset* s) {
return s->bits[0] == 0 && s->bits[1] == 0;
}
+s32 PS4_SYSV_ABI posix_sigalstack(const OrbisKernelExceptionHandlerStack* ss,
+ OrbisKernelExceptionHandlerStack* old_ss) {
+#ifdef __unix__
+ stack_t native_ss{};
+ if (ss) {
+ native_ss.ss_sp = ss->ss_sp;
+ native_ss.ss_flags = ss->ss_flags;
+ native_ss.ss_size = ss->ss_size;
+ }
+ stack_t native_old_ss{};
+ sigaltstack(&native_ss, &native_old_ss);
+ if (old_ss) {
+ old_ss->ss_sp = native_old_ss.ss_sp;
+ old_ss->ss_flags = native_old_ss.ss_flags;
+ old_ss->ss_size = native_old_ss.ss_size;
+ }
+#else
+ LOG_ERROR(Lib_Kernel, "(stubbed)");
+#endif
+ return ORBIS_OK;
+}
+
s32 PS4_SYSV_ABI posix_sigaction(s32 sig, Sigaction* act, Sigaction* oact) {
if (sig < 1 || sig > 128 || sig == POSIX_SIGTHR || sig == POSIX_SIGKILL ||
sig == POSIX_SIGSTOP) {
@@ -303,7 +347,7 @@ s32 PS4_SYSV_ABI posix_sigaction(s32 sig, Sigaction* act, Sigaction* oact) {
*__Error() = POSIX_EINVAL;
return ORBIS_FAIL;
}
-#ifndef __APPLE__
+#if !defined(__APPLE__) && !defined(__FreeBSD__)
if (native_sig >= __SIGRTMIN && native_sig < SIGRTMIN) {
LOG_ERROR(Lib_Kernel, "Guest is attempting to use the HLE libc-reserved signal {}!", sig);
*__Error() = POSIX_EINVAL;
@@ -473,9 +517,12 @@ void RegisterException(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("KiJEPEWRyUY", "libkernel", 1, "libkernel", posix_sigaction);
LIB_FUNCTION("+F7C-hdk7+E", "libkernel", 1, "libkernel", posix_sigemptyset);
LIB_FUNCTION("yH-uQW3LbX0", "libkernel", 1, "libkernel", posix_pthread_kill);
+ LIB_FUNCTION("sHziAegVp74", "libkernel", 1, "libkernel", posix_sigalstack);
+
LIB_FUNCTION("KiJEPEWRyUY", "libScePosix", 1, "libkernel", posix_sigaction);
LIB_FUNCTION("+F7C-hdk7+E", "libScePosix", 1, "libkernel", posix_sigemptyset);
LIB_FUNCTION("yH-uQW3LbX0", "libScePosix", 1, "libkernel", posix_pthread_kill);
+ LIB_FUNCTION("sHziAegVp74", "libScePosix", 1, "libkernel", posix_sigalstack);
}
} // namespace Libraries::Kernel
diff --git a/src/core/libraries/kernel/threads/exception.h b/src/core/libraries/kernel/threads/exception.h
index c07242c1d..f9655404a 100644
--- a/src/core/libraries/kernel/threads/exception.h
+++ b/src/core/libraries/kernel/threads/exception.h
@@ -12,6 +12,11 @@ class SymbolsResolver;
namespace Libraries::Kernel {
using OrbisKernelExceptionHandler = PS4_SYSV_ABI void (*)(int, void*);
+struct OrbisKernelExceptionHandlerStack {
+ void* ss_sp;
+ int ss_flags;
+ size_t ss_size;
+};
constexpr s32 POSIX_SIGHUP = 1;
constexpr s32 POSIX_SIGINT = 2;
@@ -47,7 +52,7 @@ constexpr s32 POSIX_SIGUSR2 = 31;
constexpr s32 POSIX_SIGTHR = 32;
constexpr s32 POSIX_SIGLIBRT = 33;
-#ifdef __linux__
+#if defined(__linux__) || defined(__FreeBSD__)
constexpr s32 _SIGEMT = 128;
constexpr s32 _SIGINFO = 129;
#elif !defined(_WIN32)
diff --git a/src/core/libraries/kernel/time.cpp b/src/core/libraries/kernel/time.cpp
index 3e1648b98..2967dd4bf 100644
--- a/src/core/libraries/kernel/time.cpp
+++ b/src/core/libraries/kernel/time.cpp
@@ -17,7 +17,7 @@
#include
#include "common/ntapi.h"
#else
-#ifdef __APPLE__
+#if defined(__APPLE__) || defined(__FreeBSD__)
#include
#endif
#include
@@ -501,7 +501,7 @@ s32 PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
*dst_sec = res == TIME_ZONE_ID_DAYLIGHT ? -_dstbias : 0;
}
#else
-#ifdef __APPLE__
+#if defined(__APPLE__) || defined(__FreeBSD__)
// std::chrono::current_zone() not available yet.
const auto* time_zone = date::current_zone();
#else
diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp
index 6db1ba18d..762753fd1 100644
--- a/src/core/libraries/libs.cpp
+++ b/src/core/libraries/libs.cpp
@@ -63,7 +63,6 @@
#include "core/libraries/system/posix.h"
#include "core/libraries/system/systemservice.h"
#include "core/libraries/system/userservice.h"
-#include "core/libraries/system_gesture/system_gesture.h"
#include "core/libraries/ulobjmgr/ulobjmgr.h"
#include "core/libraries/usbd/usbd.h"
#include "core/libraries/videodec/videodec.h"
@@ -120,7 +119,6 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
Libraries::Random::RegisterLib(sym);
Libraries::Usbd::RegisterLib(sym);
Libraries::Pad::RegisterLib(sym);
- Libraries::SystemGesture::RegisterLib(sym);
Libraries::Ajm::RegisterLib(sym);
Libraries::ErrorDialog::RegisterLib(sym);
Libraries::ImeDialog::RegisterLib(sym);
diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp
index 6bf4764c4..baf45b64f 100644
--- a/src/core/libraries/network/net.cpp
+++ b/src/core/libraries/network/net.cpp
@@ -663,10 +663,12 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or
return ORBIS_NET_ERROR_EBADF;
}
+#ifndef __FreeBSD__
epoll_event native_event = {.events = ConvertEpollEventsIn(event->events),
.data = {.fd = id}};
ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_ADD, *native_handle, &native_event) == 0);
epoll->events.emplace_back(id, *event);
+#endif
break;
}
case Core::FileSys::FileType::Resolver: {
@@ -711,10 +713,12 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or
return ORBIS_NET_ERROR_EBADF;
}
+#ifndef __FreeBSD__
epoll_event native_event = {.events = ConvertEpollEventsIn(event->events),
.data = {.fd = id}};
ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_MOD, *native_handle, &native_event) == 0);
*it = {id, *event};
+#endif
break;
}
default:
@@ -752,9 +756,10 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or
*sceNetErrnoLoc() = ORBIS_NET_EBADF;
return ORBIS_NET_ERROR_EBADF;
}
-
+#ifndef __FreeBSD__
ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_DEL, *native_handle, nullptr) == 0);
epoll->events.erase(it);
+#endif
break;
}
case Core::FileSys::FileType::Resolver: {
@@ -810,6 +815,9 @@ int PS4_SYSV_ABI sceNetEpollDestroy(OrbisNetId epollid) {
int PS4_SYSV_ABI sceNetEpollWait(OrbisNetId epollid, OrbisNetEpollEvent* events, int maxevents,
int timeout) {
+#ifdef __FreeBSD__
+ return 0;
+#else
auto file = FDTable::Instance()->GetEpoll(epollid);
if (!file) {
*sceNetErrnoLoc() = ORBIS_NET_EBADF;
@@ -836,7 +844,6 @@ int PS4_SYSV_ABI sceNetEpollWait(OrbisNetId epollid, OrbisNetEpollEvent* events,
}
int i = 0;
-
if (result < 0) {
LOG_ERROR(Lib_Net, "epoll_wait failed with {}", Common::GetLastErrorMsg());
switch (errno) {
@@ -905,8 +912,8 @@ int PS4_SYSV_ABI sceNetEpollWait(OrbisNetId epollid, OrbisNetEpollEvent* events,
++i;
}
}
-
return i;
+#endif
}
int* PS4_SYSV_ABI sceNetErrnoLoc() {
diff --git a/src/core/libraries/network/net_epoll.cpp b/src/core/libraries/network/net_epoll.cpp
index e64c8ac64..4f1b521ce 100644
--- a/src/core/libraries/network/net_epoll.cpp
+++ b/src/core/libraries/network/net_epoll.cpp
@@ -10,12 +10,14 @@ namespace Libraries::Net {
u32 ConvertEpollEventsIn(u32 orbis_events) {
u32 ret = 0;
+#ifndef __FreeBSD__
if ((orbis_events & ORBIS_NET_EPOLLIN) != 0) {
ret |= EPOLLIN;
}
if ((orbis_events & ORBIS_NET_EPOLLOUT) != 0) {
ret |= EPOLLOUT;
}
+#endif
return ret;
}
@@ -23,6 +25,7 @@ u32 ConvertEpollEventsIn(u32 orbis_events) {
u32 ConvertEpollEventsOut(u32 epoll_events) {
u32 ret = 0;
+#ifndef __FreeBSD__
if ((epoll_events & EPOLLIN) != 0) {
ret |= ORBIS_NET_EPOLLIN;
}
@@ -35,6 +38,7 @@ u32 ConvertEpollEventsOut(u32 epoll_events) {
if ((epoll_events & EPOLLHUP) != 0) {
ret |= ORBIS_NET_EPOLLHUP;
}
+#endif
return ret;
}
diff --git a/src/core/libraries/network/net_epoll.h b/src/core/libraries/network/net_epoll.h
index 37555484d..17716b36e 100644
--- a/src/core/libraries/network/net_epoll.h
+++ b/src/core/libraries/network/net_epoll.h
@@ -14,7 +14,8 @@
#include
#endif
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
+// ADD libepoll-shim if using freebsd!
#include
#include
#endif
@@ -82,4 +83,4 @@ private:
std::mutex m_mutex;
};
-} // namespace Libraries::Net
\ No newline at end of file
+} // namespace Libraries::Net
diff --git a/src/core/libraries/network/net_util.cpp b/src/core/libraries/network/net_util.cpp
index de47f7bc1..fd0ed877b 100644
--- a/src/core/libraries/network/net_util.cpp
+++ b/src/core/libraries/network/net_util.cpp
@@ -25,7 +25,7 @@ typedef int net_socket;
#include
#include
#endif
-#if __linux__
+#if defined(__linux__) || defined(__FreeBSD__)
#include
#include
#include
@@ -81,6 +81,8 @@ bool NetUtilInternal::RetrieveEthernetAddr() {
}
freeifaddrs(ifap);
}
+#elif defined(__FreeBSD__)
+ // todo
#else
ifreq ifr;
ifconf ifc;
@@ -226,7 +228,8 @@ bool NetUtilInternal::RetrieveDefaultGateway() {
inet_ntop(AF_INET, gateAddr, str, sizeof(str));
this->default_gateway = str;
return true;
-
+#elif defined(__FreeBSD__)
+ return true;
#else
std::ifstream route{"/proc/net/route"};
std::string line;
@@ -398,4 +401,4 @@ int NetUtilInternal::ResolveHostname(const char* hostname, Libraries::Net::Orbis
return ret;
}
-} // namespace NetUtil
\ No newline at end of file
+} // namespace NetUtil
diff --git a/src/core/libraries/np/np_manager.cpp b/src/core/libraries/np/np_manager.cpp
index 62cad455b..0ffbb682a 100644
--- a/src/core/libraries/np/np_manager.cpp
+++ b/src/core/libraries/np/np_manager.cpp
@@ -1,13 +1,13 @@
-// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
+// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include