mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-04-04 20:37:51 -06:00
Merge branch 'main' into ime-fixes-again
This commit is contained in:
commit
b2c73badaa
7
.gitmodules
vendored
7
.gitmodules
vendored
@ -126,3 +126,10 @@
|
||||
path = externals/sdl3
|
||||
url = https://github.com/shadexternals/sdl3.git
|
||||
|
||||
branch = main
|
||||
[submodule "externals/cpp-httplib"]
|
||||
path = externals/cpp-httplib
|
||||
url = https://github.com/shadexternals/cpp-httplib.git
|
||||
[submodule "externals/openal-soft"]
|
||||
path = externals/openal-soft
|
||||
url = https://github.com/shadexternals/openal-soft.git
|
||||
|
||||
@ -228,6 +228,7 @@ find_package(glslang 15 CONFIG)
|
||||
find_package(half 1.12.0 MODULE)
|
||||
find_package(magic_enum 0.9.7 CONFIG)
|
||||
find_package(PNG 1.6 MODULE)
|
||||
find_package(OpenAL CONFIG)
|
||||
find_package(RenderDoc 1.6.0 MODULE)
|
||||
find_package(SDL3_mixer 2.8.1 CONFIG)
|
||||
if (SDL3_mixer_FOUND)
|
||||
@ -496,6 +497,8 @@ set(HLE_LIBC_INTERNAL_LIB src/core/libraries/libc_internal/libc_internal.cpp
|
||||
src/core/libraries/libc_internal/libc_internal_str.h
|
||||
src/core/libraries/libc_internal/libc_internal_math.cpp
|
||||
src/core/libraries/libc_internal/libc_internal_math.h
|
||||
src/core/libraries/libc_internal/libc_internal_threads.cpp
|
||||
src/core/libraries/libc_internal/libc_internal_threads.h
|
||||
src/core/libraries/libc_internal/printf.h
|
||||
)
|
||||
|
||||
@ -615,6 +618,7 @@ set(NP_LIBS src/core/libraries/np/np_error.h
|
||||
src/core/libraries/np/np_sns_facebook_dialog.h
|
||||
src/core/libraries/np/np_partner.cpp
|
||||
src/core/libraries/np/np_partner.h
|
||||
src/core/libraries/np/object_manager.h
|
||||
)
|
||||
|
||||
set(ZLIB_LIB src/core/libraries/zlib/zlib.cpp
|
||||
@ -1114,7 +1118,7 @@ create_target_directory_groups(shadps4)
|
||||
|
||||
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG)
|
||||
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 SDL3_mixer::SDL3_mixer pugixml::pugixml)
|
||||
target_link_libraries(shadps4 PRIVATE stb::headers libusb::usb lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json miniz fdk-aac CLI11::CLI11)
|
||||
target_link_libraries(shadps4 PRIVATE stb::headers libusb::usb lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json miniz fdk-aac CLI11::CLI11 OpenAL::OpenAL Cpp_Httplib)
|
||||
|
||||
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
|
||||
target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h")
|
||||
|
||||
2
externals/CLI11
vendored
2
externals/CLI11
vendored
@ -1 +1 @@
|
||||
Subproject commit bf5a16a26a34a9a7ad75f4a7705585e44675fef0
|
||||
Subproject commit 617af272277f8c5aefdc20894b0ebef1cd6b0104
|
||||
38
externals/CMakeLists.txt
vendored
38
externals/CMakeLists.txt
vendored
@ -276,3 +276,41 @@ if (NOT TARGET CLI11::CLI11)
|
||||
set(CLI11_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||
add_subdirectory(CLI11)
|
||||
endif()
|
||||
|
||||
|
||||
#openal
|
||||
if (NOT TARGET OpenAL::OpenAL)
|
||||
set(ALSOFT_ENABLE_MODULES OFF CACHE BOOL "" FORCE)
|
||||
set(LIBTYPE "STATIC" CACHE STRING "" FORCE)
|
||||
# Disable everything we don't need
|
||||
set(ALSOFT_UTILS OFF CACHE BOOL "" FORCE)
|
||||
set(ALSOFT_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||
set(ALSOFT_TESTS OFF CACHE BOOL "" FORCE)
|
||||
set(ALSOFT_INSTALL OFF CACHE BOOL "" FORCE)
|
||||
set(ALSOFT_CONFIG OFF CACHE BOOL "" FORCE)
|
||||
|
||||
# Backends (platform-specific)
|
||||
if (WIN32)
|
||||
set(ALSOFT_BACKEND_WASAPI ON CACHE BOOL "" FORCE)
|
||||
set(ALSOFT_BACKEND_DSOUND OFF CACHE BOOL "" FORCE)
|
||||
set(ALSOFT_BACKEND_WINMM OFF CACHE BOOL "" FORCE)
|
||||
elseif (APPLE)
|
||||
set(ALSOFT_BACKEND_COREAUDIO ON CACHE BOOL "" FORCE)
|
||||
else()
|
||||
set(ALSOFT_BACKEND_ALSA ON CACHE BOOL "" FORCE)
|
||||
set(ALSOFT_BACKEND_PULSEAUDIO ON CACHE BOOL "" FORCE)
|
||||
set(ALSOFT_BACKEND_PIPEWIRE OFF CACHE BOOL "" FORCE)
|
||||
endif()
|
||||
# Headless-safe
|
||||
set(ALSOFT_BACKEND_NULL ON CACHE BOOL "" FORCE)
|
||||
|
||||
# Static build
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
||||
|
||||
add_subdirectory(openal-soft EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
# cpp-httplib
|
||||
add_library(Cpp_Httplib INTERFACE)
|
||||
target_include_directories(Cpp_Httplib INTERFACE cpp-httplib/)
|
||||
|
||||
|
||||
1
externals/cpp-httplib
vendored
Submodule
1
externals/cpp-httplib
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit f80864ca031932351abef49b74097c67f14719c6
|
||||
1
externals/openal-soft
vendored
Submodule
1
externals/openal-soft
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit f120be6e2e7d2eb37a70f8adb5a98e5a645c5349
|
||||
2
externals/sdl3
vendored
2
externals/sdl3
vendored
@ -1 +1 @@
|
||||
Subproject commit bdb72bb3f051de32c91f5deb439a50bfd51499dc
|
||||
Subproject commit 4e2fd57e77fb4a28c0eeef0670fc4121cc2cf1f9
|
||||
@ -1,7 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <fmt/core.h>
|
||||
@ -14,6 +15,8 @@
|
||||
#include "common/path_util.h"
|
||||
#include "common/scm_rev.h"
|
||||
|
||||
#include "input/input_handler.h"
|
||||
|
||||
using std::nullopt;
|
||||
using std::optional;
|
||||
using std::string;
|
||||
@ -363,7 +366,7 @@ u32 getWindowHeight() {
|
||||
}
|
||||
|
||||
u32 getInternalScreenWidth() {
|
||||
return internalScreenHeight.get();
|
||||
return internalScreenWidth.get();
|
||||
}
|
||||
|
||||
u32 getInternalScreenHeight() {
|
||||
@ -1312,19 +1315,6 @@ void setDefaultValues(bool is_game_specific) {
|
||||
constexpr std::string_view GetDefaultGlobalConfig() {
|
||||
return R"(# Anything put here will be loaded for all games,
|
||||
# alongside the game's config or default.ini depending on your preference.
|
||||
|
||||
hotkey_renderdoc_capture = f12
|
||||
hotkey_fullscreen = f11
|
||||
hotkey_show_fps = f10
|
||||
hotkey_pause = f9
|
||||
hotkey_reload_inputs = f8
|
||||
hotkey_toggle_mouse_to_joystick = f7
|
||||
hotkey_toggle_mouse_to_gyro = f6
|
||||
hotkey_toggle_mouse_to_touchpad = delete
|
||||
hotkey_quit = lctrl, lshift, end
|
||||
|
||||
hotkey_volume_up = kpplus
|
||||
hotkey_volume_down = kpminus
|
||||
)";
|
||||
}
|
||||
|
||||
@ -1402,7 +1392,7 @@ analog_deadzone = rightjoystick, 2, 127
|
||||
override_controller_color = false, 0, 0, 255
|
||||
)";
|
||||
}
|
||||
std::filesystem::path GetFoolproofInputConfigFile(const string& game_id) {
|
||||
std::filesystem::path GetInputConfigFile(const string& game_id) {
|
||||
// Read configuration file of the game, and if it doesn't exist, generate it from default
|
||||
// If that doesn't exist either, generate that from getDefaultConfig() and try again
|
||||
// If even the folder is missing, we start with that.
|
||||
@ -1441,6 +1431,39 @@ std::filesystem::path GetFoolproofInputConfigFile(const string& game_id) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (game_id == "global") {
|
||||
std::map<string, string> default_bindings_to_add = {
|
||||
{"hotkey_renderdoc_capture", "f12"},
|
||||
{"hotkey_fullscreen", "f11"},
|
||||
{"hotkey_show_fps", "f10"},
|
||||
{"hotkey_pause", "f9"},
|
||||
{"hotkey_reload_inputs", "f8"},
|
||||
{"hotkey_toggle_mouse_to_joystick", "f7"},
|
||||
{"hotkey_toggle_mouse_to_gyro", "f6"},
|
||||
{"hotkey_toggle_mouse_to_touchpad", "delete"},
|
||||
{"hotkey_quit", "lctrl, lshift, end"},
|
||||
{"hotkey_volume_up", "kpplus"},
|
||||
{"hotkey_volume_down", "kpminus"},
|
||||
};
|
||||
std::ifstream global_in(config_file);
|
||||
string line;
|
||||
while (std::getline(global_in, line)) {
|
||||
line.erase(std::remove_if(line.begin(), line.end(),
|
||||
[](unsigned char c) { return std::isspace(c); }),
|
||||
line.end());
|
||||
std::size_t equal_pos = line.find('=');
|
||||
if (equal_pos == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
std::string output_string = line.substr(0, equal_pos);
|
||||
default_bindings_to_add.erase(output_string);
|
||||
}
|
||||
global_in.close();
|
||||
std::ofstream global_out(config_file, std::ios::app);
|
||||
for (auto const& b : default_bindings_to_add) {
|
||||
global_out << b.first << " = " << b.second << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// If game-specific config doesn't exist, create it from the default config
|
||||
if (!std::filesystem::exists(config_file)) {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
@ -187,6 +187,6 @@ std::filesystem::path getAddonInstallDir();
|
||||
void setDefaultValues(bool is_game_specific = false);
|
||||
|
||||
constexpr std::string_view GetDefaultGlobalConfig();
|
||||
std::filesystem::path GetFoolproofInputConfigFile(const std::string& game_id = "");
|
||||
std::filesystem::path GetInputConfigFile(const std::string& game_id = "");
|
||||
|
||||
}; // namespace Config
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include <fmt/format.h>
|
||||
@ -208,26 +209,41 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_lock entry_loc(_mutex);
|
||||
|
||||
if (_last_entry.message == message) {
|
||||
++_last_entry.counter;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_last_entry.counter >= 2) {
|
||||
_last_entry.message += " x" + std::to_string(_last_entry.counter);
|
||||
}
|
||||
|
||||
if (_last_entry.counter >= 1) {
|
||||
if (Config::getLogType() == "async") {
|
||||
message_queue.EmplaceWait(_last_entry);
|
||||
} else {
|
||||
ForEachBackend([this](auto& backend) { backend.Write(this->_last_entry); });
|
||||
std::fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::microseconds;
|
||||
using std::chrono::steady_clock;
|
||||
|
||||
const Entry entry = {
|
||||
this->_last_entry = {
|
||||
.timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin),
|
||||
.log_class = log_class,
|
||||
.log_level = log_level,
|
||||
.filename = filename,
|
||||
.line_num = line_num,
|
||||
.function = function,
|
||||
.message = std::move(message),
|
||||
.message = message,
|
||||
.thread = Common::GetCurrentThreadName(),
|
||||
.counter = 1,
|
||||
};
|
||||
if (Config::getLogType() == "async") {
|
||||
message_queue.EmplaceWait(entry);
|
||||
} else {
|
||||
ForEachBackend([&entry](auto& backend) { backend.Write(entry); });
|
||||
std::fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@ -259,6 +275,22 @@ private:
|
||||
}
|
||||
|
||||
void StopBackendThread() {
|
||||
// log last message
|
||||
if (_last_entry.counter >= 2) {
|
||||
_last_entry.message += " x" + std::to_string(_last_entry.counter);
|
||||
}
|
||||
|
||||
if (_last_entry.counter >= 1) {
|
||||
if (Config::getLogType() == "async") {
|
||||
message_queue.EmplaceWait(_last_entry);
|
||||
} else {
|
||||
ForEachBackend([this](auto& backend) { backend.Write(this->_last_entry); });
|
||||
std::fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
this->_last_entry = {};
|
||||
|
||||
backend_thread.request_stop();
|
||||
if (backend_thread.joinable()) {
|
||||
backend_thread.join();
|
||||
@ -292,6 +324,8 @@ private:
|
||||
MPSCQueue<Entry> message_queue{};
|
||||
std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
|
||||
std::jthread backend_thread;
|
||||
Entry _last_entry;
|
||||
std::mutex _mutex;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ struct Entry {
|
||||
std::string function;
|
||||
std::string message;
|
||||
std::string thread;
|
||||
u32 counter = 0;
|
||||
};
|
||||
|
||||
} // namespace Common::Log
|
||||
|
||||
@ -32,6 +32,9 @@ static bool show_simple_fps = false;
|
||||
static bool visibility_toggled = false;
|
||||
static bool show_quit_window = false;
|
||||
|
||||
static bool show_volume = false;
|
||||
static float volume_start_time;
|
||||
|
||||
static float fps_scale = 1.0f;
|
||||
static int dump_frame_count = 1;
|
||||
|
||||
@ -454,6 +457,27 @@ void L::Draw() {
|
||||
End();
|
||||
}
|
||||
|
||||
if (show_volume) {
|
||||
float current_time = ImGui::GetTime();
|
||||
|
||||
// Show volume for 3 seconds
|
||||
if (current_time - volume_start_time >= 3.0) {
|
||||
show_volume = false;
|
||||
} else {
|
||||
SetNextWindowPos(ImVec2(ImGui::GetMainViewport()->WorkPos.x +
|
||||
ImGui::GetMainViewport()->WorkSize.x - 10,
|
||||
ImGui::GetMainViewport()->WorkPos.y + 10),
|
||||
ImGuiCond_Always, ImVec2(1.0f, 0.0f));
|
||||
|
||||
if (ImGui::Begin("Volume Window", &show_volume,
|
||||
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking)) {
|
||||
Text("Volume: %d", Config::getVolumeSlider());
|
||||
}
|
||||
End();
|
||||
}
|
||||
}
|
||||
|
||||
PopID();
|
||||
}
|
||||
|
||||
@ -482,4 +506,9 @@ void ToggleQuitWindow() {
|
||||
show_quit_window = !show_quit_window;
|
||||
}
|
||||
|
||||
void ShowVolume() {
|
||||
volume_start_time = ImGui::GetTime();
|
||||
show_volume = true;
|
||||
}
|
||||
|
||||
} // namespace Overlay
|
||||
|
||||
@ -32,5 +32,6 @@ namespace Overlay {
|
||||
void ToggleSimpleFps();
|
||||
void SetSimpleFps(bool enabled);
|
||||
void ToggleQuitWindow();
|
||||
void ShowVolume();
|
||||
|
||||
} // namespace Overlay
|
||||
|
||||
@ -35,3 +35,11 @@ bool EmulatorState::IsAutoPatchesLoadEnabled() const {
|
||||
void EmulatorState::SetAutoPatchesLoadEnabled(bool enable) {
|
||||
m_load_patches_auto = enable;
|
||||
}
|
||||
|
||||
bool EmulatorState::IsGameSpecifigConfigUsed() const {
|
||||
return m_game_specific_config_used;
|
||||
}
|
||||
|
||||
void EmulatorState::SetGameSpecifigConfigUsed(bool used) {
|
||||
m_game_specific_config_used = used;
|
||||
}
|
||||
|
||||
@ -18,6 +18,8 @@ public:
|
||||
void SetGameRunning(bool running);
|
||||
bool IsAutoPatchesLoadEnabled() const;
|
||||
void SetAutoPatchesLoadEnabled(bool enable);
|
||||
bool IsGameSpecifigConfigUsed() const;
|
||||
void SetGameSpecifigConfigUsed(bool used);
|
||||
|
||||
private:
|
||||
static std::shared_ptr<EmulatorState> s_instance;
|
||||
@ -26,4 +28,5 @@ private:
|
||||
// state variables
|
||||
bool m_running = false;
|
||||
bool m_load_patches_auto = true;
|
||||
};
|
||||
bool m_game_specific_config_used = false;
|
||||
};
|
||||
|
||||
@ -496,11 +496,11 @@ s32 PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* sta
|
||||
|
||||
state->rerouteCounter = 0;
|
||||
state->flag = 0;
|
||||
LOG_INFO(Lib_AudioOut,
|
||||
"called, handle={:#x}, state={}, output={}, channel={}, volume={}, "
|
||||
"rerouteCounter={}, flag={}",
|
||||
handle, fmt::ptr(state), state->output, state->channel, state->volume,
|
||||
state->rerouteCounter, state->flag);
|
||||
LOG_DEBUG(Lib_AudioOut,
|
||||
"called, handle={:#x}, state={}, output={}, channel={}, volume={}, "
|
||||
"rerouteCounter={}, flag={}",
|
||||
handle, fmt::ptr(state), state->output, state->channel, state->volume,
|
||||
state->rerouteCounter, state->flag);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -729,7 +729,7 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) {
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudioOutSetMixLevelPadSpk(s32 handle, s32 mixLevel) {
|
||||
LOG_ERROR(Lib_AudioOut, "(STUBBED) called");
|
||||
LOG_INFO(Lib_AudioOut, "(STUBBED) called");
|
||||
if (lazy_init.load(std::memory_order_relaxed) == 0 || audio == nullptr) {
|
||||
LOG_ERROR(Lib_AudioOut, "audio is not init");
|
||||
return ORBIS_AUDIO_OUT_ERROR_NOT_INIT;
|
||||
@ -769,6 +769,19 @@ s32 PS4_SYSV_ABI sceAudioOutSetMixLevelPadSpk(s32 handle, s32 mixLevel) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudioOutGetSystemState(OrbisAudioOutSystemState* state) {
|
||||
if (lazy_init.load(std::memory_order_relaxed) == 0 || audio == nullptr) {
|
||||
LOG_ERROR(Lib_AudioOut, "audio is not init");
|
||||
return ORBIS_AUDIO_OUT_ERROR_NOT_INIT;
|
||||
}
|
||||
if (state == nullptr) {
|
||||
return ORBIS_AUDIO_OUT_ERROR_INVALID_POINTER;
|
||||
}
|
||||
memset(state, 0, sizeof(*state));
|
||||
LOG_DEBUG(Lib_AudioOut, "called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stubbed functions
|
||||
**/
|
||||
@ -892,11 +905,6 @@ s32 PS4_SYSV_ABI sceAudioOutGetSparkVss() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudioOutGetSystemState() {
|
||||
LOG_ERROR(Lib_AudioOut, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceAudioOutInitIpmiGetSession() {
|
||||
LOG_ERROR(Lib_AudioOut, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
|
||||
@ -97,6 +97,12 @@ struct OrbisAudioOutPortState {
|
||||
u64 reserved64[2];
|
||||
};
|
||||
|
||||
struct OrbisAudioOutSystemState {
|
||||
float loudness;
|
||||
u8 reserved8[4];
|
||||
u64 reserved64[3];
|
||||
};
|
||||
|
||||
struct AudioFormatInfo {
|
||||
bool is_float;
|
||||
u8 sample_size;
|
||||
@ -162,7 +168,7 @@ s32 PS4_SYSV_ABI sceAudioOutGetSimulatedBusUsableStatusByBusType();
|
||||
s32 PS4_SYSV_ABI sceAudioOutGetSimulatedHandleStatusInfo();
|
||||
s32 PS4_SYSV_ABI sceAudioOutGetSimulatedHandleStatusInfo2();
|
||||
s32 PS4_SYSV_ABI sceAudioOutGetSparkVss();
|
||||
s32 PS4_SYSV_ABI sceAudioOutGetSystemState();
|
||||
s32 PS4_SYSV_ABI sceAudioOutGetSystemState(OrbisAudioOutSystemState* state);
|
||||
s32 PS4_SYSV_ABI sceAudioOutInit();
|
||||
s32 PS4_SYSV_ABI sceAudioOutInitIpmiGetSession();
|
||||
s32 PS4_SYSV_ABI sceAudioOutMasteringGetState();
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <SDL3/SDL_audio.h>
|
||||
#include <SDL3/SDL_hints.h>
|
||||
@ -14,15 +15,32 @@
|
||||
#include "core/libraries/audio/audioout_backend.h"
|
||||
#include "core/libraries/kernel/threads.h"
|
||||
|
||||
// SIMD support detection
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
#include <immintrin.h>
|
||||
#define HAS_SSE2
|
||||
#endif
|
||||
|
||||
#define SDL_INVALID_AUDIODEVICEID 0
|
||||
|
||||
namespace Libraries::AudioOut {
|
||||
|
||||
// Volume constants
|
||||
constexpr float VOLUME_0DB = 32768.0f; // 1 << 15
|
||||
constexpr float INV_VOLUME_0DB = 1.0f / VOLUME_0DB;
|
||||
constexpr float VOLUME_EPSILON = 0.001f;
|
||||
// Timing constants
|
||||
constexpr u64 VOLUME_CHECK_INTERVAL_US = 50000; // Check every 50ms
|
||||
constexpr u64 MIN_SLEEP_THRESHOLD_US = 10;
|
||||
constexpr u64 TIMING_RESYNC_THRESHOLD_US = 100000; // Resync if >100ms behind
|
||||
|
||||
// Queue management
|
||||
constexpr u32 QUEUE_MULTIPLIER = 4;
|
||||
// Memory alignment for SIMD
|
||||
constexpr size_t AUDIO_BUFFER_ALIGNMENT = 32;
|
||||
|
||||
// Channel positions
|
||||
enum ChannelPos {
|
||||
enum ChannelPos : u8 {
|
||||
FL = 0,
|
||||
FR = 1,
|
||||
FC = 2,
|
||||
@ -45,187 +63,210 @@ public:
|
||||
num_channels(port.format_info.num_channels), is_float(port.format_info.is_float),
|
||||
is_std(port.format_info.is_std), channel_layout(port.format_info.channel_layout) {
|
||||
|
||||
// Calculate timing
|
||||
period_us = (1000000ULL * buffer_frames + sample_rate / 2) / sample_rate;
|
||||
last_output_time = 0;
|
||||
next_output_time = 0;
|
||||
|
||||
// Allocate internal buffer
|
||||
internal_buffer_size = buffer_frames * sizeof(float) * num_channels;
|
||||
internal_buffer = std::malloc(internal_buffer_size);
|
||||
if (!internal_buffer) {
|
||||
LOG_ERROR(Lib_AudioOut, "Failed to allocate internal audio buffer");
|
||||
return;
|
||||
if (!Initialize(port.type)) {
|
||||
LOG_ERROR(Lib_AudioOut, "Failed to initialize SDL audio backend");
|
||||
}
|
||||
|
||||
// Initialize current gain
|
||||
current_gain.store(Config::getVolumeSlider() / 100.0f);
|
||||
|
||||
// Select converter function
|
||||
SelectConverter();
|
||||
|
||||
// Open SDL device
|
||||
if (!OpenDevice(port.type)) {
|
||||
std::free(internal_buffer);
|
||||
internal_buffer = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
CalculateQueueThreshold();
|
||||
}
|
||||
|
||||
~SDLPortBackend() override {
|
||||
if (stream) {
|
||||
SDL_DestroyAudioStream(stream);
|
||||
}
|
||||
if (internal_buffer) {
|
||||
std::free(internal_buffer);
|
||||
}
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
void Output(void* ptr) override {
|
||||
if (!stream || !internal_buffer) {
|
||||
if (!stream || !internal_buffer || !convert) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for volume changes and update if needed
|
||||
UpdateVolumeIfChanged();
|
||||
|
||||
// Get current time in microseconds
|
||||
u64 current_time = Kernel::sceKernelGetProcessTime();
|
||||
|
||||
if (ptr != nullptr) {
|
||||
// Simple format conversion (no volume application)
|
||||
convert(ptr, internal_buffer, buffer_frames, nullptr);
|
||||
|
||||
if (next_output_time == 0) {
|
||||
next_output_time = current_time + period_us;
|
||||
} else if (current_time > next_output_time) {
|
||||
next_output_time = current_time + period_us;
|
||||
} else {
|
||||
u64 wait_until = next_output_time;
|
||||
next_output_time += period_us;
|
||||
|
||||
if (current_time < wait_until) {
|
||||
u64 sleep_us = wait_until - current_time;
|
||||
if (sleep_us > 10) {
|
||||
sleep_us -= 10;
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(sleep_us));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
last_output_time = current_time;
|
||||
|
||||
// Check queue and clear if backed up
|
||||
if (const auto queued = SDL_GetAudioStreamQueued(stream); queued >= queue_threshold) {
|
||||
LOG_DEBUG(Lib_AudioOut, "Clearing backed up audio queue ({} >= {})", queued,
|
||||
queue_threshold);
|
||||
SDL_ClearAudioStream(stream);
|
||||
CalculateQueueThreshold();
|
||||
}
|
||||
|
||||
if (!SDL_PutAudioStreamData(stream, internal_buffer, internal_buffer_size)) {
|
||||
LOG_ERROR(Lib_AudioOut, "Failed to output to SDL audio stream: {}", SDL_GetError());
|
||||
}
|
||||
if (ptr == nullptr) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateVolumeIfChanged();
|
||||
const u64 current_time = Kernel::sceKernelGetProcessTime();
|
||||
convert(ptr, internal_buffer, buffer_frames, nullptr);
|
||||
HandleTiming(current_time);
|
||||
|
||||
if ((output_count++ & 0xF) == 0) { // Check every 16 outputs
|
||||
ManageAudioQueue();
|
||||
}
|
||||
|
||||
if (!SDL_PutAudioStreamData(stream, internal_buffer, internal_buffer_size)) [[unlikely]] {
|
||||
LOG_ERROR(Lib_AudioOut, "Failed to output to SDL audio stream: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
last_output_time.store(current_time, std::memory_order_release);
|
||||
}
|
||||
|
||||
void SetVolume(const std::array<int, 8>& ch_volumes) override {
|
||||
if (!stream) {
|
||||
if (!stream) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
float max_channel_gain = 0.0f;
|
||||
for (int i = 0; i < num_channels && i < 8; i++) {
|
||||
float channel_gain = static_cast<float>(ch_volumes[i]) / VOLUME_0DB;
|
||||
const u32 channels_to_check = std::min(num_channels, 8u);
|
||||
|
||||
for (u32 i = 0; i < channels_to_check; i++) {
|
||||
const float channel_gain = static_cast<float>(ch_volumes[i]) * INV_VOLUME_0DB;
|
||||
max_channel_gain = std::max(max_channel_gain, channel_gain);
|
||||
}
|
||||
|
||||
// Combine with global volume slider
|
||||
float total_gain = max_channel_gain * (Config::getVolumeSlider() / 100.0f);
|
||||
const float slider_gain = Config::getVolumeSlider() * 0.01f; // Faster than /100.0f
|
||||
const float total_gain = max_channel_gain * slider_gain;
|
||||
|
||||
std::lock_guard<std::mutex> lock(volume_mutex);
|
||||
const float current = current_gain.load(std::memory_order_acquire);
|
||||
if (std::abs(total_gain - current) < VOLUME_EPSILON) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply volume change
|
||||
if (SDL_SetAudioStreamGain(stream, total_gain)) {
|
||||
current_gain.store(total_gain);
|
||||
current_gain.store(total_gain, std::memory_order_release);
|
||||
LOG_DEBUG(Lib_AudioOut,
|
||||
"Set combined audio gain to {:.3f} (channel: {:.3f}, slider: {:.3f})",
|
||||
total_gain, max_channel_gain, Config::getVolumeSlider() / 100.0f);
|
||||
total_gain, max_channel_gain, slider_gain);
|
||||
} else {
|
||||
LOG_ERROR(Lib_AudioOut, "Failed to set audio stream gain: {}", SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
||||
u64 GetLastOutputTime() const {
|
||||
return last_output_time;
|
||||
return last_output_time.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<bool> volume_update_needed{false};
|
||||
u64 last_volume_check_time{0};
|
||||
static constexpr u64 VOLUME_CHECK_INTERVAL_US = 50000; // Check every 50ms
|
||||
bool Initialize(OrbisAudioOutPort type) {
|
||||
// Calculate timing parameters
|
||||
period_us = (1000000ULL * buffer_frames + sample_rate / 2) / sample_rate;
|
||||
|
||||
// Allocate aligned internal buffer for SIMD operations
|
||||
internal_buffer_size = buffer_frames * sizeof(float) * num_channels;
|
||||
|
||||
#ifdef _WIN32
|
||||
internal_buffer = _aligned_malloc(internal_buffer_size, AUDIO_BUFFER_ALIGNMENT);
|
||||
#else
|
||||
if (posix_memalign(&internal_buffer, AUDIO_BUFFER_ALIGNMENT, internal_buffer_size) != 0) {
|
||||
internal_buffer = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!internal_buffer) {
|
||||
LOG_ERROR(Lib_AudioOut, "Failed to allocate aligned audio buffer of size {}",
|
||||
internal_buffer_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize current gain
|
||||
current_gain.store(Config::getVolumeSlider() * 0.01f, std::memory_order_relaxed);
|
||||
|
||||
if (!SelectConverter()) {
|
||||
FreeAlignedBuffer();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open SDL device
|
||||
if (!OpenDevice(type)) {
|
||||
FreeAlignedBuffer();
|
||||
return false;
|
||||
}
|
||||
|
||||
CalculateQueueThreshold();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Cleanup() {
|
||||
if (stream) {
|
||||
SDL_DestroyAudioStream(stream);
|
||||
stream = nullptr;
|
||||
}
|
||||
FreeAlignedBuffer();
|
||||
}
|
||||
|
||||
void FreeAlignedBuffer() {
|
||||
if (internal_buffer) {
|
||||
#ifdef _WIN32
|
||||
_aligned_free(internal_buffer);
|
||||
#else
|
||||
free(internal_buffer);
|
||||
#endif
|
||||
internal_buffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateVolumeIfChanged() {
|
||||
u64 current_time = Kernel::sceKernelGetProcessTime();
|
||||
const u64 current_time = Kernel::sceKernelGetProcessTime();
|
||||
|
||||
// Only check volume every 50ms to reduce overhead
|
||||
if (current_time - last_volume_check_time >= VOLUME_CHECK_INTERVAL_US) {
|
||||
last_volume_check_time = current_time;
|
||||
if (current_time - last_volume_check_time < VOLUME_CHECK_INTERVAL_US) {
|
||||
return;
|
||||
}
|
||||
|
||||
float config_volume = Config::getVolumeSlider() / 100.0f;
|
||||
float stored_gain = current_gain.load();
|
||||
last_volume_check_time = current_time;
|
||||
|
||||
if (std::abs(config_volume - stored_gain) > 0.001f) {
|
||||
if (SDL_SetAudioStreamGain(stream, config_volume)) {
|
||||
current_gain.store(config_volume);
|
||||
LOG_DEBUG(Lib_AudioOut, "Updated audio gain to {:.3f}", config_volume);
|
||||
} else {
|
||||
LOG_ERROR(Lib_AudioOut, "Failed to set audio stream gain: {}", SDL_GetError());
|
||||
}
|
||||
const float config_volume = Config::getVolumeSlider() * 0.01f;
|
||||
const float stored_gain = current_gain.load(std::memory_order_acquire);
|
||||
|
||||
// Only update if the difference is significant
|
||||
if (std::abs(config_volume - stored_gain) > VOLUME_EPSILON) {
|
||||
if (SDL_SetAudioStreamGain(stream, config_volume)) {
|
||||
current_gain.store(config_volume, std::memory_order_release);
|
||||
LOG_DEBUG(Lib_AudioOut, "Updated audio gain to {:.3f}", config_volume);
|
||||
} else {
|
||||
LOG_ERROR(Lib_AudioOut, "Failed to set audio stream gain: {}", SDL_GetError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandleTiming(u64 current_time) {
|
||||
if (next_output_time == 0) [[unlikely]] {
|
||||
// First output - set initial timing
|
||||
next_output_time = current_time + period_us;
|
||||
return;
|
||||
}
|
||||
|
||||
const s64 time_diff = static_cast<s64>(current_time - next_output_time);
|
||||
|
||||
if (time_diff > static_cast<s64>(TIMING_RESYNC_THRESHOLD_US)) [[unlikely]] {
|
||||
// We're far behind - resync
|
||||
next_output_time = current_time + period_us;
|
||||
} else if (time_diff < 0) {
|
||||
// We're ahead of schedule - wait
|
||||
const u64 time_to_wait = static_cast<u64>(-time_diff);
|
||||
next_output_time += period_us;
|
||||
|
||||
if (time_to_wait > MIN_SLEEP_THRESHOLD_US) {
|
||||
// Sleep for most of the wait period
|
||||
const u64 sleep_duration = time_to_wait - MIN_SLEEP_THRESHOLD_US;
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(sleep_duration));
|
||||
}
|
||||
} else {
|
||||
// Slightly behind or on time - just advance
|
||||
next_output_time += period_us;
|
||||
}
|
||||
}
|
||||
|
||||
void ManageAudioQueue() {
|
||||
const auto queued = SDL_GetAudioStreamQueued(stream);
|
||||
|
||||
if (queued >= queue_threshold) [[unlikely]] {
|
||||
LOG_DEBUG(Lib_AudioOut, "Clearing backed up audio queue ({} >= {})", queued,
|
||||
queue_threshold);
|
||||
SDL_ClearAudioStream(stream);
|
||||
CalculateQueueThreshold();
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenDevice(OrbisAudioOutPort type) {
|
||||
const SDL_AudioSpec fmt = {
|
||||
.format = SDL_AUDIO_F32LE, // Always use float for internal processing
|
||||
.format = SDL_AUDIO_F32LE,
|
||||
.channels = static_cast<u8>(num_channels),
|
||||
.freq = static_cast<int>(sample_rate),
|
||||
};
|
||||
|
||||
// Determine device name
|
||||
std::string device_name = GetDeviceName(type);
|
||||
SDL_AudioDeviceID dev_id = SDL_INVALID_AUDIODEVICEID;
|
||||
// Determine device
|
||||
const std::string device_name = GetDeviceName(type);
|
||||
const SDL_AudioDeviceID dev_id = SelectAudioDevice(device_name, type);
|
||||
|
||||
if (device_name == "None") {
|
||||
LOG_INFO(Lib_AudioOut, "Audio device disabled for port type {}",
|
||||
static_cast<int>(type));
|
||||
if (dev_id == SDL_INVALID_AUDIODEVICEID) {
|
||||
return false;
|
||||
} else if (device_name.empty() || device_name == "Default Device") {
|
||||
dev_id = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
|
||||
} else {
|
||||
int num_devices = 0;
|
||||
SDL_AudioDeviceID* dev_array = SDL_GetAudioPlaybackDevices(&num_devices);
|
||||
|
||||
if (dev_array) {
|
||||
bool found = false;
|
||||
for (int i = 0; i < num_devices; i++) {
|
||||
const char* dev_name = SDL_GetAudioDeviceName(dev_array[i]);
|
||||
if (dev_name && std::string(dev_name) == device_name) {
|
||||
dev_id = dev_array[i];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
SDL_free(dev_array);
|
||||
|
||||
if (!found) {
|
||||
LOG_WARNING(Lib_AudioOut, "Audio device '{}' not found, using default",
|
||||
device_name);
|
||||
dev_id = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING(Lib_AudioOut, "No audio devices found, using default");
|
||||
dev_id = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
|
||||
}
|
||||
}
|
||||
|
||||
// Create audio stream
|
||||
@ -235,30 +276,15 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set channel map
|
||||
if (num_channels > 0) {
|
||||
std::vector<int> channel_map(num_channels);
|
||||
|
||||
if (is_std && num_channels == 8) {
|
||||
// Standard 8CH layout
|
||||
channel_map = {FL, FR, FC, LF, STD_SL, STD_SR, STD_BL, STD_BR};
|
||||
} else {
|
||||
// Use provided channel layout
|
||||
for (int i = 0; i < num_channels; i++) {
|
||||
channel_map[i] = channel_layout[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (!SDL_SetAudioStreamInputChannelMap(stream, channel_map.data(), num_channels)) {
|
||||
LOG_ERROR(Lib_AudioOut, "Failed to set channel map: {}", SDL_GetError());
|
||||
SDL_DestroyAudioStream(stream);
|
||||
stream = nullptr;
|
||||
return false;
|
||||
}
|
||||
// Configure channel mapping
|
||||
if (!ConfigureChannelMap()) {
|
||||
SDL_DestroyAudioStream(stream);
|
||||
stream = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set initial volume
|
||||
float initial_gain = current_gain.load();
|
||||
const float initial_gain = current_gain.load(std::memory_order_relaxed);
|
||||
if (!SDL_SetAudioStreamGain(stream, initial_gain)) {
|
||||
LOG_WARNING(Lib_AudioOut, "Failed to set initial audio gain: {}", SDL_GetError());
|
||||
}
|
||||
@ -276,24 +302,81 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string GetDeviceName(OrbisAudioOutPort type) {
|
||||
SDL_AudioDeviceID SelectAudioDevice(const std::string& device_name, OrbisAudioOutPort type) {
|
||||
if (device_name == "None") {
|
||||
LOG_INFO(Lib_AudioOut, "Audio device disabled for port type {}",
|
||||
static_cast<int>(type));
|
||||
return SDL_INVALID_AUDIODEVICEID;
|
||||
}
|
||||
|
||||
if (device_name.empty() || device_name == "Default Device") {
|
||||
return SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
|
||||
}
|
||||
|
||||
// Search for specific device
|
||||
int num_devices = 0;
|
||||
SDL_AudioDeviceID* dev_array = SDL_GetAudioPlaybackDevices(&num_devices);
|
||||
|
||||
if (!dev_array) {
|
||||
LOG_WARNING(Lib_AudioOut, "No audio devices found, using default");
|
||||
return SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
|
||||
}
|
||||
|
||||
SDL_AudioDeviceID selected_device = SDL_INVALID_AUDIODEVICEID;
|
||||
|
||||
for (int i = 0; i < num_devices; i++) {
|
||||
const char* dev_name = SDL_GetAudioDeviceName(dev_array[i]);
|
||||
if (dev_name && device_name == dev_name) {
|
||||
selected_device = dev_array[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_free(dev_array);
|
||||
|
||||
if (selected_device == SDL_INVALID_AUDIODEVICEID) {
|
||||
LOG_WARNING(Lib_AudioOut, "Audio device '{}' not found, using default", device_name);
|
||||
return SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
|
||||
}
|
||||
|
||||
return selected_device;
|
||||
}
|
||||
|
||||
bool ConfigureChannelMap() {
|
||||
if (num_channels == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<int> channel_map(num_channels);
|
||||
|
||||
if (is_std && num_channels == 8) {
|
||||
// Standard 8CH layout requires remapping
|
||||
channel_map = {FL, FR, FC, LF, STD_SL, STD_SR, STD_BL, STD_BR};
|
||||
} else {
|
||||
std::copy_n(channel_layout.begin(), num_channels, channel_map.begin());
|
||||
}
|
||||
|
||||
if (!SDL_SetAudioStreamInputChannelMap(stream, channel_map.data(), num_channels)) {
|
||||
LOG_ERROR(Lib_AudioOut, "Failed to set channel map: {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string GetDeviceName(OrbisAudioOutPort type) const {
|
||||
switch (type) {
|
||||
case OrbisAudioOutPort::Main:
|
||||
case OrbisAudioOutPort::Bgm:
|
||||
return Config::getMainOutputDevice();
|
||||
// case OrbisAudioOutPort::Voice:
|
||||
// case OrbisAudioOutPort::Personal:
|
||||
// return Config::getHeadphoneOutputDevice();
|
||||
case OrbisAudioOutPort::PadSpk:
|
||||
return Config::getPadSpkOutputDevice();
|
||||
// case OrbisAudioOutPort::Aux:
|
||||
// return Config::getSpecialOutputDevice();
|
||||
default:
|
||||
return Config::getMainOutputDevice();
|
||||
}
|
||||
}
|
||||
|
||||
void SelectConverter() {
|
||||
bool SelectConverter() {
|
||||
if (is_float) {
|
||||
switch (num_channels) {
|
||||
case 1:
|
||||
@ -303,15 +386,11 @@ private:
|
||||
convert = &ConvertF32Stereo;
|
||||
break;
|
||||
case 8:
|
||||
if (is_std) {
|
||||
convert = &ConvertF32Std8CH;
|
||||
} else {
|
||||
convert = &ConvertF32_8CH;
|
||||
}
|
||||
convert = is_std ? &ConvertF32Std8CH : &ConvertF32_8CH;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Lib_AudioOut, "Unsupported float channel count: {}", num_channels);
|
||||
convert = nullptr;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
switch (num_channels) {
|
||||
@ -319,32 +398,43 @@ private:
|
||||
convert = &ConvertS16Mono;
|
||||
break;
|
||||
case 2:
|
||||
#if defined(HAS_SSE2)
|
||||
convert = &ConvertS16StereoSIMD;
|
||||
#else
|
||||
convert = &ConvertS16Stereo;
|
||||
#endif
|
||||
break;
|
||||
case 8:
|
||||
#if defined(HAS_SSE2)
|
||||
convert = &ConvertS16_8CH_SIMD;
|
||||
#else
|
||||
convert = &ConvertS16_8CH;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Lib_AudioOut, "Unsupported S16 channel count: {}", num_channels);
|
||||
convert = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CalculateQueueThreshold() {
|
||||
if (!stream)
|
||||
if (!stream) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_AudioSpec discard;
|
||||
int sdl_buffer_frames;
|
||||
int sdl_buffer_frames = 0;
|
||||
|
||||
if (!SDL_GetAudioDeviceFormat(SDL_GetAudioStreamDevice(stream), &discard,
|
||||
&sdl_buffer_frames)) {
|
||||
LOG_WARNING(Lib_AudioOut, "Failed to get SDL buffer size: {}", SDL_GetError());
|
||||
sdl_buffer_frames = 0;
|
||||
}
|
||||
|
||||
u32 sdl_buffer_size = sdl_buffer_frames * sizeof(float) * num_channels;
|
||||
queue_threshold = std::max(guest_buffer_size, sdl_buffer_size) * 4;
|
||||
const u32 sdl_buffer_size = sdl_buffer_frames * sizeof(float) * num_channels;
|
||||
queue_threshold = std::max(guest_buffer_size, sdl_buffer_size) * QUEUE_MULTIPLIER;
|
||||
|
||||
LOG_DEBUG(Lib_AudioOut, "Audio queue threshold: {} bytes (SDL buffer: {} frames)",
|
||||
queue_threshold, sdl_buffer_frames);
|
||||
@ -352,15 +442,12 @@ private:
|
||||
|
||||
using ConverterFunc = void (*)(const void* src, void* dst, u32 frames, const float* volumes);
|
||||
|
||||
// Remove volume parameter and application from all converters
|
||||
static void ConvertS16Mono(const void* src, void* dst, u32 frames, const float*) {
|
||||
const s16* s = static_cast<const s16*>(src);
|
||||
float* d = static_cast<float*>(dst);
|
||||
|
||||
constexpr float inv_scale = 1.0f / VOLUME_0DB;
|
||||
|
||||
for (u32 i = 0; i < frames; i++) {
|
||||
d[i] = s[i] * inv_scale;
|
||||
d[i] = s[i] * INV_VOLUME_0DB;
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,28 +455,82 @@ private:
|
||||
const s16* s = static_cast<const s16*>(src);
|
||||
float* d = static_cast<float*>(dst);
|
||||
|
||||
constexpr float inv_scale = 1.0f / VOLUME_0DB;
|
||||
|
||||
for (u32 i = 0; i < frames; i++) {
|
||||
d[i * 2] = s[i * 2] * inv_scale;
|
||||
d[i * 2 + 1] = s[i * 2 + 1] * inv_scale;
|
||||
const u32 num_samples = frames << 1; // * 2
|
||||
for (u32 i = 0; i < num_samples; i++) {
|
||||
d[i] = s[i] * INV_VOLUME_0DB;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAS_SSE2
|
||||
static void ConvertS16StereoSIMD(const void* src, void* dst, u32 frames, const float*) {
|
||||
const s16* s = static_cast<const s16*>(src);
|
||||
float* d = static_cast<float*>(dst);
|
||||
|
||||
const __m128 scale = _mm_set1_ps(INV_VOLUME_0DB);
|
||||
const u32 num_samples = frames << 1;
|
||||
u32 i = 0;
|
||||
|
||||
// Process 8 samples at a time (4 stereo frames)
|
||||
for (; i + 8 <= num_samples; i += 8) {
|
||||
// Load 8 s16 values
|
||||
__m128i s16_vals = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&s[i]));
|
||||
|
||||
// Convert to 32-bit integers
|
||||
__m128i s32_lo = _mm_cvtepi16_epi32(s16_vals);
|
||||
__m128i s32_hi = _mm_cvtepi16_epi32(_mm_srli_si128(s16_vals, 8));
|
||||
|
||||
// Convert to float and scale
|
||||
__m128 f_lo = _mm_mul_ps(_mm_cvtepi32_ps(s32_lo), scale);
|
||||
__m128 f_hi = _mm_mul_ps(_mm_cvtepi32_ps(s32_hi), scale);
|
||||
|
||||
// Store results
|
||||
_mm_storeu_ps(&d[i], f_lo);
|
||||
_mm_storeu_ps(&d[i + 4], f_hi);
|
||||
}
|
||||
|
||||
// Handle remaining samples
|
||||
for (; i < num_samples; i++) {
|
||||
d[i] = s[i] * INV_VOLUME_0DB;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ConvertS16_8CH(const void* src, void* dst, u32 frames, const float*) {
|
||||
const s16* s = static_cast<const s16*>(src);
|
||||
float* d = static_cast<float*>(dst);
|
||||
|
||||
constexpr float inv_scale = 1.0f / VOLUME_0DB;
|
||||
|
||||
for (u32 i = 0; i < frames; i++) {
|
||||
for (int ch = 0; ch < 8; ch++) {
|
||||
d[i * 8 + ch] = s[i * 8 + ch] * inv_scale;
|
||||
}
|
||||
const u32 num_samples = frames << 3; // * 8
|
||||
for (u32 i = 0; i < num_samples; i++) {
|
||||
d[i] = s[i] * INV_VOLUME_0DB;
|
||||
}
|
||||
}
|
||||
|
||||
// Float converters become simple memcpy or passthrough
|
||||
#ifdef HAS_SSE2
|
||||
static void ConvertS16_8CH_SIMD(const void* src, void* dst, u32 frames, const float*) {
|
||||
const s16* s = static_cast<const s16*>(src);
|
||||
float* d = static_cast<float*>(dst);
|
||||
|
||||
const __m128 scale = _mm_set1_ps(INV_VOLUME_0DB);
|
||||
const u32 num_samples = frames << 3;
|
||||
u32 i = 0;
|
||||
|
||||
// Process 8 samples at a time
|
||||
for (; i + 8 <= num_samples; i += 8) {
|
||||
__m128i s16_vals = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&s[i]));
|
||||
__m128i s32_lo = _mm_cvtepi16_epi32(s16_vals);
|
||||
__m128i s32_hi = _mm_cvtepi16_epi32(_mm_srli_si128(s16_vals, 8));
|
||||
__m128 f_lo = _mm_mul_ps(_mm_cvtepi32_ps(s32_lo), scale);
|
||||
__m128 f_hi = _mm_mul_ps(_mm_cvtepi32_ps(s32_hi), scale);
|
||||
_mm_storeu_ps(&d[i], f_lo);
|
||||
_mm_storeu_ps(&d[i + 4], f_hi);
|
||||
}
|
||||
|
||||
for (; i < num_samples; i++) {
|
||||
d[i] = s[i] * INV_VOLUME_0DB;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ConvertF32Mono(const void* src, void* dst, u32 frames, const float*) {
|
||||
std::memcpy(dst, src, frames * sizeof(float));
|
||||
}
|
||||
@ -406,50 +547,54 @@ private:
|
||||
const float* s = static_cast<const float*>(src);
|
||||
float* d = static_cast<float*>(dst);
|
||||
|
||||
// Channel remapping for standard 8CH layout
|
||||
for (u32 i = 0; i < frames; i++) {
|
||||
d[i * 8 + FL] = s[i * 8 + FL];
|
||||
d[i * 8 + FR] = s[i * 8 + FR];
|
||||
d[i * 8 + FC] = s[i * 8 + FC];
|
||||
d[i * 8 + LF] = s[i * 8 + LF];
|
||||
d[i * 8 + SL] = s[i * 8 + STD_SL]; // Channel remapping still needed
|
||||
d[i * 8 + SR] = s[i * 8 + STD_SR];
|
||||
d[i * 8 + BL] = s[i * 8 + STD_BL];
|
||||
d[i * 8 + BR] = s[i * 8 + STD_BR];
|
||||
const u32 offset = i << 3; // * 8
|
||||
|
||||
d[offset + FL] = s[offset + FL];
|
||||
d[offset + FR] = s[offset + FR];
|
||||
d[offset + FC] = s[offset + FC];
|
||||
d[offset + LF] = s[offset + LF];
|
||||
d[offset + SL] = s[offset + STD_SL];
|
||||
d[offset + SR] = s[offset + STD_SR];
|
||||
d[offset + BL] = s[offset + STD_BL];
|
||||
d[offset + BR] = s[offset + STD_BR];
|
||||
}
|
||||
}
|
||||
|
||||
// Member variables
|
||||
u32 frame_size;
|
||||
u32 guest_buffer_size;
|
||||
u32 buffer_frames;
|
||||
u32 sample_rate;
|
||||
u32 num_channels;
|
||||
bool is_float;
|
||||
bool is_std;
|
||||
std::array<int, 8> channel_layout;
|
||||
// Audio format parameters
|
||||
const u32 frame_size;
|
||||
const u32 guest_buffer_size;
|
||||
const u32 buffer_frames;
|
||||
const u32 sample_rate;
|
||||
const u32 num_channels;
|
||||
const bool is_float;
|
||||
const bool is_std;
|
||||
const std::array<int, 8> channel_layout;
|
||||
|
||||
u64 period_us;
|
||||
u64 last_output_time;
|
||||
u64 next_output_time;
|
||||
alignas(64) u64 period_us{0};
|
||||
alignas(64) std::atomic<u64> last_output_time{0};
|
||||
u64 next_output_time{0};
|
||||
u64 last_volume_check_time{0};
|
||||
u32 output_count{0};
|
||||
|
||||
// Buffers
|
||||
u32 internal_buffer_size;
|
||||
void* internal_buffer;
|
||||
u32 internal_buffer_size{0};
|
||||
void* internal_buffer{nullptr};
|
||||
|
||||
// Converter function
|
||||
ConverterFunc convert;
|
||||
// Converter function pointer
|
||||
ConverterFunc convert{nullptr};
|
||||
|
||||
// Volume tracking
|
||||
std::atomic<float> current_gain{1.0f};
|
||||
mutable std::mutex volume_mutex;
|
||||
// Volume management
|
||||
alignas(64) std::atomic<float> current_gain{1.0f};
|
||||
|
||||
// SDL
|
||||
SDL_AudioStream* stream{};
|
||||
u32 queue_threshold{};
|
||||
// SDL audio stream
|
||||
SDL_AudioStream* stream{nullptr};
|
||||
u32 queue_threshold{0};
|
||||
};
|
||||
|
||||
std::unique_ptr<PortBackend> SDLAudioOut::Open(PortOut& port) {
|
||||
return std::make_unique<SDLPortBackend>(port);
|
||||
}
|
||||
|
||||
} // namespace Libraries::AudioOut
|
||||
} // namespace Libraries::AudioOut
|
||||
|
||||
@ -65,6 +65,9 @@ constexpr int ORBIS_KERNEL_O_DSYNC = 0x1000;
|
||||
constexpr int ORBIS_KERNEL_O_DIRECT = 0x00010000;
|
||||
constexpr int ORBIS_KERNEL_O_DIRECTORY = 0x00020000;
|
||||
|
||||
s32 PS4_SYSV_ABI posix_open(const char* path, s32 flags, u16 mode);
|
||||
s32 PS4_SYSV_ABI posix_close(s32 fd);
|
||||
s64 PS4_SYSV_ABI posix_lseek(s32 fd, s64 offset, s32 whence);
|
||||
s64 PS4_SYSV_ABI sceKernelWrite(s32 fd, const void* buf, u64 nbytes);
|
||||
s64 PS4_SYSV_ABI sceKernelRead(s32 fd, void* buf, u64 nbytes);
|
||||
s64 PS4_SYSV_ABI sceKernelPread(s32 fd, void* buf, u64 nbytes, s64 offset);
|
||||
|
||||
@ -191,6 +191,26 @@ s32 PS4_SYSV_ABI sceKernelGetModuleInfo(s32 handle, Core::OrbisKernelModuleInfo*
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelGetModuleInfo2(s32 handle, Core::OrbisKernelModuleInfo* info) {
|
||||
if (info == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
if (info->st_size != sizeof(Core::OrbisKernelModuleInfo)) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
auto* module = linker->GetModule(handle);
|
||||
if (module == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
if (module->IsSystemLib()) {
|
||||
return ORBIS_KERNEL_ERROR_EPERM;
|
||||
}
|
||||
*info = module->GetModuleInfo();
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelGetModuleInfoInternal(s32 handle, Core::OrbisKernelModuleInfoEx* info) {
|
||||
if (info == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EFAULT;
|
||||
@ -230,6 +250,31 @@ s32 PS4_SYSV_ABI sceKernelGetModuleList(s32* handles, u64 num_array, u64* out_co
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelGetModuleList2(s32* handles, u64 num_array, u64* out_count) {
|
||||
if (handles == nullptr || out_count == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
u64 id = 0;
|
||||
u64 index = 0;
|
||||
auto* module = linker->GetModule(id);
|
||||
while (module != nullptr && index < num_array) {
|
||||
if (!module->IsSystemLib()) {
|
||||
handles[index++] = id;
|
||||
}
|
||||
id++;
|
||||
module = linker->GetModule(id);
|
||||
}
|
||||
|
||||
if (index == num_array && module != nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_ENOMEM;
|
||||
}
|
||||
|
||||
*out_count = index;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI exit(s32 status) {
|
||||
UNREACHABLE_MSG("Exiting with status code {}", status);
|
||||
return 0;
|
||||
@ -249,8 +294,11 @@ void RegisterProcess(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("RpQJJVKTiFM", "libkernel", 1, "libkernel", sceKernelGetModuleInfoForUnwind);
|
||||
LIB_FUNCTION("f7KBOafysXo", "libkernel", 1, "libkernel", sceKernelGetModuleInfoFromAddr);
|
||||
LIB_FUNCTION("kUpgrXIrz7Q", "libkernel", 1, "libkernel", sceKernelGetModuleInfo);
|
||||
LIB_FUNCTION("QgsKEUfkqMA", "libkernel", 1, "libkernel", sceKernelGetModuleInfo2);
|
||||
LIB_FUNCTION("QgsKEUfkqMA", "libkernel_module_info", 1, "libkernel", sceKernelGetModuleInfo2);
|
||||
LIB_FUNCTION("HZO7xOos4xc", "libkernel", 1, "libkernel", sceKernelGetModuleInfoInternal);
|
||||
LIB_FUNCTION("IuxnUuXk6Bg", "libkernel", 1, "libkernel", sceKernelGetModuleList);
|
||||
LIB_FUNCTION("ZzzC3ZGVAkc", "libkernel", 1, "libkernel", sceKernelGetModuleList2);
|
||||
LIB_FUNCTION("6Z83sYWFlA8", "libkernel", 1, "libkernel", exit);
|
||||
}
|
||||
|
||||
|
||||
@ -28,6 +28,16 @@ int PS4_SYSV_ABI posix_pthread_create(PthreadT* thread, const PthreadAttrT* attr
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_join(PthreadT pthread, void** thread_return);
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_init(PthreadMutexAttrT* attr);
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_settype(PthreadMutexAttrT* attr, PthreadMutexType type);
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_destroy(PthreadMutexAttrT* attr);
|
||||
|
||||
int PS4_SYSV_ABI scePthreadMutexInit(PthreadMutexT* mutex, const PthreadMutexAttrT* mutex_attr,
|
||||
const char* name);
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_lock(PthreadMutexT* mutex);
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_unlock(PthreadMutexT* mutex);
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_destroy(PthreadMutexT* mutex);
|
||||
|
||||
void RegisterThreads(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
class Thread {
|
||||
|
||||
@ -261,7 +261,6 @@ int PS4_SYSV_ABI posix_pthread_mutex_lock(PthreadMutexT* mutex) {
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_timedlock(PthreadMutexT* mutex,
|
||||
const OrbisKernelTimespec* abstime) {
|
||||
CHECK_AND_INIT_MUTEX
|
||||
UNREACHABLE();
|
||||
return (*mutex)->Lock(abstime);
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "libc_internal_math.h"
|
||||
#include "libc_internal_memory.h"
|
||||
#include "libc_internal_str.h"
|
||||
#include "libc_internal_threads.h"
|
||||
#include "printf.h"
|
||||
|
||||
namespace Libraries::LibcInternal {
|
||||
@ -20,5 +21,11 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
RegisterlibSceLibcInternalStr(sym);
|
||||
RegisterlibSceLibcInternalMemory(sym);
|
||||
RegisterlibSceLibcInternalIo(sym);
|
||||
RegisterlibSceLibcInternalThreads(sym);
|
||||
}
|
||||
|
||||
void ForceRegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
// Used to forcibly enable HLEs for broken LLE functions.
|
||||
ForceRegisterlibSceLibcInternalIo(sym);
|
||||
}
|
||||
} // namespace Libraries::LibcInternal
|
||||
@ -15,4 +15,5 @@ namespace Libraries::LibcInternal {
|
||||
// so everything is just in the .cpp file
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
void ForceRegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::LibcInternal
|
||||
@ -3,21 +3,484 @@
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
|
||||
#include <common/va_ctx.h>
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/file_system.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "core/libraries/kernel/posix_error.h"
|
||||
#include "core/libraries/libc_internal/libc_internal_io.h"
|
||||
#include "core/libraries/libc_internal/libc_internal_threads.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "libc_internal_io.h"
|
||||
#include "printf.h"
|
||||
|
||||
namespace Libraries::LibcInternal {
|
||||
int PS4_SYSV_ABI internal_snprintf(char* s, size_t n, VA_ARGS) {
|
||||
|
||||
s32 PS4_SYSV_ABI internal_snprintf(char* s, u64 n, VA_ARGS) {
|
||||
VA_CTX(ctx);
|
||||
return snprintf_ctx(s, n, &ctx);
|
||||
}
|
||||
|
||||
std::map<s32, OrbisFILE*> g_files{};
|
||||
// Constants for tracking accurate file indexes.
|
||||
// Since the file struct is exposed to the application, accuracy is important.
|
||||
static constexpr s32 g_initial_files = 5;
|
||||
static constexpr s32 g_max_files = 0x100;
|
||||
|
||||
OrbisFILE* PS4_SYSV_ABI internal__Fofind() {
|
||||
u64 index = g_initial_files;
|
||||
while (index != g_max_files) {
|
||||
OrbisFILE* file = g_files[index];
|
||||
// If file doesn't exist, create it.
|
||||
if (file == nullptr) {
|
||||
file = new OrbisFILE();
|
||||
if (file == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
// Store new file in the array, initialize default values, and return it.
|
||||
g_files[index] = file;
|
||||
file->_Mode = 0x80;
|
||||
file->_Idx = index;
|
||||
return file;
|
||||
}
|
||||
// Special case, files with mode 0 are returned?
|
||||
if (file->_Mode == 0) {
|
||||
file->_Mode = 0xff7f;
|
||||
return file;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PS4_SYSV_ABI internal__Lockfilelock(OrbisFILE* file) {
|
||||
if (file != nullptr && file->_Mutex != nullptr) {
|
||||
internal__Mtxlock(&file->_Mutex);
|
||||
}
|
||||
}
|
||||
|
||||
void PS4_SYSV_ABI internal__Unlockfilelock(OrbisFILE* file) {
|
||||
if (file != nullptr && file->_Mutex != nullptr) {
|
||||
internal__Mtxunlock(&file->_Mutex);
|
||||
}
|
||||
}
|
||||
|
||||
OrbisFILE* PS4_SYSV_ABI internal__Foprep(const char* path, const char* mode, OrbisFILE* file,
|
||||
s32 fd, s32 s_mode, s32 flag) {
|
||||
if (file == nullptr) {
|
||||
*Kernel::__Error() = POSIX_ENOMEM;
|
||||
}
|
||||
|
||||
// Preserve mode and index
|
||||
Libraries::Kernel::PthreadMutexT mtx = file->_Mutex;
|
||||
Libraries::Kernel::PthreadMutexT* mtx_ptr = &file->_Mutex;
|
||||
u8 file_index = file->_Idx;
|
||||
u16 file_mode = file->_Mode & 0x80;
|
||||
|
||||
// Real library does a memcpy using a static global FILE object.
|
||||
// This stored file is just zeros, with the only exception being a handle of -1.
|
||||
memset(file, 0, sizeof(OrbisFILE));
|
||||
file->_Handle = -1;
|
||||
|
||||
// Not sure what this magic is for, but I'll replicate it.
|
||||
u8* ptr = &file->_Cbuf;
|
||||
// Note: this field is supposed to be a pthread mutex.
|
||||
// Since we don't export pthread HLEs for other functions, I'll avoid handling this for now.
|
||||
file->_Mutex = nullptr;
|
||||
file->_Idx = file_index;
|
||||
file->_Buf = ptr;
|
||||
file->_Bend = &file->unk2;
|
||||
file->_Next = ptr;
|
||||
file->_Rend = ptr;
|
||||
file->_WRend = ptr;
|
||||
file->_Wend = ptr;
|
||||
file->_WWend = ptr;
|
||||
file->_Rback = ptr;
|
||||
file->_WRback = &file->unk1;
|
||||
|
||||
// Parse inputted mode string
|
||||
const char* mode_str = mode;
|
||||
u16 calc_mode = 0;
|
||||
u16 access_mode = 0;
|
||||
if (mode_str[0] == 'r') {
|
||||
calc_mode = 1 | file_mode;
|
||||
} else if (mode_str[0] == 'w') {
|
||||
calc_mode = 0x1a | file_mode;
|
||||
} else if (mode_str[0] == 'a') {
|
||||
calc_mode = 0x16 | file_mode;
|
||||
} else {
|
||||
// Closes the file and returns EINVAL.
|
||||
file->_Mode = file_mode;
|
||||
if (flag == 0) {
|
||||
internal__Mtxinit(mtx_ptr, nullptr);
|
||||
} else {
|
||||
file->_Mutex = mtx;
|
||||
internal__Unlockfilelock(file);
|
||||
}
|
||||
internal_fclose(file);
|
||||
*Kernel::__Error() = POSIX_EINVAL;
|
||||
return nullptr;
|
||||
}
|
||||
file->_Mode = calc_mode;
|
||||
|
||||
do {
|
||||
// This is all basically straight from decomp, need to cleanup at some point.
|
||||
if (mode_str[1] == '+') {
|
||||
file_mode = 3;
|
||||
if ((~calc_mode & 3) == 0) {
|
||||
break;
|
||||
}
|
||||
} else if (mode_str[1] != 'b') {
|
||||
file_mode = 0x20;
|
||||
if ((calc_mode & 0x20) != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
mode_str++;
|
||||
calc_mode = file_mode | calc_mode;
|
||||
file->_Mode = calc_mode;
|
||||
} while (true);
|
||||
|
||||
if (path == nullptr && fd >= 0) {
|
||||
// I guess this is for some internal behavior?
|
||||
file->_Handle = fd;
|
||||
} else {
|
||||
fd = internal__Fopen(path, calc_mode, s_mode == 0x55);
|
||||
file->_Handle = fd;
|
||||
}
|
||||
|
||||
// Error case
|
||||
if (fd < 0) {
|
||||
// Closes the file, but ensures errno is unchanged.
|
||||
if (flag == 0) {
|
||||
internal__Mtxinit(mtx_ptr, nullptr);
|
||||
} else {
|
||||
file->_Mutex = mtx;
|
||||
internal__Unlockfilelock(file);
|
||||
}
|
||||
s32 old_errno = *Kernel::__Error();
|
||||
internal_fclose(file);
|
||||
*Kernel::__Error() = old_errno;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (flag == 0) {
|
||||
char mtx_name[0x20];
|
||||
std::snprintf(mtx_name, 0x20, "FileFD:0x%08X", fd);
|
||||
internal__Mtxinit(mtx_ptr, mtx_name);
|
||||
} else {
|
||||
file->_Mutex = mtx;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI internal__Fopen(const char* path, u16 mode, bool flag) {
|
||||
u32 large_mode = mode;
|
||||
u16 open_mode = 0600;
|
||||
if (!flag) {
|
||||
open_mode = 0666;
|
||||
}
|
||||
// Straight from decomp, should probably get cleaned up at some point.
|
||||
s32 creat_flag = large_mode << 5 & 0x200;
|
||||
s32 excl_flag = large_mode << 5 & 0x800;
|
||||
s32 misc_flags = (large_mode & 8) * 0x80 + (large_mode & 4) * 2;
|
||||
// Real library has an array for this, where large_mode & 3 is used as an index.
|
||||
// That array has values [0, 0, 1, 2], so this call should match the result.
|
||||
s32 access_flag = std::max<s32>((large_mode & 3) - 1, 0);
|
||||
s32 open_flags = creat_flag | misc_flags | excl_flag | access_flag;
|
||||
return Libraries::Kernel::posix_open(path, open_flags, open_mode);
|
||||
}
|
||||
|
||||
OrbisFILE* PS4_SYSV_ABI internal_fopen(const char* path, const char* mode) {
|
||||
std::scoped_lock lk{g_file_mtx};
|
||||
LOG_INFO(Lib_LibcInternal, "called, path {}, mode {}", path, mode);
|
||||
OrbisFILE* file = internal__Fofind();
|
||||
return internal__Foprep(path, mode, file, -1, 0, 0);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI internal_fflush(OrbisFILE* file) {
|
||||
if (file == nullptr) {
|
||||
std::scoped_lock lk{g_file_mtx};
|
||||
s32 fflush_result = 0;
|
||||
for (auto& file : g_files) {
|
||||
s32 res = internal_fflush(file.second);
|
||||
if (res < 0) {
|
||||
fflush_result = -1;
|
||||
}
|
||||
}
|
||||
return fflush_result;
|
||||
}
|
||||
if ((file->_Mode & 0x2000) != 0) {
|
||||
internal__Lockfilelock(file);
|
||||
u16 file_mode = file->_Mode;
|
||||
u8* file_buf_start = file->_Buf;
|
||||
u8* file_buf_end = file->_Next;
|
||||
while (file_buf_start < file_buf_end) {
|
||||
u64 size_to_write = static_cast<u64>(file_buf_end - file_buf_start);
|
||||
s32 write_bytes =
|
||||
Libraries::Kernel::sceKernelWrite(file->_Handle, file_buf_start, size_to_write);
|
||||
if (write_bytes < 1) {
|
||||
file_buf_start = file->_Buf;
|
||||
file->_Next = file_buf_start;
|
||||
file->_Wend = file_buf_start;
|
||||
file->_WWend = file_buf_start;
|
||||
u8* off_mode = reinterpret_cast<u8*>(&file->_Mode) + 1;
|
||||
*off_mode = *off_mode | 2;
|
||||
internal__Unlockfilelock(file);
|
||||
return -1;
|
||||
}
|
||||
file_buf_end = file->_Next;
|
||||
file_buf_start += write_bytes;
|
||||
}
|
||||
file->_Next = file_buf_start;
|
||||
file->_Wend = file_buf_start;
|
||||
file->_WWend = file_buf_start;
|
||||
file->_Mode = file_mode & 0xdfff;
|
||||
internal__Unlockfilelock(file);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 PS4_SYSV_ABI internal__Nnl(OrbisFILE* file, u8* val1, u8* val2) {
|
||||
if (val1 < val2) {
|
||||
return val2 - val1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI internal__Fspos(OrbisFILE* file, Orbisfpos_t* file_pos, s64 offset, s32 whence) {
|
||||
if ((file->_Mode & 3) == 0) {
|
||||
return -1;
|
||||
}
|
||||
if (internal_fflush(file) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (whence >= 3) {
|
||||
*Libraries::Kernel::__Error() = POSIX_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (file_pos != nullptr) {
|
||||
offset = offset + file_pos->_Off;
|
||||
}
|
||||
if (whence == 1 && (file->_Mode & 0x1000) != 0) {
|
||||
s64 val1 = internal__Nnl(file, file->_Rback, &file->_Cbuf);
|
||||
u8* rsave_ptr = file->_Rsave;
|
||||
if (rsave_ptr == nullptr) {
|
||||
rsave_ptr = file->_Rend;
|
||||
}
|
||||
s64 val2 = internal__Nnl(file, file->_Next, rsave_ptr);
|
||||
s64 val3 = internal__Nnl(file, file->_Next, file->_WRend);
|
||||
offset = offset - (val1 + val2 + val3);
|
||||
}
|
||||
s64 result = 0;
|
||||
if (whence == 2 || (whence == 1 && offset != 0) || (whence == 0 && offset != -1)) {
|
||||
result = Libraries::Kernel::posix_lseek(file->_Handle, offset, whence);
|
||||
}
|
||||
if (result == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
u16 file_mode = file->_Mode;
|
||||
if ((file_mode & 0x3000) != 0) {
|
||||
u8* file_buf = file->_Buf;
|
||||
file->_Next = file_buf;
|
||||
file->_Rend = file_buf;
|
||||
file->_WRend = file_buf;
|
||||
file->_Wend = file_buf;
|
||||
file->_WWend = file_buf;
|
||||
file->_Rback = &file->_Cbuf;
|
||||
file->_WRback = &file->unk1;
|
||||
file->_Rsave = nullptr;
|
||||
}
|
||||
if (file_pos != nullptr) {
|
||||
std::memcpy(&file->_Wstate, &file_pos->_Wstate, sizeof(Orbis_Mbstatet));
|
||||
}
|
||||
file->_Mode = file_mode & 0xceff;
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI internal_fseek(OrbisFILE* file, s64 offset, s32 whence) {
|
||||
internal__Lockfilelock(file);
|
||||
LOG_TRACE(Lib_LibcInternal, "called, file handle {:#x}, offset {:#x}, whence {:#x}",
|
||||
file->_Handle, offset, whence);
|
||||
s32 result = internal__Fspos(file, nullptr, offset, whence);
|
||||
internal__Unlockfilelock(file);
|
||||
return result;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI internal__Frprep(OrbisFILE* file) {
|
||||
if (file->_Rend > file->_Next) {
|
||||
return 1;
|
||||
}
|
||||
if ((file->_Mode & 0x100) != 0) {
|
||||
return 0;
|
||||
}
|
||||
u16 mode = file->_Mode;
|
||||
if ((mode & 0xa001) != 1) {
|
||||
// Lot of magic here, might be valuable to figure out what this does.
|
||||
file->_Mode = (((mode ^ 0x8000) >> 0xf) << 0xe) | mode | 0x200;
|
||||
return -1;
|
||||
}
|
||||
|
||||
u8* file_buf = file->_Buf;
|
||||
if ((mode & 0x800) == 0 && file_buf == &file->_Cbuf) {
|
||||
// Allocate a new file buffer, for now, we'll use host malloc to create it.
|
||||
// When we have an HLE for malloc, that should be used instead.
|
||||
u8* new_buffer = std::bit_cast<u8*>(std::malloc(0x10000));
|
||||
if (new_buffer == nullptr) {
|
||||
file->_Buf = file_buf;
|
||||
file->_Bend = file_buf + 1;
|
||||
} else {
|
||||
file->_Mode = file->_Mode | 0x40;
|
||||
file->_Buf = new_buffer;
|
||||
file->_Bend = new_buffer + 0x10000;
|
||||
file->_WRend = new_buffer;
|
||||
file->_WWend = new_buffer;
|
||||
file_buf = new_buffer;
|
||||
}
|
||||
}
|
||||
file->_Next = file_buf;
|
||||
file->_Rend = file_buf;
|
||||
file->_Wend = file_buf;
|
||||
// Intentional shrinking here, library treats value as 32-bit.
|
||||
s32 read_result =
|
||||
Libraries::Kernel::sceKernelRead(file->_Handle, file_buf, file->_Bend - file_buf);
|
||||
if (read_result < 0) {
|
||||
u8* off_mode = reinterpret_cast<u8*>(&file->_Mode) + 1;
|
||||
*off_mode = *off_mode | 0x42;
|
||||
return -1;
|
||||
} else if (read_result != 0) {
|
||||
file->_Mode = file->_Mode | 0x5000;
|
||||
file->_Rend = file->_Rend + read_result;
|
||||
return 1;
|
||||
}
|
||||
file->_Mode = (file->_Mode & 0xaeff) | 0x4100;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 PS4_SYSV_ABI internal_fread(char* ptr, u64 size, u64 nmemb, OrbisFILE* file) {
|
||||
if (size == 0 || nmemb == 0) {
|
||||
return 0;
|
||||
}
|
||||
internal__Lockfilelock(file);
|
||||
LOG_TRACE(Lib_LibcInternal, "called, file handle {:#x}, size {:#x}, nmemb {:#x}", file->_Handle,
|
||||
size, nmemb);
|
||||
s64 total_size = size * nmemb;
|
||||
s64 remaining_size = total_size;
|
||||
if ((file->_Mode & 0x4000) != 0) {
|
||||
while (remaining_size != 0) {
|
||||
u8* rback_ptr = file->_Rback;
|
||||
if (&file->_Cbuf <= rback_ptr) {
|
||||
break;
|
||||
}
|
||||
file->_Rback = rback_ptr + 1;
|
||||
*ptr = *rback_ptr;
|
||||
ptr++;
|
||||
remaining_size--;
|
||||
}
|
||||
}
|
||||
|
||||
while (remaining_size != 0) {
|
||||
u8* file_ptr = file->_Rsave;
|
||||
if (file_ptr == nullptr) {
|
||||
file_ptr = file->_Rend;
|
||||
} else {
|
||||
file->_Rend = file_ptr;
|
||||
file->_Rsave = nullptr;
|
||||
}
|
||||
u8* src = file->_Next;
|
||||
if (file_ptr <= src) {
|
||||
s32 res = internal__Frprep(file);
|
||||
if (res < 1) {
|
||||
internal__Unlockfilelock(file);
|
||||
return (total_size - remaining_size) / size;
|
||||
}
|
||||
src = file->_Next;
|
||||
file_ptr = file->_Rend;
|
||||
}
|
||||
u64 copy_bytes = std::min<u64>(file_ptr - src, remaining_size);
|
||||
std::memcpy(ptr, src, copy_bytes);
|
||||
file->_Next += copy_bytes;
|
||||
ptr += copy_bytes;
|
||||
remaining_size -= copy_bytes;
|
||||
}
|
||||
internal__Unlockfilelock(file);
|
||||
return (total_size - remaining_size) / size;
|
||||
}
|
||||
|
||||
void PS4_SYSV_ABI internal__Fofree(OrbisFILE* file) {
|
||||
u8* cbuf_ptr = &file->_Cbuf;
|
||||
s8 trunc_mode = static_cast<s8>(file->_Mode);
|
||||
|
||||
file->_Mode = 0;
|
||||
file->_Handle = -1;
|
||||
file->_Buf = cbuf_ptr;
|
||||
file->_Next = cbuf_ptr;
|
||||
file->_Rend = cbuf_ptr;
|
||||
file->_WRend = cbuf_ptr;
|
||||
file->_Wend = cbuf_ptr;
|
||||
file->_WWend = cbuf_ptr;
|
||||
file->_Rback = cbuf_ptr;
|
||||
file->_WRback = &file->unk1;
|
||||
if (trunc_mode < 0) {
|
||||
// Remove file from vector
|
||||
g_files.erase(file->_Idx);
|
||||
internal__Mtxdst(&file->_Mutex);
|
||||
free(file);
|
||||
}
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI internal_fclose(OrbisFILE* file) {
|
||||
if (file == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_INFO(Lib_LibcInternal, "called, file handle {:#x}", file->_Handle);
|
||||
if ((file->_Mode & 3) == 0 || file->_Handle < 0) {
|
||||
std::scoped_lock lk{g_file_mtx};
|
||||
internal__Fofree(file);
|
||||
*Libraries::Kernel::__Error() = POSIX_EBADF;
|
||||
} else {
|
||||
s32 fflush_result = internal_fflush(file);
|
||||
std::scoped_lock lk{g_file_mtx};
|
||||
if ((file->_Mode & 0x40) != 0) {
|
||||
std::free(file->_Buf);
|
||||
}
|
||||
file->_Buf = nullptr;
|
||||
s32 close_result = Libraries::Kernel::posix_close(file->_Handle);
|
||||
internal__Fofree(file);
|
||||
// Need to figure out what exactly this means.
|
||||
return ~-(close_result == 0) | fflush_result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RegisterlibSceLibcInternalIo(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("eLdDw6l0-bU", "libSceLibcInternal", 1, "libSceLibcInternal", internal_snprintf);
|
||||
LIB_FUNCTION("MUjC4lbHrK4", "libSceLibcInternal", 1, "libSceLibcInternal", internal_fflush);
|
||||
LIB_FUNCTION("xGT4Mc55ViQ", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Fofind);
|
||||
LIB_FUNCTION("dREVnZkAKRE", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Foprep);
|
||||
LIB_FUNCTION("sQL8D-jio7U", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Fopen);
|
||||
LIB_FUNCTION("A+Y3xfrWLLo", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Fspos);
|
||||
LIB_FUNCTION("Ss3108pBuZY", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Nnl);
|
||||
LIB_FUNCTION("9s3P+LCvWP8", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Frprep);
|
||||
LIB_FUNCTION("jVDuvE3s5Bs", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Fofree);
|
||||
LIB_FUNCTION("vZkmJmvqueY", "libSceLibcInternal", 1, "libSceLibcInternal",
|
||||
internal__Lockfilelock);
|
||||
LIB_FUNCTION("0x7rx8TKy2Y", "libSceLibcInternal", 1, "libSceLibcInternal",
|
||||
internal__Unlockfilelock);
|
||||
}
|
||||
|
||||
void ForceRegisterlibSceLibcInternalIo(Core::Loader::SymbolsResolver* sym) {
|
||||
// Goal is to be minimally intrusive here to allow LLE for printf/stdout writes.
|
||||
LIB_FUNCTION("xeYO4u7uyJ0", "libSceLibcInternal", 1, "libSceLibcInternal", internal_fopen);
|
||||
LIB_FUNCTION("rQFVBXp-Cxg", "libSceLibcInternal", 1, "libSceLibcInternal", internal_fseek);
|
||||
LIB_FUNCTION("lbB+UlZqVG0", "libSceLibcInternal", 1, "libSceLibcInternal", internal_fread);
|
||||
LIB_FUNCTION("uodLYyUip20", "libSceLibcInternal", 1, "libSceLibcInternal", internal_fclose);
|
||||
}
|
||||
|
||||
} // namespace Libraries::LibcInternal
|
||||
@ -3,12 +3,96 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/kernel/threads.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::LibcInternal {
|
||||
|
||||
static std::recursive_mutex g_file_mtx{};
|
||||
|
||||
union Orbis__mbstate_t {
|
||||
u8 __mbstate8[128];
|
||||
s64 _mbstateL;
|
||||
};
|
||||
|
||||
struct Orbis_Mbstatet {
|
||||
u64 _Wchar;
|
||||
u16 _Byte, _State;
|
||||
s32 : 32;
|
||||
};
|
||||
|
||||
struct Orbisfpos_t {
|
||||
s64 _Off;
|
||||
Orbis_Mbstatet _Wstate;
|
||||
};
|
||||
|
||||
struct Orbis__sbuf {
|
||||
u8* _base;
|
||||
s32 _size;
|
||||
};
|
||||
|
||||
struct OrbisFILE {
|
||||
u16 _Mode;
|
||||
u8 _Idx;
|
||||
s32 _Handle;
|
||||
u8 *_Buf, *_Bend, *_Next;
|
||||
u8 *_Rend, *_Wend, *_Rback;
|
||||
u16 *_WRback, _WBack[2];
|
||||
u16 unk1;
|
||||
u8 *_Rsave, *_WRend, *_WWend;
|
||||
Orbis_Mbstatet _Wstate;
|
||||
u8* _Tmpnam;
|
||||
u8 _Back[6], _Cbuf;
|
||||
u8 unk2;
|
||||
Libraries::Kernel::PthreadMutexT _Mutex;
|
||||
u8* _p;
|
||||
s32 _r;
|
||||
s32 _w;
|
||||
s16 _flags;
|
||||
s16 _file;
|
||||
Orbis__sbuf _bf;
|
||||
s32 _lbfsize;
|
||||
void* _cookie;
|
||||
s32 PS4_SYSV_ABI (*_close)(void*);
|
||||
s32 PS4_SYSV_ABI (*_read)(void*, char*, s32);
|
||||
Orbisfpos_t PS4_SYSV_ABI (*_seek)(void*, Orbisfpos_t, s32);
|
||||
s32 (*_write)(void*, const char*, s32);
|
||||
Orbis__sbuf _ub;
|
||||
u8* _up;
|
||||
s32 _ur;
|
||||
u8 _ubuf[3];
|
||||
u8 _nbuf[1];
|
||||
Orbis__sbuf _lb;
|
||||
s32 _blksize;
|
||||
Orbisfpos_t _offset;
|
||||
void* _fl_mutex;
|
||||
void* _fl_owner;
|
||||
s32 _fl_count;
|
||||
s32 _orientation;
|
||||
Orbis__mbstate_t _mbstate;
|
||||
};
|
||||
|
||||
s32 PS4_SYSV_ABI internal_snprintf(char* s, u64 n, VA_ARGS);
|
||||
void PS4_SYSV_ABI internal__Lockfilelock(OrbisFILE* file);
|
||||
void PS4_SYSV_ABI internal__Unlockfilelock(OrbisFILE* file);
|
||||
OrbisFILE* PS4_SYSV_ABI internal__Fofind();
|
||||
OrbisFILE* PS4_SYSV_ABI internal__Foprep(const char* path, const char* mode, OrbisFILE* file,
|
||||
s32 fd, s32 flag1, s32 flag2);
|
||||
s32 PS4_SYSV_ABI internal__Fopen(const char* path, u16 mode, bool flag);
|
||||
OrbisFILE* PS4_SYSV_ABI internal_fopen(const char* path, const char* mode);
|
||||
s64 PS4_SYSV_ABI internal__Nnl(OrbisFILE* file, u8* val1, u8* val2);
|
||||
s32 PS4_SYSV_ABI internal__Fspos(OrbisFILE* file, Orbisfpos_t* file_pos, s64 offset, s32 whence);
|
||||
s32 PS4_SYSV_ABI internal_fflush(OrbisFILE* file);
|
||||
s32 PS4_SYSV_ABI internal_fseek(OrbisFILE* file, s64 offset, s32 whence);
|
||||
s32 PS4_SYSV_ABI internal__Frprep(OrbisFILE* file);
|
||||
u64 PS4_SYSV_ABI internal_fread(char* ptr, u64 size, u64 nmemb, OrbisFILE* file);
|
||||
s32 PS4_SYSV_ABI internal_fclose(OrbisFILE* file);
|
||||
|
||||
void RegisterlibSceLibcInternalIo(Core::Loader::SymbolsResolver* sym);
|
||||
void ForceRegisterlibSceLibcInternalIo(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::LibcInternal
|
||||
65
src/core/libraries/libc_internal/libc_internal_threads.cpp
Normal file
65
src/core/libraries/libc_internal/libc_internal_threads.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/libraries/kernel/threads.h"
|
||||
#include "core/libraries/libc_internal/libc_internal_threads.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
namespace Libraries::LibcInternal {
|
||||
|
||||
void getMutexName(char* buf, u64 size, const char* name) {
|
||||
if (name != nullptr) {
|
||||
std::snprintf(buf, size, "SceLibcI_%s", name);
|
||||
} else {
|
||||
std::snprintf(buf, size, "SceLibcI");
|
||||
}
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI internal__Mtxinit(Libraries::Kernel::PthreadMutexT* mtx, const char* name) {
|
||||
char mtx_name[0x20];
|
||||
getMutexName(mtx_name, sizeof(mtx_name), name);
|
||||
|
||||
Libraries::Kernel::PthreadMutexAttrT attr{};
|
||||
s32 result = Libraries::Kernel::posix_pthread_mutexattr_init(&attr);
|
||||
if (result != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
result = Libraries::Kernel::posix_pthread_mutexattr_settype(
|
||||
&attr, Libraries::Kernel::PthreadMutexType::Recursive);
|
||||
if (result == 0) {
|
||||
s32 mtx_init_result = Libraries::Kernel::scePthreadMutexInit(mtx, &attr, mtx_name);
|
||||
result = Libraries::Kernel::posix_pthread_mutexattr_destroy(&attr);
|
||||
if (mtx_init_result == 0 && result == 0) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
Libraries::Kernel::posix_pthread_mutexattr_destroy(&attr);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI internal__Mtxlock(Libraries::Kernel::PthreadMutexT* mtx) {
|
||||
s32 result = Libraries::Kernel::posix_pthread_mutex_lock(mtx);
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI internal__Mtxunlock(Libraries::Kernel::PthreadMutexT* mtx) {
|
||||
s32 result = Libraries::Kernel::posix_pthread_mutex_unlock(mtx);
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI internal__Mtxdst(Libraries::Kernel::PthreadMutexT* mtx) {
|
||||
s32 result = Libraries::Kernel::posix_pthread_mutex_destroy(mtx);
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
void RegisterlibSceLibcInternalThreads(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("z7STeF6abuU", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Mtxinit);
|
||||
LIB_FUNCTION("pE4Ot3CffW0", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Mtxlock);
|
||||
LIB_FUNCTION("cMwgSSmpE5o", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Mtxunlock);
|
||||
LIB_FUNCTION("LaPaA6mYA38", "libSceLibcInternal", 1, "libSceLibcInternal", internal__Mtxdst);
|
||||
}
|
||||
|
||||
} // namespace Libraries::LibcInternal
|
||||
22
src/core/libraries/libc_internal/libc_internal_threads.h
Normal file
22
src/core/libraries/libc_internal/libc_internal_threads.h
Normal file
@ -0,0 +1,22 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/kernel/threads.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::LibcInternal {
|
||||
|
||||
s32 PS4_SYSV_ABI internal__Mtxinit(Libraries::Kernel::PthreadMutexT* mtx, const char* name);
|
||||
s32 PS4_SYSV_ABI internal__Mtxlock(Libraries::Kernel::PthreadMutexT* mtx);
|
||||
s32 PS4_SYSV_ABI internal__Mtxunlock(Libraries::Kernel::PthreadMutexT* mtx);
|
||||
s32 PS4_SYSV_ABI internal__Mtxdst(Libraries::Kernel::PthreadMutexT* mtx);
|
||||
|
||||
void RegisterlibSceLibcInternalThreads(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::LibcInternal
|
||||
@ -80,6 +80,7 @@ namespace Libraries {
|
||||
void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
|
||||
LOG_INFO(Lib_Kernel, "Initializing HLE libraries");
|
||||
Libraries::Kernel::RegisterLib(sym);
|
||||
Libraries::LibcInternal::ForceRegisterLib(sym);
|
||||
Libraries::GnmDriver::RegisterLib(sym);
|
||||
Libraries::VideoOut::RegisterLib(sym);
|
||||
Libraries::UserService::RegisterLib(sym);
|
||||
|
||||
@ -13,4 +13,12 @@ constexpr int ORBIS_NP_ERROR_INVALID_SIZE = 0x80550011;
|
||||
constexpr int ORBIS_NP_ERROR_ABORTED = 0x80550012;
|
||||
constexpr int ORBIS_NP_ERROR_REQUEST_MAX = 0x80550013;
|
||||
constexpr int ORBIS_NP_ERROR_REQUEST_NOT_FOUND = 0x80550014;
|
||||
constexpr int ORBIS_NP_ERROR_INVALID_ID = 0x80550015;
|
||||
constexpr int ORBIS_NP_ERROR_INVALID_ID = 0x80550015;
|
||||
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT = 0x80550704;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_OBJECTS = 0x80550706;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT = 0x8055070c;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_INVALID_ID = 0x8055070e;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT = 0x80550714;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID = 0x80550718;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_NPID = 0x80550719;
|
||||
@ -93,6 +93,8 @@ struct OrbisNpCreateAsyncRequestParameter {
|
||||
void RegisterNpCallback(std::string key, std::function<void()> cb);
|
||||
void DeregisterNpCallback(std::string key);
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpGetNpId(Libraries::UserService::OrbisUserServiceUserId user_id,
|
||||
OrbisNpId* np_id);
|
||||
s32 PS4_SYSV_ABI sceNpGetOnlineId(Libraries::UserService::OrbisUserServiceUserId user_id,
|
||||
OrbisNpOnlineId* online_id);
|
||||
|
||||
|
||||
@ -1,15 +1,87 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fstream>
|
||||
#include <future>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/np/np_error.h"
|
||||
#include "core/libraries/np/np_manager.h"
|
||||
#include "core/libraries/np/np_tus.h"
|
||||
#include "core/libraries/np/np_types.h"
|
||||
#include "core/libraries/np/object_manager.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
|
||||
namespace Libraries::Np::NpTus {
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTssCreateNpTitleCtx() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
int PackReqId(int libCtxId, int reqId) {
|
||||
return ((libCtxId & 0xFFFF) << 16) | (reqId & 0xFFFF);
|
||||
}
|
||||
|
||||
std::pair<int, int> UnpackReqId(int reqId) {
|
||||
return {reqId >> 16, reqId & 0xFFFF};
|
||||
}
|
||||
|
||||
bool IsReqId(int id) {
|
||||
return id > (1 << 16);
|
||||
}
|
||||
|
||||
struct NpTusRequest {
|
||||
std::future<int> task;
|
||||
|
||||
void Start(auto lambda) {
|
||||
this->task = std::async(std::launch::async, lambda);
|
||||
}
|
||||
};
|
||||
|
||||
using NpTusRequestsManager =
|
||||
ObjectManager<NpTusRequest, 32, ORBIS_NP_COMMUNITY_ERROR_INVALID_ID,
|
||||
ORBIS_NP_COMMUNITY_ERROR_INVALID_ID, ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_OBJECTS>;
|
||||
|
||||
struct NpTusTitleContext {
|
||||
u32 serviceLabel;
|
||||
OrbisNpId npId;
|
||||
NpTusRequestsManager requestsManager;
|
||||
|
||||
s32 GetRequest(int reqId, NpTusRequest** out) {
|
||||
NpTusRequest* req = nullptr;
|
||||
if (auto ret = requestsManager.GetObject(reqId, &req); ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
*out = req;
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 DeleteRequest(int reqId) {
|
||||
return requestsManager.DeleteObject(reqId);
|
||||
}
|
||||
};
|
||||
|
||||
using NpTusContextManager =
|
||||
ObjectManager<NpTusTitleContext, 32, ORBIS_NP_COMMUNITY_ERROR_INVALID_ID,
|
||||
ORBIS_NP_COMMUNITY_ERROR_INVALID_ID, ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_OBJECTS>;
|
||||
|
||||
static NpTusContextManager ctxManager;
|
||||
|
||||
s32 GetRequest(int requestId, NpTusRequest** out) {
|
||||
auto [ctxId, reqId] = UnpackReqId(requestId);
|
||||
|
||||
NpTusTitleContext* ctx = nullptr;
|
||||
if (auto ret = ctxManager.GetObject(ctxId, &ctx); ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
NpTusRequest* req = nullptr;
|
||||
if (auto ret = ctx->GetRequest(reqId, &req); ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
*out = req;
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -33,9 +105,34 @@ s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableVUserAsync() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusCreateNpTitleCtx() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
s32 PS4_SYSV_ABI sceNpTusCreateNpTitleCtx(OrbisNpServiceLabel serviceLabel, OrbisNpId* npId) {
|
||||
if (!npId) {
|
||||
return ORBIS_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
if (serviceLabel == ORBIS_NP_INVALID_SERVICE_LABEL) {
|
||||
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
LOG_ERROR(Lib_NpTus, "serviceLabel = {}, npId->data = {}", serviceLabel, npId->handle.data);
|
||||
|
||||
return ctxManager.CreateObject(serviceLabel, *npId);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusCreateNpTitleCtxA(OrbisNpServiceLabel serviceLabel,
|
||||
Libraries::UserService::OrbisUserServiceUserId userId) {
|
||||
LOG_ERROR(Lib_NpTus, "serviceLabel = {}, userId = {}", serviceLabel, userId);
|
||||
OrbisNpId npId;
|
||||
auto ret = NpManager::sceNpGetNpId(userId, &npId);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return sceNpTusCreateNpTitleCtx(serviceLabel, &npId);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTssCreateNpTitleCtx(OrbisNpServiceLabel serviceLabel, OrbisNpId* npId) {
|
||||
LOG_INFO(Lib_NpTus, "redirecting");
|
||||
return sceNpTusCreateNpTitleCtx(serviceLabel, npId);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotData() {
|
||||
@ -58,14 +155,33 @@ s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariableAsync() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusGetData() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataAsync(int reqId, OrbisNpId* npId, OrbisNpTusSlotId slotId,
|
||||
OrbisNpTusDataStatus* dataStatus, u64 dataStatusSize,
|
||||
void* data, u64 dataSize, void* option) {
|
||||
LOG_INFO(
|
||||
Lib_NpTus,
|
||||
"reqId = {:#x}, slotId = {}, dataStatusSize = {}, data = {}, dataSize = {}, option = {}",
|
||||
reqId, slotId, dataStatusSize, data, dataSize, fmt::ptr(option));
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataAsync() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
s32 PS4_SYSV_ABI sceNpTusGetData(int reqId, OrbisNpId* npId, OrbisNpTusSlotId slotId,
|
||||
OrbisNpTusDataStatus* dataStatus, u64 dataStatusSize, void* data,
|
||||
u64 dataSize, void* option) {
|
||||
LOG_ERROR(
|
||||
Lib_NpTus,
|
||||
"reqId = {:#x}, slotId = {}, dataStatusSize = {}, data = {}, dataSize = {}, option = {}",
|
||||
reqId, slotId, dataStatusSize, data, dataSize, fmt::ptr(option));
|
||||
|
||||
auto ret = sceNpTusGetDataAsync(reqId, npId, slotId, dataStatus, dataStatusSize, data, dataSize,
|
||||
option);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
sceNpTusWaitAsync(reqId, &ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataVUser() {
|
||||
@ -118,13 +234,45 @@ s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusVUserAsync() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariable() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableAsync(int reqId, OrbisNpId* npId,
|
||||
OrbisNpTusSlotId* slotIds, s64* variables,
|
||||
u64 variablesSize, int arrayLen, void* option) {
|
||||
LOG_INFO(Lib_NpTus,
|
||||
"reqId = {}, npId = {}, slotIds = {}, variables = {}, variablesSize = {}, arrayLen = "
|
||||
"{}, option = {}",
|
||||
reqId, npId ? npId->handle.data : "", fmt::ptr(slotIds), fmt::ptr(variables),
|
||||
variablesSize, arrayLen, fmt::ptr(option));
|
||||
|
||||
NpTusRequest* req = nullptr;
|
||||
if (auto ret = GetRequest(reqId, &req); ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
req->Start([=]() {
|
||||
//
|
||||
return 0;
|
||||
});
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableAsync() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariable(int reqId, OrbisNpId* npId, OrbisNpTusSlotId* slotIds,
|
||||
s64* variables, u64 variablesSize, int arrayLen,
|
||||
void* option) {
|
||||
LOG_INFO(Lib_NpTus,
|
||||
"reqId = {}, npId = {}, slotIds = {}, variables = {}, variablesSize = {}, arrayLen = "
|
||||
"{}, option = {}",
|
||||
reqId, npId ? npId->handle.data : "", fmt::ptr(slotIds), fmt::ptr(variables),
|
||||
variablesSize, arrayLen, fmt::ptr(option));
|
||||
|
||||
auto ret = sceNpTusGetMultiSlotVariableAsync(reqId, npId, slotIds, variables, variablesSize,
|
||||
arrayLen, option);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
sceNpTusWaitAsync(reqId, &ret);
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -143,8 +291,24 @@ s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatus() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusAsync() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusAsync(int reqId, OrbisNpId* npIds,
|
||||
OrbisNpTusSlotId slotId,
|
||||
OrbisNpTusDataStatus* statuses,
|
||||
u64 statusesBytes, int arrayLen,
|
||||
void* option) {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) reqId = {:#x}, slotId = {}, arrayLen = {}, option = {}", reqId,
|
||||
slotId, arrayLen, fmt::ptr(option));
|
||||
|
||||
NpTusRequest* req = nullptr;
|
||||
if (auto ret = GetRequest(reqId, &req); ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
req->Start([=]() {
|
||||
//
|
||||
return 0;
|
||||
});
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -178,13 +342,49 @@ s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableVUserAsync() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusSetData() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataAsync(int reqId, OrbisNpId* npId, OrbisNpTusSlotId slotId,
|
||||
u64 totalSize, u64 sendSize, const void* data,
|
||||
const OrbisNpTusDataInfo* info, u64 infoSize,
|
||||
const OrbisNpId* lastChangedAuthor,
|
||||
Libraries::Rtc::OrbisRtcTick lastChanged, void* option) {
|
||||
LOG_INFO(Lib_NpTus,
|
||||
"reqId = {:#x}, npId = {}, slotId = {}, totalSize = {}, sendSize = {}, "
|
||||
"info->size = {}, infoSize = {}, lastChangedAuthor = {}",
|
||||
reqId, npId ? npId->handle.data : "", slotId, totalSize, sendSize,
|
||||
info ? info->size : 0, infoSize,
|
||||
lastChangedAuthor ? lastChangedAuthor->handle.data : "");
|
||||
|
||||
NpTusRequest* req = nullptr;
|
||||
if (auto ret = GetRequest(reqId, &req); ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
req->Start([=]() {
|
||||
//
|
||||
return 0;
|
||||
});
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataAsync() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
s32 PS4_SYSV_ABI sceNpTusSetData(int reqId, OrbisNpId* npId, OrbisNpTusSlotId slotId, u64 totalSize,
|
||||
u64 sendSize, const void* data, const OrbisNpTusDataInfo* info,
|
||||
u64 infoSize, const OrbisNpId* lastChangedAuthor,
|
||||
Libraries::Rtc::OrbisRtcTick lastChanged, void* option) {
|
||||
LOG_INFO(Lib_NpTus,
|
||||
"reqId = {:#x}, npId = {}, slotId = {}, totalSize = {}, sendSize = {}, "
|
||||
"info->size = {}, infoSize = {}, lastChangedAuthor = {}",
|
||||
reqId, npId ? npId->handle.data : "", slotId, totalSize, sendSize,
|
||||
info ? info->size : 0, infoSize,
|
||||
lastChangedAuthor ? lastChangedAuthor->handle.data : "");
|
||||
|
||||
auto ret = sceNpTusSetDataAsync(reqId, npId, slotId, totalSize, sendSize, data, info, infoSize,
|
||||
lastChangedAuthor, lastChanged, option);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
sceNpTusWaitAsync(reqId, &ret);
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -203,8 +403,39 @@ s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariable() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariableAsync() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariableAsync(int reqId, OrbisNpId* npId,
|
||||
OrbisNpTusSlotId* slotIds, s64* variables,
|
||||
int arrayLen, void* option) {
|
||||
LOG_INFO(Lib_NpTus,
|
||||
"reqId = {}, npId = {}, slotIds = {}, variables = {}, arrayLen = {}, option = {}",
|
||||
reqId, npId ? npId->handle.data : "", fmt::ptr(slotIds), fmt::ptr(variables), arrayLen,
|
||||
fmt::ptr(option));
|
||||
|
||||
if (!slotIds || !variables) {
|
||||
return ORBIS_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
if (arrayLen < 1 || option) {
|
||||
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
if (arrayLen > 64) {
|
||||
return ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
|
||||
}
|
||||
if (std::ranges::any_of(
|
||||
std::vector<std::reference_wrapper<OrbisNpTusSlotId>>(slotIds, slotIds + arrayLen),
|
||||
[](auto id) { return id < 0; })) {
|
||||
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
NpTusRequest* req = nullptr;
|
||||
if (auto ret = GetRequest(reqId, &req); ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
req->Start([=]() {
|
||||
//
|
||||
return 0;
|
||||
});
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -228,19 +459,59 @@ s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableVUserAsync() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTssCreateNpTitleCtxA() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
s32 PS4_SYSV_ABI sceNpTssCreateNpTitleCtxA(OrbisNpServiceLabel serviceLabel,
|
||||
Libraries::UserService::OrbisUserServiceUserId userId) {
|
||||
LOG_DEBUG(Lib_NpTus, "redirecting");
|
||||
return sceNpTusCreateNpTitleCtxA(serviceLabel, userId);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTssGetDataAsync(int reqId, OrbisNpTssSlotId slotId,
|
||||
OrbisNpTssDataStatus* dataStatus, u64 dataStatusSize,
|
||||
void* data, u64 dataSize, OrbisNpTssGetDataOptParam* option) {
|
||||
LOG_INFO(Lib_NpTus, "reqId = {:#x}, slotId = {}, dataStatusSize = {}, dataSize = {}", reqId,
|
||||
slotId, dataStatusSize, dataSize);
|
||||
|
||||
if (option && option->size != 0x20) {
|
||||
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
|
||||
}
|
||||
if (dataStatus && dataStatusSize != 0x18) {
|
||||
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
|
||||
}
|
||||
if (slotId < 0 || slotId > 15) {
|
||||
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
NpTusRequest* req = nullptr;
|
||||
if (auto ret = GetRequest(reqId, &req); ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
req->Start([=]() {
|
||||
if (dataStatus) {
|
||||
dataStatus->status = OrbisNpTssStatus::Ok;
|
||||
dataStatus->contentLength = 0;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTssGetData() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
s32 PS4_SYSV_ABI sceNpTssGetData(int reqId, OrbisNpTssSlotId slotId,
|
||||
OrbisNpTssDataStatus* dataStatus, u64 dataStatusSize, void* data,
|
||||
u64 dataSize, OrbisNpTssGetDataOptParam* option) {
|
||||
LOG_INFO(Lib_NpTus, "reqId = {:#x}, slotId = {}, dataStatusSize = {}, dataSize = {}", reqId,
|
||||
slotId, dataStatusSize, dataSize);
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTssGetDataAsync() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
auto ret =
|
||||
sceNpTssGetDataAsync(reqId, slotId, dataStatus, dataStatusSize, data, dataSize, option);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
sceNpTusWaitAsync(reqId, &ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTssGetSmallStorage() {
|
||||
@ -313,14 +584,20 @@ s32 PS4_SYSV_ABI sceNpTusChangeModeForOtherSaveDataOwners() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusCreateNpTitleCtxA() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
s32 PS4_SYSV_ABI sceNpTusCreateRequest(int libCtxId) {
|
||||
LOG_INFO(Lib_NpTus, "libCtxId = {}", libCtxId);
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusCreateRequest() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
NpTusTitleContext* ctx = nullptr;
|
||||
if (auto ret = ctxManager.GetObject(libCtxId, &ctx); ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto req = ctx->requestsManager.CreateObject();
|
||||
if (req < 0) {
|
||||
return req;
|
||||
}
|
||||
|
||||
return PackReqId(libCtxId, req);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusCreateTitleCtx() {
|
||||
@ -368,24 +645,70 @@ s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariableVUserAsync() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteNpTitleCtx() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteNpTitleCtx(int ctxId) {
|
||||
LOG_INFO(Lib_NpTus, "ctxId = {}", ctxId);
|
||||
|
||||
return ctxManager.DeleteObject(ctxId);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteRequest(int requestId) {
|
||||
LOG_INFO(Lib_NpTus, "requestId = {:#x}", requestId);
|
||||
|
||||
auto [ctxId, reqId] = UnpackReqId(requestId);
|
||||
|
||||
NpTusTitleContext* ctx = nullptr;
|
||||
if (auto ret = ctxManager.GetObject(ctxId, &ctx); ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ctx->DeleteRequest(reqId);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataAAsync(int reqId, OrbisNpAccountId accountId,
|
||||
OrbisNpTusSlotId slotId, OrbisNpTusDataStatusA* dataStatus,
|
||||
u64 dataStatusSize, void* data, u64 dataSize, void* option) {
|
||||
LOG_INFO(Lib_NpTus,
|
||||
"reqId = {:#x}, accountId = {:#x}, slotId = {}, dataStatus = {}, dataStatusSize = {}, "
|
||||
"dataSize = {}",
|
||||
reqId, accountId, slotId, fmt::ptr(dataStatus), dataStatusSize, dataSize);
|
||||
|
||||
if (slotId < 0 || option) {
|
||||
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
if (dataStatusSize != sizeof(OrbisNpTusDataStatusA)) {
|
||||
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
NpTusRequest* req = nullptr;
|
||||
if (auto ret = GetRequest(reqId, &req); ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
req->Start([=]() {
|
||||
//
|
||||
return 0;
|
||||
});
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteRequest() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataA(int reqId, OrbisNpAccountId accountId, OrbisNpTusSlotId slotId,
|
||||
OrbisNpTusDataStatusA* dataStatus, u64 dataStatusSize, void* data,
|
||||
u64 dataSize, void* option) {
|
||||
LOG_INFO(Lib_NpTus,
|
||||
"reqId = {:#x}, accountId = {:#x}, slotId = {}, dataStatus = {}, dataStatusSize = {}, "
|
||||
"dataSize = {}",
|
||||
reqId, accountId, slotId, fmt::ptr(dataStatus), dataStatusSize, dataSize);
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataA() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
auto ret = sceNpTusGetDataAAsync(reqId, accountId, slotId, dataStatus, dataStatusSize, data,
|
||||
dataSize, option);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataAAsync() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
sceNpTusWaitAsync(reqId, &ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataAVUser() {
|
||||
@ -458,13 +781,62 @@ s32 PS4_SYSV_ABI sceNpTusGetFriendsVariableForCrossSaveAsync() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusA() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusAAsync(int reqId, OrbisNpAccountId accountId,
|
||||
OrbisNpTusSlotId* slotIds,
|
||||
OrbisNpTusDataStatusA* statuses,
|
||||
u64 statusesSize, int arrayLen,
|
||||
void* option) {
|
||||
LOG_ERROR(Lib_NpTus, "reqId = {:#x}, accountId = {}, arrayLen = {}, option = {}", reqId,
|
||||
accountId, arrayLen, fmt::ptr(option));
|
||||
|
||||
if (!slotIds || !statuses) {
|
||||
return ORBIS_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
if (arrayLen < 1 || option) {
|
||||
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
if (arrayLen * sizeof(OrbisNpTusDataStatusA) != statusesSize) {
|
||||
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
|
||||
}
|
||||
if (arrayLen > 64) {
|
||||
return ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
|
||||
}
|
||||
if (std::ranges::any_of(
|
||||
std::vector<std::reference_wrapper<OrbisNpTusSlotId>>(slotIds, slotIds + arrayLen),
|
||||
[](auto id) { return id < 0; })) {
|
||||
return ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
// if sdk_ver >= 5.50 clear the statuses array
|
||||
|
||||
NpTusRequest* req = nullptr;
|
||||
if (auto ret = GetRequest(reqId, &req); ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
req->Start([=]() {
|
||||
//
|
||||
return 0;
|
||||
});
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusAAsync() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusA(int reqId, OrbisNpAccountId accountId,
|
||||
OrbisNpTusSlotId* slotIds,
|
||||
OrbisNpTusDataStatusA* statuses, u64 statusesSize,
|
||||
int arrayLen, void* option) {
|
||||
LOG_ERROR(Lib_NpTus, "reqId = {:#x}, accountId = {}, arrayLen = {}, option = {}", reqId,
|
||||
accountId, arrayLen, fmt::ptr(option));
|
||||
|
||||
auto ret = sceNpTusGetMultiSlotDataStatusAAsync(reqId, accountId, slotIds, statuses,
|
||||
statusesSize, arrayLen, option);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
sceNpTusWaitAsync(reqId, &ret);
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -618,18 +990,72 @@ s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableForCrossSaveVUserAsync() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusPollAsync() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
s32 PS4_SYSV_ABI sceNpTusPollAsync(int reqId, int* result) {
|
||||
LOG_INFO(Lib_NpTus, "reqId = {:#x}", reqId);
|
||||
|
||||
NpTusRequest* req = nullptr;
|
||||
if (auto ret = GetRequest(reqId, &req); ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!req->task.valid()) {
|
||||
LOG_ERROR(Lib_NpTus, "request not started");
|
||||
return 1;
|
||||
}
|
||||
if (req->task.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
|
||||
LOG_DEBUG(Lib_NpTus, "request finished");
|
||||
if (result) {
|
||||
*result = req->task.get();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataAAsync(int reqId, OrbisNpAccountId accountId,
|
||||
OrbisNpTusSlotId slotId, u64 totalSize, u64 sendSize,
|
||||
const void* data, const OrbisNpTusDataInfo* info,
|
||||
u64 infoSize, const OrbisNpAccountId* lastChangedAuthor,
|
||||
Libraries::Rtc::OrbisRtcTick* lastChanged, void* option) {
|
||||
LOG_INFO(Lib_NpTus,
|
||||
"reqId = {:#x}, accountId = {}, slotId = {}, totalSize = {}, sendSize = {}, "
|
||||
"info->size = {}, infoSize = {}, lastChangedAuthor = {}",
|
||||
reqId, accountId, slotId, totalSize, sendSize, info ? info->size : 0, infoSize,
|
||||
lastChangedAuthor ? *lastChangedAuthor : 0);
|
||||
|
||||
NpTusRequest* req = nullptr;
|
||||
if (auto ret = GetRequest(reqId, &req); ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
req->Start([=]() {
|
||||
//
|
||||
return 0;
|
||||
});
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataA() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataA(int reqId, OrbisNpAccountId accountId, OrbisNpTusSlotId slotId,
|
||||
u64 totalSize, u64 sendSize, const void* data,
|
||||
const OrbisNpTusDataInfo* info, u64 infoSize,
|
||||
const OrbisNpAccountId* lastChangedAuthor,
|
||||
Libraries::Rtc::OrbisRtcTick* lastChanged, void* option) {
|
||||
LOG_INFO(Lib_NpTus,
|
||||
"reqId = {:#x}, accountId = {}, slotId = {}, totalSize = {}, sendSize = {}, "
|
||||
"info->size = {}, infoSize = {}, lastChangedAuthor = {}",
|
||||
reqId, accountId, slotId, totalSize, sendSize, info ? info->size : 0, infoSize,
|
||||
lastChangedAuthor ? *lastChangedAuthor : 0);
|
||||
|
||||
auto ret = sceNpTusSetDataAAsync(reqId, accountId, slotId, totalSize, sendSize, data, info,
|
||||
infoSize, lastChangedAuthor, lastChanged, option);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
sceNpTusWaitAsync(reqId, &ret);
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataAAsync() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -713,8 +1139,25 @@ s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableForCrossSaveVUserAsync() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusWaitAsync() {
|
||||
LOG_ERROR(Lib_NpTus, "(STUBBED) called");
|
||||
s32 PS4_SYSV_ABI sceNpTusWaitAsync(int reqId, int* result) {
|
||||
LOG_INFO(Lib_NpTus, "reqId = {:#x}", reqId);
|
||||
|
||||
NpTusRequest* req = nullptr;
|
||||
if (auto ret = GetRequest(reqId, &req); ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!req->task.valid()) {
|
||||
LOG_ERROR(Lib_NpTus, "request not started");
|
||||
return 1;
|
||||
}
|
||||
|
||||
req->task.wait();
|
||||
|
||||
LOG_DEBUG(Lib_NpTus, "request finished");
|
||||
if (result) {
|
||||
*result = req->task.get();
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/rtc/rtc.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
@ -11,148 +12,76 @@ class SymbolsResolver;
|
||||
|
||||
namespace Libraries::Np::NpTus {
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTssCreateNpTitleCtx();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariable();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusCreateNpTitleCtx();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotData();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotDataAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariable();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariableAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetData();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsDataStatus();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsDataStatusAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsVariable();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsVariableAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatus();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariable();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatus();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariable();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetData();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariable();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariableAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariable();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTssCreateNpTitleCtxA();
|
||||
s32 PS4_SYSV_ABI sceNpTssGetData();
|
||||
s32 PS4_SYSV_ABI sceNpTssGetDataAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTssGetSmallStorage();
|
||||
s32 PS4_SYSV_ABI sceNpTssGetSmallStorageAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTssGetStorage();
|
||||
s32 PS4_SYSV_ABI sceNpTssGetStorageAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusAbortRequest();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableA();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableAVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableAVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableForCrossSave();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableForCrossSaveAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableForCrossSaveVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableForCrossSaveVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusChangeModeForOtherSaveDataOwners();
|
||||
s32 PS4_SYSV_ABI sceNpTusCreateNpTitleCtxA();
|
||||
s32 PS4_SYSV_ABI sceNpTusCreateRequest();
|
||||
s32 PS4_SYSV_ABI sceNpTusCreateTitleCtx();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotDataA();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotDataAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotDataVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotDataVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariableA();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariableAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariableVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariableVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteNpTitleCtx();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteRequest();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataA();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataAVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataAVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataForCrossSave();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataForCrossSaveAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataForCrossSaveVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataForCrossSaveVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsDataStatusA();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsDataStatusAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsDataStatusForCrossSave();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsDataStatusForCrossSaveAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsVariableA();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsVariableAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsVariableForCrossSave();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsVariableForCrossSaveAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusA();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusAVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusAVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusForCrossSave();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusForCrossSaveAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusForCrossSaveVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusForCrossSaveVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableA();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableAVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableAVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableForCrossSave();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableForCrossSaveAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableForCrossSaveVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableForCrossSaveVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusA();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusAVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusAVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusForCrossSave();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusForCrossSaveAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusForCrossSaveVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusForCrossSaveVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableA();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableAVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableAVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableForCrossSave();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableForCrossSaveAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableForCrossSaveVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableForCrossSaveVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusPollAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataA();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataAVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataAVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariableA();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariableAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariableVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariableVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetThreadParam();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetTimeout();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableA();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableAVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableAVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableForCrossSave();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableForCrossSaveAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableForCrossSaveVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableForCrossSaveVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusWaitAsync();
|
||||
using OrbisNpTssSlotId = s32;
|
||||
using OrbisNpTusSlotId = s32;
|
||||
|
||||
struct OrbisNpTusVariable {
|
||||
OrbisNpId npId;
|
||||
int set;
|
||||
Libraries::Rtc::OrbisRtcTick lastChanged;
|
||||
u8 pad1[4];
|
||||
OrbisNpId lastChangedAuthor;
|
||||
s64 variable;
|
||||
s64 oldVariable;
|
||||
OrbisNpAccountId owner;
|
||||
OrbisNpAccountId lastChangedAuthorId;
|
||||
};
|
||||
|
||||
struct OrbisNpTusDataInfo {
|
||||
u64 size;
|
||||
u8 data[384];
|
||||
};
|
||||
|
||||
struct OrbisNpTusDataStatus {
|
||||
OrbisNpId npId;
|
||||
int set;
|
||||
Libraries::Rtc::OrbisRtcTick lastChanged;
|
||||
OrbisNpId lastChangedAuthor;
|
||||
u8 pad2[4];
|
||||
void* data;
|
||||
u64 dataSize;
|
||||
OrbisNpTusDataInfo info;
|
||||
};
|
||||
|
||||
static_assert(sizeof(OrbisNpTusDataStatus) == 0x1F0);
|
||||
|
||||
struct OrbisNpTusDataStatusA {
|
||||
OrbisNpOnlineId onlineId;
|
||||
u8 pad[16];
|
||||
int set;
|
||||
Libraries::Rtc::OrbisRtcTick lastChanged;
|
||||
OrbisNpOnlineId lastChangedAuthor;
|
||||
u8 pad2[20];
|
||||
void* data;
|
||||
u64 dataSize;
|
||||
OrbisNpTusDataInfo info;
|
||||
OrbisNpAccountId owner;
|
||||
OrbisNpAccountId lastChangedAuthorId;
|
||||
u8 pad3[16];
|
||||
};
|
||||
|
||||
static_assert(sizeof(OrbisNpTusDataStatusA) == 0x210);
|
||||
|
||||
enum class OrbisNpTssStatus : int {
|
||||
Ok = 0,
|
||||
Partial = 1,
|
||||
NotModified = 2,
|
||||
};
|
||||
|
||||
struct OrbisNpTssDataStatus {
|
||||
Libraries::Rtc::OrbisRtcTick modified;
|
||||
OrbisNpTssStatus status;
|
||||
u64 contentLength;
|
||||
};
|
||||
|
||||
struct OrbisNpTssGetDataOptParam {
|
||||
u64 size;
|
||||
u64* offset;
|
||||
u64* last;
|
||||
void* param;
|
||||
};
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTusWaitAsync(int reqId, int* result);
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::Np::NpTus
|
||||
@ -54,12 +54,12 @@ s32 PS4_SYSV_ABI sceNpWebApiCreateServicePushEventFilter(
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpWebApiDeletePushEventFilter(s32 libCtxId, s32 filterId) {
|
||||
LOG_INFO(Lib_NpWebApi, "called, libCtxId = {:#x}, filterId = {:#x}");
|
||||
LOG_INFO(Lib_NpWebApi, "called, libCtxId = {:#x}, filterId = {:#x}", libCtxId, filterId);
|
||||
return deletePushEventFilter(libCtxId, filterId);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpWebApiDeleteServicePushEventFilter(s32 libCtxId, s32 filterId) {
|
||||
LOG_INFO(Lib_NpWebApi, "called, libCtxId = {:#x}, filterId = {:#x}");
|
||||
LOG_INFO(Lib_NpWebApi, "called, libCtxId = {:#x}, filterId = {:#x}", libCtxId, filterId);
|
||||
return deleteServicePushEventFilter(libCtxId, filterId);
|
||||
}
|
||||
|
||||
|
||||
@ -65,7 +65,7 @@ s32 createLibraryContext(s32 libHttpCtxId, u64 poolSize, const char* name, s32 t
|
||||
|
||||
OrbisNpWebApiContext* findAndValidateContext(s32 libCtxId, s32 flag) {
|
||||
std::scoped_lock lk{g_global_mutex};
|
||||
if (libCtxId < 1 || libCtxId >= 0x8000) {
|
||||
if (libCtxId < 1 || libCtxId >= 0x8000 || !g_contexts.contains(libCtxId)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto& context = g_contexts[libCtxId];
|
||||
|
||||
56
src/core/libraries/np/object_manager.h
Normal file
56
src/core/libraries/np/object_manager.h
Normal file
@ -0,0 +1,56 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
|
||||
template <typename T, size_t N, int INVALID_OBJECT_ID_ERROR, int OBJECT_NOT_FOUND_ERROR,
|
||||
int MAX_OBJECTS_ERROR>
|
||||
struct ObjectManager {
|
||||
s32 GetObject(int objectId, T** out) {
|
||||
std::scoped_lock lk{mutex};
|
||||
if (objectId < 1 || objectId > N) {
|
||||
return INVALID_OBJECT_ID_ERROR;
|
||||
}
|
||||
auto obj = objects[objectId - 1];
|
||||
if (!obj) {
|
||||
return OBJECT_NOT_FOUND_ERROR;
|
||||
}
|
||||
*out = obj;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
s32 CreateObject(Args&&... args) {
|
||||
std::scoped_lock lk{mutex};
|
||||
|
||||
if (auto slot = std::ranges::find(objects, nullptr); slot != objects.end()) {
|
||||
*slot = new T{args...};
|
||||
|
||||
return std::ranges::distance(objects.begin(), slot) + 1;
|
||||
}
|
||||
|
||||
return MAX_OBJECTS_ERROR;
|
||||
}
|
||||
|
||||
s32 DeleteObject(int objectId) {
|
||||
std::scoped_lock lk{mutex};
|
||||
|
||||
if (objectId < 1 || objectId > N) {
|
||||
return INVALID_OBJECT_ID_ERROR;
|
||||
}
|
||||
auto obj = objects[objectId - 1];
|
||||
if (!obj) {
|
||||
return OBJECT_NOT_FOUND_ERROR;
|
||||
}
|
||||
|
||||
delete obj;
|
||||
objects[objectId - 1] = nullptr;
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mutex;
|
||||
std::array<T*, N> objects = {nullptr};
|
||||
};
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/config.h"
|
||||
@ -94,18 +94,6 @@ int PS4_SYSV_ABI scePadGetCapability() {
|
||||
|
||||
int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerInformation* pInfo) {
|
||||
LOG_DEBUG(Lib_Pad, "called handle = {}", handle);
|
||||
if (handle < 0) {
|
||||
pInfo->touchPadInfo.pixelDensity = 1;
|
||||
pInfo->touchPadInfo.resolution.x = 1920;
|
||||
pInfo->touchPadInfo.resolution.y = 950;
|
||||
pInfo->stickInfo.deadZoneLeft = 1;
|
||||
pInfo->stickInfo.deadZoneRight = 1;
|
||||
pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD;
|
||||
pInfo->connectedCount = 1;
|
||||
pInfo->connected = false;
|
||||
pInfo->deviceClass = OrbisPadDeviceClass::Standard;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
pInfo->touchPadInfo.pixelDensity = 1;
|
||||
pInfo->touchPadInfo.resolution.x = 1920;
|
||||
pInfo->touchPadInfo.resolution.y = 950;
|
||||
@ -113,8 +101,12 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn
|
||||
pInfo->stickInfo.deadZoneRight = 1;
|
||||
pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD;
|
||||
pInfo->connectedCount = 1;
|
||||
pInfo->connected = true;
|
||||
pInfo->deviceClass = OrbisPadDeviceClass::Standard;
|
||||
if (handle < 0) {
|
||||
pInfo->connected = false;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
pInfo->connected = true;
|
||||
if (Config::getUseSpecialPad()) {
|
||||
pInfo->connectionType = ORBIS_PAD_PORT_TYPE_SPECIAL;
|
||||
pInfo->deviceClass = (OrbisPadDeviceClass)Config::getSpecialPadClass();
|
||||
@ -302,20 +294,16 @@ int PS4_SYSV_ABI scePadOutputReport() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
|
||||
LOG_TRACE(Lib_Pad, "called");
|
||||
int connected_count = 0;
|
||||
bool connected = false;
|
||||
Input::State states[64];
|
||||
auto* controller = Common::Singleton<GameController>::Instance();
|
||||
const auto* engine = controller->GetEngine();
|
||||
int ret_num = controller->ReadStates(states, num, &connected, &connected_count);
|
||||
|
||||
int ProcessStates(s32 handle, OrbisPadData* pData, Input::State* states, s32 num, bool connected,
|
||||
u32 connected_count) {
|
||||
if (!connected) {
|
||||
ret_num = 1;
|
||||
pData[0] = {};
|
||||
pData[0].orientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
pData[0].connected = false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ret_num; i++) {
|
||||
for (int i = 0; i < num; i++) {
|
||||
pData[i].buttons = states[i].buttonsState;
|
||||
pData[i].leftStick.x = states[i].axes[static_cast<int>(Input::Axis::LeftX)];
|
||||
pData[i].leftStick.y = states[i].axes[static_cast<int>(Input::Axis::LeftY)];
|
||||
@ -323,20 +311,16 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
|
||||
pData[i].rightStick.y = states[i].axes[static_cast<int>(Input::Axis::RightY)];
|
||||
pData[i].analogButtons.l2 = states[i].axes[static_cast<int>(Input::Axis::TriggerLeft)];
|
||||
pData[i].analogButtons.r2 = states[i].axes[static_cast<int>(Input::Axis::TriggerRight)];
|
||||
pData[i].acceleration.x = states[i].acceleration.x;
|
||||
pData[i].acceleration.y = states[i].acceleration.y;
|
||||
pData[i].acceleration.z = states[i].acceleration.z;
|
||||
pData[i].angularVelocity.x = states[i].angularVelocity.x;
|
||||
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};
|
||||
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;
|
||||
pData[i].orientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
|
||||
auto* controller = Common::Singleton<GameController>::Instance();
|
||||
const auto* engine = controller->GetEngine();
|
||||
if (engine && handle == 1) {
|
||||
const auto gyro_poll_rate = engine->GetAccelPollRate();
|
||||
if (gyro_poll_rate != 0.0f) {
|
||||
@ -354,7 +338,6 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
|
||||
controller->SetLastOrientation(outputOrientation);
|
||||
}
|
||||
}
|
||||
|
||||
pData[i].touchData.touchNum =
|
||||
(states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0);
|
||||
|
||||
@ -409,7 +392,18 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
|
||||
pData[i].deviceUniqueDataLen = 0;
|
||||
}
|
||||
|
||||
return ret_num;
|
||||
return num;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
|
||||
LOG_TRACE(Lib_Pad, "called");
|
||||
int connected_count = 0;
|
||||
bool connected = false;
|
||||
std::vector<Input::State> states(64);
|
||||
auto* controller = Common::Singleton<GameController>::Instance();
|
||||
const auto* engine = controller->GetEngine();
|
||||
int ret_num = controller->ReadStates(states.data(), num, &connected, &connected_count);
|
||||
return ProcessStates(handle, pData, states.data(), ret_num, connected, connected_count);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePadReadBlasterForTracker() {
|
||||
@ -439,95 +433,11 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) {
|
||||
}
|
||||
auto* controller = Common::Singleton<GameController>::Instance();
|
||||
const auto* engine = controller->GetEngine();
|
||||
int connectedCount = 0;
|
||||
bool isConnected = false;
|
||||
int connected_count = 0;
|
||||
bool connected = false;
|
||||
Input::State state;
|
||||
controller->ReadState(&state, &isConnected, &connectedCount);
|
||||
pData->buttons = state.buttonsState;
|
||||
pData->leftStick.x = state.axes[static_cast<int>(Input::Axis::LeftX)];
|
||||
pData->leftStick.y = state.axes[static_cast<int>(Input::Axis::LeftY)];
|
||||
pData->rightStick.x = state.axes[static_cast<int>(Input::Axis::RightX)];
|
||||
pData->rightStick.x = state.axes[static_cast<int>(Input::Axis::RightX)];
|
||||
pData->rightStick.y = state.axes[static_cast<int>(Input::Axis::RightY)];
|
||||
pData->analogButtons.l2 = state.axes[static_cast<int>(Input::Axis::TriggerLeft)];
|
||||
pData->analogButtons.r2 = state.axes[static_cast<int>(Input::Axis::TriggerRight)];
|
||||
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};
|
||||
|
||||
// Only do this on handle 1 for now
|
||||
if (engine && handle == 1) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
float deltaTime =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(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->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 = 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 = state.touchpad[1].ID;
|
||||
pData->timestamp = state.time;
|
||||
pData->connected = true; // isConnected; //TODO fix me proper
|
||||
pData->connectedCount = 1; // connectedCount;
|
||||
pData->deviceUniqueDataLen = 0;
|
||||
|
||||
controller->ReadState(&state, &connected, &connected_count);
|
||||
ProcessStates(handle, pData, &state, 1, connected, connected_count);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/alignment.h"
|
||||
@ -73,7 +73,7 @@ void MemoryManager::SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1
|
||||
}
|
||||
|
||||
u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 size) {
|
||||
static constexpr u64 MinSizeToClamp = 3_GB;
|
||||
static constexpr u64 MinSizeToClamp = 1_GB;
|
||||
// Dont bother with clamping if the size is small so we dont pay a map lookup on every buffer.
|
||||
if (size < MinSizeToClamp) {
|
||||
return size;
|
||||
@ -349,7 +349,8 @@ s32 MemoryManager::Free(PAddr phys_addr, u64 size, bool is_checked) {
|
||||
}
|
||||
|
||||
s32 MemoryManager::PoolCommit(VAddr virtual_addr, u64 size, MemoryProt prot, s32 mtype) {
|
||||
std::scoped_lock lk{mutex, unmap_mutex};
|
||||
std::scoped_lock lk{unmap_mutex};
|
||||
std::unique_lock lk2{mutex};
|
||||
ASSERT_MSG(IsValidMapping(virtual_addr, size), "Attempted to access invalid address {:#x}",
|
||||
virtual_addr);
|
||||
|
||||
@ -434,6 +435,7 @@ s32 MemoryManager::PoolCommit(VAddr virtual_addr, u64 size, MemoryProt prot, s32
|
||||
// Merge this VMA with similar nearby areas
|
||||
MergeAdjacent(vma_map, new_vma_handle);
|
||||
|
||||
lk2.unlock();
|
||||
if (IsValidGpuMapping(mapped_addr, size)) {
|
||||
rasterizer->MapMemory(mapped_addr, size);
|
||||
}
|
||||
@ -554,7 +556,7 @@ s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, Memo
|
||||
}
|
||||
|
||||
// Acquire writer lock.
|
||||
std::scoped_lock lk2{mutex};
|
||||
std::unique_lock lk2{mutex};
|
||||
|
||||
// Create VMA representing this mapping.
|
||||
auto new_vma_handle = CreateArea(virtual_addr, size, prot, flags, type, name, alignment);
|
||||
@ -650,6 +652,8 @@ s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, Memo
|
||||
// TRACK_ALLOC(mapped_addr, size, "VMEM");
|
||||
}
|
||||
|
||||
lk2.unlock();
|
||||
|
||||
// If this is not a reservation, then map to GPU and address space
|
||||
if (IsValidGpuMapping(mapped_addr, size)) {
|
||||
rasterizer->MapMemory(mapped_addr, size);
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/config.h"
|
||||
#include "common/types.h"
|
||||
#include "core/loader/elf.h"
|
||||
#include "core/loader/symbols_resolver.h"
|
||||
@ -164,6 +165,14 @@ public:
|
||||
return elf.IsSharedLib();
|
||||
}
|
||||
|
||||
bool IsSystemLib() {
|
||||
auto system_path = Config::getSysModulesPath();
|
||||
if (file.string().starts_with(system_path.string().c_str())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T = VAddr>
|
||||
T GetProcParam() const noexcept {
|
||||
return reinterpret_cast<T>(proc_param_virtual_addr);
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
#include "common/singleton.h"
|
||||
#include "core/debugger.h"
|
||||
#include "core/devtools/widget/module_list.h"
|
||||
#include "core/emulator_state.h"
|
||||
#include "core/file_format/psf.h"
|
||||
#include "core/file_format/trp.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
@ -206,6 +207,13 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
|
||||
Config::load(Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / (id + ".toml"),
|
||||
true);
|
||||
|
||||
if (std::filesystem::exists(Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) /
|
||||
(id + ".toml"))) {
|
||||
EmulatorState::GetInstance()->SetGameSpecifigConfigUsed(true);
|
||||
} else {
|
||||
EmulatorState::GetInstance()->SetGameSpecifigConfigUsed(false);
|
||||
}
|
||||
|
||||
// Initialize logging as soon as possible
|
||||
if (!id.empty() && Config::getSeparateLogFilesEnabled()) {
|
||||
Common::Log::Initialize(id + ".log");
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
@ -61,110 +61,51 @@ void State::OnAccel(const float accel[3]) {
|
||||
acceleration.z = accel[2];
|
||||
}
|
||||
|
||||
GameController::GameController() {
|
||||
m_states_num = 0;
|
||||
m_last_state = State();
|
||||
}
|
||||
GameController::GameController() : m_states_queue(64) {}
|
||||
|
||||
void GameController::ReadState(State* state, bool* isConnected, int* connectedCount) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
*isConnected = m_connected;
|
||||
*connectedCount = m_connected_count;
|
||||
*state = GetLastState();
|
||||
*state = m_state;
|
||||
}
|
||||
|
||||
int GameController::ReadStates(State* states, int states_num, bool* isConnected,
|
||||
int* connectedCount) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
*isConnected = m_connected;
|
||||
*connectedCount = m_connected_count;
|
||||
|
||||
int ret_num = 0;
|
||||
|
||||
if (m_connected) {
|
||||
if (m_states_num == 0) {
|
||||
ret_num = 1;
|
||||
states[0] = m_last_state;
|
||||
} else {
|
||||
for (uint32_t i = 0; i < m_states_num; i++) {
|
||||
if (ret_num >= states_num) {
|
||||
break;
|
||||
}
|
||||
auto index = (m_first_state + i) % MAX_STATES;
|
||||
if (!m_private[index].obtained) {
|
||||
m_private[index].obtained = true;
|
||||
|
||||
states[ret_num++] = m_states[index];
|
||||
}
|
||||
std::lock_guard lg(m_states_queue_mutex);
|
||||
for (int i = 0; i < states_num; i++) {
|
||||
auto o_state = m_states_queue.Pop();
|
||||
if (!o_state) {
|
||||
break;
|
||||
}
|
||||
states[ret_num++] = *o_state;
|
||||
}
|
||||
}
|
||||
|
||||
return ret_num;
|
||||
}
|
||||
|
||||
State GameController::GetLastState() const {
|
||||
if (m_states_num == 0) {
|
||||
return m_last_state;
|
||||
}
|
||||
const u32 last = (m_first_state + m_states_num - 1) % MAX_STATES;
|
||||
return m_states[last];
|
||||
}
|
||||
|
||||
void GameController::AddState(const State& state) {
|
||||
if (m_states_num >= MAX_STATES) {
|
||||
m_states_num = MAX_STATES - 1;
|
||||
m_first_state = (m_first_state + 1) % MAX_STATES;
|
||||
}
|
||||
|
||||
const u32 index = (m_first_state + m_states_num) % MAX_STATES;
|
||||
m_states[index] = state;
|
||||
m_last_state = state;
|
||||
m_private[index].obtained = false;
|
||||
m_states_num++;
|
||||
}
|
||||
|
||||
void GameController::CheckButton(int id, OrbisPadButtonDataOffset button, bool is_pressed) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
auto state = GetLastState();
|
||||
|
||||
state.time = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
state.OnButton(button, is_pressed);
|
||||
|
||||
AddState(state);
|
||||
void GameController::Button(int id, OrbisPadButtonDataOffset button, bool is_pressed) {
|
||||
m_state.OnButton(button, is_pressed);
|
||||
PushState();
|
||||
}
|
||||
|
||||
void GameController::Axis(int id, Input::Axis axis, int value) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
auto state = GetLastState();
|
||||
|
||||
state.time = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
state.OnAxis(axis, value);
|
||||
|
||||
AddState(state);
|
||||
m_state.OnAxis(axis, value);
|
||||
PushState();
|
||||
}
|
||||
|
||||
void GameController::Gyro(int id, const float gyro[3]) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
auto state = GetLastState();
|
||||
state.time = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
|
||||
// Update the angular velocity (gyro data)
|
||||
state.OnGyro(gyro);
|
||||
|
||||
AddState(state);
|
||||
m_state.OnGyro(gyro);
|
||||
PushState();
|
||||
}
|
||||
|
||||
void GameController::Acceleration(int id, const float acceleration[3]) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
auto state = GetLastState();
|
||||
state.time = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
|
||||
// Update the acceleration values
|
||||
state.OnAccel(acceleration);
|
||||
|
||||
AddState(state);
|
||||
m_state.OnAccel(acceleration);
|
||||
PushState();
|
||||
}
|
||||
|
||||
void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
|
||||
@ -206,7 +147,6 @@ void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) {
|
||||
if (!m_engine) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock _{m_mutex};
|
||||
m_engine->SetLightBarRGB(r, g, b);
|
||||
}
|
||||
|
||||
@ -214,39 +154,29 @@ void GameController::SetVibration(u8 smallMotor, u8 largeMotor) {
|
||||
if (!m_engine) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock _{m_mutex};
|
||||
m_engine->SetVibration(smallMotor, largeMotor);
|
||||
}
|
||||
|
||||
void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, float y) {
|
||||
if (touchIndex < 2) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
auto state = GetLastState();
|
||||
|
||||
state.time = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
state.OnTouchpad(touchIndex, touchDown, x, y);
|
||||
|
||||
AddState(state);
|
||||
m_state.OnTouchpad(touchIndex, touchDown, x, y);
|
||||
PushState();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@ -254,47 +184,38 @@ void GameController::SetSecondaryTouchCount(u8 touchCount) {
|
||||
}
|
||||
|
||||
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> engine) {
|
||||
std::scoped_lock _{m_mutex};
|
||||
m_engine = std::move(engine);
|
||||
if (m_engine) {
|
||||
m_engine->Init();
|
||||
@ -305,24 +226,17 @@ Engine* GameController::GetEngine() {
|
||||
return m_engine.get();
|
||||
}
|
||||
|
||||
void GameController::PushState() {
|
||||
std::lock_guard lg(m_states_queue_mutex);
|
||||
m_state.time = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
m_states_queue.Push(m_state);
|
||||
}
|
||||
|
||||
u32 GameController::Poll() {
|
||||
if (m_connected) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
auto time = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
if (m_states_num == 0) {
|
||||
auto diff = (time - m_last_state.time) / 1000;
|
||||
if (diff >= 100) {
|
||||
AddState(GetLastState());
|
||||
}
|
||||
} else {
|
||||
auto index = (m_first_state - 1 + m_states_num) % MAX_STATES;
|
||||
auto diff = (time - m_states[index].time) / 1000;
|
||||
if (m_private[index].obtained && diff >= 100) {
|
||||
AddState(GetLastState());
|
||||
}
|
||||
}
|
||||
PushState();
|
||||
}
|
||||
return 100;
|
||||
return 33;
|
||||
}
|
||||
|
||||
} // namespace Input
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
@ -6,7 +6,10 @@
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include <SDL3/SDL_gamepad.h>
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/pad/pad.h"
|
||||
|
||||
@ -63,7 +66,36 @@ inline int GetAxis(int min, int max, int value) {
|
||||
return std::clamp((255 * (value - min)) / (max - min), 0, 255);
|
||||
}
|
||||
|
||||
constexpr u32 MAX_STATES = 32;
|
||||
template <class T>
|
||||
class RingBufferQueue {
|
||||
public:
|
||||
RingBufferQueue(size_t size) : m_storage(size) {}
|
||||
|
||||
void Push(T item) {
|
||||
const size_t index = (m_begin + m_size) % m_storage.size();
|
||||
m_storage[index] = std::move(item);
|
||||
if (m_size < m_storage.size()) {
|
||||
m_size += 1;
|
||||
} else {
|
||||
m_begin = (m_begin + 1) % m_storage.size();
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<T> Pop() {
|
||||
if (m_size == 0) {
|
||||
return {};
|
||||
}
|
||||
const size_t index = m_begin;
|
||||
m_begin = (m_begin + 1) % m_storage.size();
|
||||
m_size -= 1;
|
||||
return std::move(m_storage[index]);
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_begin = 0;
|
||||
size_t m_size = 0;
|
||||
std::vector<T> m_storage;
|
||||
};
|
||||
|
||||
class GameController {
|
||||
public:
|
||||
@ -72,9 +104,8 @@ public:
|
||||
|
||||
void ReadState(State* state, bool* isConnected, int* connectedCount);
|
||||
int ReadStates(State* states, int states_num, bool* isConnected, int* connectedCount);
|
||||
State GetLastState() const;
|
||||
void CheckButton(int id, Libraries::Pad::OrbisPadButtonDataOffset button, bool isPressed);
|
||||
void AddState(const State& state);
|
||||
|
||||
void Button(int id, Libraries::Pad::OrbisPadButtonDataOffset button, bool isPressed);
|
||||
void Axis(int id, Input::Axis axis, int value);
|
||||
void Gyro(int id, const float gyro[3]);
|
||||
void Acceleration(int id, const float acceleration[3]);
|
||||
@ -105,26 +136,22 @@ public:
|
||||
Libraries::Pad::OrbisFQuaternion& orientation);
|
||||
|
||||
private:
|
||||
struct StateInternal {
|
||||
bool obtained = false;
|
||||
};
|
||||
void PushState();
|
||||
|
||||
std::mutex m_mutex;
|
||||
bool m_connected = true;
|
||||
State m_last_state;
|
||||
int m_connected_count = 0;
|
||||
u32 m_states_num = 0;
|
||||
u32 m_first_state = 0;
|
||||
int m_connected_count = 1;
|
||||
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<State, MAX_STATES> m_states;
|
||||
std::array<StateInternal, MAX_STATES> m_private;
|
||||
std::chrono::steady_clock::time_point m_last_update = {};
|
||||
Libraries::Pad::OrbisFQuaternion m_orientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
|
||||
State m_state;
|
||||
|
||||
std::mutex m_states_queue_mutex;
|
||||
RingBufferQueue<State> m_states_queue;
|
||||
|
||||
std::unique_ptr<Engine> m_engine = nullptr;
|
||||
};
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "input_handler.h"
|
||||
@ -22,6 +22,8 @@
|
||||
#include "common/elf_info.h"
|
||||
#include "common/io_file.h"
|
||||
#include "common/path_util.h"
|
||||
#include "core/devtools/layer.h"
|
||||
#include "core/emulator_state.h"
|
||||
#include "input/controller.h"
|
||||
#include "input/input_mouse.h"
|
||||
|
||||
@ -200,6 +202,8 @@ InputBinding GetBindingFromString(std::string& line) {
|
||||
input = InputID(InputType::Axis, string_to_axis_map.at(t).axis);
|
||||
} else if (string_to_cbutton_map.find(t) != string_to_cbutton_map.end()) {
|
||||
input = InputID(InputType::Controller, string_to_cbutton_map.at(t));
|
||||
} else if (string_to_hotkey_map.find(t) != string_to_hotkey_map.end()) {
|
||||
input = InputID(InputType::Controller, string_to_hotkey_map.at(t));
|
||||
} else {
|
||||
// Invalid token found; return default binding
|
||||
LOG_DEBUG(Input, "Invalid token found: {}", t);
|
||||
@ -220,8 +224,8 @@ InputBinding GetBindingFromString(std::string& line) {
|
||||
|
||||
void ParseInputConfig(const std::string game_id = "") {
|
||||
std::string game_id_or_default = Config::GetUseUnifiedInputConfig() ? "default" : game_id;
|
||||
const auto config_file = Config::GetFoolproofInputConfigFile(game_id_or_default);
|
||||
const auto global_config_file = Config::GetFoolproofInputConfigFile("global");
|
||||
const auto config_file = Config::GetInputConfigFile(game_id_or_default);
|
||||
const auto global_config_file = Config::GetInputConfigFile("global");
|
||||
|
||||
// we reset these here so in case the user fucks up or doesn't include some of these,
|
||||
// we can fall back to default
|
||||
@ -394,19 +398,23 @@ void ParseInputConfig(const std::string game_id = "") {
|
||||
|
||||
// normal cases
|
||||
InputBinding binding = GetBindingFromString(input_string);
|
||||
BindingConnection connection(InputID(), nullptr);
|
||||
auto button_it = string_to_cbutton_map.find(output_string);
|
||||
auto axis_it = string_to_axis_map.find(output_string);
|
||||
if (binding.IsEmpty()) {
|
||||
LOG_WARNING(Input, "Invalid format at line: {}, data: \"{}\", skipping line.",
|
||||
lineCount, line);
|
||||
return;
|
||||
}
|
||||
BindingConnection connection(InputID(), nullptr);
|
||||
auto button_it = string_to_cbutton_map.find(output_string);
|
||||
auto hotkey_it = string_to_hotkey_map.find(output_string);
|
||||
auto axis_it = string_to_axis_map.find(output_string);
|
||||
if (button_it != string_to_cbutton_map.end()) {
|
||||
connection = BindingConnection(
|
||||
binding, &*std::ranges::find(output_array, ControllerOutput(button_it->second)));
|
||||
connections.insert(connections.end(), connection);
|
||||
|
||||
} else if (hotkey_it != string_to_hotkey_map.end()) {
|
||||
connection = BindingConnection(
|
||||
binding, &*std::ranges::find(output_array, ControllerOutput(hotkey_it->second)));
|
||||
connections.insert(connections.end(), connection);
|
||||
} else if (axis_it != string_to_axis_map.end()) {
|
||||
int value_to_set = binding.keys[2].type == InputType::Axis ? 0 : axis_it->second.value;
|
||||
connection = BindingConnection(
|
||||
@ -544,19 +552,20 @@ void ControllerOutput::FinalizeUpdate() {
|
||||
}
|
||||
old_button_state = new_button_state;
|
||||
old_param = *new_param;
|
||||
bool is_game_specific = EmulatorState::GetInstance()->IsGameSpecifigConfigUsed();
|
||||
if (button != SDL_GAMEPAD_BUTTON_INVALID) {
|
||||
switch (button) {
|
||||
case SDL_GAMEPAD_BUTTON_TOUCHPAD_LEFT:
|
||||
controller->SetTouchpadState(0, new_button_state, 0.25f, 0.5f);
|
||||
controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state);
|
||||
controller->Button(0, SDLGamepadToOrbisButton(button), new_button_state);
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_TOUCHPAD_CENTER:
|
||||
controller->SetTouchpadState(0, new_button_state, 0.50f, 0.5f);
|
||||
controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state);
|
||||
controller->Button(0, SDLGamepadToOrbisButton(button), new_button_state);
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_TOUCHPAD_RIGHT:
|
||||
controller->SetTouchpadState(0, new_button_state, 0.75f, 0.5f);
|
||||
controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state);
|
||||
controller->Button(0, SDLGamepadToOrbisButton(button), new_button_state);
|
||||
break;
|
||||
case LEFTJOYSTICK_HALFMODE:
|
||||
leftjoystick_halfmode = new_button_state;
|
||||
@ -589,10 +598,14 @@ void ControllerOutput::FinalizeUpdate() {
|
||||
PushSDLEvent(SDL_EVENT_RDOC_CAPTURE);
|
||||
break;
|
||||
case HOTKEY_VOLUME_UP:
|
||||
Config::setVolumeSlider(Config::getVolumeSlider() + 10, true);
|
||||
Config::setVolumeSlider(std::clamp(Config::getVolumeSlider() + 10, 0, 500),
|
||||
is_game_specific);
|
||||
Overlay::ShowVolume();
|
||||
break;
|
||||
case HOTKEY_VOLUME_DOWN:
|
||||
Config::setVolumeSlider(Config::getVolumeSlider() - 10, true);
|
||||
Config::setVolumeSlider(std::clamp(Config::getVolumeSlider() - 10, 0, 500),
|
||||
is_game_specific);
|
||||
Overlay::ShowVolume();
|
||||
break;
|
||||
case HOTKEY_QUIT:
|
||||
PushSDLEvent(SDL_EVENT_QUIT_DIALOG);
|
||||
@ -604,7 +617,7 @@ void ControllerOutput::FinalizeUpdate() {
|
||||
SetMouseGyroRollMode(new_button_state);
|
||||
break;
|
||||
default: // is a normal key (hopefully)
|
||||
controller->CheckButton(0, SDLGamepadToOrbisButton(button), new_button_state);
|
||||
controller->Button(0, SDLGamepadToOrbisButton(button), new_button_state);
|
||||
break;
|
||||
}
|
||||
} else if (axis != SDL_GAMEPAD_AXIS_INVALID && positive_axis) {
|
||||
@ -635,12 +648,12 @@ void ControllerOutput::FinalizeUpdate() {
|
||||
case Axis::TriggerLeft:
|
||||
ApplyDeadzone(new_param, lefttrigger_deadzone);
|
||||
controller->Axis(0, c_axis, GetAxis(0x0, 0x7f, *new_param));
|
||||
controller->CheckButton(0, OrbisPadButtonDataOffset::L2, *new_param > 0x20);
|
||||
controller->Button(0, OrbisPadButtonDataOffset::L2, *new_param > 0x20);
|
||||
return;
|
||||
case Axis::TriggerRight:
|
||||
ApplyDeadzone(new_param, righttrigger_deadzone);
|
||||
controller->Axis(0, c_axis, GetAxis(0x0, 0x7f, *new_param));
|
||||
controller->CheckButton(0, OrbisPadButtonDataOffset::R2, *new_param > 0x20);
|
||||
controller->Button(0, OrbisPadButtonDataOffset::R2, *new_param > 0x20);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
@ -138,6 +138,8 @@ const std::map<std::string, u32> string_to_cbutton_map = {
|
||||
{"rpaddle_high", SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1},
|
||||
{"rpaddle_low", SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2},
|
||||
{"mouse_gyro_roll_mode", MOUSE_GYRO_ROLL_MODE},
|
||||
};
|
||||
const std::map<std::string, u32> string_to_hotkey_map = {
|
||||
{"hotkey_pause", HOTKEY_PAUSE},
|
||||
{"hotkey_fullscreen", HOTKEY_FULLSCREEN},
|
||||
{"hotkey_show_fps", HOTKEY_SIMPLE_FPS},
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cmath>
|
||||
@ -104,8 +104,8 @@ void EmulateTouchpad(GameController* controller, u32 interval) {
|
||||
controller->SetTouchpadState(0, (mouse_buttons & SDL_BUTTON_LMASK) != 0,
|
||||
std::clamp(x / g_window->GetWidth(), 0.0f, 1.0f),
|
||||
std::clamp(y / g_window->GetHeight(), 0.0f, 1.0f));
|
||||
controller->CheckButton(0, Libraries::Pad::OrbisPadButtonDataOffset::TouchPad,
|
||||
(mouse_buttons & SDL_BUTTON_RMASK) != 0);
|
||||
controller->Button(0, Libraries::Pad::OrbisPadButtonDataOffset::TouchPad,
|
||||
(mouse_buttons & SDL_BUTTON_RMASK) != 0);
|
||||
}
|
||||
|
||||
void ApplyMouseInputBlockers() {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "SDL3/SDL_events.h"
|
||||
@ -269,9 +269,14 @@ namespace Frontend {
|
||||
|
||||
using namespace Libraries::Pad;
|
||||
|
||||
static Uint32 SDLCALL PollController(void* userdata, SDL_TimerID timer_id, Uint32 interval) {
|
||||
std::mutex motion_control_mutex;
|
||||
float gyro_buf[3] = {0.0f, 0.0f, 0.0f}, accel_buf[3] = {0.0f, -9.81f, 0.0f};
|
||||
static Uint32 SDLCALL PollGyroAndAccel(void* userdata, SDL_TimerID timer_id, Uint32 interval) {
|
||||
auto* controller = reinterpret_cast<Input::GameController*>(userdata);
|
||||
return controller->Poll();
|
||||
std::scoped_lock l{motion_control_mutex};
|
||||
controller->Gyro(0, gyro_buf);
|
||||
controller->Acceleration(0, accel_buf);
|
||||
return 4;
|
||||
}
|
||||
|
||||
WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_,
|
||||
@ -407,12 +412,16 @@ void WindowSDL::WaitEvent() {
|
||||
// AND IT DOESN'T EVEN USE PROPER ENUMS
|
||||
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE:
|
||||
switch ((SDL_SensorType)event.gsensor.sensor) {
|
||||
case SDL_SENSOR_GYRO:
|
||||
controller->Gyro(0, event.gsensor.data);
|
||||
case SDL_SENSOR_GYRO: {
|
||||
std::scoped_lock l{motion_control_mutex};
|
||||
memcpy(gyro_buf, event.gsensor.data, sizeof(gyro_buf));
|
||||
break;
|
||||
case SDL_SENSOR_ACCEL:
|
||||
controller->Acceleration(0, event.gsensor.data);
|
||||
}
|
||||
case SDL_SENSOR_ACCEL: {
|
||||
std::scoped_lock l{motion_control_mutex};
|
||||
memcpy(accel_buf, event.gsensor.data, sizeof(accel_buf));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -471,7 +480,7 @@ void WindowSDL::WaitEvent() {
|
||||
}
|
||||
|
||||
void WindowSDL::InitTimers() {
|
||||
SDL_AddTimer(100, &PollController, controller);
|
||||
SDL_AddTimer(4, &PollGyroAndAccel, controller);
|
||||
SDL_AddTimer(33, Input::MousePolling, (void*)controller);
|
||||
}
|
||||
|
||||
@ -540,7 +549,7 @@ void WindowSDL::OnGamepadEvent(const SDL_Event* event) {
|
||||
// as it would break the entire touchpad handling
|
||||
// You can still bind other things to it though
|
||||
if (event->gbutton.button == SDL_GAMEPAD_BUTTON_TOUCHPAD) {
|
||||
controller->CheckButton(0, OrbisPadButtonDataOffset::TouchPad, input_down);
|
||||
controller->Button(0, OrbisPadButtonDataOffset::TouchPad, input_down);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -583,6 +583,14 @@ void Translator::S_MULK_I32(const GcnInst& inst) {
|
||||
// SOP1
|
||||
|
||||
void Translator::S_MOV(const GcnInst& inst) {
|
||||
if (inst.dst[0].field == OperandField::ScalarGPR) {
|
||||
if (inst.src[0].field == OperandField::ExecLo) {
|
||||
ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[0].code), ir.GetExec());
|
||||
return;
|
||||
} else if (inst.src[0].field == OperandField::ExecHi) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
SetDst(inst.dst[0], GetSrc(inst.src[0]));
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user