diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ceb915f6a..588236b14 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -76,18 +76,13 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{ runner.os }}-sdl-cache-cmake-build
with:
append-timestamp: false
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- - name: Setup VS Environment
- uses: ilammy/msvc-dev-cmd@v1.13.0
- with:
- arch: amd64
-
- name: Configure CMake
run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
@@ -111,7 +106,7 @@ jobs:
- name: Setup Qt
uses: jurplel/install-qt-action@v4
with:
- version: 6.9.0
+ version: 6.9.1
host: windows
target: desktop
arch: win64_msvc2022_64
@@ -130,18 +125,13 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{ runner.os }}-qt-cache-cmake-build
with:
append-timestamp: false
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- - name: Setup VS Environment
- uses: ilammy/msvc-dev-cmd@v1.13.0
- with:
- arch: amd64
-
- name: Configure CMake
run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
@@ -186,7 +176,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{runner.os}}-sdl-cache-cmake-build
with:
@@ -228,7 +218,7 @@ jobs:
- name: Setup Qt
uses: jurplel/install-qt-action@v4
with:
- version: 6.9.0
+ version: 6.9.1
host: mac
target: desktop
arch: clang_64
@@ -247,7 +237,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{runner.os}}-qt-cache-cmake-build
with:
@@ -301,7 +291,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{ runner.os }}-sdl-cache-cmake-build
with:
@@ -362,7 +352,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{ runner.os }}-qt-cache-cmake-build
with:
@@ -409,7 +399,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{ runner.os }}-sdl-gcc-cache-cmake-build
with:
@@ -445,7 +435,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{ runner.os }}-qt-gcc-cache-cmake-build
with:
@@ -494,7 +484,7 @@ jobs:
with:
token: ${{ secrets.SHADPS4_TOKEN_REPO }}
name: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}"
- tag: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}"
+ tag: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.fullhash }}"
draft: false
prerelease: true
body: "Full Changelog: [${{ env.last_release_tag }}...${{ needs.get-info.outputs.shorthash }}](https://github.com/shadps4-emu/shadPS4/compare/${{ env.last_release_tag }}...${{ needs.get-info.outputs.fullhash }})"
@@ -530,14 +520,14 @@ jobs:
# Check if release already exists and get ID
release_id=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
- "https://api.github.com/repos/$REPO/releases/tags/Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}" | jq -r '.id')
+ "https://api.github.com/repos/$REPO/releases/tags/Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.fullhash }}" | jq -r '.id')
if [[ "$release_id" == "null" ]]; then
echo "Creating release in $REPO for $filename"
release_id=$(curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
-d '{
- "tag_name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}",
+ "tag_name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.fullhash }}",
"name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}",
"draft": false,
"prerelease": true,
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7962488c8..6dfe9348a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -296,6 +296,8 @@ set(AJM_LIB src/core/libraries/ajm/ajm.cpp
set(AUDIO_LIB src/core/libraries/audio/audioin.cpp
src/core/libraries/audio/audioin.h
+ src/core/libraries/voice/voice.cpp
+ src/core/libraries/voice/voice.h
src/core/libraries/audio/audioout.cpp
src/core/libraries/audio/audioout.h
src/core/libraries/audio/audioout_backend.h
@@ -603,6 +605,8 @@ set(CAMERA_LIBS src/core/libraries/camera/camera.cpp
set(COMPANION_LIBS src/core/libraries/companion/companion_httpd.cpp
src/core/libraries/companion/companion_httpd.h
+ src/core/libraries/companion/companion_util.cpp
+ src/core/libraries/companion/companion_util.h
src/core/libraries/companion/companion_error.h
)
set(DEV_TOOLS src/core/devtools/layer.cpp
@@ -960,6 +964,7 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
src/video_core/texture_cache/tile_manager.cpp
src/video_core/texture_cache/tile_manager.h
src/video_core/texture_cache/types.h
+ src/video_core/texture_cache/host_compatibility.cpp
src/video_core/texture_cache/host_compatibility.h
src/video_core/page_manager.cpp
src/video_core/page_manager.h
diff --git a/documents/building-linux.md b/documents/building-linux.md
index cdc8ba12f..bd07b2eff 100644
--- a/documents/building-linux.md
+++ b/documents/building-linux.md
@@ -29,7 +29,7 @@ sudo dnf install clang git cmake libatomic alsa-lib-devel \
openssl-devel libevdev-devel libudev-devel libXext-devel \
qt6-qtbase-devel qt6-qtbase-private-devel \
qt6-qtmultimedia-devel qt6-qtsvg-devel qt6-qttools-devel \
- vulkan-devel vulkan-validation-layers libpng-devel
+ vulkan-devel vulkan-validation-layers libpng-devel libuuid-devel
```
#### Arch Linux
diff --git a/src/common/config.cpp b/src/common/config.cpp
index 6bccd0f37..6565ab82a 100644
--- a/src/common/config.cpp
+++ b/src/common/config.cpp
@@ -69,7 +69,7 @@ static bool vkGuestMarkers = false;
static bool rdocEnable = false;
static bool isFpsColor = true;
static bool isSeparateLogFilesEnabled = false;
-static s16 cursorState = HideCursorState::Idle;
+static int cursorState = HideCursorState::Idle;
static int cursorHideTimeout = 5; // 5 seconds (default)
static double trophyNotificationDuration = 6.0;
static bool useUnifiedInputConfig = true;
@@ -78,6 +78,7 @@ static int controllerCustomColorRGB[3] = {0, 0, 255};
static bool compatibilityData = false;
static bool checkCompatibilityOnStartup = false;
static std::string trophyKey;
+static bool isPSNSignedIn = false;
// Gui
static bool load_game_size = true;
@@ -730,6 +731,14 @@ void setShowBackgroundImage(bool show) {
showBackgroundImage = show;
}
+bool getPSNSignedIn() {
+ return isPSNSignedIn;
+}
+
+void setPSNSignedIn(bool sign) {
+ isPSNSignedIn = sign;
+}
+
void load(const std::filesystem::path& path) {
// If the configuration file does not exist, create it and return
std::error_code error;
@@ -754,6 +763,7 @@ void load(const std::filesystem::path& path) {
isNeo = toml::find_or(general, "isPS4Pro", false);
isDevKit = toml::find_or(general, "isDevKit", false);
+ isPSNSignedIn = toml::find_or(general, "isPSNSignedIn", false);
playBGM = toml::find_or(general, "playBGM", false);
isTrophyPopupDisabled = toml::find_or(general, "isTrophyPopupDisabled", false);
trophyNotificationDuration =
@@ -953,6 +963,7 @@ void save(const std::filesystem::path& path) {
data["General"]["isPS4Pro"] = isNeo;
data["General"]["isDevKit"] = isDevKit;
+ data["General"]["isPSNSignedIn"] = isPSNSignedIn;
data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled;
data["General"]["trophyNotificationDuration"] = trophyNotificationDuration;
data["General"]["playBGM"] = playBGM;
@@ -1098,6 +1109,7 @@ void setDefaultValues() {
isHDRAllowed = false;
isNeo = false;
isDevKit = false;
+ isPSNSignedIn = false;
isFullscreen = false;
isTrophyPopupDisabled = false;
playBGM = false;
diff --git a/src/common/config.h b/src/common/config.h
index aba23621c..404854ae2 100644
--- a/src/common/config.h
+++ b/src/common/config.h
@@ -14,7 +14,7 @@ struct GameInstallDir {
bool enabled;
};
-enum HideCursorState : s16 { Never, Idle, Always };
+enum HideCursorState : int { Never, Idle, Always };
void load(const std::filesystem::path& path);
void save(const std::filesystem::path& path);
@@ -39,6 +39,7 @@ bool getCompatibilityEnabled();
bool getCheckCompatibilityOnStartup();
int getBackgroundImageOpacity();
bool getShowBackgroundImage();
+bool getPSNSignedIn();
std::string getLogFilter();
std::string getLogType();
@@ -111,6 +112,7 @@ void setCompatibilityEnabled(bool use);
void setCheckCompatibilityOnStartup(bool use);
void setBackgroundImageOpacity(int opacity);
void setShowBackgroundImage(bool show);
+void setPSNSignedIn(bool sign);
void setCursorState(s16 cursorState);
void setCursorHideTimeout(int newcursorHideTimeout);
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index 231cbf849..05935fbdc 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -140,6 +140,8 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Lib, SigninDialog) \
SUB(Lib, Camera) \
SUB(Lib, CompanionHttpd) \
+ SUB(Lib, CompanionUtil) \
+ SUB(Lib, Voice) \
CLS(Frontend) \
CLS(Render) \
SUB(Render, Vulkan) \
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index e4eae59af..1da84b219 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -98,6 +98,7 @@ enum class Class : u8 {
Lib_Fiber, ///< The LibSceFiber implementation.
Lib_Vdec2, ///< The LibSceVideodec2 implementation.
Lib_Videodec, ///< The LibSceVideodec implementation.
+ Lib_Voice, ///< The LibSceVoice implementation.
Lib_RazorCpu, ///< The LibRazorCpu implementation.
Lib_Mouse, ///< The LibSceMouse implementation
Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation
@@ -107,6 +108,7 @@ enum class Class : u8 {
Lib_SigninDialog, ///< The LibSigninDialog implementation.
Lib_Camera, ///< The LibCamera implementation.
Lib_CompanionHttpd, ///< The LibCompanionHttpd implementation.
+ Lib_CompanionUtil, ///< The LibCompanionUtil implementation.
Frontend, ///< Emulator UI
Render, ///< Video Core
Render_Vulkan, ///< Vulkan backend
diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in
index 71c4c2d0a..0b113eb31 100644
--- a/src/common/scm_rev.cpp.in
+++ b/src/common/scm_rev.cpp.in
@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include
+
#include "common/scm_rev.h"
namespace Common {
@@ -15,5 +17,26 @@ constexpr char g_scm_remote_name[] = "@GIT_REMOTE_NAME@";
constexpr char g_scm_remote_url[] = "@GIT_REMOTE_URL@";
constexpr char g_scm_date[] = "@BUILD_DATE@";
+const std::string GetRemoteNameFromLink() {
+ std::string remote_url(Common::g_scm_remote_url);
+ std::string remote_host;
+ try {
+ if (remote_url.starts_with("http")) {
+ if (*remote_url.rbegin() == '/') {
+ remote_url.pop_back();
+ }
+ remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
+ } else if (remote_url.starts_with("git@")) {
+ auto after_comma_pos = remote_url.find(':') + 1, slash_pos = remote_url.find('/');
+ remote_host = remote_url.substr(after_comma_pos, slash_pos - after_comma_pos);
+ } else {
+ remote_host = "unknown";
+ }
+ } catch (...) {
+ remote_host = "unknown";
+ }
+ return remote_host;
+}
+
} // namespace
diff --git a/src/common/scm_rev.h b/src/common/scm_rev.h
index 36b844e94..2f6d770bb 100644
--- a/src/common/scm_rev.h
+++ b/src/common/scm_rev.h
@@ -3,6 +3,8 @@
#pragma once
+#include
+
namespace Common {
extern const char g_version[];
@@ -15,4 +17,6 @@ extern const char g_scm_remote_name[];
extern const char g_scm_remote_url[];
extern const char g_scm_date[];
+const std::string GetRemoteNameFromLink();
+
} // namespace Common
diff --git a/src/core/cpu_patches.cpp b/src/core/cpu_patches.cpp
index 8937ef04b..8512858e9 100644
--- a/src/core/cpu_patches.cpp
+++ b/src/core/cpu_patches.cpp
@@ -88,7 +88,8 @@ static bool FilterTcbAccess(const ZydisDecodedOperand* operands) {
dst_op.reg.value <= ZYDIS_REGISTER_R15;
}
-static void GenerateTcbAccess(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
+static void GenerateTcbAccess(void* /* address */, const ZydisDecodedOperand* operands,
+ Xbyak::CodeGenerator& c) {
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
#if defined(_WIN32)
@@ -126,7 +127,8 @@ static bool FilterNoSSE4a(const ZydisDecodedOperand*) {
return !cpu.has(Cpu::tSSE4a);
}
-static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
+static void GenerateEXTRQ(void* /* address */, const ZydisDecodedOperand* operands,
+ Xbyak::CodeGenerator& c) {
bool immediateForm = operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE;
@@ -245,7 +247,8 @@ static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenera
}
}
-static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
+static void GenerateINSERTQ(void* /* address */, const ZydisDecodedOperand* operands,
+ Xbyak::CodeGenerator& c) {
bool immediateForm = operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
operands[3].type == ZYDIS_OPERAND_TYPE_IMMEDIATE;
@@ -383,8 +386,44 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene
}
}
+static void ReplaceMOVNT(void* address, u8 rep_prefix) {
+ // Find the opcode byte
+ // There can be any amount of prefixes but the instruction can't be more than 15 bytes
+ // And we know for sure this is a MOVNTSS/MOVNTSD
+ bool found = false;
+ bool rep_prefix_found = false;
+ int index = 0;
+ u8* ptr = reinterpret_cast(address);
+ for (int i = 0; i < 15; i++) {
+ if (ptr[i] == rep_prefix) {
+ rep_prefix_found = true;
+ } else if (ptr[i] == 0x2B) {
+ index = i;
+ found = true;
+ break;
+ }
+ }
+
+ // Some sanity checks
+ ASSERT(found);
+ ASSERT(index >= 2);
+ ASSERT(ptr[index - 1] == 0x0F);
+ ASSERT(rep_prefix_found);
+
+ // This turns the MOVNTSS/MOVNTSD to a MOVSS/MOVSD m, xmm
+ ptr[index] = 0x11;
+}
+
+static void ReplaceMOVNTSS(void* address, const ZydisDecodedOperand*, Xbyak::CodeGenerator&) {
+ ReplaceMOVNT(address, 0xF3);
+}
+
+static void ReplaceMOVNTSD(void* address, const ZydisDecodedOperand*, Xbyak::CodeGenerator&) {
+ ReplaceMOVNT(address, 0xF2);
+}
+
using PatchFilter = bool (*)(const ZydisDecodedOperand*);
-using InstructionGenerator = void (*)(const ZydisDecodedOperand*, Xbyak::CodeGenerator&);
+using InstructionGenerator = void (*)(void*, const ZydisDecodedOperand*, Xbyak::CodeGenerator&);
struct PatchInfo {
/// Filter for more granular patch conditions past just the instruction mnemonic.
PatchFilter filter;
@@ -400,6 +439,8 @@ static const std::unordered_map Patches = {
// SSE4a
{ZYDIS_MNEMONIC_EXTRQ, {FilterNoSSE4a, GenerateEXTRQ, true}},
{ZYDIS_MNEMONIC_INSERTQ, {FilterNoSSE4a, GenerateINSERTQ, true}},
+ {ZYDIS_MNEMONIC_MOVNTSS, {FilterNoSSE4a, ReplaceMOVNTSS, false}},
+ {ZYDIS_MNEMONIC_MOVNTSD, {FilterNoSSE4a, ReplaceMOVNTSD, false}},
#if defined(_WIN32)
// Windows needs a trampoline.
@@ -477,7 +518,7 @@ static std::pair TryPatch(u8* code, PatchModule* module) {
auto& trampoline_gen = module->trampoline_gen;
const auto trampoline_ptr = trampoline_gen.getCurr();
- patch_info.generator(operands, trampoline_gen);
+ patch_info.generator(code, operands, trampoline_gen);
// Return to the following instruction at the end of the trampoline.
trampoline_gen.jmp(code + instruction.length);
@@ -485,7 +526,7 @@ static std::pair TryPatch(u8* code, PatchModule* module) {
// Replace instruction with near jump to the trampoline.
patch_gen.jmp(trampoline_ptr, Xbyak::CodeGenerator::LabelType::T_NEAR);
} else {
- patch_info.generator(operands, patch_gen);
+ patch_info.generator(code, operands, patch_gen);
}
const auto patch_size = patch_gen.getCurr() - code;
diff --git a/src/core/libraries/companion/companion_error.h b/src/core/libraries/companion/companion_error.h
index 2d1a3833c..0459c33f8 100644
--- a/src/core/libraries/companion/companion_error.h
+++ b/src/core/libraries/companion/companion_error.h
@@ -3,6 +3,8 @@
#pragma once
+#include "common/types.h"
+
// companion_httpd error codes
constexpr int ORBIS_COMPANION_HTTPD_ERROR_UNKNOWN = 0x80E40001;
constexpr int ORBIS_COMPANION_HTTPD_ERROR_FATAL = 0x80E40002;
@@ -18,3 +20,8 @@ constexpr int ORBIS_COMPANION_HTTPD_ERROR_NOT_STARTED = 0x80E4000B;
constexpr int ORBIS_COMPANION_HTTPD_ERROR_ALREADY_REGISTERED = 0x80E4000;
constexpr int ORBIS_COMPANION_HTTPD_ERROR_NOT_CONNECTED = 0x80E4000D;
constexpr int ORBIS_COMPANION_HTTPD_ERROR_USER_NOT_FOUND = 0x80E4000E;
+
+// companion_util error codes
+constexpr u32 ORBIS_COMPANION_UTIL_INVALID_ARGUMENT = 0x80AD0004;
+constexpr u32 ORBIS_COMPANION_UTIL_INVALID_POINTER = 0x80AD0006;
+constexpr u32 ORBIS_COMPANION_UTIL_NO_EVENT = 0x80AD0008;
\ No newline at end of file
diff --git a/src/core/libraries/companion/companion_util.cpp b/src/core/libraries/companion/companion_util.cpp
new file mode 100644
index 000000000..c144ebdcc
--- /dev/null
+++ b/src/core/libraries/companion/companion_util.cpp
@@ -0,0 +1,72 @@
+// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/logging/log.h"
+#include "companion_error.h"
+#include "core/libraries/companion/companion_util.h"
+#include "core/libraries/error_codes.h"
+#include "core/libraries/libs.h"
+
+namespace Libraries::CompanionUtil {
+
+u32 PS4_SYSV_ABI getEvent(sceCompanionUtilContext* ctx, sceCompanionUtilEvent* outEvent,
+ s32 param_3) {
+ if (outEvent == 0) {
+ return ORBIS_COMPANION_UTIL_INVALID_ARGUMENT;
+ }
+
+ if (ctx == nullptr) {
+ return ORBIS_COMPANION_UTIL_INVALID_POINTER;
+ }
+
+ uint8_t* base = ctx->blob;
+ int flag = *reinterpret_cast(base + 0x178);
+ if (flag == 0) {
+ return ORBIS_COMPANION_UTIL_NO_EVENT;
+ }
+
+ return ORBIS_COMPANION_UTIL_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionUtilGetEvent(sceCompanionUtilEvent* outEvent) {
+ sceCompanionUtilContext* ctx = nullptr;
+ u32 ret = getEvent(ctx, outEvent, 1);
+
+ LOG_DEBUG(Lib_CompanionUtil, "(STUBBED) called ret: {}", ret);
+ return ret;
+}
+
+s32 PS4_SYSV_ABI sceCompanionUtilGetRemoteOskEvent() {
+ LOG_ERROR(Lib_CompanionUtil, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionUtilInitialize() {
+ LOG_ERROR(Lib_CompanionUtil, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionUtilOptParamInitialize() {
+ LOG_ERROR(Lib_CompanionUtil, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionUtilTerminate() {
+ LOG_ERROR(Lib_CompanionUtil, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+void RegisterlibSceCompanionUtil(Core::Loader::SymbolsResolver* sym) {
+ LIB_FUNCTION("cE5Msy11WhU", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1,
+ sceCompanionUtilGetEvent);
+ LIB_FUNCTION("MaVrz79mT5o", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1,
+ sceCompanionUtilGetRemoteOskEvent);
+ LIB_FUNCTION("xb1xlIhf0QY", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1,
+ sceCompanionUtilInitialize);
+ LIB_FUNCTION("IPN-FRSrafk", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1,
+ sceCompanionUtilOptParamInitialize);
+ LIB_FUNCTION("H1fYQd5lFAI", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1,
+ sceCompanionUtilTerminate);
+};
+
+} // namespace Libraries::CompanionUtil
\ No newline at end of file
diff --git a/src/core/libraries/companion/companion_util.h b/src/core/libraries/companion/companion_util.h
new file mode 100644
index 000000000..921b5b21e
--- /dev/null
+++ b/src/core/libraries/companion/companion_util.h
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/types.h"
+
+namespace Core::Loader {
+class SymbolsResolver;
+}
+
+namespace Libraries::CompanionUtil {
+
+constexpr u32 ORBIS_COMPANION_UTIL_OK = 0;
+
+struct sceCompanionUtilEvent {
+ std::uint8_t blob[0x104]{}; /// 0x104 bytes of data, dont know what it is exactly
+};
+
+struct sceCompanionUtilContext {
+ std::uint8_t blob[0x27B]{}; /// 0x27B bytes of data, dont know what it is exactly
+};
+
+u32 PS4_SYSV_ABI getEvent(sceCompanionUtilContext* ctx, sceCompanionUtilEvent* outEvent,
+ s32 param_3);
+s32 PS4_SYSV_ABI sceCompanionUtilGetEvent(sceCompanionUtilEvent* outEvent);
+s32 PS4_SYSV_ABI sceCompanionUtilGetRemoteOskEvent();
+s32 PS4_SYSV_ABI sceCompanionUtilInitialize();
+s32 PS4_SYSV_ABI sceCompanionUtilOptParamInitialize();
+s32 PS4_SYSV_ABI sceCompanionUtilTerminate();
+
+void RegisterlibSceCompanionUtil(Core::Loader::SymbolsResolver* sym);
+} // namespace Libraries::CompanionUtil
\ No newline at end of file
diff --git a/src/core/libraries/kernel/equeue.cpp b/src/core/libraries/kernel/equeue.cpp
index c33a0b09e..911ae4cd5 100644
--- a/src/core/libraries/kernel/equeue.cpp
+++ b/src/core/libraries/kernel/equeue.cpp
@@ -98,6 +98,11 @@ bool EqueueInternal::RemoveEvent(u64 id, s16 filter) {
}
int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) {
+ if (HasSmallTimer()) {
+ // If a small timer is set, just wait for it to expire.
+ return WaitForSmallTimer(ev, num, micros);
+ }
+
int count = 0;
const auto predicate = [&] {
@@ -140,6 +145,8 @@ bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) {
if (event.event.ident == ident && event.event.filter == filter) {
if (filter == SceKernelEvent::Filter::VideoOut) {
event.TriggerDisplay(trigger_data);
+ } else if (filter == SceKernelEvent::Filter::User) {
+ event.TriggerUser(trigger_data);
} else {
event.Trigger(trigger_data);
}
@@ -267,27 +274,15 @@ int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int
return ORBIS_KERNEL_ERROR_EINVAL;
}
- // When the timeout is nullptr, we wait indefinitely
- if (eq->HasSmallTimer()) {
- if (timo == nullptr) {
- *out = eq->WaitForSmallTimer(ev, num, 0);
- } else if (*timo == 0) {
- // Only events that have already arrived at the time of this function call can be
- // received
- *out = eq->GetTriggeredEvents(ev, num);
- } else {
- *out = eq->WaitForSmallTimer(ev, num, *timo);
- }
+ if (timo == nullptr) {
+ // When the timeout is nullptr, we wait indefinitely
+ *out = eq->WaitForEvents(ev, num, 0);
+ } else if (*timo == 0) {
+ // Only events that have already arrived at the time of this function call can be received
+ *out = eq->GetTriggeredEvents(ev, num);
} else {
- if (timo == nullptr) {
- *out = eq->WaitForEvents(ev, num, 0);
- } else if (*timo == 0) {
- // Only events that have already arrived at the time of this function call can be
- // received
- *out = eq->GetTriggeredEvents(ev, num);
- } else {
- *out = eq->WaitForEvents(ev, num, *timo);
- }
+ // Wait for up to the specified timeout value
+ *out = eq->WaitForEvents(ev, num, *timo);
}
if (*out == 0) {
diff --git a/src/core/libraries/kernel/equeue.h b/src/core/libraries/kernel/equeue.h
index a0367c66a..e6e3c0c53 100644
--- a/src/core/libraries/kernel/equeue.h
+++ b/src/core/libraries/kernel/equeue.h
@@ -98,6 +98,12 @@ struct EqueueEvent {
event.data = reinterpret_cast(data);
}
+ void TriggerUser(void* data) {
+ is_triggered = true;
+ event.fflags++;
+ event.udata = data;
+ }
+
void TriggerDisplay(void* data) {
is_triggered = true;
if (data != nullptr) {
diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp
index 180850217..930640d0e 100644
--- a/src/core/libraries/kernel/kernel.cpp
+++ b/src/core/libraries/kernel/kernel.cpp
@@ -273,6 +273,10 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
Libraries::Net::sceNetInetNtop); // TODO fix it to sys_ ...
LIB_FUNCTION("4n51s0zEf0c", "libScePosix", 1, "libkernel", 1, 1,
Libraries::Net::sceNetInetPton); // TODO fix it to sys_ ...
+ LIB_FUNCTION("XVL8So3QJUk", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_connect);
+ LIB_FUNCTION("3e+4Iv7IJ8U", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_accept);
+ LIB_FUNCTION("aNeavPDNKzA", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_sendmsg);
+ LIB_FUNCTION("pxnCmagrtao", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_listen);
}
} // namespace Libraries::Kernel
diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp
index cb41a664a..f02ddafdc 100644
--- a/src/core/libraries/kernel/memory.cpp
+++ b/src/core/libraries/kernel/memory.cpp
@@ -8,7 +8,6 @@
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "common/singleton.h"
-#include "core/file_sys/fs.h"
#include "core/libraries/kernel/kernel.h"
#include "core/libraries/kernel/memory.h"
#include "core/libraries/kernel/orbis_error.h"
@@ -152,7 +151,8 @@ s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u
const VAddr in_addr = reinterpret_cast(*addr);
const auto map_flags = static_cast(flags);
- s32 result = memory->Reserve(addr, in_addr, len, map_flags, alignment);
+ s32 result = memory->MapMemory(addr, in_addr, len, Core::MemoryProt::NoAccess, map_flags,
+ Core::VMAType::Reserved, "anon", false, -1, alignment);
if (result == 0) {
LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr));
}
@@ -222,9 +222,10 @@ s32 PS4_SYSV_ABI sceKernelMapDirectMemory2(void** addr, u64 len, s32 type, s32 p
return ret;
}
-s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t len, int prot,
- int flags, const char* name) {
-
+s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags,
+ const char* name) {
+ LOG_INFO(Kernel_Vmm, "in_addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}, name = '{}'",
+ fmt::ptr(*addr_in_out), len, prot, flags, name);
if (len == 0 || !Common::Is16KBAligned(len)) {
LOG_ERROR(Kernel_Vmm, "len is 0 or not 16kb multiple");
return ORBIS_KERNEL_ERROR_EINVAL;
@@ -243,18 +244,14 @@ s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t
const VAddr in_addr = reinterpret_cast(*addr_in_out);
const auto mem_prot = static_cast(prot);
const auto map_flags = static_cast(flags);
- SCOPE_EXIT {
- LOG_INFO(Kernel_Vmm,
- "in_addr = {:#x}, out_addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}",
- in_addr, fmt::ptr(*addr_in_out), len, prot, flags);
- };
auto* memory = Core::Memory::Instance();
- return memory->MapMemory(addr_in_out, in_addr, len, mem_prot, map_flags,
- Core::VMAType::Flexible, name);
+ const auto ret = memory->MapMemory(addr_in_out, in_addr, len, mem_prot, map_flags,
+ Core::VMAType::Flexible, name);
+ LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr_in_out));
+ return ret;
}
-s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int prot,
- int flags) {
+s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags) {
return sceKernelMapNamedFlexibleMemory(addr_in_out, len, prot, flags, "anon");
}
@@ -263,13 +260,26 @@ int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void**
return memory->QueryProtection(std::bit_cast(addr), start, end, prot);
}
-int PS4_SYSV_ABI sceKernelMProtect(const void* addr, size_t size, int prot) {
+s32 PS4_SYSV_ABI sceKernelMprotect(const void* addr, u64 size, s32 prot) {
+ LOG_INFO(Kernel_Vmm, "called addr = {}, size = {:#x}, prot = {:#x}", fmt::ptr(addr), size,
+ prot);
Core::MemoryManager* memory_manager = Core::Memory::Instance();
Core::MemoryProt protection_flags = static_cast(prot);
return memory_manager->Protect(std::bit_cast(addr), size, protection_flags);
}
-int PS4_SYSV_ABI sceKernelMTypeProtect(const void* addr, size_t size, int mtype, int prot) {
+s32 PS4_SYSV_ABI posix_mprotect(const void* addr, u64 size, s32 prot) {
+ s32 result = sceKernelMprotect(addr, size, prot);
+ if (result < 0) {
+ ErrSceToPosix(result);
+ return -1;
+ }
+ return result;
+}
+
+s32 PS4_SYSV_ABI sceKernelMtypeprotect(const void* addr, u64 size, s32 mtype, s32 prot) {
+ LOG_INFO(Kernel_Vmm, "called addr = {}, size = {:#x}, prot = {:#x}", fmt::ptr(addr), size,
+ prot);
Core::MemoryManager* memory_manager = Core::Memory::Instance();
Core::MemoryProt protection_flags = static_cast(prot);
return memory_manager->Protect(std::bit_cast(addr), size, protection_flags);
@@ -344,7 +354,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
break;
}
case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_PROTECT: {
- result = sceKernelMProtect(entries[i].start, entries[i].length, entries[i].protection);
+ result = sceKernelMprotect(entries[i].start, entries[i].length, entries[i].protection);
LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i,
entries[i].operation, entries[i].length, result);
break;
@@ -359,7 +369,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
break;
}
case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_TYPE_PROTECT: {
- result = sceKernelMTypeProtect(entries[i].start, entries[i].length, entries[i].type,
+ result = sceKernelMtypeprotect(entries[i].start, entries[i].length, entries[i].type,
entries[i].protection);
LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i,
entries[i].operation, entries[i].length, result);
@@ -380,7 +390,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
return result;
}
-s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, const char* name) {
+s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, u64 len, const char* name) {
if (name == nullptr) {
LOG_ERROR(Kernel_Vmm, "name is invalid!");
return ORBIS_KERNEL_ERROR_EFAULT;
@@ -396,8 +406,8 @@ s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, cons
return ORBIS_OK;
}
-s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_t len,
- size_t alignment, u64* physAddrOut) {
+s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, u64 len, u64 alignment,
+ u64* physAddrOut) {
if (searchStart < 0 || searchEnd <= searchStart) {
LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!");
return ORBIS_KERNEL_ERROR_EINVAL;
@@ -439,10 +449,10 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_
return ORBIS_OK;
}
-s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t alignment, int flags,
- void** addrOut) {
- LOG_INFO(Kernel_Vmm, "addrIn = {}, len = {:#x}, alignment = {:#x}, flags = {:#x}",
- fmt::ptr(addrIn), len, alignment, flags);
+s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addr_in, u64 len, u64 alignment, s32 flags,
+ void** addr_out) {
+ LOG_INFO(Kernel_Vmm, "addr_in = {}, len = {:#x}, alignment = {:#x}, flags = {:#x}",
+ fmt::ptr(addr_in), len, alignment, flags);
if (len == 0 || !Common::Is2MBAligned(len)) {
LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 2MB aligned!");
@@ -456,14 +466,16 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t ali
}
auto* memory = Core::Memory::Instance();
- const VAddr in_addr = reinterpret_cast(addrIn);
+ const VAddr in_addr = reinterpret_cast(addr_in);
const auto map_flags = static_cast(flags);
- memory->PoolReserve(addrOut, in_addr, len, map_flags, alignment);
+ u64 map_alignment = alignment == 0 ? 2_MB : alignment;
- return ORBIS_OK;
+ return memory->MapMemory(addr_out, std::bit_cast(addr_in), len,
+ Core::MemoryProt::NoAccess, map_flags, Core::VMAType::PoolReserved,
+ "anon", false, -1, map_alignment);
}
-s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int prot, int flags) {
+s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, u64 len, s32 type, s32 prot, s32 flags) {
if (addr == nullptr) {
LOG_ERROR(Kernel_Vmm, "Address is invalid!");
return ORBIS_KERNEL_ERROR_EINVAL;
@@ -482,7 +494,7 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int
return memory->PoolCommit(in_addr, len, mem_prot);
}
-s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags) {
+s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, u64 len, s32 flags) {
if (addr == nullptr) {
LOG_ERROR(Kernel_Vmm, "Address is invalid!");
return ORBIS_KERNEL_ERROR_EINVAL;
@@ -523,12 +535,12 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry*
break;
}
case OrbisKernelMemoryPoolOpcode::Protect: {
- result = sceKernelMProtect(entry.protect_params.addr, entry.protect_params.len,
+ result = sceKernelMprotect(entry.protect_params.addr, entry.protect_params.len,
entry.protect_params.prot);
break;
}
case OrbisKernelMemoryPoolOpcode::TypeProtect: {
- result = sceKernelMTypeProtect(
+ result = sceKernelMtypeprotect(
entry.type_protect_params.addr, entry.type_protect_params.len,
entry.type_protect_params.type, entry.type_protect_params.prot);
break;
@@ -553,30 +565,48 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry*
return result;
}
-int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, size_t offset,
- void** res) {
- LOG_INFO(Kernel_Vmm, "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, offset = {}",
- fmt::ptr(addr), len, prot, flags, fd, offset);
- auto* h = Common::Singleton::Instance();
+void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, s32 prot, s32 flags, s32 fd, s64 phys_addr) {
+ LOG_INFO(Kernel_Vmm,
+ "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, phys_addr = {}",
+ fmt::ptr(addr), len, prot, flags, fd, phys_addr);
+
+ void* addr_out;
auto* memory = Core::Memory::Instance();
const auto mem_prot = static_cast(prot);
const auto mem_flags = static_cast(flags);
+
+ s32 result = ORBIS_OK;
if (fd == -1) {
- return memory->MapMemory(res, std::bit_cast(addr), len, mem_prot, mem_flags,
- Core::VMAType::Flexible);
+ result = memory->MapMemory(&addr_out, std::bit_cast(addr), len, mem_prot, mem_flags,
+ Core::VMAType::Flexible);
} else {
- const uintptr_t handle = h->GetFile(fd)->f.GetFileMapping();
- return memory->MapFile(res, std::bit_cast(addr), len, mem_prot, mem_flags, handle,
- offset);
+ result = memory->MapFile(&addr_out, std::bit_cast(addr), len, mem_prot, mem_flags,
+ fd, phys_addr);
}
+
+ if (result != ORBIS_OK) {
+ // If the memory mappings fail, mmap sets errno to the appropriate error code,
+ // then returns (void*)-1;
+ ErrSceToPosix(result);
+ return reinterpret_cast(-1);
+ }
+
+ return addr_out;
}
-void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, int prot, int flags, int fd, u64 offset) {
- void* ptr;
- LOG_INFO(Kernel_Vmm, "posix mmap redirect to sceKernelMmap");
- int result = sceKernelMmap(addr, len, prot, flags, fd, offset, &ptr);
- ASSERT(result == 0);
- return ptr;
+s32 PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, s32 prot, s32 flags, s32 fd, s64 phys_addr,
+ void** res) {
+ void* addr_out = posix_mmap(addr, len, prot, flags, fd, phys_addr);
+
+ if (addr_out == reinterpret_cast(-1)) {
+ // posix_mmap failed, calculate and return the appropriate kernel error code using errno.
+ LOG_ERROR(Kernel_Fs, "error = {}", *__Error());
+ return ErrnoToSceKernelError(*__Error());
+ }
+
+ // Set the outputted address
+ *res = addr_out;
+ return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceKernelConfiguredFlexibleMemorySize(u64* sizeOut) {
@@ -630,6 +660,9 @@ int PS4_SYSV_ABI sceKernelSetPrtAperture(int id, VAddr address, size_t size) {
"PRT aperture id = {}, address = {:#x}, size = {:#x} is set but not used", id,
address, size);
+ auto* memory = Core::Memory::Instance();
+ memory->SetPrtArea(id, address, size);
+
PrtApertures[id] = {address, size};
return ORBIS_OK;
}
@@ -678,8 +711,9 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("n1-v6FgU7MQ", "libkernel", 1, "libkernel", 1, 1,
sceKernelConfiguredFlexibleMemorySize);
- LIB_FUNCTION("9bfdLIyuwCY", "libkernel", 1, "libkernel", 1, 1, sceKernelMTypeProtect);
- LIB_FUNCTION("vSMAm3cxYTY", "libkernel", 1, "libkernel", 1, 1, sceKernelMProtect);
+ LIB_FUNCTION("vSMAm3cxYTY", "libkernel", 1, "libkernel", 1, 1, sceKernelMprotect);
+ LIB_FUNCTION("YQOfxL4QfeU", "libScePosix", 1, "libkernel", 1, 1, posix_mprotect);
+ LIB_FUNCTION("9bfdLIyuwCY", "libkernel", 1, "libkernel", 1, 1, sceKernelMtypeprotect);
// Memory pool
LIB_FUNCTION("qCSfqDILlns", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolExpand);
diff --git a/src/core/libraries/kernel/memory.h b/src/core/libraries/kernel/memory.h
index 92e158a00..ea42e7546 100644
--- a/src/core/libraries/kernel/memory.h
+++ b/src/core/libraries/kernel/memory.h
@@ -141,15 +141,14 @@ s32 PS4_SYSV_ABI sceKernelAvailableDirectMemorySize(u64 searchStart, u64 searchE
s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, int flags, OrbisVirtualQueryInfo* info,
size_t infoSize);
s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u64 alignment);
-s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addrInOut, std::size_t len, int prot,
- int flags, const char* name);
-s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int prot,
- int flags);
+s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags,
+ const char* name);
+s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags);
int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** end, u32* prot);
-int PS4_SYSV_ABI sceKernelMProtect(const void* addr, size_t size, int prot);
+s32 PS4_SYSV_ABI sceKernelMprotect(const void* addr, u64 size, s32 prot);
-int PS4_SYSV_ABI sceKernelMTypeProtect(const void* addr, size_t size, int mtype, int prot);
+s32 PS4_SYSV_ABI sceKernelMtypeprotect(const void* addr, u64 size, s32 mtype, s32 prot);
int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInfo* query_info,
size_t infoSize);
@@ -165,14 +164,14 @@ s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEnt
s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEntries,
int* numEntriesOut, int flags);
-s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, const char* name);
+s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, u64 len, const char* name);
-s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_t len,
- size_t alignment, u64* physAddrOut);
-s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t alignment, int flags,
- void** addrOut);
-s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int prot, int flags);
-s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags);
+s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, u64 len, u64 alignment,
+ u64* physAddrOut);
+s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addr_in, u64 len, u64 alignment, s32 flags,
+ void** addr_out);
+s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, u64 len, s32 type, s32 prot, s32 flags);
+s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, u64 len, s32 flags);
s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry* entries, s32 count,
s32* num_processed, s32 flags);
diff --git a/src/core/libraries/kernel/threads/mutex.cpp b/src/core/libraries/kernel/threads/mutex.cpp
index 956e5ef65..3dbade96a 100644
--- a/src/core/libraries/kernel/threads/mutex.cpp
+++ b/src/core/libraries/kernel/threads/mutex.cpp
@@ -426,6 +426,7 @@ void RegisterMutex(Core::Loader::SymbolsResolver* sym) {
// Posix
LIB_FUNCTION("ttHNfU+qDBU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_init);
LIB_FUNCTION("7H0iTOciTLo", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_lock);
+ LIB_FUNCTION("Io9+nTKXZtA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_timedlock);
LIB_FUNCTION("2Z+PpY6CaJg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock);
LIB_FUNCTION("ltCfaGr2JGE", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_destroy);
LIB_FUNCTION("dQHWEsJtoE4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutexattr_init);
diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp
index 61310bfb5..59b427d22 100644
--- a/src/core/libraries/kernel/threads/pthread.cpp
+++ b/src/core/libraries/kernel/threads/pthread.cpp
@@ -576,8 +576,19 @@ int PS4_SYSV_ABI posix_pthread_getaffinity_np(PthreadT thread, size_t cpusetsize
if (thread == nullptr || cpusetp == nullptr) {
return POSIX_EINVAL;
}
+
+ auto* thread_state = ThrState::Instance();
+ if (thread == g_curthread) {
+ g_curthread->lock.lock();
+ } else if (auto ret = thread_state->FindThread(thread, /*include dead*/ 0); ret != 0) {
+ return ret;
+ }
+
auto* attr_ptr = &thread->attr;
- return posix_pthread_attr_getaffinity_np(&attr_ptr, cpusetsize, cpusetp);
+ auto ret = posix_pthread_attr_getaffinity_np(&attr_ptr, cpusetsize, cpusetp);
+
+ thread->lock.unlock();
+ return ret;
}
int PS4_SYSV_ABI posix_pthread_setaffinity_np(PthreadT thread, size_t cpusetsize,
@@ -585,11 +596,23 @@ int PS4_SYSV_ABI posix_pthread_setaffinity_np(PthreadT thread, size_t cpusetsize
if (thread == nullptr || cpusetp == nullptr) {
return POSIX_EINVAL;
}
- auto* attr_ptr = &thread->attr;
- if (const auto ret = posix_pthread_attr_setaffinity_np(&attr_ptr, cpusetsize, cpusetp)) {
+
+ auto* thread_state = ThrState::Instance();
+ if (thread == g_curthread) {
+ g_curthread->lock.lock();
+ } else if (auto ret = thread_state->FindThread(thread, /*include dead*/ 0); ret != 0) {
return ret;
}
- return thread->SetAffinity(thread->attr.cpuset);
+
+ auto* attr_ptr = &thread->attr;
+ auto ret = posix_pthread_attr_setaffinity_np(&attr_ptr, cpusetsize, cpusetp);
+
+ if (ret == ORBIS_OK) {
+ ret = thread->SetAffinity(thread->attr.cpuset);
+ }
+
+ thread->lock.unlock();
+ return ret;
}
int PS4_SYSV_ABI scePthreadGetaffinity(PthreadT thread, u64* mask) {
diff --git a/src/core/libraries/kernel/threads/pthread_attr.cpp b/src/core/libraries/kernel/threads/pthread_attr.cpp
index 71f6438a6..e098b00a4 100644
--- a/src/core/libraries/kernel/threads/pthread_attr.cpp
+++ b/src/core/libraries/kernel/threads/pthread_attr.cpp
@@ -306,6 +306,8 @@ void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) {
posix_pthread_attr_getdetachstate);
LIB_FUNCTION("JKyG3SWyA10", "libScePosix", 1, "libkernel", 1, 1,
posix_pthread_attr_setguardsize);
+ LIB_FUNCTION("qlk9pSLsUmM", "libScePosix", 1, "libkernel", 1, 1,
+ posix_pthread_attr_getschedparam);
// Orbis
LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1,
diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp
index 2ab46d3a0..762c1e762 100644
--- a/src/core/libraries/libs.cpp
+++ b/src/core/libraries/libs.cpp
@@ -10,6 +10,7 @@
#include "core/libraries/avplayer/avplayer.h"
#include "core/libraries/camera/camera.h"
#include "core/libraries/companion/companion_httpd.h"
+#include "core/libraries/companion/companion_util.h"
#include "core/libraries/disc_map/disc_map.h"
#include "core/libraries/game_live_streaming/gamelivestreaming.h"
#include "core/libraries/gnmdriver/gnmdriver.h"
@@ -59,6 +60,7 @@
#include "core/libraries/videodec/videodec.h"
#include "core/libraries/videodec/videodec2.h"
#include "core/libraries/videoout/video_out.h"
+#include "core/libraries/voice/voice.h"
#include "core/libraries/web_browser_dialog/webbrowserdialog.h"
#include "core/libraries/zlib/zlib_sce.h"
#include "fiber/fiber.h"
@@ -126,6 +128,8 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
Libraries::SigninDialog::RegisterlibSceSigninDialog(sym);
Libraries::Camera::RegisterlibSceCamera(sym);
Libraries::CompanionHttpd::RegisterlibSceCompanionHttpd(sym);
+ Libraries::CompanionUtil::RegisterlibSceCompanionUtil(sym);
+ Libraries::Voice::RegisterlibSceVoice(sym);
}
} // namespace Libraries
diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp
index 0ef4a84f5..9607f0d78 100644
--- a/src/core/libraries/network/net.cpp
+++ b/src/core/libraries/network/net.cpp
@@ -955,16 +955,148 @@ u16 PS4_SYSV_ABI sceNetHtons(u16 host16) {
return htons(host16);
}
-const char* PS4_SYSV_ABI sceNetInetNtop(int af, const void* src, char* dst, u32 size) {
#ifdef WIN32
- const char* res = InetNtopA(af, src, dst, size);
-#else
- const char* res = inet_ntop(af, src, dst, size);
-#endif
- if (res == nullptr) {
- UNREACHABLE();
+// there isn't a strlcpy function in windows so implement one
+u64 strlcpy(char* dst, const char* src, u64 size) {
+ u64 src_len = strlen(src);
+
+ if (size > 0) {
+ u64 copy_len = (src_len >= size) ? (size - 1) : src_len;
+ memcpy(dst, src, copy_len);
+ dst[copy_len] = '\0';
}
- return dst;
+
+ return src_len;
+}
+
+#endif
+
+const char* freebsd_inet_ntop4(const char* src, char* dst, u64 size) {
+ static const char fmt[] = "%u.%u.%u.%u";
+ char tmp[sizeof "255.255.255.255"];
+ int l;
+
+ l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]);
+ if (l <= 0 || (socklen_t)l >= size) {
+ return nullptr;
+ }
+ strlcpy(dst, tmp, size);
+ return (dst);
+}
+
+const char* freebsd_inet_ntop6(const char* src, char* dst, u64 size) {
+ /*
+ * Note that int32_t and int16_t need only be "at least" large enough
+ * to contain a value of the specified size. On some systems, like
+ * Crays, there is no such thing as an integer variable with 16 bits.
+ * Keep this in mind if you think this function should have been coded
+ * to use pointer overlays. All the world's not a VAX.
+ */
+ char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
+ struct {
+ int base, len;
+ } best, cur;
+#define NS_IN6ADDRSZ 16
+#define NS_INT16SZ 2
+ u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
+ int i;
+
+ /*
+ * Preprocess:
+ * Copy the input (bytewise) array into a wordwise array.
+ * Find the longest run of 0x00's in src[] for :: shorthanding.
+ */
+ memset(words, '\0', sizeof words);
+ for (i = 0; i < NS_IN6ADDRSZ; i++)
+ words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
+ best.base = -1;
+ best.len = 0;
+ cur.base = -1;
+ cur.len = 0;
+ for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+ if (words[i] == 0) {
+ if (cur.base == -1)
+ cur.base = i, cur.len = 1;
+ else
+ cur.len++;
+ } else {
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len)
+ best = cur;
+ cur.base = -1;
+ }
+ }
+ }
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len)
+ best = cur;
+ }
+ if (best.base != -1 && best.len < 2)
+ best.base = -1;
+
+ /*
+ * Format the result.
+ */
+ tp = tmp;
+ for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+ /* Are we inside the best run of 0x00's? */
+ if (best.base != -1 && i >= best.base && i < (best.base + best.len)) {
+ if (i == best.base)
+ *tp++ = ':';
+ continue;
+ }
+ /* Are we following an initial run of 0x00s or any real hex? */
+ if (i != 0)
+ *tp++ = ':';
+ /* Is this address an encapsulated IPv4? */
+ if (i == 6 && best.base == 0 &&
+ (best.len == 6 || (best.len == 7 && words[7] != 0x0001) ||
+ (best.len == 5 && words[5] == 0xffff))) {
+ if (!freebsd_inet_ntop4(src + 12, tp, sizeof tmp - (tp - tmp)))
+ return nullptr;
+ tp += strlen(tp);
+ break;
+ }
+ tp += sprintf(tp, "%x", words[i]);
+ }
+ /* Was it a trailing run of 0x00's? */
+ if (best.base != -1 && (best.base + best.len) == (NS_IN6ADDRSZ / NS_INT16SZ))
+ *tp++ = ':';
+ *tp++ = '\0';
+
+ /*
+ * Check for overflow, copy, and we're done.
+ */
+ if ((u64)(tp - tmp) > size) {
+ return nullptr;
+ }
+ strcpy(dst, tmp);
+ return (dst);
+}
+const char* PS4_SYSV_ABI sceNetInetNtop(int af, const void* src, char* dst, u32 size) {
+ if (!(src && dst)) {
+ *sceNetErrnoLoc() = ORBIS_NET_ENOSPC;
+ LOG_ERROR(Lib_Net, "returned ORBIS_NET_ENOSPC");
+ return nullptr;
+ }
+ const char* returnvalue = nullptr;
+ switch (af) {
+ case ORBIS_NET_AF_INET:
+ returnvalue = freebsd_inet_ntop4((const char*)src, dst, size);
+ break;
+ case ORBIS_NET_AF_INET6:
+ returnvalue = freebsd_inet_ntop6((const char*)src, dst, size);
+ break;
+ default:
+ *sceNetErrnoLoc() = ORBIS_NET_EAFNOSUPPORT;
+ LOG_ERROR(Lib_Net, "returned ORBIS_NET_EAFNOSUPPORT");
+ return nullptr;
+ }
+ if (returnvalue == nullptr) {
+ *sceNetErrnoLoc() = ORBIS_NET_ENOSPC;
+ LOG_ERROR(Lib_Net, "returned ORBIS_NET_ENOSPC");
+ }
+ return returnvalue;
}
int PS4_SYSV_ABI sceNetInetNtopWithScopeId() {
diff --git a/src/core/libraries/network/net.h b/src/core/libraries/network/net.h
index 812ee6bd7..1393ecb1d 100644
--- a/src/core/libraries/network/net.h
+++ b/src/core/libraries/network/net.h
@@ -20,6 +20,10 @@ class SymbolsResolver;
namespace Libraries::Net {
+enum OrbisNetFamily : u32 {
+ ORBIS_NET_AF_INET = 2,
+ ORBIS_NET_AF_INET6 = 28,
+};
enum OrbisNetSocketType : u32 {
ORBIS_NET_SOCK_STREAM = 1,
ORBIS_NET_SOCK_DGRAM = 2,
diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp
index a60dcd86f..bc920b5a9 100644
--- a/src/core/libraries/np_manager/np_manager.cpp
+++ b/src/core/libraries/np_manager/np_manager.cpp
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include "common/config.h"
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
@@ -10,6 +11,8 @@
namespace Libraries::NpManager {
+#define SIGNEDIN_STATUS (Config::getPSNSignedIn() ? ORBIS_OK : ORBIS_NP_ERROR_SIGNED_OUT)
+
int PS4_SYSV_ABI Func_EF4378573542A508() {
LOG_ERROR(Lib_NpManager, "(STUBBED) called");
return ORBIS_OK;
@@ -921,9 +924,16 @@ int PS4_SYSV_ABI sceNpGetAccountCountry() {
return ORBIS_OK;
}
-int PS4_SYSV_ABI sceNpGetAccountCountryA() {
- LOG_ERROR(Lib_NpManager, "(STUBBED) called");
- return ORBIS_OK;
+int PS4_SYSV_ABI sceNpGetAccountCountryA(OrbisUserServiceUserId user_id,
+ OrbisNpCountryCode* country_code) {
+ LOG_INFO(Lib_NpManager, "(STUBBED) called, user_id = {}", user_id);
+ if (country_code == nullptr) {
+ return ORBIS_NP_ERROR_INVALID_ARGUMENT;
+ }
+ ::memset(country_code, 0, sizeof(OrbisNpCountryCode));
+ // TODO: get NP country code from config
+ ::memcpy(country_code->country_code, "us", 2);
+ return SIGNEDIN_STATUS;
}
int PS4_SYSV_ABI sceNpGetAccountDateOfBirth() {
@@ -941,8 +951,8 @@ int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id)
if (online_id == nullptr || account_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
- *account_id = 0;
- return ORBIS_NP_ERROR_SIGNED_OUT;
+ *account_id = 0xFEEDFACE;
+ return SIGNEDIN_STATUS;
}
int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account_id) {
@@ -950,8 +960,8 @@ int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account
if (account_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
- *account_id = 0;
- return ORBIS_NP_ERROR_SIGNED_OUT;
+ *account_id = 0xFEEDFACE;
+ return SIGNEDIN_STATUS;
}
int PS4_SYSV_ABI sceNpGetAccountLanguage() {
@@ -984,7 +994,9 @@ int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id)
if (np_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
- return ORBIS_NP_ERROR_SIGNED_OUT;
+ memset(np_id, 0, sizeof(OrbisNpId));
+ strncpy(np_id->handle.data, Config::getUserName().c_str(), sizeof(np_id->handle.data));
+ return SIGNEDIN_STATUS;
}
int PS4_SYSV_ABI sceNpGetNpReachabilityState() {
@@ -997,7 +1009,9 @@ int PS4_SYSV_ABI sceNpGetOnlineId(OrbisUserServiceUserId user_id, OrbisNpOnlineI
if (online_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
- return ORBIS_NP_ERROR_SIGNED_OUT;
+ memset(online_id, 0, sizeof(OrbisNpOnlineId));
+ strncpy(online_id->data, Config::getUserName().c_str(), sizeof(online_id->data));
+ return SIGNEDIN_STATUS;
}
int PS4_SYSV_ABI sceNpGetParentalControlInfo() {
@@ -1014,8 +1028,8 @@ int PS4_SYSV_ABI sceNpGetState(OrbisUserServiceUserId user_id, OrbisNpState* sta
if (state == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
- *state = OrbisNpState::SignedOut;
- LOG_DEBUG(Lib_NpManager, "Signed out");
+ *state = Config::getPSNSignedIn() ? OrbisNpState::SignedIn : OrbisNpState::SignedOut;
+ LOG_DEBUG(Lib_NpManager, "Signed {}", Config::getPSNSignedIn() ? "in" : "out");
return ORBIS_OK;
}
diff --git a/src/core/libraries/np_manager/np_manager.h b/src/core/libraries/np_manager/np_manager.h
index 02a1a32f6..1078a9f3e 100644
--- a/src/core/libraries/np_manager/np_manager.h
+++ b/src/core/libraries/np_manager/np_manager.h
@@ -32,6 +32,12 @@ struct OrbisNpId {
u8 reserved[8];
};
+struct OrbisNpCountryCode {
+ char country_code[2];
+ char end;
+ char pad;
+};
+
int PS4_SYSV_ABI Func_EF4378573542A508();
int PS4_SYSV_ABI _sceNpIpcCreateMemoryFromKernel();
int PS4_SYSV_ABI _sceNpIpcCreateMemoryFromPool();
@@ -215,7 +221,8 @@ int PS4_SYSV_ABI sceNpCreateRequest();
int PS4_SYSV_ABI sceNpDeleteRequest(int reqId);
int PS4_SYSV_ABI sceNpGetAccountAge();
int PS4_SYSV_ABI sceNpGetAccountCountry();
-int PS4_SYSV_ABI sceNpGetAccountCountryA();
+int PS4_SYSV_ABI sceNpGetAccountCountryA(OrbisUserServiceUserId user_id,
+ OrbisNpCountryCode* country_code);
int PS4_SYSV_ABI sceNpGetAccountDateOfBirth();
int PS4_SYSV_ABI sceNpGetAccountDateOfBirthA();
int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id);
diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp
index 5dfc68e90..42582783b 100644
--- a/src/core/libraries/pad/pad.cpp
+++ b/src/core/libraries/pad/pad.cpp
@@ -316,22 +316,79 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
pData[i].angularVelocity.y = states[i].angularVelocity.y;
pData[i].angularVelocity.z = states[i].angularVelocity.z;
pData[i].orientation = {0.0f, 0.0f, 0.0f, 1.0f};
- if (engine) {
+ pData[i].acceleration.x = states[i].acceleration.x * 0.098;
+ pData[i].acceleration.y = states[i].acceleration.y * 0.098;
+ pData[i].acceleration.z = states[i].acceleration.z * 0.098;
+ pData[i].angularVelocity.x = states[i].angularVelocity.x;
+ pData[i].angularVelocity.y = states[i].angularVelocity.y;
+ pData[i].angularVelocity.z = states[i].angularVelocity.z;
+
+ if (engine && handle == 1) {
const auto gyro_poll_rate = engine->GetAccelPollRate();
if (gyro_poll_rate != 0.0f) {
- GameController::CalculateOrientation(pData[i].acceleration,
- pData[i].angularVelocity,
- 1.0f / gyro_poll_rate, pData[i].orientation);
+ auto now = std::chrono::steady_clock::now();
+ float deltaTime = std::chrono::duration_cast(
+ now - controller->GetLastUpdate())
+ .count() /
+ 1000000.0f;
+ controller->SetLastUpdate(now);
+ Libraries::Pad::OrbisFQuaternion lastOrientation = controller->GetLastOrientation();
+ Libraries::Pad::OrbisFQuaternion outputOrientation = {0.0f, 0.0f, 0.0f, 1.0f};
+ GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity,
+ deltaTime, lastOrientation, outputOrientation);
+ pData[i].orientation = outputOrientation;
+ controller->SetLastOrientation(outputOrientation);
}
}
+
pData[i].touchData.touchNum =
(states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0);
+
+ if (handle == 1) {
+ if (controller->GetTouchCount() >= 127) {
+ controller->SetTouchCount(0);
+ }
+
+ if (controller->GetSecondaryTouchCount() >= 127) {
+ controller->SetSecondaryTouchCount(0);
+ }
+
+ if (pData->touchData.touchNum == 1 && controller->GetPreviousTouchNum() == 0) {
+ controller->SetTouchCount(controller->GetTouchCount() + 1);
+ controller->SetSecondaryTouchCount(controller->GetTouchCount());
+ } else if (pData->touchData.touchNum == 2 && controller->GetPreviousTouchNum() == 1) {
+ controller->SetSecondaryTouchCount(controller->GetSecondaryTouchCount() + 1);
+ } else if (pData->touchData.touchNum == 0 && controller->GetPreviousTouchNum() > 0) {
+ if (controller->GetTouchCount() < controller->GetSecondaryTouchCount()) {
+ controller->SetTouchCount(controller->GetSecondaryTouchCount());
+ } else {
+ if (controller->WasSecondaryTouchReset()) {
+ controller->SetTouchCount(controller->GetSecondaryTouchCount());
+ controller->UnsetSecondaryTouchResetBool();
+ }
+ }
+ }
+
+ controller->SetPreviousTouchNum(pData->touchData.touchNum);
+
+ if (pData->touchData.touchNum == 1) {
+ states[i].touchpad[0].ID = controller->GetTouchCount();
+ states[i].touchpad[1].ID = 0;
+ } else if (pData->touchData.touchNum == 2) {
+ states[i].touchpad[0].ID = controller->GetTouchCount();
+ states[i].touchpad[1].ID = controller->GetSecondaryTouchCount();
+ }
+ } else {
+ states[i].touchpad[0].ID = 1;
+ states[i].touchpad[1].ID = 2;
+ }
+
pData[i].touchData.touch[0].x = states[i].touchpad[0].x;
pData[i].touchData.touch[0].y = states[i].touchpad[0].y;
- pData[i].touchData.touch[0].id = 1;
+ pData[i].touchData.touch[0].id = states[i].touchpad[0].ID;
pData[i].touchData.touch[1].x = states[i].touchpad[1].x;
pData[i].touchData.touch[1].y = states[i].touchpad[1].y;
- pData[i].touchData.touch[1].id = 2;
+ pData[i].touchData.touch[1].id = states[i].touchpad[1].ID;
pData[i].connected = connected;
pData[i].timestamp = states[i].time;
pData[i].connectedCount = connected_count;
@@ -376,31 +433,85 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) {
pData->leftStick.x = state.axes[static_cast(Input::Axis::LeftX)];
pData->leftStick.y = state.axes[static_cast(Input::Axis::LeftY)];
pData->rightStick.x = state.axes[static_cast(Input::Axis::RightX)];
+ pData->rightStick.x = state.axes[static_cast(Input::Axis::RightX)];
pData->rightStick.y = state.axes[static_cast(Input::Axis::RightY)];
pData->analogButtons.l2 = state.axes[static_cast(Input::Axis::TriggerLeft)];
pData->analogButtons.r2 = state.axes[static_cast(Input::Axis::TriggerRight)];
- pData->acceleration.x = state.acceleration.x;
- pData->acceleration.y = state.acceleration.y;
- pData->acceleration.z = state.acceleration.z;
+ pData->acceleration.x = state.acceleration.x * 0.098;
+ pData->acceleration.y = state.acceleration.y * 0.098;
+ pData->acceleration.z = state.acceleration.z * 0.098;
pData->angularVelocity.x = state.angularVelocity.x;
pData->angularVelocity.y = state.angularVelocity.y;
pData->angularVelocity.z = state.angularVelocity.z;
pData->orientation = {0.0f, 0.0f, 0.0f, 1.0f};
- if (engine) {
+
+ // Only do this on handle 1 for now
+ if (engine && handle == 1) {
const auto gyro_poll_rate = engine->GetAccelPollRate();
if (gyro_poll_rate != 0.0f) {
+ auto now = std::chrono::steady_clock::now();
+ float deltaTime = std::chrono::duration_cast(
+ now - controller->GetLastUpdate())
+ .count() /
+ 1000000.0f;
+ controller->SetLastUpdate(now);
+ Libraries::Pad::OrbisFQuaternion lastOrientation = controller->GetLastOrientation();
+ Libraries::Pad::OrbisFQuaternion outputOrientation = {0.0f, 0.0f, 0.0f, 1.0f};
GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity,
- 1.0f / gyro_poll_rate, pData->orientation);
+ deltaTime, lastOrientation, outputOrientation);
+ pData->orientation = outputOrientation;
+ controller->SetLastOrientation(outputOrientation);
}
}
pData->touchData.touchNum =
(state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0);
+
+ // Only do this on handle 1 for now
+ if (handle == 1) {
+ if (controller->GetTouchCount() >= 127) {
+ controller->SetTouchCount(0);
+ }
+
+ if (controller->GetSecondaryTouchCount() >= 127) {
+ controller->SetSecondaryTouchCount(0);
+ }
+
+ if (pData->touchData.touchNum == 1 && controller->GetPreviousTouchNum() == 0) {
+ controller->SetTouchCount(controller->GetTouchCount() + 1);
+ controller->SetSecondaryTouchCount(controller->GetTouchCount());
+ } else if (pData->touchData.touchNum == 2 && controller->GetPreviousTouchNum() == 1) {
+ controller->SetSecondaryTouchCount(controller->GetSecondaryTouchCount() + 1);
+ } else if (pData->touchData.touchNum == 0 && controller->GetPreviousTouchNum() > 0) {
+ if (controller->GetTouchCount() < controller->GetSecondaryTouchCount()) {
+ controller->SetTouchCount(controller->GetSecondaryTouchCount());
+ } else {
+ if (controller->WasSecondaryTouchReset()) {
+ controller->SetTouchCount(controller->GetSecondaryTouchCount());
+ controller->UnsetSecondaryTouchResetBool();
+ }
+ }
+ }
+
+ controller->SetPreviousTouchNum(pData->touchData.touchNum);
+
+ if (pData->touchData.touchNum == 1) {
+ state.touchpad[0].ID = controller->GetTouchCount();
+ state.touchpad[1].ID = 0;
+ } else if (pData->touchData.touchNum == 2) {
+ state.touchpad[0].ID = controller->GetTouchCount();
+ state.touchpad[1].ID = controller->GetSecondaryTouchCount();
+ }
+ } else {
+ state.touchpad[0].ID = 1;
+ state.touchpad[1].ID = 2;
+ }
+
pData->touchData.touch[0].x = state.touchpad[0].x;
pData->touchData.touch[0].y = state.touchpad[0].y;
- pData->touchData.touch[0].id = 1;
+ pData->touchData.touch[0].id = state.touchpad[0].ID;
pData->touchData.touch[1].x = state.touchpad[1].x;
pData->touchData.touch[1].y = state.touchpad[1].y;
- pData->touchData.touch[1].id = 2;
+ pData->touchData.touch[1].id = state.touchpad[1].ID;
pData->timestamp = state.time;
pData->connected = true; // isConnected; //TODO fix me proper
pData->connectedCount = 1; // connectedCount;
diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp
index c5208b6dd..da715b3bf 100644
--- a/src/core/libraries/videoout/video_out.cpp
+++ b/src/core/libraries/videoout/video_out.cpp
@@ -282,7 +282,12 @@ s32 PS4_SYSV_ABI sceVideoOutGetVblankStatus(int handle, SceVideoOutVblankStatus*
s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutionStatus* status) {
LOG_INFO(Lib_VideoOut, "called");
- *status = driver->GetPort(handle)->resolution;
+ auto* port = driver->GetPort(handle);
+ if (!port || !port->is_open) {
+ return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
+ }
+
+ *status = port->resolution;
return ORBIS_OK;
}
diff --git a/src/core/libraries/voice/voice.cpp b/src/core/libraries/voice/voice.cpp
new file mode 100644
index 000000000..caa16431a
--- /dev/null
+++ b/src/core/libraries/voice/voice.cpp
@@ -0,0 +1,203 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/logging/log.h"
+#include "core/libraries/error_codes.h"
+#include "core/libraries/libs.h"
+#include "core/libraries/voice/voice.h"
+
+namespace Libraries::Voice {
+
+s32 PS4_SYSV_ABI sceVoiceConnectIPortToOPort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceCreatePort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceDeletePort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceDisconnectIPortFromOPort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceEnd() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceGetBitRate(u32 port_id, u32* bitrate) {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ *bitrate = 48000;
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceGetMuteFlag() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceGetPortAttr() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceGetPortInfo(u32 port_id, OrbisVoicePortInfo* info) {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ info->port_type = 0;
+ info->state = 3;
+ info->byte_count = 0;
+ info->frame_size = 1;
+ info->edge_count = 0;
+ info->reserved = 0;
+
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceGetResourceInfo() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceGetVolume() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceInit() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceInitHQ() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoicePausePort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoicePausePortAll() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceReadFromOPort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceResetPort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceResumePort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceResumePortAll() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceSetBitRate() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceSetMuteFlag() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceSetMuteFlagAll() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceSetThreadsParams() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceSetVolume() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceStart() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceStop() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceUpdatePort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceVADAdjustment() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceVADSetVersion() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceWriteToIPort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+void RegisterlibSceVoice(Core::Loader::SymbolsResolver* sym) {
+ LIB_FUNCTION("oV9GAdJ23Gw", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceConnectIPortToOPort);
+ LIB_FUNCTION("nXpje5yNpaE", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceCreatePort);
+ LIB_FUNCTION("b7kJI+nx2hg", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceDeletePort);
+ LIB_FUNCTION("ajVj3QG2um4", "libSceVoice", 1, "libSceVoice", 0, 0,
+ sceVoiceDisconnectIPortFromOPort);
+ LIB_FUNCTION("Oo0S5PH7FIQ", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceEnd);
+ LIB_FUNCTION("cJLufzou6bc", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetBitRate);
+ LIB_FUNCTION("Pc4z1QjForU", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetMuteFlag);
+ LIB_FUNCTION("elcxZTEfHZM", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetPortAttr);
+ LIB_FUNCTION("CrLqDwWLoXM", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetPortInfo);
+ LIB_FUNCTION("Z6QV6j7igvE", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetResourceInfo);
+ LIB_FUNCTION("jjkCjneOYSs", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetVolume);
+ LIB_FUNCTION("9TrhuGzberQ", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceInit);
+ LIB_FUNCTION("IPHvnM5+g04", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceInitHQ);
+ LIB_FUNCTION("x0slGBQW+wY", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoicePausePort);
+ LIB_FUNCTION("Dinob0yMRl8", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoicePausePortAll);
+ LIB_FUNCTION("cQ6DGsQEjV4", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceReadFromOPort);
+ LIB_FUNCTION("udAxvCePkUs", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceResetPort);
+ LIB_FUNCTION("gAgN+HkiEzY", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceResumePort);
+ LIB_FUNCTION("jbkJFmOZ9U0", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceResumePortAll);
+ LIB_FUNCTION("TexwmOHQsDg", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetBitRate);
+ LIB_FUNCTION("gwUynkEgNFY", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetMuteFlag);
+ LIB_FUNCTION("oUha0S-Ij9Q", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetMuteFlagAll);
+ LIB_FUNCTION("clyKUyi3RYU", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetThreadsParams);
+ LIB_FUNCTION("QBFoAIjJoXQ", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetVolume);
+ LIB_FUNCTION("54phPH2LZls", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceStart);
+ LIB_FUNCTION("Ao2YNSA7-Qo", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceStop);
+ LIB_FUNCTION("jSZNP7xJrcw", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceUpdatePort);
+ LIB_FUNCTION("hg9T73LlRiU", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceVADAdjustment);
+ LIB_FUNCTION("wFeAxEeEi-8", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceVADSetVersion);
+ LIB_FUNCTION("YeJl6yDlhW0", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceWriteToIPort);
+};
+
+} // namespace Libraries::Voice
\ No newline at end of file
diff --git a/src/core/libraries/voice/voice.h b/src/core/libraries/voice/voice.h
new file mode 100644
index 000000000..8f008f2cc
--- /dev/null
+++ b/src/core/libraries/voice/voice.h
@@ -0,0 +1,56 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/types.h"
+
+namespace Core::Loader {
+class SymbolsResolver;
+}
+
+namespace Libraries::Voice {
+
+struct OrbisVoicePortInfo {
+ s32 port_type;
+ s32 state;
+ u32* edge;
+ u32 byte_count;
+ u32 frame_size;
+ u16 edge_count;
+ u16 reserved;
+};
+
+s32 PS4_SYSV_ABI sceVoiceConnectIPortToOPort();
+s32 PS4_SYSV_ABI sceVoiceCreatePort();
+s32 PS4_SYSV_ABI sceVoiceDeletePort();
+s32 PS4_SYSV_ABI sceVoiceDisconnectIPortFromOPort();
+s32 PS4_SYSV_ABI sceVoiceEnd();
+s32 PS4_SYSV_ABI sceVoiceGetBitRate(u32 port_id, u32* bitrate);
+s32 PS4_SYSV_ABI sceVoiceGetMuteFlag();
+s32 PS4_SYSV_ABI sceVoiceGetPortAttr();
+s32 PS4_SYSV_ABI sceVoiceGetPortInfo(u32 port_id, OrbisVoicePortInfo* info);
+s32 PS4_SYSV_ABI sceVoiceGetResourceInfo();
+s32 PS4_SYSV_ABI sceVoiceGetVolume();
+s32 PS4_SYSV_ABI sceVoiceInit();
+s32 PS4_SYSV_ABI sceVoiceInitHQ();
+s32 PS4_SYSV_ABI sceVoicePausePort();
+s32 PS4_SYSV_ABI sceVoicePausePortAll();
+s32 PS4_SYSV_ABI sceVoiceReadFromOPort();
+s32 PS4_SYSV_ABI sceVoiceResetPort();
+s32 PS4_SYSV_ABI sceVoiceResumePort();
+s32 PS4_SYSV_ABI sceVoiceResumePortAll();
+s32 PS4_SYSV_ABI sceVoiceSetBitRate();
+s32 PS4_SYSV_ABI sceVoiceSetMuteFlag();
+s32 PS4_SYSV_ABI sceVoiceSetMuteFlagAll();
+s32 PS4_SYSV_ABI sceVoiceSetThreadsParams();
+s32 PS4_SYSV_ABI sceVoiceSetVolume();
+s32 PS4_SYSV_ABI sceVoiceStart();
+s32 PS4_SYSV_ABI sceVoiceStop();
+s32 PS4_SYSV_ABI sceVoiceUpdatePort();
+s32 PS4_SYSV_ABI sceVoiceVADAdjustment();
+s32 PS4_SYSV_ABI sceVoiceVADSetVersion();
+s32 PS4_SYSV_ABI sceVoiceWriteToIPort();
+
+void RegisterlibSceVoice(Core::Loader::SymbolsResolver* sym);
+} // namespace Libraries::Voice
\ No newline at end of file
diff --git a/src/core/libraries/zlib/zlib.cpp b/src/core/libraries/zlib/zlib.cpp
index 899cb5bf6..b304992ad 100644
--- a/src/core/libraries/zlib/zlib.cpp
+++ b/src/core/libraries/zlib/zlib.cpp
@@ -51,7 +51,7 @@ void ZlibTaskThread(const std::stop_token& stop) {
if (!task_queue_cv.wait(lock, stop, [&] { return !task_queue.empty(); })) {
break;
}
- task = task_queue.back();
+ task = task_queue.front();
task_queue.pop();
}
@@ -136,7 +136,7 @@ s32 PS4_SYSV_ABI sceZlibWaitForDone(u64* request_id, const u32* timeout) {
} else {
done_queue_cv.wait(lock, pred);
}
- *request_id = done_queue.back();
+ *request_id = done_queue.front();
done_queue.pop();
}
return ORBIS_OK;
diff --git a/src/core/linker.cpp b/src/core/linker.cpp
index 3e6d8c22e..1f45caf12 100644
--- a/src/core/linker.cpp
+++ b/src/core/linker.cpp
@@ -117,6 +117,18 @@ void Linker::Execute(const std::vector args) {
Common::SetCurrentThreadName("GAME_MainThread");
LoadSharedLibraries();
+ // Simulate libSceGnmDriver initialization, which maps a chunk of direct memory.
+ // Some games fail without accurately emulating this behavior.
+ s64 phys_addr{};
+ s32 result = Libraries::Kernel::sceKernelAllocateDirectMemory(
+ 0, Libraries::Kernel::sceKernelGetDirectMemorySize(), 0x10000, 0x10000, 3, &phys_addr);
+ if (result == 0) {
+ void* addr{reinterpret_cast(0xfe0000000)};
+ result = Libraries::Kernel::sceKernelMapNamedDirectMemory(
+ &addr, 0x10000, 0x13, 0, phys_addr, 0x10000, "SceGnmDriver");
+ }
+ ASSERT_MSG(result == 0, "Unable to emulate libSceGnmDriver initialization");
+
// Start main module.
EntryParams params{};
params.argc = 1;
@@ -320,21 +332,22 @@ bool Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Modul
sr.type = sym_type;
const auto* record = m_hle_symbols.FindSymbol(sr);
- if (!record) {
- // Check if it an export function
- const auto* p = FindExportedModule(*module, *library);
- if (p && p->export_sym.GetSize() > 0) {
- record = p->export_sym.FindSymbol(sr);
- }
- }
if (record) {
*return_info = *record;
-
Core::Devtools::Widget::ModuleList::AddModule(sr.library);
-
return true;
}
+ // Check if it an export function
+ const auto* p = FindExportedModule(*module, *library);
+ if (p && p->export_sym.GetSize() > 0) {
+ record = p->export_sym.FindSymbol(sr);
+ if (record) {
+ *return_info = *record;
+ return true;
+ }
+ }
+
const auto aeronid = AeroLib::FindByNid(sr.name.c_str());
if (aeronid) {
return_info->name = aeronid->name;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index ca6a0d6cd..e738f85a1 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -5,6 +5,7 @@
#include "common/assert.h"
#include "common/config.h"
#include "common/debug.h"
+#include "core/file_sys/fs.h"
#include "core/libraries/kernel/memory.h"
#include "core/libraries/kernel/orbis_error.h"
#include "core/libraries/kernel/process.h"
@@ -67,7 +68,7 @@ void MemoryManager::SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1
}
u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 size) {
- static constexpr u64 MinSizeToClamp = 512_MB;
+ static constexpr u64 MinSizeToClamp = 2_MB;
// Dont bother with clamping if the size is small so we dont pay a map lookup on every buffer.
if (size < MinSizeToClamp) {
return size;
@@ -94,6 +95,46 @@ u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 size) {
return clamped_size;
}
+void MemoryManager::SetPrtArea(u32 id, VAddr address, u64 size) {
+ PrtArea& area = prt_areas[id];
+ if (area.mapped) {
+ rasterizer->UnmapMemory(area.start, area.end - area.start);
+ }
+
+ area.start = address;
+ area.end = address + size;
+ area.mapped = true;
+
+ // Pretend the entire PRT area is mapped to avoid GPU tracking errors.
+ // The caches will use CopySparseMemory to fetch data which avoids unmapped areas.
+ rasterizer->MapMemory(address, size);
+}
+
+void MemoryManager::CopySparseMemory(VAddr virtual_addr, u8* dest, u64 size) {
+ const bool is_sparse = std::ranges::any_of(
+ prt_areas, [&](const PrtArea& area) { return area.Overlaps(virtual_addr, size); });
+ if (!is_sparse) {
+ std::memcpy(dest, std::bit_cast(virtual_addr), size);
+ return;
+ }
+
+ auto vma = FindVMA(virtual_addr);
+ ASSERT_MSG(vma->second.Contains(virtual_addr, 0),
+ "Attempted to access invalid GPU address {:#x}", virtual_addr);
+ while (size) {
+ u64 copy_size = std::min(vma->second.size - (virtual_addr - vma->first), size);
+ if (vma->second.IsFree()) {
+ std::memset(dest, 0, copy_size);
+ } else {
+ std::memcpy(dest, std::bit_cast(virtual_addr), copy_size);
+ }
+ size -= copy_size;
+ virtual_addr += copy_size;
+ dest += copy_size;
+ ++vma;
+ }
+}
+
bool MemoryManager::TryWriteBacking(void* address, const void* data, u32 num_bytes) {
const VAddr virtual_addr = std::bit_cast(address);
const auto& vma = FindVMA(virtual_addr)->second;
@@ -214,90 +255,6 @@ void MemoryManager::Free(PAddr phys_addr, size_t size) {
MergeAdjacent(dmem_map, dmem_area);
}
-int MemoryManager::PoolReserve(void** out_addr, VAddr virtual_addr, size_t size,
- MemoryMapFlags flags, u64 alignment) {
- std::scoped_lock lk{mutex};
- alignment = alignment > 0 ? alignment : 2_MB;
- VAddr min_address = Common::AlignUp(impl.SystemManagedVirtualBase(), alignment);
- VAddr mapped_addr = Common::AlignUp(virtual_addr, alignment);
-
- // Fixed mapping means the virtual address must exactly match the provided one.
- if (True(flags & MemoryMapFlags::Fixed)) {
- // Make sure we're mapping to a valid address
- mapped_addr = mapped_addr > min_address ? mapped_addr : min_address;
- auto vma = FindVMA(mapped_addr)->second;
- size_t remaining_size = vma.base + vma.size - mapped_addr;
- // If the VMA is mapped or there's not enough space, unmap the region first.
- if (vma.IsMapped() || remaining_size < size) {
- UnmapMemoryImpl(mapped_addr, size);
- vma = FindVMA(mapped_addr)->second;
- }
- }
-
- if (False(flags & MemoryMapFlags::Fixed)) {
- // When MemoryMapFlags::Fixed is not specified, and mapped_addr is 0,
- // search from address 0x200000000 instead.
- mapped_addr = mapped_addr == 0 ? 0x200000000 : mapped_addr;
- mapped_addr = SearchFree(mapped_addr, size, alignment);
- if (mapped_addr == -1) {
- // No suitable memory areas to map to
- return ORBIS_KERNEL_ERROR_ENOMEM;
- }
- }
-
- // Add virtual memory area
- const auto new_vma_handle = CarveVMA(mapped_addr, size);
- auto& new_vma = new_vma_handle->second;
- new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
- new_vma.prot = MemoryProt::NoAccess;
- new_vma.name = "anon";
- new_vma.type = VMAType::PoolReserved;
-
- *out_addr = std::bit_cast(mapped_addr);
- return ORBIS_OK;
-}
-
-int MemoryManager::Reserve(void** out_addr, VAddr virtual_addr, size_t size, MemoryMapFlags flags,
- u64 alignment) {
- std::scoped_lock lk{mutex};
-
- virtual_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
- alignment = alignment > 0 ? alignment : 16_KB;
- VAddr mapped_addr = alignment > 0 ? Common::AlignUp(virtual_addr, alignment) : virtual_addr;
-
- // Fixed mapping means the virtual address must exactly match the provided one.
- if (True(flags & MemoryMapFlags::Fixed)) {
- auto vma = FindVMA(mapped_addr)->second;
- size_t remaining_size = vma.base + vma.size - mapped_addr;
- // If the VMA is mapped or there's not enough space, unmap the region first.
- if (vma.IsMapped() || remaining_size < size) {
- UnmapMemoryImpl(mapped_addr, size);
- vma = FindVMA(mapped_addr)->second;
- }
- }
-
- // Find the first free area starting with provided virtual address.
- if (False(flags & MemoryMapFlags::Fixed)) {
- mapped_addr = SearchFree(mapped_addr, size, alignment);
- if (mapped_addr == -1) {
- // No suitable memory areas to map to
- return ORBIS_KERNEL_ERROR_ENOMEM;
- }
- }
-
- // Add virtual memory area
- const auto new_vma_handle = CarveVMA(mapped_addr, size);
- auto& new_vma = new_vma_handle->second;
- new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
- new_vma.prot = MemoryProt::NoAccess;
- new_vma.name = "anon";
- new_vma.type = VMAType::Reserved;
- MergeAdjacent(vma_map, new_vma_handle);
-
- *out_addr = std::bit_cast(mapped_addr);
- return ORBIS_OK;
-}
-
int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot) {
std::scoped_lock lk{mutex};
@@ -351,7 +308,7 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot)
return ORBIS_OK;
}
-int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot,
+s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
MemoryMapFlags flags, VMAType type, std::string_view name,
bool is_exec, PAddr phys_addr, u64 alignment) {
std::scoped_lock lk{mutex};
@@ -366,17 +323,18 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M
VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
// Fixed mapping means the virtual address must exactly match the provided one.
- if (True(flags & MemoryMapFlags::Fixed)) {
+ // On a PS4, the Fixed flag is ignored if address 0 is provided.
+ if (True(flags & MemoryMapFlags::Fixed) && virtual_addr != 0) {
auto vma = FindVMA(mapped_addr)->second;
- size_t remaining_size = vma.base + vma.size - mapped_addr;
// There's a possible edge case where we're mapping to a partially reserved range.
// To account for this, unmap any reserved areas within this mapping range first.
auto unmap_addr = mapped_addr;
auto unmap_size = size;
+
// If flag NoOverwrite is provided, don't overwrite mapped VMAs.
// When it isn't provided, VMAs can be overwritten regardless of if they're mapped.
while ((False(flags & MemoryMapFlags::NoOverwrite) || !vma.IsMapped()) &&
- unmap_addr < mapped_addr + size && remaining_size < size) {
+ unmap_addr < mapped_addr + size) {
auto unmapped = UnmapBytesFromEntry(unmap_addr, vma, unmap_size);
unmap_addr += unmapped;
unmap_size -= unmapped;
@@ -384,51 +342,65 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M
}
vma = FindVMA(mapped_addr)->second;
- remaining_size = vma.base + vma.size - mapped_addr;
+ auto remaining_size = vma.base + vma.size - mapped_addr;
if (vma.IsMapped() || remaining_size < size) {
LOG_ERROR(Kernel_Vmm, "Unable to map {:#x} bytes at address {:#x}", size, mapped_addr);
return ORBIS_KERNEL_ERROR_ENOMEM;
}
- }
-
- // Find the first free area starting with provided virtual address.
- if (False(flags & MemoryMapFlags::Fixed)) {
- // Provided address needs to be aligned before we can map.
+ } else {
+ // When MemoryMapFlags::Fixed is not specified, and mapped_addr is 0,
+ // search from address 0x200000000 instead.
alignment = alignment > 0 ? alignment : 16_KB;
- mapped_addr = SearchFree(Common::AlignUp(mapped_addr, alignment), size, alignment);
+ mapped_addr = virtual_addr == 0 ? 0x200000000 : mapped_addr;
+ mapped_addr = SearchFree(mapped_addr, size, alignment);
if (mapped_addr == -1) {
// No suitable memory areas to map to
return ORBIS_KERNEL_ERROR_ENOMEM;
}
}
- // Perform the mapping.
- *out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec);
- TRACK_ALLOC(*out_addr, size, "VMEM");
+ // Create a memory area representing this mapping.
+ const auto new_vma_handle = CarveVMA(mapped_addr, size);
+ auto& new_vma = new_vma_handle->second;
- auto& new_vma = CarveVMA(mapped_addr, size)->second;
- new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
- new_vma.prot = prot;
- new_vma.name = name;
- new_vma.type = type;
- new_vma.is_exec = is_exec;
-
- if (type == VMAType::Direct) {
- new_vma.phys_base = phys_addr;
- }
+ // If type is Flexible, we need to track how much flexible memory is used here.
if (type == VMAType::Flexible) {
flexible_usage += size;
}
- if (IsValidGpuMapping(mapped_addr, size)) {
- rasterizer->MapMemory(mapped_addr, size);
+ new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
+ new_vma.prot = prot;
+ new_vma.name = name;
+ new_vma.type = type;
+ new_vma.phys_base = phys_addr == -1 ? 0 : phys_addr;
+ new_vma.is_exec = is_exec;
+
+ if (type == VMAType::Reserved) {
+ // Technically this should be done for direct and flexible mappings too,
+ // But some Windows-specific limitations make that hard to accomplish.
+ MergeAdjacent(vma_map, new_vma_handle);
}
+ if (type == VMAType::Reserved || type == VMAType::PoolReserved) {
+ // For Reserved/PoolReserved mappings, we don't perform any address space allocations.
+ // Just set out_addr to mapped_addr instead.
+ *out_addr = std::bit_cast(mapped_addr);
+ } else {
+ // If this is not a reservation, then map to GPU and address space
+ if (IsValidGpuMapping(mapped_addr, size)) {
+ rasterizer->MapMemory(mapped_addr, size);
+ }
+ *out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec);
+ }
+
+ TRACK_ALLOC(*out_addr, size, "VMEM");
return ORBIS_OK;
}
-int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot,
- MemoryMapFlags flags, uintptr_t fd, size_t offset) {
+s32 MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
+ MemoryMapFlags flags, s32 fd, s64 phys_addr) {
+ auto* h = Common::Singleton::Instance();
+
VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
const size_t size_aligned = Common::AlignUp(size, 16_KB);
@@ -449,8 +421,19 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem
vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size);
}
- // Map the file.
- impl.MapFile(mapped_addr, size_aligned, offset, std::bit_cast(prot), fd);
+ // Get the file to map
+ auto file = h->GetFile(fd);
+ if (file == nullptr) {
+ return ORBIS_KERNEL_ERROR_EBADF;
+ }
+
+ const auto handle = file->f.GetFileMapping();
+
+ impl.MapFile(mapped_addr, size_aligned, phys_addr, std::bit_cast(prot), handle);
+
+ if (prot >= MemoryProt::GpuRead) {
+ ASSERT_MSG(false, "Files cannot be mapped to GPU memory");
+ }
// Add virtual memory area
auto& new_vma = CarveVMA(mapped_addr, size_aligned)->second;
@@ -485,14 +468,15 @@ s32 MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) {
}
if (type == VMAType::Pooled) {
+ // We always map PoolCommitted memory to GPU, so unmap when decomitting.
+ if (IsValidGpuMapping(virtual_addr, size)) {
+ rasterizer->UnmapMemory(virtual_addr, size);
+ }
+
// Track how much pooled memory is decommitted
pool_budget += size;
}
- if (IsValidGpuMapping(virtual_addr, size)) {
- rasterizer->UnmapMemory(virtual_addr, size);
- }
-
// Mark region as free and attempt to coalesce it with neighbours.
const auto new_it = CarveVMA(virtual_addr, size);
auto& vma = new_it->second;
@@ -528,18 +512,16 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma
const auto adjusted_size =
vma_base_size - start_in_vma < size ? vma_base_size - start_in_vma : size;
const bool has_backing = type == VMAType::Direct || type == VMAType::File;
+ const auto prot = vma_base.prot;
if (type == VMAType::Free) {
return adjusted_size;
}
+
if (type == VMAType::Flexible) {
flexible_usage -= adjusted_size;
}
- if (IsValidGpuMapping(virtual_addr, adjusted_size)) {
- rasterizer->UnmapMemory(virtual_addr, adjusted_size);
- }
-
// Mark region as free and attempt to coalesce it with neighbours.
const auto new_it = CarveVMA(virtual_addr, adjusted_size);
auto& vma = new_it->second;
@@ -552,6 +534,11 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma
auto& post_merge_vma = post_merge_it->second;
bool readonly_file = post_merge_vma.prot == MemoryProt::CpuRead && type == VMAType::File;
if (type != VMAType::Reserved && type != VMAType::PoolReserved) {
+ // If this mapping has GPU access, unmap from GPU.
+ if (IsValidGpuMapping(virtual_addr, size)) {
+ rasterizer->UnmapMemory(virtual_addr, size);
+ }
+
// Unmap the memory region.
impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + adjusted_size,
phys_base, is_exec, has_backing, readonly_file);
@@ -605,20 +592,8 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s
vma_base.size - start_in_vma < size ? vma_base.size - start_in_vma : size;
if (vma_base.type == VMAType::Free) {
- LOG_ERROR(Kernel_Vmm, "Cannot change protection on free memory region");
- return ORBIS_KERNEL_ERROR_EINVAL;
- }
-
- // Validate protection flags
- constexpr static MemoryProt valid_flags = MemoryProt::NoAccess | MemoryProt::CpuRead |
- MemoryProt::CpuReadWrite | MemoryProt::GpuRead |
- MemoryProt::GpuWrite | MemoryProt::GpuReadWrite;
-
- MemoryProt invalid_flags = prot & ~valid_flags;
- if (u32(invalid_flags) != 0 && u32(invalid_flags) != u32(MemoryProt::NoAccess)) {
- LOG_ERROR(Kernel_Vmm, "Invalid protection flags: prot = {:#x}, invalid flags = {:#x}",
- u32(prot), u32(invalid_flags));
- return ORBIS_KERNEL_ERROR_EINVAL;
+ // On PS4, protecting freed memory does nothing.
+ return adjusted_size;
}
// Change protection
@@ -650,11 +625,25 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s
s32 MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) {
std::scoped_lock lk{mutex};
- s64 protected_bytes = 0;
+ // Validate protection flags
+ constexpr static MemoryProt valid_flags = MemoryProt::NoAccess | MemoryProt::CpuRead |
+ MemoryProt::CpuReadWrite | MemoryProt::GpuRead |
+ MemoryProt::GpuWrite | MemoryProt::GpuReadWrite;
+
+ MemoryProt invalid_flags = prot & ~valid_flags;
+ if (invalid_flags != MemoryProt::NoAccess) {
+ LOG_ERROR(Kernel_Vmm, "Invalid protection flags");
+ return ORBIS_KERNEL_ERROR_EINVAL;
+ }
+
+ // Align addr and size to the nearest page boundary.
auto aligned_addr = Common::AlignDown(addr, 16_KB);
auto aligned_size = Common::AlignUp(size + addr - aligned_addr, 16_KB);
- do {
+
+ // Protect all VMAs between aligned_addr and aligned_addr + aligned_size.
+ s64 protected_bytes = 0;
+ while (protected_bytes < aligned_size) {
auto it = FindVMA(aligned_addr + protected_bytes);
auto& vma_base = it->second;
ASSERT_MSG(vma_base.Contains(addr + protected_bytes, 0), "Address {:#x} is out of bounds",
@@ -667,7 +656,7 @@ s32 MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) {
return result;
}
protected_bytes += result;
- } while (protected_bytes < aligned_size);
+ }
return ORBIS_OK;
}
@@ -798,12 +787,31 @@ s32 MemoryManager::SetDirectMemoryType(s64 phys_addr, s32 memory_type) {
return ORBIS_OK;
}
-void MemoryManager::NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name) {
- auto it = FindVMA(virtual_addr);
+void MemoryManager::NameVirtualRange(VAddr virtual_addr, u64 size, std::string_view name) {
+ // Sizes are aligned up to the nearest 16_KB
+ auto aligned_size = Common::AlignUp(size, 16_KB);
+ // Addresses are aligned down to the nearest 16_KB
+ auto aligned_addr = Common::AlignDown(virtual_addr, 16_KB);
- ASSERT_MSG(it->second.Contains(virtual_addr, size),
- "Range provided is not fully contained in vma");
- it->second.name = name;
+ auto it = FindVMA(aligned_addr);
+ s64 remaining_size = aligned_size;
+ auto current_addr = aligned_addr;
+ while (remaining_size > 0) {
+ // Nothing needs to be done to free VMAs
+ if (!it->second.IsFree()) {
+ if (remaining_size < it->second.size) {
+ // We should split VMAs here, but this could cause trouble for Windows.
+ // Instead log a warning and name the whole VMA.
+ // it = CarveVMA(current_addr, remaining_size);
+ LOG_WARNING(Kernel_Vmm, "Trying to partially name a range");
+ }
+ auto& vma = it->second;
+ vma.name = name;
+ }
+ remaining_size -= it->second.size;
+ current_addr += it->second.size;
+ it = FindVMA(current_addr);
+ }
}
void MemoryManager::InvalidateMemory(const VAddr addr, const u64 size) const {
@@ -824,6 +832,8 @@ VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment)
ASSERT_MSG(virtual_addr <= max_search_address, "Input address {:#x} is out of bounds",
virtual_addr);
+ // Align up the virtual_addr first.
+ virtual_addr = Common::AlignUp(virtual_addr, alignment);
auto it = FindVMA(virtual_addr);
// If the VMA is free and contains the requested mapping we are done.
diff --git a/src/core/memory.h b/src/core/memory.h
index 883b48854..68f9c26c4 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -172,6 +172,10 @@ public:
u64 ClampRangeSize(VAddr virtual_addr, u64 size);
+ void SetPrtArea(u32 id, VAddr address, u64 size);
+
+ void CopySparseMemory(VAddr source, u8* dest, u64 size);
+
bool TryWriteBacking(void* address, const void* data, u32 num_bytes);
void SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1, bool use_extended_mem2);
@@ -183,20 +187,14 @@ public:
void Free(PAddr phys_addr, size_t size);
- int PoolReserve(void** out_addr, VAddr virtual_addr, size_t size, MemoryMapFlags flags,
- u64 alignment = 0);
-
- int Reserve(void** out_addr, VAddr virtual_addr, size_t size, MemoryMapFlags flags,
- u64 alignment = 0);
-
int PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot);
- int MapMemory(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot,
+ s32 MapMemory(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
MemoryMapFlags flags, VMAType type, std::string_view name = "anon",
bool is_exec = false, PAddr phys_addr = -1, u64 alignment = 0);
- int MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot,
- MemoryMapFlags flags, uintptr_t fd, size_t offset);
+ s32 MapFile(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
+ MemoryMapFlags flags, s32 fd, s64 phys_addr);
s32 PoolDecommit(VAddr virtual_addr, size_t size);
@@ -221,7 +219,7 @@ public:
s32 SetDirectMemoryType(s64 phys_addr, s32 memory_type);
- void NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name);
+ void NameVirtualRange(VAddr virtual_addr, u64 size, std::string_view name);
void InvalidateMemory(VAddr addr, u64 size) const;
@@ -281,6 +279,18 @@ private:
size_t pool_budget{};
Vulkan::Rasterizer* rasterizer{};
+ struct PrtArea {
+ VAddr start;
+ VAddr end;
+ bool mapped;
+
+ bool Overlaps(VAddr test_address, u64 test_size) const {
+ const VAddr overlap_end = test_address + test_size;
+ return start < overlap_end && test_address < end;
+ }
+ };
+ std::array prt_areas{};
+
friend class ::Core::Devtools::Widget::MemoryMapViewer;
};
diff --git a/src/emulator.cpp b/src/emulator.cpp
index 2ad8446ab..bb50b8686 100644
--- a/src/emulator.cpp
+++ b/src/emulator.cpp
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include
#include
#include
@@ -62,8 +63,13 @@ Emulator::~Emulator() {
Config::saveMainWindow(config_dir / "config.toml");
}
-void Emulator::Run(const std::filesystem::path& file, const std::vector args) {
+void Emulator::Run(std::filesystem::path file, const std::vector args) {
+ if (std::filesystem::is_directory(file)) {
+ file /= "eboot.bin";
+ }
+
const auto eboot_name = file.filename().string();
+
auto game_folder = file.parent_path();
if (const auto game_folder_name = game_folder.filename().string();
game_folder_name.ends_with("-UPDATE") || game_folder_name.ends_with("-patch")) {
@@ -114,6 +120,11 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector", id, title, app_version);
std::string window_title = "";
std::string remote_url(Common::g_scm_remote_url);
- std::string remote_host;
- try {
- if (*remote_url.rbegin() == '/') {
- remote_url.pop_back();
- }
- remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
- } catch (...) {
- remote_host = "unknown";
- }
+ std::string remote_host = Common::GetRemoteNameFromLink();
if (Common::g_is_release) {
if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
window_title = fmt::format("shadPS4 v{} | {}", Common::g_version, game_title);
@@ -258,7 +261,11 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorGetHostPath("/app0/" + eboot_name);
- linker->LoadModule(eboot_path);
+ if (linker->LoadModule(eboot_path) == -1) {
+ LOG_CRITICAL(Loader, "Failed to load game's eboot.bin: {}",
+ std::filesystem::absolute(eboot_path).string());
+ std::quick_exit(0);
+ }
// check if we have system modules to load
LoadSystemModules(game_info.game_serial);
diff --git a/src/emulator.h b/src/emulator.h
index 08c2807a1..257ccd694 100644
--- a/src/emulator.h
+++ b/src/emulator.h
@@ -25,7 +25,7 @@ public:
Emulator();
~Emulator();
- void Run(const std::filesystem::path& file, const std::vector args = {});
+ void Run(std::filesystem::path file, const std::vector args = {});
void UpdatePlayTime(const std::string& serial);
private:
diff --git a/src/input/controller.cpp b/src/input/controller.cpp
index bb8db9a7c..42cabb837 100644
--- a/src/input/controller.cpp
+++ b/src/input/controller.cpp
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include
@@ -165,69 +165,37 @@ void GameController::Acceleration(int id, const float acceleration[3]) {
AddState(state);
}
-// Stolen from
-// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs
-float eInt[3] = {0.0f, 0.0f, 0.0f}; // Integral error terms
-const float Kp = 50.0f; // Proportional gain
-const float Ki = 1.0f; // Integral gain
-Libraries::Pad::OrbisFQuaternion o = {1, 0, 0, 0};
void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
Libraries::Pad::OrbisFVector3& angularVelocity,
float deltaTime,
+ Libraries::Pad::OrbisFQuaternion& lastOrientation,
Libraries::Pad::OrbisFQuaternion& orientation) {
- float ax = acceleration.x, ay = acceleration.y, az = acceleration.z;
- float gx = angularVelocity.x, gy = angularVelocity.y, gz = angularVelocity.z;
+ Libraries::Pad::OrbisFQuaternion q = lastOrientation;
+ Libraries::Pad::OrbisFQuaternion ω = {angularVelocity.x, angularVelocity.y, angularVelocity.z,
+ 0.0f};
- float q1 = o.w, q2 = o.x, q3 = o.y, q4 = o.z;
+ Libraries::Pad::OrbisFQuaternion qω = {q.w * ω.x + q.x * ω.w + q.y * ω.z - q.z * ω.y,
+ q.w * ω.y + q.y * ω.w + q.z * ω.x - q.x * ω.z,
+ q.w * ω.z + q.z * ω.w + q.x * ω.y - q.y * ω.x,
+ q.w * ω.w - q.x * ω.x - q.y * ω.y - q.z * ω.z};
- // Normalize accelerometer measurement
- float norm = std::sqrt(ax * ax + ay * ay + az * az);
- if (norm == 0.0f || deltaTime == 0.0f)
- return; // Handle NaN
- norm = 1.0f / norm;
- ax *= norm;
- ay *= norm;
- az *= norm;
+ Libraries::Pad::OrbisFQuaternion qDot = {0.5f * qω.x, 0.5f * qω.y, 0.5f * qω.z, 0.5f * qω.w};
- // Estimated direction of gravity
- float vx = 2.0f * (q2 * q4 - q1 * q3);
- float vy = 2.0f * (q1 * q2 + q3 * q4);
- float vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
+ q.x += qDot.x * deltaTime;
+ q.y += qDot.y * deltaTime;
+ q.z += qDot.z * deltaTime;
+ q.w += qDot.w * deltaTime;
- // Error is cross product between estimated direction and measured direction of gravity
- float ex = (ay * vz - az * vy);
- float ey = (az * vx - ax * vz);
- float ez = (ax * vy - ay * vx);
- if (Ki > 0.0f) {
- eInt[0] += ex * deltaTime; // Accumulate integral error
- eInt[1] += ey * deltaTime;
- eInt[2] += ez * deltaTime;
- } else {
- eInt[0] = eInt[1] = eInt[2] = 0.0f; // Prevent integral wind-up
- }
+ float norm = std::sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
+ q.x /= norm;
+ q.y /= norm;
+ q.z /= norm;
+ q.w /= norm;
- // Apply feedback terms
- gx += Kp * ex + Ki * eInt[0];
- gy += Kp * ey + Ki * eInt[1];
- gz += Kp * ez + Ki * eInt[2];
-
- //// Integrate rate of change of quaternion
- q1 += (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * deltaTime);
- q2 += (q1 * gx + q3 * gz - q4 * gy) * (0.5f * deltaTime);
- q3 += (q1 * gy - q2 * gz + q4 * gx) * (0.5f * deltaTime);
- q4 += (q1 * gz + q2 * gy - q3 * gx) * (0.5f * deltaTime);
-
- // Normalize quaternion
- norm = std::sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4);
- norm = 1.0f / norm;
- orientation.w = q1 * norm;
- orientation.x = q2 * norm;
- orientation.y = q3 * norm;
- orientation.z = q4 * norm;
- o.w = q1 * norm;
- o.x = q2 * norm;
- o.y = q3 * norm;
- o.z = q4 * norm;
+ orientation.x = q.x;
+ orientation.y = q.y;
+ orientation.z = q.z;
+ orientation.w = q.w;
LOG_DEBUG(Lib_Pad, "Calculated orientation: {:.2f} {:.2f} {:.2f} {:.2f}", orientation.x,
orientation.y, orientation.z, orientation.w);
}
@@ -260,6 +228,69 @@ void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, f
}
}
+u8 GameController::GetTouchCount() {
+ std::scoped_lock lock{m_mutex};
+ return m_touch_count;
+}
+
+void GameController::SetTouchCount(u8 touchCount) {
+ std::scoped_lock lock{m_mutex};
+ m_touch_count = touchCount;
+}
+
+u8 GameController::GetSecondaryTouchCount() {
+ std::scoped_lock lock{m_mutex};
+ return m_secondary_touch_count;
+}
+
+void GameController::SetSecondaryTouchCount(u8 touchCount) {
+ std::scoped_lock lock{m_mutex};
+ m_secondary_touch_count = touchCount;
+ if (touchCount == 0) {
+ m_was_secondary_reset = true;
+ }
+}
+
+u8 GameController::GetPreviousTouchNum() {
+ std::scoped_lock lock{m_mutex};
+ return m_previous_touchnum;
+}
+
+void GameController::SetPreviousTouchNum(u8 touchNum) {
+ std::scoped_lock lock{m_mutex};
+ m_previous_touchnum = touchNum;
+}
+
+bool GameController::WasSecondaryTouchReset() {
+ std::scoped_lock lock{m_mutex};
+ return m_was_secondary_reset;
+}
+
+void GameController::UnsetSecondaryTouchResetBool() {
+ std::scoped_lock lock{m_mutex};
+ m_was_secondary_reset = false;
+}
+
+void GameController::SetLastOrientation(Libraries::Pad::OrbisFQuaternion& orientation) {
+ std::scoped_lock lock{m_mutex};
+ m_orientation = orientation;
+}
+
+Libraries::Pad::OrbisFQuaternion GameController::GetLastOrientation() {
+ std::scoped_lock lock{m_mutex};
+ return m_orientation;
+}
+
+std::chrono::steady_clock::time_point GameController::GetLastUpdate() {
+ std::scoped_lock lock{m_mutex};
+ return m_last_update;
+}
+
+void GameController::SetLastUpdate(std::chrono::steady_clock::time_point lastUpdate) {
+ std::scoped_lock lock{m_mutex};
+ m_last_update = lastUpdate;
+}
+
void GameController::SetEngine(std::unique_ptr engine) {
std::scoped_lock _{m_mutex};
m_engine = std::move(engine);
diff --git a/src/input/controller.h b/src/input/controller.h
index bbaed75ea..f427a55ec 100644
--- a/src/input/controller.h
+++ b/src/input/controller.h
@@ -23,6 +23,7 @@ enum class Axis {
};
struct TouchpadEntry {
+ u8 ID = 0;
bool state{};
u16 x{};
u16 y{};
@@ -82,9 +83,23 @@ public:
Engine* GetEngine();
u32 Poll();
+ u8 GetTouchCount();
+ void SetTouchCount(u8 touchCount);
+ u8 GetSecondaryTouchCount();
+ void SetSecondaryTouchCount(u8 touchCount);
+ u8 GetPreviousTouchNum();
+ void SetPreviousTouchNum(u8 touchNum);
+ bool WasSecondaryTouchReset();
+ void UnsetSecondaryTouchResetBool();
+
+ void SetLastOrientation(Libraries::Pad::OrbisFQuaternion& orientation);
+ Libraries::Pad::OrbisFQuaternion GetLastOrientation();
+ std::chrono::steady_clock::time_point GetLastUpdate();
+ void SetLastUpdate(std::chrono::steady_clock::time_point lastUpdate);
static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
Libraries::Pad::OrbisFVector3& angularVelocity,
float deltaTime,
+ Libraries::Pad::OrbisFQuaternion& lastOrientation,
Libraries::Pad::OrbisFQuaternion& orientation);
private:
@@ -98,8 +113,15 @@ private:
int m_connected_count = 0;
u32 m_states_num = 0;
u32 m_first_state = 0;
+ u8 m_touch_count = 0;
+ u8 m_secondary_touch_count = 0;
+ u8 m_previous_touch_count = 0;
+ u8 m_previous_touchnum = 0;
+ bool m_was_secondary_reset = false;
std::array m_states;
std::array m_private;
+ std::chrono::steady_clock::time_point m_last_update = {};
+ Libraries::Pad::OrbisFQuaternion m_orientation = {0.0f, 0.0f, 0.0f, 1.0f};
std::unique_ptr m_engine = nullptr;
};
diff --git a/src/qt_gui/check_update.cpp b/src/qt_gui/check_update.cpp
index 550fdddb5..b0858840a 100644
--- a/src/qt_gui/check_update.cpp
+++ b/src/qt_gui/check_update.cpp
@@ -137,7 +137,7 @@ tr("The Auto Updater allows up to 60 update checks per hour.\\nYou have reached
}
}
- latestRev = latestVersion.right(7);
+ latestRev = latestVersion.right(40);
latestDate = jsonObj["published_at"].toString();
QJsonArray assets = jsonObj["assets"].toArray();
@@ -167,7 +167,7 @@ tr("The Auto Updater allows up to 60 update checks per hour.\\nYou have reached
QDateTime dateTime = QDateTime::fromString(latestDate, Qt::ISODate);
latestDate = dateTime.isValid() ? dateTime.toString("yyyy-MM-dd HH:mm:ss") : "Unknown date";
- if (latestRev == currentRev.left(7)) {
+ if (latestRev == currentRev) {
if (showMessage) {
QMessageBox::information(this, tr("Auto Updater"),
tr("Your version is already up to date!"));
@@ -215,7 +215,7 @@ void CheckUpdate::setupUI(const QString& downloadUrl, const QString& latestDate,
"%3 | "
"(%4) | "
"
")
- .arg(currentRev.left(7), currentDate, latestRev, latestDate);
+ .arg(currentRev.left(7), currentDate, latestRev.left(7), latestDate);
QLabel* updateLabel = new QLabel(updateText, this);
layout->addWidget(updateLabel);
diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp
index 1966aa52b..906a3066e 100644
--- a/src/qt_gui/main_window.cpp
+++ b/src/qt_gui/main_window.cpp
@@ -56,15 +56,7 @@ bool MainWindow::Init() {
setMinimumSize(720, 405);
std::string window_title = "";
std::string remote_url(Common::g_scm_remote_url);
- std::string remote_host;
- try {
- if (*remote_url.rbegin() == '/') {
- remote_url.pop_back();
- }
- remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
- } catch (...) {
- remote_host = "unknown";
- }
+ std::string remote_host = Common::GetRemoteNameFromLink();
if (Common::g_is_release) {
if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
window_title = fmt::format("shadPS4 v{}", Common::g_version);
@@ -380,16 +372,15 @@ void MainWindow::CreateConnects() {
connect(ui->refreshGameListAct, &QAction::triggered, this, &MainWindow::RefreshGameTable);
connect(ui->refreshButton, &QPushButton::clicked, this, &MainWindow::RefreshGameTable);
connect(ui->showGameListAct, &QAction::triggered, this, &MainWindow::ShowGameList);
- connect(this, &MainWindow::ExtractionFinished, this, &MainWindow::RefreshGameTable);
connect(ui->toggleLabelsAct, &QAction::toggled, this, &MainWindow::toggleLabelsUnderIcons);
connect(ui->fullscreenButton, &QPushButton::clicked, this, &MainWindow::toggleFullscreen);
connect(ui->sizeSlider, &QSlider::valueChanged, this, [this](int value) {
if (isTableList) {
m_game_list_frame->icon_size =
- 36 + value; // 36 is the minimum icon size to use due to text disappearing.
- m_game_list_frame->ResizeIcons(36 + value);
- Config::setIconSize(36 + value);
+ 48 + value; // 48 is the minimum icon size to use due to text disappearing.
+ m_game_list_frame->ResizeIcons(48 + value);
+ Config::setIconSize(48 + value);
Config::setSliderPosition(value);
} else {
m_game_grid_frame->icon_size = 69 + value;
diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h
index a5ec08d36..97c56433d 100644
--- a/src/qt_gui/main_window.h
+++ b/src/qt_gui/main_window.h
@@ -29,7 +29,6 @@ class MainWindow : public QMainWindow {
Q_OBJECT
signals:
void WindowResized(QResizeEvent* event);
- void ExtractionFinished();
public:
explicit MainWindow(QWidget* parent = nullptr);
diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts
index e73386c96..9568388cc 100644
--- a/src/qt_gui/translations/es_ES.ts
+++ b/src/qt_gui/translations/es_ES.ts
@@ -26,7 +26,7 @@
Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n
- Los cheats/patches son experimentales.\nÚselos con precaución.\n\nDescargue los cheats individualmente seleccionando el repositorio y haciendo clic en el botón de descarga.\nEn la pestaña Patches, puede descargar todos los patches a la vez, elegir cuáles desea usar y guardar la selección.\n\nComo no desarrollamos los Cheats/Patches,\npor favor informe los problemas al autor del cheat.\n\n¿Creaste un nuevo cheat? Visita:\n
+ Los trucos/parches son experimentales.\nÚselos con precaución.\n\nPuede descargar cada truco seleccionando el repositorio y haciendo clic en el botón de descarga.\nEn la pestaña Parches podrá descargar todos los parches a la vez, elegir cuáles desea usar y guardar la selección.\n\nComo no desarrollamos los trucos/parches,\ndebe informar de cualquier problema a sus autores correspondientes.\n\n¿Creaste un truco nuevo? Visita:\n
No Image Available
@@ -2048,7 +2048,7 @@
* Unsupported Vulkan Version
- * Unsupported Vulkan Version
+ * Versión de Vulkan no soportada
diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts
index 34d31f240..9f254e272 100644
--- a/src/qt_gui/translations/pt_BR.ts
+++ b/src/qt_gui/translations/pt_BR.ts
@@ -2048,7 +2048,7 @@
* Unsupported Vulkan Version
- * Unsupported Vulkan Version
+ * Versão do Vulkan não suportada
diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts
index 9539ca139..c6d641470 100644
--- a/src/qt_gui/translations/tr_TR.ts
+++ b/src/qt_gui/translations/tr_TR.ts
@@ -138,7 +138,7 @@
File Exists
- Dosya mevcut
+ Dosya Mevcut
File already exists. Do you want to replace it?
@@ -1221,7 +1221,7 @@
Exit shadPS4
- shadPS4'ten Çık
+ shadPS4 Çıkış
Exit the application.
@@ -1381,7 +1381,7 @@
Game Boot
- Oyun Başlatma
+ Oyun Başlat
Only one file can be selected!
@@ -2048,7 +2048,7 @@
* Unsupported Vulkan Version
- * Unsupported Vulkan Version
+ * Desteklenmeyen Vulkan Sürümü
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
index d7c73ca8f..a342b47b6 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
@@ -68,6 +68,22 @@ Id BufferAtomicU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id
});
}
+Id BufferAtomicU32CmpSwap(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value,
+ Id cmp_value,
+ Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id, Id, Id)) {
+ const auto& buffer = ctx.buffers[handle];
+ if (Sirit::ValidId(buffer.offset)) {
+ address = ctx.OpIAdd(ctx.U32[1], address, buffer.offset);
+ }
+ const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(2u));
+ const auto [id, pointer_type] = buffer[EmitContext::PointerType::U32];
+ const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index);
+ const auto [scope, semantics]{AtomicArgs(ctx)};
+ return BufferAtomicU32BoundsCheck(ctx, index, buffer.size_dwords, [&] {
+ return (ctx.*atomic_func)(ctx.U32[1], ptr, scope, semantics, semantics, value, cmp_value);
+ });
+}
+
Id ImageAtomicU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value,
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
const auto& texture = ctx.images[handle & 0xFFFF];
@@ -175,6 +191,12 @@ Id EmitBufferAtomicSwap32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre
return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicExchange);
}
+Id EmitBufferAtomicCmpSwap32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value,
+ Id cmp_value) {
+ return BufferAtomicU32CmpSwap(ctx, inst, handle, address, value, cmp_value,
+ &Sirit::Module::OpAtomicCompareExchange);
+}
+
Id EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value) {
return ImageAtomicU32(ctx, inst, handle, coords, value, &Sirit::Module::OpAtomicIAdd);
}
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
index 09f9732bf..b9707224c 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -96,6 +96,8 @@ Id EmitBufferAtomicAnd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addres
Id EmitBufferAtomicOr32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicXor32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicSwap32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
+Id EmitBufferAtomicCmpSwap32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value,
+ Id cmp_value);
Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, Id index);
Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, u32 comp);
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, u32 comp);
@@ -372,6 +374,7 @@ Id EmitBitCount64(EmitContext& ctx, Id value);
Id EmitBitwiseNot32(EmitContext& ctx, Id value);
Id EmitFindSMsb32(EmitContext& ctx, Id value);
Id EmitFindUMsb32(EmitContext& ctx, Id value);
+Id EmitFindUMsb64(EmitContext& ctx, Id value);
Id EmitFindILsb32(EmitContext& ctx, Id value);
Id EmitFindILsb64(EmitContext& ctx, Id value);
Id EmitSMin32(EmitContext& ctx, Id a, Id b);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
index 10bfbb2ab..1a995354d 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
@@ -229,6 +229,20 @@ Id EmitFindUMsb32(EmitContext& ctx, Id value) {
return ctx.OpFindUMsb(ctx.U32[1], value);
}
+Id EmitFindUMsb64(EmitContext& ctx, Id value) {
+ // Vulkan restricts some bitwise operations to 32-bit only, so decompose into
+ // two 32-bit values and select the correct result.
+ const Id unpacked{ctx.OpBitcast(ctx.U32[2], value)};
+ const Id hi{ctx.OpCompositeExtract(ctx.U32[1], unpacked, 1U)};
+ const Id lo{ctx.OpCompositeExtract(ctx.U32[1], unpacked, 0U)};
+ const Id hi_msb{ctx.OpFindUMsb(ctx.U32[1], hi)};
+ const Id lo_msb{ctx.OpFindUMsb(ctx.U32[1], lo)};
+ const Id found_hi{ctx.OpINotEqual(ctx.U1[1], hi_msb, ctx.ConstU32(u32(-1)))};
+ const Id shifted_hi{ctx.OpIAdd(ctx.U32[1], hi_msb, ctx.ConstU32(32u))};
+ // value == 0 case is checked in IREmitter
+ return ctx.OpSelect(ctx.U32[1], found_hi, shifted_hi, lo_msb);
+}
+
Id EmitFindILsb32(EmitContext& ctx, Id value) {
return ctx.OpFindILsb(ctx.U32[1], value);
}
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index 68bfcc0d0..9e51f8e60 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -307,7 +307,9 @@ void EmitContext::DefineInterpolatedAttribs() {
const Id p2{OpCompositeExtract(F32[4], p_array, 2U)};
const Id p10{OpFSub(F32[4], p1, p0)};
const Id p20{OpFSub(F32[4], p2, p0)};
- const Id bary_coord{OpLoad(F32[3], gl_bary_coord_id)};
+ const Id bary_coord{OpLoad(F32[3], IsLinear(info.interp_qualifiers[i])
+ ? bary_coord_linear_id
+ : bary_coord_persp_id)};
const Id bary_coord_y{OpCompositeExtract(F32[1], bary_coord, 1)};
const Id bary_coord_z{OpCompositeExtract(F32[1], bary_coord, 2)};
const Id p10_y{OpVectorTimesScalar(F32[4], p10, bary_coord_y)};
@@ -411,8 +413,14 @@ void EmitContext::DefineInputs() {
DefineVariable(U1[1], spv::BuiltIn::FrontFacing, spv::StorageClass::Input);
}
if (profile.needs_manual_interpolation) {
- gl_bary_coord_id =
- DefineVariable(F32[3], spv::BuiltIn::BaryCoordKHR, spv::StorageClass::Input);
+ if (info.has_perspective_interp) {
+ bary_coord_persp_id =
+ DefineVariable(F32[3], spv::BuiltIn::BaryCoordKHR, spv::StorageClass::Input);
+ }
+ if (info.has_linear_interp) {
+ bary_coord_linear_id = DefineVariable(F32[3], spv::BuiltIn::BaryCoordNoPerspKHR,
+ spv::StorageClass::Input);
+ }
}
for (s32 i = 0; i < runtime_info.fs_info.num_inputs; i++) {
const auto& input = runtime_info.fs_info.inputs[i];
@@ -435,9 +443,12 @@ void EmitContext::DefineInputs() {
} else {
attr_id = DefineInput(type, semantic);
Name(attr_id, fmt::format("fs_in_attr{}", semantic));
- }
- if (input.is_flat) {
- Decorate(attr_id, spv::Decoration::Flat);
+
+ if (input.is_flat) {
+ Decorate(attr_id, spv::Decoration::Flat);
+ } else if (IsLinear(info.interp_qualifiers[i])) {
+ Decorate(attr_id, spv::Decoration::NoPerspective);
+ }
}
input_params[semantic] =
GetAttributeInfo(AmdGpu::NumberFormat::Float, attr_id, num_components, false);
@@ -634,7 +645,8 @@ void EmitContext::DefineOutputs() {
}
break;
}
- case LogicalStage::Fragment:
+ case LogicalStage::Fragment: {
+ u32 num_render_targets = 0;
for (u32 i = 0; i < IR::NumRenderTargets; i++) {
const IR::Attribute mrt{IR::Attribute::RenderTarget0 + i};
if (!info.stores.GetAny(mrt)) {
@@ -643,11 +655,21 @@ void EmitContext::DefineOutputs() {
const u32 num_components = info.stores.NumComponents(mrt);
const AmdGpu::NumberFormat num_format{runtime_info.fs_info.color_buffers[i].num_format};
const Id type{GetAttributeType(*this, num_format)[num_components]};
- const Id id{DefineOutput(type, i)};
+ Id id;
+ if (runtime_info.fs_info.dual_source_blending) {
+ id = DefineOutput(type, 0);
+ Decorate(id, spv::Decoration::Index, i);
+ } else {
+ id = DefineOutput(type, i);
+ }
Name(id, fmt::format("frag_color{}", i));
frag_outputs[i] = GetAttributeInfo(num_format, id, num_components, true);
+ ++num_render_targets;
}
+ ASSERT_MSG(!runtime_info.fs_info.dual_source_blending || num_render_targets == 2,
+ "Dual source blending enabled, there must be exactly two MRT exports");
break;
+ }
case LogicalStage::Geometry: {
output_position = DefineVariable(F32[4], spv::BuiltIn::Position, spv::StorageClass::Output);
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
index a2e0d2f47..20d936cf0 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
@@ -293,8 +293,8 @@ public:
Id shared_memory_u32_type{};
- Id interpolate_func{};
- Id gl_bary_coord_id{};
+ Id bary_coord_persp_id{};
+ Id bary_coord_linear_id{};
struct TextureDefinition {
const VectorIds* data_types;
diff --git a/src/shader_recompiler/frontend/copy_shader.cpp b/src/shader_recompiler/frontend/copy_shader.cpp
index 8750e2b18..4b5869e1d 100644
--- a/src/shader_recompiler/frontend/copy_shader.cpp
+++ b/src/shader_recompiler/frontend/copy_shader.cpp
@@ -67,6 +67,9 @@ CopyShaderData ParseCopyShader(std::span code) {
if (last_attr != IR::Attribute::Position0) {
data.num_attrs = static_cast(last_attr) - static_cast(IR::Attribute::Param0) + 1;
+ const auto it = data.attr_map.begin();
+ const u32 comp_stride = std::next(it)->first - it->first;
+ data.output_vertices = comp_stride / 64;
}
return data;
diff --git a/src/shader_recompiler/frontend/copy_shader.h b/src/shader_recompiler/frontend/copy_shader.h
index 55cc31ebd..24c7060ed 100644
--- a/src/shader_recompiler/frontend/copy_shader.h
+++ b/src/shader_recompiler/frontend/copy_shader.h
@@ -3,8 +3,8 @@
#pragma once
+#include