Merge remote-tracking branch 'origin/main'

This commit is contained in:
kalaposfos13 2024-12-26 09:40:29 +01:00
commit 7792fd0342
134 changed files with 8659 additions and 5279 deletions

View File

@ -16,7 +16,7 @@ if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
project(shadPS4)
project(shadPS4 CXX C ASM)
# Forcing PIE makes sure that the base address is high enough so that it doesn't clash with the PS4 memory.
if(UNIX AND NOT APPLE)
@ -392,7 +392,8 @@ set(USBD_LIB src/core/libraries/usbd/usbd.cpp
src/core/libraries/usbd/usbd.h
)
set(FIBER_LIB src/core/libraries/fiber/fiber.cpp
set(FIBER_LIB src/core/libraries/fiber/fiber_context.s
src/core/libraries/fiber/fiber.cpp
src/core/libraries/fiber/fiber.h
src/core/libraries/fiber/fiber_error.h
)
@ -630,6 +631,8 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h
src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp
src/shader_recompiler/backend/spirv/emit_spirv_quad_rect.cpp
src/shader_recompiler/backend/spirv/emit_spirv_quad_rect.h
src/shader_recompiler/backend/spirv/emit_spirv_select.cpp
src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp
src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
@ -896,7 +899,8 @@ if (ENABLE_DISCORD_RPC)
target_compile_definitions(shadps4 PRIVATE ENABLE_DISCORD_RPC)
endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
# Optional due to https://github.com/shadps4-emu/shadPS4/issues/1704
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND ENABLE_USERFAULTFD)
target_compile_definitions(shadps4 PRIVATE ENABLE_USERFAULTFD)
endif()

View File

@ -25,7 +25,7 @@ Once you are within the installer:
Beware, this requires you to create a Qt account. If you do not want to do this, please follow the MSYS2/MinGW compilation method instead.
1. Under the current, non beta version of Qt (at the time of writing 6.7.3), select the option `MSVC 2022 64-bit` or similar.
1. Under the current, non beta version of Qt (at the time of writing 6.7.3), select the option `MSVC 2022 64-bit` or similar, as well as `QT Multimedia`.
If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `MSVC 2022 ARM64` instead.
Go through the installation normally. If you know what you are doing, you may unselect individual components that eat up too much disk space.

View File

@ -189,7 +189,11 @@ add_library(Dear_ImGui
target_include_directories(Dear_ImGui INTERFACE dear_imgui/)
# Tracy
option(TRACY_ENABLE "" ON)
if (CMAKE_BUILD_TYPE STREQUAL "Release")
option(TRACY_ENABLE "" OFF)
else()
option(TRACY_ENABLE "" ON)
endif()
option(TRACY_NO_CRASH_HANDLER "" ON) # Otherwise texture cache exceptions will be treaten as a crash
option(TRACY_ON_DEMAND "" ON)
option(TRACY_NO_FRAME_IMAGE "" ON)

View File

@ -34,6 +34,7 @@ namespace Config {
static bool isNeo = false;
static bool isFullscreen = false;
static bool playBGM = false;
static bool isTrophyPopupDisabled = false;
static int BGMvolume = 50;
static bool enableDiscordRPC = false;
static u32 screenWidth = 1280;
@ -64,6 +65,8 @@ static bool vkCrashDiagnostic = false;
static s16 cursorState = HideCursorState::Idle;
static int cursorHideTimeout = 5; // 5 seconds (default)
static bool separateupdatefolder = false;
static bool compatibilityData = false;
static bool checkCompatibilityOnStartup = false;
// Gui
std::vector<std::filesystem::path> settings_install_dirs = {};
@ -96,6 +99,10 @@ bool isFullscreenMode() {
return isFullscreen;
}
bool getisTrophyPopupDisabled() {
return isTrophyPopupDisabled;
}
bool getPlayBGM() {
return playBGM;
}
@ -224,6 +231,14 @@ bool getSeparateUpdateEnabled() {
return separateupdatefolder;
}
bool getCompatibilityEnabled() {
return compatibilityData;
}
bool getCheckCompatibilityOnStartup() {
return checkCompatibilityOnStartup;
}
void setGpuId(s32 selectedGpuId) {
gpuId = selectedGpuId;
}
@ -284,6 +299,10 @@ void setFullscreenMode(bool enable) {
isFullscreen = enable;
}
void setisTrophyPopupDisabled(bool disable) {
isTrophyPopupDisabled = disable;
}
void setPlayBGM(bool enable) {
playBGM = enable;
}
@ -344,6 +363,14 @@ void setSeparateUpdateEnabled(bool use) {
separateupdatefolder = use;
}
void setCompatibilityEnabled(bool use) {
compatibilityData = use;
}
void setCheckCompatibilityOnStartup(bool use) {
checkCompatibilityOnStartup = use;
}
void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) {
main_window_geometry_x = x;
main_window_geometry_y = y;
@ -531,6 +558,7 @@ void load(const std::filesystem::path& path) {
isNeo = toml::find_or<bool>(general, "isPS4Pro", false);
isFullscreen = toml::find_or<bool>(general, "Fullscreen", false);
playBGM = toml::find_or<bool>(general, "playBGM", false);
isTrophyPopupDisabled = toml::find_or<bool>(general, "isTrophyPopupDisabled", false);
BGMvolume = toml::find_or<int>(general, "BGMvolume", 50);
enableDiscordRPC = toml::find_or<bool>(general, "enableDiscordRPC", true);
logFilter = toml::find_or<std::string>(general, "logFilter", "");
@ -544,6 +572,9 @@ void load(const std::filesystem::path& path) {
isShowSplash = toml::find_or<bool>(general, "showSplash", true);
isAutoUpdate = toml::find_or<bool>(general, "autoUpdate", false);
separateupdatefolder = toml::find_or<bool>(general, "separateUpdateEnabled", false);
compatibilityData = toml::find_or<bool>(general, "compatibilityEnabled", false);
checkCompatibilityOnStartup =
toml::find_or<bool>(general, "checkCompatibilityOnStartup", false);
}
if (data.contains("Input")) {
@ -646,6 +677,7 @@ void save(const std::filesystem::path& path) {
data["General"]["isPS4Pro"] = isNeo;
data["General"]["Fullscreen"] = isFullscreen;
data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled;
data["General"]["playBGM"] = playBGM;
data["General"]["BGMvolume"] = BGMvolume;
data["General"]["enableDiscordRPC"] = enableDiscordRPC;
@ -656,6 +688,8 @@ void save(const std::filesystem::path& path) {
data["General"]["showSplash"] = isShowSplash;
data["General"]["autoUpdate"] = isAutoUpdate;
data["General"]["separateUpdateEnabled"] = separateupdatefolder;
data["General"]["compatibilityEnabled"] = compatibilityData;
data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup;
data["Input"]["cursorState"] = cursorState;
data["Input"]["cursorHideTimeout"] = cursorHideTimeout;
data["Input"]["backButtonBehavior"] = backButtonBehavior;
@ -740,6 +774,7 @@ void saveMainWindow(const std::filesystem::path& path) {
void setDefaultValues() {
isNeo = false;
isFullscreen = false;
isTrophyPopupDisabled = false;
playBGM = false;
BGMvolume = 50;
enableDiscordRPC = true;
@ -775,6 +810,8 @@ void setDefaultValues() {
m_language = 1;
gpuId = -1;
separateupdatefolder = false;
compatibilityData = false;
checkCompatibilityOnStartup = false;
}
constexpr std::string_view GetDefaultKeyboardConfig() {

View File

@ -19,8 +19,11 @@ bool isNeoMode();
bool isFullscreenMode();
bool getPlayBGM();
int getBGMvolume();
bool getisTrophyPopupDisabled();
bool getEnableDiscordRPC();
bool getSeparateUpdateEnabled();
bool getCompatibilityEnabled();
bool getCheckCompatibilityOnStartup();
std::string getLogFilter();
std::string getLogType();
@ -60,6 +63,7 @@ void setGpuId(s32 selectedGpuId);
void setScreenWidth(u32 width);
void setScreenHeight(u32 height);
void setFullscreenMode(bool enable);
void setisTrophyPopupDisabled(bool disable);
void setPlayBGM(bool enable);
void setBGMvolume(int volume);
void setEnableDiscordRPC(bool enable);
@ -69,6 +73,8 @@ void setUserName(const std::string& type);
void setUpdateChannel(const std::string& type);
void setSeparateUpdateEnabled(bool use);
void setGameInstallDirs(const std::vector<std::filesystem::path>& settings_install_dirs_config);
void setCompatibilityEnabled(bool use);
void setCheckCompatibilityOnStartup(bool use);
void setCursorState(s16 cursorState);
void setCursorHideTimeout(int newcursorHideTimeout);

View File

@ -14,7 +14,11 @@
#include <tracy/Tracy.hpp>
static inline bool IsProfilerConnected() {
#if TRACY_ENABLE
return tracy::GetProfiler().IsConnected();
#else
return false;
#endif
}
#define TRACY_GPU_ENABLED 0

View File

@ -1,25 +1,25 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <cstddef>
#include <type_traits>
namespace Common {
/// Ceiled integer division.
template <typename N, typename D>
requires std::is_integral_v<N> && std::is_unsigned_v<D>
[[nodiscard]] constexpr N DivCeil(N number, D divisor) {
return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
}
/// Ceiled integer division with logarithmic divisor in base 2
template <typename N, typename D>
requires std::is_integral_v<N> && std::is_unsigned_v<D>
[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) {
return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
}
} // namespace Common
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <cstddef>
#include <type_traits>
namespace Common {
/// Ceiled integer division.
template <typename N, typename D>
requires std::is_integral_v<N> && std::is_unsigned_v<D>
[[nodiscard]] constexpr N DivCeil(N number, D divisor) {
return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
}
/// Ceiled integer division with logarithmic divisor in base 2
template <typename N, typename D>
requires std::is_integral_v<N> && std::is_unsigned_v<D>
[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) {
return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
}
} // namespace Common

View File

@ -207,7 +207,7 @@ public:
return WriteSpan(string);
}
static size_t WriteBytes(const std::filesystem::path path, std::span<const u8> data) {
static size_t WriteBytes(const std::filesystem::path path, const auto& data) {
IOFile out(path, FileAccessMode::Write);
return out.Write(data);
}

View File

@ -1,30 +1,30 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef _WIN32
#include "ntapi.h"
NtClose_t NtClose = nullptr;
NtSetInformationFile_t NtSetInformationFile = nullptr;
NtCreateThread_t NtCreateThread = nullptr;
NtTerminateThread_t NtTerminateThread = nullptr;
NtQueueApcThreadEx_t NtQueueApcThreadEx = nullptr;
namespace Common::NtApi {
void Initialize() {
HMODULE nt_handle = GetModuleHandleA("ntdll.dll");
// http://stackoverflow.com/a/31411628/4725495
NtClose = (NtClose_t)GetProcAddress(nt_handle, "NtClose");
NtSetInformationFile =
(NtSetInformationFile_t)GetProcAddress(nt_handle, "NtSetInformationFile");
NtCreateThread = (NtCreateThread_t)GetProcAddress(nt_handle, "NtCreateThread");
NtTerminateThread = (NtTerminateThread_t)GetProcAddress(nt_handle, "NtTerminateThread");
NtQueueApcThreadEx = (NtQueueApcThreadEx_t)GetProcAddress(nt_handle, "NtQueueApcThreadEx");
}
} // namespace Common::NtApi
#endif
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef _WIN32
#include "ntapi.h"
NtClose_t NtClose = nullptr;
NtSetInformationFile_t NtSetInformationFile = nullptr;
NtCreateThread_t NtCreateThread = nullptr;
NtTerminateThread_t NtTerminateThread = nullptr;
NtQueueApcThreadEx_t NtQueueApcThreadEx = nullptr;
namespace Common::NtApi {
void Initialize() {
HMODULE nt_handle = GetModuleHandleA("ntdll.dll");
// http://stackoverflow.com/a/31411628/4725495
NtClose = (NtClose_t)GetProcAddress(nt_handle, "NtClose");
NtSetInformationFile =
(NtSetInformationFile_t)GetProcAddress(nt_handle, "NtSetInformationFile");
NtCreateThread = (NtCreateThread_t)GetProcAddress(nt_handle, "NtCreateThread");
NtTerminateThread = (NtTerminateThread_t)GetProcAddress(nt_handle, "NtTerminateThread");
NtQueueApcThreadEx = (NtQueueApcThreadEx_t)GetProcAddress(nt_handle, "NtQueueApcThreadEx");
}
} // namespace Common::NtApi
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,53 +1,53 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/spin_lock.h"
#if _MSC_VER
#include <intrin.h>
#if _M_AMD64
#define __x86_64__ 1
#endif
#if _M_ARM64
#define __aarch64__ 1
#endif
#else
#if __x86_64__
#include <xmmintrin.h>
#endif
#endif
namespace {
void ThreadPause() {
#if __x86_64__
_mm_pause();
#elif __aarch64__ && _MSC_VER
__yield();
#elif __aarch64__
asm("yield");
#endif
}
} // Anonymous namespace
namespace Common {
void SpinLock::lock() {
while (lck.test_and_set(std::memory_order_acquire)) {
ThreadPause();
}
}
void SpinLock::unlock() {
lck.clear(std::memory_order_release);
}
bool SpinLock::try_lock() {
if (lck.test_and_set(std::memory_order_acquire)) {
return false;
}
return true;
}
} // namespace Common
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/spin_lock.h"
#if _MSC_VER
#include <intrin.h>
#if _M_AMD64
#define __x86_64__ 1
#endif
#if _M_ARM64
#define __aarch64__ 1
#endif
#else
#if __x86_64__
#include <xmmintrin.h>
#endif
#endif
namespace {
void ThreadPause() {
#if __x86_64__
_mm_pause();
#elif __aarch64__ && _MSC_VER
__yield();
#elif __aarch64__
asm("yield");
#endif
}
} // Anonymous namespace
namespace Common {
void SpinLock::lock() {
while (lck.test_and_set(std::memory_order_acquire)) {
ThreadPause();
}
}
void SpinLock::unlock() {
lck.clear(std::memory_order_release);
}
bool SpinLock::try_lock() {
if (lck.test_and_set(std::memory_order_acquire)) {
return false;
}
return true;
}
} // namespace Common

View File

@ -1,33 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
namespace Common {
/**
* SpinLock class
* a lock similar to mutex that forces a thread to spin wait instead calling the
* supervisor. Should be used on short sequences of code.
*/
class SpinLock {
public:
SpinLock() = default;
SpinLock(const SpinLock&) = delete;
SpinLock& operator=(const SpinLock&) = delete;
SpinLock(SpinLock&&) = delete;
SpinLock& operator=(SpinLock&&) = delete;
void lock();
void unlock();
[[nodiscard]] bool try_lock();
private:
std::atomic_flag lck = ATOMIC_FLAG_INIT;
};
} // namespace Common
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
namespace Common {
/**
* SpinLock class
* a lock similar to mutex that forces a thread to spin wait instead calling the
* supervisor. Should be used on short sequences of code.
*/
class SpinLock {
public:
SpinLock() = default;
SpinLock(const SpinLock&) = delete;
SpinLock& operator=(const SpinLock&) = delete;
SpinLock(SpinLock&&) = delete;
SpinLock& operator=(SpinLock&&) = delete;
void lock();
void unlock();
[[nodiscard]] bool try_lock();
private:
std::atomic_flag lck = ATOMIC_FLAG_INIT;
};
} // namespace Common

View File

@ -1,61 +1,61 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <utility>
namespace Common {
/// General purpose function wrapper similar to std::function.
/// Unlike std::function, the captured values don't have to be copyable.
/// This class can be moved but not copied.
template <typename ResultType, typename... Args>
class UniqueFunction {
class CallableBase {
public:
virtual ~CallableBase() = default;
virtual ResultType operator()(Args&&...) = 0;
};
template <typename Functor>
class Callable final : public CallableBase {
public:
Callable(Functor&& functor_) : functor{std::move(functor_)} {}
~Callable() override = default;
ResultType operator()(Args&&... args) override {
return functor(std::forward<Args>(args)...);
}
private:
Functor functor;
};
public:
UniqueFunction() = default;
template <typename Functor>
UniqueFunction(Functor&& functor)
: callable{std::make_unique<Callable<Functor>>(std::move(functor))} {}
UniqueFunction& operator=(UniqueFunction&& rhs) noexcept = default;
UniqueFunction(UniqueFunction&& rhs) noexcept = default;
UniqueFunction& operator=(const UniqueFunction&) = delete;
UniqueFunction(const UniqueFunction&) = delete;
ResultType operator()(Args&&... args) const {
return (*callable)(std::forward<Args>(args)...);
}
explicit operator bool() const noexcept {
return static_cast<bool>(callable);
}
private:
std::unique_ptr<CallableBase> callable;
};
} // namespace Common
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <utility>
namespace Common {
/// General purpose function wrapper similar to std::function.
/// Unlike std::function, the captured values don't have to be copyable.
/// This class can be moved but not copied.
template <typename ResultType, typename... Args>
class UniqueFunction {
class CallableBase {
public:
virtual ~CallableBase() = default;
virtual ResultType operator()(Args&&...) = 0;
};
template <typename Functor>
class Callable final : public CallableBase {
public:
Callable(Functor&& functor_) : functor{std::move(functor_)} {}
~Callable() override = default;
ResultType operator()(Args&&... args) override {
return functor(std::forward<Args>(args)...);
}
private:
Functor functor;
};
public:
UniqueFunction() = default;
template <typename Functor>
UniqueFunction(Functor&& functor)
: callable{std::make_unique<Callable<Functor>>(std::move(functor))} {}
UniqueFunction& operator=(UniqueFunction&& rhs) noexcept = default;
UniqueFunction(UniqueFunction&& rhs) noexcept = default;
UniqueFunction& operator=(const UniqueFunction&) = delete;
UniqueFunction(const UniqueFunction&) = delete;
ResultType operator()(Args&&... args) const {
return (*callable)(std::forward<Args>(args)...);
}
explicit operator bool() const noexcept {
return static_cast<bool>(callable);
}
private:
std::unique_ptr<CallableBase> callable;
};
} // namespace Common

View File

@ -8,7 +8,7 @@
namespace Common {
constexpr char VERSION[] = "0.4.1 WIP";
constexpr char VERSION[] = "0.5.1 WIP";
constexpr bool isRelease = false;
} // namespace Common

View File

@ -9,7 +9,7 @@
#include "splash.h"
bool Splash::Open(const std::filesystem::path& filepath) {
ASSERT_MSG(filepath.stem().string() != "png", "Unexpected file format passed");
ASSERT_MSG(filepath.extension().string() == ".png", "Unexpected file format passed");
Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read);
if (!file.IsOpen()) {

View File

@ -171,6 +171,9 @@ void HandleTable::DeleteHandle(int d) {
File* HandleTable::GetFile(int d) {
std::scoped_lock lock{m_mutex};
if (d < 0 || d >= m_files.size()) {
return nullptr;
}
return m_files.at(d);
}

View File

@ -2,6 +2,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <magic_enum/magic_enum.hpp>
#include "common/assert.h"
@ -13,7 +15,22 @@
namespace Libraries::AudioOut {
static std::unique_ptr<SDLAudioOut> audio;
struct PortOut {
void* impl;
u32 samples_num;
u32 freq;
OrbisAudioOutParamFormat format;
OrbisAudioOutPort type;
int channels_num;
bool is_float;
std::array<int, 8> volume;
u8 sample_size;
bool is_open;
};
std::shared_mutex ports_mutex;
std::array<PortOut, SCE_AUDIO_OUT_NUM_PORTS> ports_out{};
static std::unique_ptr<AudioOutBackend> audio;
static std::string_view GetAudioOutPort(OrbisAudioOutPort port) {
switch (port) {
@ -70,6 +87,58 @@ static std::string_view GetAudioOutParamAttr(OrbisAudioOutParamAttr attr) {
}
}
static bool IsFormatFloat(const OrbisAudioOutParamFormat format) {
switch (format) {
case OrbisAudioOutParamFormat::S16Mono:
case OrbisAudioOutParamFormat::S16Stereo:
case OrbisAudioOutParamFormat::S16_8CH:
case OrbisAudioOutParamFormat::S16_8CH_Std:
return false;
case OrbisAudioOutParamFormat::FloatMono:
case OrbisAudioOutParamFormat::FloatStereo:
case OrbisAudioOutParamFormat::Float_8CH:
case OrbisAudioOutParamFormat::Float_8CH_Std:
return true;
default:
UNREACHABLE_MSG("Unknown format");
}
}
static int GetFormatNumChannels(const OrbisAudioOutParamFormat format) {
switch (format) {
case OrbisAudioOutParamFormat::S16Mono:
case OrbisAudioOutParamFormat::FloatMono:
return 1;
case OrbisAudioOutParamFormat::S16Stereo:
case OrbisAudioOutParamFormat::FloatStereo:
return 2;
case OrbisAudioOutParamFormat::S16_8CH:
case OrbisAudioOutParamFormat::Float_8CH:
case OrbisAudioOutParamFormat::S16_8CH_Std:
case OrbisAudioOutParamFormat::Float_8CH_Std:
return 8;
default:
UNREACHABLE_MSG("Unknown format");
}
}
static u8 GetFormatSampleSize(const OrbisAudioOutParamFormat format) {
switch (format) {
case OrbisAudioOutParamFormat::S16Mono:
case OrbisAudioOutParamFormat::S16Stereo:
case OrbisAudioOutParamFormat::S16_8CH:
case OrbisAudioOutParamFormat::S16_8CH_Std:
return 2;
case OrbisAudioOutParamFormat::FloatMono:
case OrbisAudioOutParamFormat::FloatStereo:
case OrbisAudioOutParamFormat::Float_8CH:
case OrbisAudioOutParamFormat::Float_8CH_Std:
return 4;
default:
UNREACHABLE_MSG("Unknown format");
}
}
int PS4_SYSV_ABI sceAudioOutDeviceIdOpen() {
LOG_ERROR(Lib_AudioOut, "(STUBBED) called");
return ORBIS_OK;
@ -110,8 +179,21 @@ int PS4_SYSV_ABI sceAudioOutChangeAppModuleState() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudioOutClose() {
LOG_ERROR(Lib_AudioOut, "(STUBBED) called");
int PS4_SYSV_ABI sceAudioOutClose(s32 handle) {
LOG_INFO(Lib_AudioOut, "handle = {}", handle);
if (handle < 1 || handle > SCE_AUDIO_OUT_NUM_PORTS) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
std::scoped_lock lock(ports_mutex);
auto& port = ports_out.at(handle - 1);
if (!port.is_open) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
audio->Close(port.impl);
port.impl = nullptr;
port.is_open = false;
return ORBIS_OK;
}
@ -180,16 +262,21 @@ int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* sta
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
const auto [type, channels_num] = audio->GetStatus(handle);
std::scoped_lock lock(ports_mutex);
const auto& port = ports_out.at(handle - 1);
if (!port.is_open) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
state->rerouteCounter = 0;
state->volume = 127;
switch (type) {
switch (port.type) {
case OrbisAudioOutPort::Main:
case OrbisAudioOutPort::Bgm:
case OrbisAudioOutPort::Voice:
state->output = 1;
state->channel = (channels_num > 2 ? 2 : channels_num);
state->channel = port.channels_num > 2 ? 2 : port.channels_num;
break;
case OrbisAudioOutPort::Personal:
case OrbisAudioOutPort::Padspk:
@ -276,7 +363,7 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
u32 sample_rate,
OrbisAudioOutParamExtendedInformation param_type) {
LOG_INFO(Lib_AudioOut,
"AudioOutOpen id = {} port_type = {} index = {} lenght= {} sample_rate = {} "
"id = {} port_type = {} index = {} length = {} sample_rate = {} "
"param_type = {} attr = {}",
user_id, GetAudioOutPort(port_type), index, length, sample_rate,
GetAudioOutParamFormat(param_type.data_format),
@ -310,7 +397,26 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
LOG_ERROR(Lib_AudioOut, "Invalid format attribute");
return ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT;
}
return audio->Open(port_type, length, sample_rate, format);
std::scoped_lock lock{ports_mutex};
const auto port = std::ranges::find(ports_out, false, &PortOut::is_open);
if (port == ports_out.end()) {
LOG_ERROR(Lib_AudioOut, "Audio ports are full");
return ORBIS_AUDIO_OUT_ERROR_PORT_FULL;
}
port->is_open = true;
port->type = port_type;
port->samples_num = length;
port->freq = sample_rate;
port->format = format;
port->is_float = IsFormatFloat(format);
port->channels_num = GetFormatNumChannels(format);
port->sample_size = GetFormatSampleSize(format);
port->volume.fill(SCE_AUDIO_OUT_VOLUME_0DB);
port->impl = audio->Open(port->is_float, port->channels_num, port->freq);
return std::distance(ports_out.begin(), port) + 1;
}
int PS4_SYSV_ABI sceAudioOutOpenEx() {
@ -326,7 +432,15 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, const void* ptr) {
// Nothing to output
return ORBIS_OK;
}
return audio->Output(handle, ptr);
auto& port = ports_out.at(handle - 1);
if (!port.is_open) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
const size_t data_size = port.samples_num * port.sample_size * port.channels_num;
audio->Output(port.impl, ptr, data_size);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num) {
@ -431,7 +545,42 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) {
if (handle < 1 || handle > SCE_AUDIO_OUT_NUM_PORTS) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
return audio->SetVolume(handle, flag, vol);
std::scoped_lock lock(ports_mutex);
auto& port = ports_out.at(handle - 1);
if (!port.is_open) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
for (int i = 0; i < port.channels_num; i++, flag >>= 1u) {
auto bit = flag & 0x1u;
if (bit == 1) {
int src_index = i;
if (port.format == OrbisAudioOutParamFormat::Float_8CH_Std ||
port.format == OrbisAudioOutParamFormat::S16_8CH_Std) {
switch (i) {
case 4:
src_index = 6;
break;
case 5:
src_index = 7;
break;
case 6:
src_index = 4;
break;
case 7:
src_index = 5;
break;
default:
break;
}
}
port.volume[i] = vol[src_index];
}
}
audio->SetVolume(port.impl, port.volume);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudioOutSetVolumeDown() {

View File

@ -64,7 +64,7 @@ int PS4_SYSV_ABI sceAudioOutA3dExit();
int PS4_SYSV_ABI sceAudioOutA3dInit();
int PS4_SYSV_ABI sceAudioOutAttachToApplicationByPid();
int PS4_SYSV_ABI sceAudioOutChangeAppModuleState();
int PS4_SYSV_ABI sceAudioOutClose();
int PS4_SYSV_ABI sceAudioOutClose(s32 handle);
int PS4_SYSV_ABI sceAudioOutDetachFromApplicationByPid();
int PS4_SYSV_ABI sceAudioOutExConfigureOutputMode();
int PS4_SYSV_ABI sceAudioOutExGetSystemInfo();

View File

@ -0,0 +1,19 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
namespace Libraries::AudioOut {
class AudioOutBackend {
public:
AudioOutBackend() = default;
virtual ~AudioOutBackend() = default;
virtual void* Open(bool is_float, int num_channels, u32 sample_rate) = 0;
virtual void Close(void* impl) = 0;
virtual void Output(void* impl, const void* ptr, size_t size) = 0;
virtual void SetVolume(void* impl, std::array<int, 8> ch_volumes) = 0;
};
} // namespace Libraries::AudioOut

View File

@ -1,141 +1,44 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <mutex>
#include <SDL3/SDL_audio.h>
#include <SDL3/SDL_init.h>
#include <SDL3/SDL_timer.h>
#include "common/assert.h"
#include "core/libraries/audio/audioout_error.h"
#include "core/libraries/audio/sdl_audio.h"
namespace Libraries::AudioOut {
constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold
s32 SDLAudioOut::Open(OrbisAudioOutPort type, u32 samples_num, u32 freq,
OrbisAudioOutParamFormat format) {
std::scoped_lock lock{m_mutex};
const auto port = std::ranges::find(ports_out, false, &PortOut::is_open);
if (port == ports_out.end()) {
LOG_ERROR(Lib_AudioOut, "Audio ports are full");
return ORBIS_AUDIO_OUT_ERROR_PORT_FULL;
}
port->is_open = true;
port->type = type;
port->samples_num = samples_num;
port->freq = freq;
port->format = format;
SDL_AudioFormat sampleFormat;
switch (format) {
case OrbisAudioOutParamFormat::S16Mono:
sampleFormat = SDL_AUDIO_S16;
port->channels_num = 1;
port->sample_size = 2;
break;
case OrbisAudioOutParamFormat::FloatMono:
sampleFormat = SDL_AUDIO_F32;
port->channels_num = 1;
port->sample_size = 4;
break;
case OrbisAudioOutParamFormat::S16Stereo:
sampleFormat = SDL_AUDIO_S16;
port->channels_num = 2;
port->sample_size = 2;
break;
case OrbisAudioOutParamFormat::FloatStereo:
sampleFormat = SDL_AUDIO_F32;
port->channels_num = 2;
port->sample_size = 4;
break;
case OrbisAudioOutParamFormat::S16_8CH:
sampleFormat = SDL_AUDIO_S16;
port->channels_num = 8;
port->sample_size = 2;
break;
case OrbisAudioOutParamFormat::Float_8CH:
sampleFormat = SDL_AUDIO_F32;
port->channels_num = 8;
port->sample_size = 4;
break;
case OrbisAudioOutParamFormat::S16_8CH_Std:
sampleFormat = SDL_AUDIO_S16;
port->channels_num = 8;
port->sample_size = 2;
break;
case OrbisAudioOutParamFormat::Float_8CH_Std:
sampleFormat = SDL_AUDIO_F32;
port->channels_num = 8;
port->sample_size = 4;
break;
default:
UNREACHABLE_MSG("Unknown format");
}
port->volume.fill(Libraries::AudioOut::SCE_AUDIO_OUT_VOLUME_0DB);
void* SDLAudioOut::Open(bool is_float, int num_channels, u32 sample_rate) {
SDL_AudioSpec fmt;
SDL_zero(fmt);
fmt.format = sampleFormat;
fmt.channels = port->channels_num;
fmt.freq = freq;
port->stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, NULL, NULL);
SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(port->stream));
return std::distance(ports_out.begin(), port) + 1;
fmt.format = is_float ? SDL_AUDIO_F32 : SDL_AUDIO_S16;
fmt.channels = num_channels;
fmt.freq = sample_rate;
auto* stream =
SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, nullptr, nullptr);
SDL_ResumeAudioStreamDevice(stream);
return stream;
}
s32 SDLAudioOut::Output(s32 handle, const void* ptr) {
auto& port = ports_out.at(handle - 1);
if (!port.is_open) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
void SDLAudioOut::Close(void* impl) {
SDL_DestroyAudioStream(static_cast<SDL_AudioStream*>(impl));
}
const size_t data_size = port.samples_num * port.sample_size * port.channels_num;
bool result = SDL_PutAudioStreamData(port.stream, ptr, data_size);
while (SDL_GetAudioStreamAvailable(port.stream) > AUDIO_STREAM_BUFFER_THRESHOLD) {
void SDLAudioOut::Output(void* impl, const void* ptr, size_t size) {
auto* stream = static_cast<SDL_AudioStream*>(impl);
SDL_PutAudioStreamData(stream, ptr, size);
while (SDL_GetAudioStreamAvailable(stream) > AUDIO_STREAM_BUFFER_THRESHOLD) {
SDL_Delay(0);
}
return result ? ORBIS_OK : -1;
}
s32 SDLAudioOut::SetVolume(s32 handle, s32 bitflag, s32* volume) {
using Libraries::AudioOut::OrbisAudioOutParamFormat;
auto& port = ports_out.at(handle - 1);
if (!port.is_open) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
for (int i = 0; i < port.channels_num; i++, bitflag >>= 1u) {
auto bit = bitflag & 0x1u;
if (bit == 1) {
int src_index = i;
if (port.format == OrbisAudioOutParamFormat::Float_8CH_Std ||
port.format == OrbisAudioOutParamFormat::S16_8CH_Std) {
switch (i) {
case 4:
src_index = 6;
break;
case 5:
src_index = 7;
break;
case 6:
src_index = 4;
break;
case 7:
src_index = 5;
break;
default:
break;
}
}
port.volume[i] = volume[src_index];
}
}
return ORBIS_OK;
void SDLAudioOut::SetVolume(void* impl, std::array<int, 8> ch_volumes) {
// Not yet implemented
}
} // namespace Libraries::AudioOut

View File

@ -3,40 +3,16 @@
#pragma once
#include <shared_mutex>
#include <SDL3/SDL_audio.h>
#include "core/libraries/audio/audioout.h"
#include "core/libraries/audio/audioout_backend.h"
namespace Libraries::AudioOut {
class SDLAudioOut {
class SDLAudioOut final : public AudioOutBackend {
public:
explicit SDLAudioOut() = default;
~SDLAudioOut() = default;
s32 Open(OrbisAudioOutPort type, u32 samples_num, u32 freq, OrbisAudioOutParamFormat format);
s32 Output(s32 handle, const void* ptr);
s32 SetVolume(s32 handle, s32 bitflag, s32* volume);
constexpr std::pair<OrbisAudioOutPort, int> GetStatus(s32 handle) const {
const auto& port = ports_out.at(handle - 1);
return std::make_pair(port.type, port.channels_num);
}
private:
struct PortOut {
SDL_AudioStream* stream;
u32 samples_num;
u32 freq;
OrbisAudioOutParamFormat format;
OrbisAudioOutPort type;
int channels_num;
std::array<int, 8> volume;
u8 sample_size;
bool is_open;
};
std::shared_mutex m_mutex;
std::array<PortOut, Libraries::AudioOut::SCE_AUDIO_OUT_NUM_PORTS> ports_out{};
void* Open(bool is_float, int num_channels, u32 sample_rate) override;
void Close(void* impl) override;
void Output(void* impl, const void* ptr, size_t size) override;
void SetVolume(void* impl, std::array<int, 8> ch_volumes) override;
};
} // namespace Libraries::AudioOut

View File

@ -1,281 +1,484 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "fiber.h"
#include "common/logging/log.h"
#include "core/libraries/fiber/fiber_error.h"
#include "core/libraries/libs.h"
#include "core/tls.h"
#ifdef _WIN64
#include <windows.h>
#endif
namespace Libraries::Fiber {
static constexpr u64 kFiberSignature = 0x054ad954;
thread_local SceFiber* gCurrentFiber = nullptr;
thread_local void* gFiberThread = nullptr;
void FiberEntry(void* param) {
SceFiber* fiber = static_cast<SceFiber*>(param);
u64 argRun = 0;
u64 argRet = 0;
gCurrentFiber = fiber;
if (fiber->pArgRun != nullptr) {
argRun = *fiber->pArgRun;
}
Core::ExecuteGuest(fiber->entry, fiber->argOnInitialize, argRun);
UNREACHABLE();
}
s32 PS4_SYSV_ABI sceFiberInitialize(SceFiber* fiber, const char* name, SceFiberEntry entry,
u64 argOnInitialize, void* addrContext, u64 sizeContext,
const SceFiberOptParam* optParam) {
LOG_INFO(Lib_Fiber, "called: name = {}", name);
if (!fiber || !name || !entry) {
return ORBIS_FIBER_ERROR_NULL;
}
fiber->signature = kFiberSignature;
fiber->entry = entry;
fiber->argOnInitialize = argOnInitialize;
fiber->argRun = 0;
fiber->pArgRun = &fiber->argRun;
fiber->argReturn = 0;
fiber->pArgReturn = &fiber->argReturn;
fiber->sizeContext = sizeContext;
fiber->state = FiberState::Init;
#ifdef _WIN64
fiber->handle = CreateFiber(sizeContext, FiberEntry, fiber);
#else
UNREACHABLE_MSG("Missing implementation");
#endif
strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH);
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberOptParamInitialize(SceFiberOptParam* optParam) {
LOG_ERROR(Lib_Fiber, "called");
if (!optParam) {
return ORBIS_FIBER_ERROR_NULL;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberFinalize(SceFiber* fiber) {
LOG_TRACE(Lib_Fiber, "called");
if (!fiber) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber % 8 != 0) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (fiber->signature != kFiberSignature) {
return ORBIS_FIBER_ERROR_INVALID;
}
if (fiber->state != FiberState::Run) {
return ORBIS_FIBER_ERROR_STATE;
}
fiber->signature = 0;
fiber->state = FiberState::None;
#ifdef _WIN64
DeleteFiber(fiber->handle);
#else
UNREACHABLE_MSG("Missing implementation");
#endif
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberRun(SceFiber* fiber, u64 argOnRunTo, u64* argOnReturn) {
LOG_TRACE(Lib_Fiber, "called");
if (!fiber) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber % 8 != 0) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (fiber->signature != kFiberSignature) {
return ORBIS_FIBER_ERROR_INVALID;
}
if (fiber->state == FiberState::Run) {
return ORBIS_FIBER_ERROR_STATE;
}
if (gFiberThread == nullptr) {
#ifdef _WIN64
gFiberThread = ConvertThreadToFiber(nullptr);
#else
UNREACHABLE_MSG("Missing implementation");
#endif
}
gCurrentFiber = fiber;
if (fiber->pArgRun != nullptr) {
*fiber->pArgRun = argOnRunTo;
}
fiber->pArgReturn = argOnReturn;
fiber->state = FiberState::Run;
#ifdef _WIN64
SwitchToFiber(fiber->handle);
#else
UNREACHABLE_MSG("Missing implementation");
#endif
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberSwitch(SceFiber* fiber, u64 argOnRunTo, u64* argOnRun) {
LOG_TRACE(Lib_Fiber, "called");
if (!fiber) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber % 8 != 0) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (fiber->signature != kFiberSignature) {
return ORBIS_FIBER_ERROR_INVALID;
}
if (gCurrentFiber == nullptr) {
return ORBIS_FIBER_ERROR_PERMISSION;
}
if (fiber->state == FiberState::Run) {
return ORBIS_FIBER_ERROR_STATE;
}
gCurrentFiber->state = FiberState::Suspend;
// TODO: argOnRun
*fiber->pArgRun = argOnRunTo;
fiber->state = FiberState::Run;
gCurrentFiber = fiber;
#ifdef _WIN64
SwitchToFiber(fiber->handle);
#else
UNREACHABLE_MSG("Missing implementation");
#endif
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberGetSelf(SceFiber** fiber) {
LOG_TRACE(Lib_Fiber, "called");
if (!fiber || !gCurrentFiber) {
return ORBIS_FIBER_ERROR_NULL;
}
if (gCurrentFiber->signature != kFiberSignature) {
return ORBIS_FIBER_ERROR_PERMISSION;
}
*fiber = gCurrentFiber;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 argOnReturn, u64* argOnRun) {
LOG_TRACE(Lib_Fiber, "called");
if (gCurrentFiber->signature != kFiberSignature) {
return ORBIS_FIBER_ERROR_PERMISSION;
}
if (gCurrentFiber->pArgReturn != nullptr) {
*gCurrentFiber->pArgReturn = argOnReturn;
}
// TODO: argOnRun
gCurrentFiber->state = FiberState::Suspend;
gCurrentFiber = nullptr;
#ifdef _WIN64
SwitchToFiber(gFiberThread);
#else
UNREACHABLE_MSG("Missing implementation");
#endif
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberGetInfo(SceFiber* fiber, SceFiberInfo* fiberInfo) {
LOG_INFO(Lib_Fiber, "called");
if (!fiber || !fiberInfo) {
return ORBIS_FIBER_ERROR_NULL;
}
fiberInfo->entry = fiber->entry;
fiberInfo->argOnInitialize = fiber->argOnInitialize;
fiberInfo->addrContext = nullptr;
fiberInfo->sizeContext = fiber->sizeContext;
fiberInfo->sizeContextMargin = 0;
strncpy(fiberInfo->name, fiber->name, ORBIS_FIBER_MAX_NAME_LENGTH);
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags) {
LOG_ERROR(Lib_Fiber, "called");
if (flags != 0) {
return ORBIS_FIBER_ERROR_INVALID;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck() {
LOG_ERROR(Lib_Fiber, "called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberRename(SceFiber* fiber, const char* name) {
LOG_INFO(Lib_Fiber, "called, name = {}", name);
if (!fiber || !name) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber % 8 != 0) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH);
return ORBIS_OK;
}
void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("hVYD7Ou2pCQ", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize);
LIB_FUNCTION("asjUJJ+aa8s", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberOptParamInitialize);
LIB_FUNCTION("JeNX5F-NzQU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberFinalize);
LIB_FUNCTION("a0LLrZWac0M", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRun);
LIB_FUNCTION("PFT2S-tJ7Uk", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberSwitch);
LIB_FUNCTION("p+zLIOg27zU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetSelf);
LIB_FUNCTION("B0ZX2hx9DMw", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberReturnToThread);
LIB_FUNCTION("uq2Y5BFz0PE", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetInfo);
LIB_FUNCTION("Lcqty+QNWFc", "libSceFiber", 1, "libSceFiber", 1, 1,
sceFiberStartContextSizeCheck);
LIB_FUNCTION("Kj4nXMpnM8Y", "libSceFiber", 1, "libSceFiber", 1, 1,
sceFiberStopContextSizeCheck);
LIB_FUNCTION("JzyT91ucGDc", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRename);
}
} // namespace Libraries::Fiber
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "fiber.h"
#include "common/logging/log.h"
#include "core/libraries/fiber/fiber_error.h"
#include "core/libraries/libs.h"
#include "core/tls.h"
namespace Libraries::Fiber {
static constexpr u32 kFiberSignature0 = 0xdef1649c;
static constexpr u32 kFiberSignature1 = 0xb37592a0;
static constexpr u32 kFiberOptSignature = 0xbb40e64d;
static constexpr u64 kFiberStackSignature = 0x7149f2ca7149f2ca;
static constexpr u64 kFiberStackSizeCheck = 0xdeadbeefdeadbeef;
static std::atomic<u32> context_size_check = false;
OrbisFiberContext* GetFiberContext() {
return Core::GetTcbBase()->tcb_fiber;
}
extern "C" s32 PS4_SYSV_ABI _sceFiberSetJmp(OrbisFiberContext* ctx) asm("_sceFiberSetJmp");
extern "C" s32 PS4_SYSV_ABI _sceFiberLongJmp(OrbisFiberContext* ctx) asm("_sceFiberLongJmp");
extern "C" void PS4_SYSV_ABI _sceFiberSwitchEntry(OrbisFiberData* data,
bool set_fpu) asm("_sceFiberSwitchEntry");
extern "C" void PS4_SYSV_ABI _sceFiberForceQuit(u64 ret) asm("_sceFiberForceQuit");
extern "C" void PS4_SYSV_ABI _sceFiberForceQuit(u64 ret) {
OrbisFiberContext* g_ctx = GetFiberContext();
g_ctx->return_val = ret;
_sceFiberLongJmp(g_ctx);
}
void PS4_SYSV_ABI _sceFiberCheckStackOverflow(OrbisFiberContext* ctx) {
u64* stack_base = reinterpret_cast<u64*>(ctx->current_fiber->addr_context);
if (stack_base && *stack_base != kFiberStackSignature) {
UNREACHABLE_MSG("Stack overflow detected in fiber.");
}
}
void PS4_SYSV_ABI _sceFiberSwitchToFiber(OrbisFiber* fiber, u64 arg_on_run_to,
OrbisFiberContext* ctx) {
OrbisFiberContext* fiber_ctx = fiber->context;
if (fiber_ctx) {
ctx->arg_on_run_to = arg_on_run_to;
_sceFiberLongJmp(fiber_ctx);
__builtin_trap();
}
OrbisFiberData data{};
if (ctx->prev_fiber) {
OrbisFiber* prev_fiber = ctx->prev_fiber;
ctx->prev_fiber = nullptr;
data.state = reinterpret_cast<u32*>(&prev_fiber->state);
} else {
data.state = nullptr;
}
data.entry = fiber->entry;
data.arg_on_initialize = fiber->arg_on_initialize;
data.arg_on_run_to = arg_on_run_to;
data.stack_addr =
reinterpret_cast<void*>(reinterpret_cast<u64>(fiber->addr_context) + fiber->size_context);
if (fiber->flags & FiberFlags::SetFpuRegs) {
data.fpucw = 0x037f;
data.mxcsr = 0x9fc0;
_sceFiberSwitchEntry(&data, true);
} else {
_sceFiberSwitchEntry(&data, false);
}
__builtin_trap();
}
void PS4_SYSV_ABI _sceFiberSwitch(OrbisFiber* cur_fiber, OrbisFiber* fiber, u64 arg_on_run_to,
OrbisFiberContext* ctx) {
ctx->prev_fiber = cur_fiber;
ctx->current_fiber = fiber;
if (fiber->addr_context == nullptr) {
ctx->prev_fiber = nullptr;
OrbisFiberData data{};
data.entry = fiber->entry;
data.arg_on_initialize = fiber->arg_on_initialize;
data.arg_on_run_to = arg_on_run_to;
data.stack_addr = reinterpret_cast<void*>(ctx->rsp & ~15);
data.state = reinterpret_cast<u32*>(&cur_fiber->state);
if (fiber->flags & FiberFlags::SetFpuRegs) {
data.fpucw = 0x037f;
data.mxcsr = 0x9fc0;
_sceFiberSwitchEntry(&data, true);
} else {
_sceFiberSwitchEntry(&data, false);
}
__builtin_trap();
}
_sceFiberSwitchToFiber(fiber, arg_on_run_to, ctx);
__builtin_trap();
}
void PS4_SYSV_ABI _sceFiberTerminate(OrbisFiber* fiber, u64 arg_on_return, OrbisFiberContext* ctx) {
ctx->arg_on_return = arg_on_return;
_sceFiberLongJmp(ctx);
__builtin_trap();
}
s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry,
u64 arg_on_initialize, void* addr_context, u64 size_context,
const OrbisFiberOptParam* opt_param, u32 build_ver) {
if (!fiber || !name || !entry) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber & 7 || (u64)addr_context & 15) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (opt_param && (u64)opt_param & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (size_context && size_context < ORBIS_FIBER_CONTEXT_MINIMUM_SIZE) {
return ORBIS_FIBER_ERROR_RANGE;
}
if (size_context & 15) {
return ORBIS_FIBER_ERROR_INVALID;
}
if (!addr_context && size_context) {
return ORBIS_FIBER_ERROR_INVALID;
}
if (addr_context && !size_context) {
return ORBIS_FIBER_ERROR_INVALID;
}
if (opt_param && opt_param->magic != kFiberOptSignature) {
return ORBIS_FIBER_ERROR_INVALID;
}
u32 flags = FiberFlags::None;
if (build_ver >= 0x3500000) {
flags |= FiberFlags::SetFpuRegs;
}
if (context_size_check) {
flags |= FiberFlags::ContextSizeCheck;
}
strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH);
fiber->entry = entry;
fiber->arg_on_initialize = arg_on_initialize;
fiber->addr_context = addr_context;
fiber->size_context = size_context;
fiber->context = nullptr;
fiber->flags = flags;
/*
A low stack area is problematic, as we can easily
cause a stack overflow with our HLE.
*/
if (size_context && size_context <= 4096) {
LOG_WARNING(Lib_Fiber, "Fiber initialized with small stack area.");
}
fiber->magic_start = kFiberSignature0;
fiber->magic_end = kFiberSignature1;
if (addr_context != nullptr) {
fiber->context_start = addr_context;
fiber->context_end =
reinterpret_cast<void*>(reinterpret_cast<u64>(addr_context) + size_context);
/* Apply signature to start of stack */
*(u64*)addr_context = kFiberStackSignature;
if (flags & FiberFlags::ContextSizeCheck) {
u64* stack_start = reinterpret_cast<u64*>(fiber->context_start);
u64* stack_end = reinterpret_cast<u64*>(fiber->context_end);
u64* stack_ptr = stack_start + 1;
while (stack_ptr < stack_end) {
*stack_ptr++ = kFiberStackSizeCheck;
}
}
}
fiber->state = FiberState::Idle;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberOptParamInitialize(OrbisFiberOptParam* opt_param) {
if (!opt_param) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)opt_param & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
opt_param->magic = kFiberOptSignature;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberFinalize(OrbisFiber* fiber) {
if (!fiber) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
return ORBIS_FIBER_ERROR_INVALID;
}
FiberState expected = FiberState::Idle;
if (!fiber->state.compare_exchange_strong(expected, FiberState::Terminated)) {
return ORBIS_FIBER_ERROR_STATE;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return) {
if (!fiber) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
return ORBIS_FIBER_ERROR_INVALID;
}
Core::Tcb* tcb = Core::GetTcbBase();
if (tcb->tcb_fiber) {
return ORBIS_FIBER_ERROR_PERMISSION;
}
FiberState expected = FiberState::Idle;
if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) {
return ORBIS_FIBER_ERROR_STATE;
}
OrbisFiberContext ctx{};
ctx.current_fiber = fiber;
ctx.prev_fiber = nullptr;
ctx.return_val = 0;
tcb->tcb_fiber = &ctx;
s32 jmp = _sceFiberSetJmp(&ctx);
if (!jmp) {
if (fiber->addr_context) {
_sceFiberSwitchToFiber(fiber, arg_on_run_to, &ctx);
__builtin_trap();
}
OrbisFiberData data{};
data.entry = fiber->entry;
data.arg_on_initialize = fiber->arg_on_initialize;
data.arg_on_run_to = arg_on_run_to;
data.stack_addr = reinterpret_cast<void*>(ctx.rsp & ~15);
data.state = nullptr;
if (fiber->flags & FiberFlags::SetFpuRegs) {
data.fpucw = 0x037f;
data.mxcsr = 0x9fc0;
_sceFiberSwitchEntry(&data, true);
} else {
_sceFiberSwitchEntry(&data, false);
}
}
OrbisFiber* cur_fiber = ctx.current_fiber;
ctx.current_fiber = nullptr;
cur_fiber->state = FiberState::Idle;
if (ctx.return_val != 0) {
/* Fiber entry returned! This should never happen. */
UNREACHABLE_MSG("Fiber entry function returned.");
}
if (arg_on_return) {
*arg_on_return = ctx.arg_on_return;
}
tcb->tcb_fiber = nullptr;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run) {
if (!fiber) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
return ORBIS_FIBER_ERROR_INVALID;
}
OrbisFiberContext* g_ctx = GetFiberContext();
if (!g_ctx) {
return ORBIS_FIBER_ERROR_PERMISSION;
}
FiberState expected = FiberState::Idle;
if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) {
return ORBIS_FIBER_ERROR_STATE;
}
OrbisFiber* cur_fiber = g_ctx->current_fiber;
if (cur_fiber->addr_context == nullptr) {
_sceFiberSwitch(cur_fiber, fiber, arg_on_run_to, g_ctx);
__builtin_trap();
}
OrbisFiberContext ctx{};
s32 jmp = _sceFiberSetJmp(&ctx);
if (!jmp) {
cur_fiber->context = &ctx;
_sceFiberCheckStackOverflow(g_ctx);
_sceFiberSwitch(cur_fiber, fiber, arg_on_run_to, g_ctx);
__builtin_trap();
}
g_ctx = GetFiberContext();
if (g_ctx->prev_fiber) {
g_ctx->prev_fiber->state = FiberState::Idle;
g_ctx->prev_fiber = nullptr;
}
if (arg_on_run) {
*arg_on_run = g_ctx->arg_on_run_to;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberGetSelf(OrbisFiber** fiber) {
if (!fiber) {
return ORBIS_FIBER_ERROR_NULL;
}
OrbisFiberContext* g_ctx = GetFiberContext();
if (!g_ctx) {
return ORBIS_FIBER_ERROR_PERMISSION;
}
*fiber = g_ctx->current_fiber;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 arg_on_return, u64* arg_on_run) {
OrbisFiberContext* g_ctx = GetFiberContext();
if (!g_ctx) {
return ORBIS_FIBER_ERROR_PERMISSION;
}
OrbisFiber* cur_fiber = g_ctx->current_fiber;
if (cur_fiber->addr_context) {
OrbisFiberContext ctx{};
s32 jmp = _sceFiberSetJmp(&ctx);
if (jmp) {
g_ctx = GetFiberContext();
if (g_ctx->prev_fiber) {
g_ctx->prev_fiber->state = FiberState::Idle;
g_ctx->prev_fiber = nullptr;
}
if (arg_on_run) {
*arg_on_run = g_ctx->arg_on_run_to;
}
return ORBIS_OK;
}
cur_fiber->context = &ctx;
_sceFiberCheckStackOverflow(g_ctx);
}
_sceFiberTerminate(cur_fiber, arg_on_return, g_ctx);
__builtin_trap();
}
s32 PS4_SYSV_ABI sceFiberGetInfo(OrbisFiber* fiber, OrbisFiberInfo* fiber_info) {
if (!fiber || !fiber_info) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber & 7 || (u64)fiber_info & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (fiber_info->size != sizeof(OrbisFiberInfo)) {
return ORBIS_FIBER_ERROR_INVALID;
}
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
return ORBIS_FIBER_ERROR_INVALID;
}
fiber_info->entry = fiber->entry;
fiber_info->arg_on_initialize = fiber->arg_on_initialize;
fiber_info->addr_context = fiber->addr_context;
fiber_info->size_context = fiber->size_context;
strncpy(fiber_info->name, fiber->name, ORBIS_FIBER_MAX_NAME_LENGTH);
fiber_info->size_context_margin = -1;
if (fiber->flags & FiberFlags::ContextSizeCheck && fiber->addr_context != nullptr) {
u64 stack_margin = 0;
u64* stack_start = reinterpret_cast<u64*>(fiber->context_start);
u64* stack_end = reinterpret_cast<u64*>(fiber->context_end);
if (*stack_start == kFiberStackSignature) {
u64* stack_ptr = stack_start + 1;
while (stack_ptr < stack_end) {
if (*stack_ptr == kFiberStackSizeCheck) {
stack_ptr++;
}
}
stack_margin =
reinterpret_cast<u64>(stack_ptr) - reinterpret_cast<u64>(stack_start + 1);
}
fiber_info->size_context_margin = stack_margin;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags) {
if (flags != 0) {
return ORBIS_FIBER_ERROR_INVALID;
}
u32 expected = 0;
if (!context_size_check.compare_exchange_strong(expected, 1u)) {
return ORBIS_FIBER_ERROR_STATE;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck() {
u32 expected = 1;
if (!context_size_check.compare_exchange_strong(expected, 0u)) {
return ORBIS_FIBER_ERROR_STATE;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name) {
if (!fiber || !name) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
return ORBIS_FIBER_ERROR_INVALID;
}
strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH);
return ORBIS_OK;
}
void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("hVYD7Ou2pCQ", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize);
LIB_FUNCTION("7+OJIpko9RY", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize);
LIB_FUNCTION("asjUJJ+aa8s", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberOptParamInitialize);
LIB_FUNCTION("JeNX5F-NzQU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberFinalize);
LIB_FUNCTION("a0LLrZWac0M", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRun);
LIB_FUNCTION("PFT2S-tJ7Uk", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberSwitch);
LIB_FUNCTION("p+zLIOg27zU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetSelf);
LIB_FUNCTION("B0ZX2hx9DMw", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberReturnToThread);
LIB_FUNCTION("uq2Y5BFz0PE", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetInfo);
LIB_FUNCTION("Lcqty+QNWFc", "libSceFiber", 1, "libSceFiber", 1, 1,
sceFiberStartContextSizeCheck);
LIB_FUNCTION("Kj4nXMpnM8Y", "libSceFiber", 1, "libSceFiber", 1, 1,
sceFiberStopContextSizeCheck);
LIB_FUNCTION("JzyT91ucGDc", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRename);
}
} // namespace Libraries::Fiber

View File

@ -1,78 +1,118 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/assert.h"
#include "common/types.h"
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::Fiber {
#define ORBIS_FIBER_MAX_NAME_LENGTH (31)
typedef void PS4_SYSV_ABI (*SceFiberEntry)(u64 argOnInitialize, u64 argOnRun);
enum FiberState : u32 {
None = 0u,
Init = 1u,
Run = 2u,
Suspend = 3u,
};
struct SceFiber {
u64 signature;
FiberState state;
SceFiberEntry entry;
u64 argOnInitialize;
u64 argRun;
u64* pArgRun;
u64 argReturn;
u64* pArgReturn;
u64 sizeContext;
char name[ORBIS_FIBER_MAX_NAME_LENGTH];
void* handle;
};
static_assert(sizeof(SceFiber) <= 256);
struct SceFiberInfo {
u64 size;
SceFiberEntry entry;
u64 argOnInitialize;
void* addrContext;
u64 sizeContext;
char name[ORBIS_FIBER_MAX_NAME_LENGTH + 1];
u64 sizeContextMargin;
};
static_assert(sizeof(SceFiberInfo) <= 128);
using SceFiberOptParam = void*;
s32 PS4_SYSV_ABI sceFiberInitialize(SceFiber* fiber, const char* name, SceFiberEntry entry,
u64 argOnInitialize, void* addrContext, u64 sizeContext,
const SceFiberOptParam* optParam);
s32 PS4_SYSV_ABI sceFiberOptParamInitialize(SceFiberOptParam* optParam);
s32 PS4_SYSV_ABI sceFiberFinalize(SceFiber* fiber);
s32 PS4_SYSV_ABI sceFiberRun(SceFiber* fiber, u64 argOnRunTo, u64* argOnReturn);
s32 PS4_SYSV_ABI sceFiberSwitch(SceFiber* fiber, u64 argOnRunTo, u64* argOnRun);
s32 PS4_SYSV_ABI sceFiberGetSelf(SceFiber** fiber);
s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 argOnReturn, u64* argOnRun);
s32 PS4_SYSV_ABI sceFiberGetInfo(SceFiber* fiber, SceFiberInfo* fiberInfo);
s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags);
s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck(void);
s32 PS4_SYSV_ABI sceFiberRename(SceFiber* fiber, const char* name);
void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym);
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/assert.h"
#include "common/types.h"
#include <atomic>
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::Fiber {
#define ORBIS_FIBER_MAX_NAME_LENGTH (31)
#define ORBIS_FIBER_CONTEXT_MINIMUM_SIZE (512)
typedef void PS4_SYSV_ABI (*OrbisFiberEntry)(u64 arg_on_initialize, u64 arg_on_run);
enum FiberState : u32 {
Run = 1u,
Idle = 2u,
Terminated = 3u,
};
enum FiberFlags : u32 {
None = 0x0,
NoUlobjmgr = 0x1,
ContextSizeCheck = 0x10,
SetFpuRegs = 0x100,
};
struct OrbisFiber;
struct OrbisFiberContext {
struct {
u64 rax, rcx, rdx, rbx, rsp, rbp, r8, r9, r10, r11, r12, r13, r14, r15;
u16 fpucw;
u32 mxcsr;
};
OrbisFiber* current_fiber;
OrbisFiber* prev_fiber;
u64 arg_on_run_to;
u64 arg_on_return;
u64 return_val;
};
struct OrbisFiberData {
OrbisFiberEntry entry;
u64 arg_on_initialize;
u64 arg_on_run_to;
void* stack_addr;
u32* state;
u16 fpucw;
s8 pad[2];
u32 mxcsr;
};
struct OrbisFiber {
u32 magic_start;
std::atomic<FiberState> state;
OrbisFiberEntry entry;
u64 arg_on_initialize;
void* addr_context;
u64 size_context;
char name[ORBIS_FIBER_MAX_NAME_LENGTH + 1];
OrbisFiberContext* context;
u32 flags;
void* context_start;
void* context_end;
u32 magic_end;
};
static_assert(sizeof(OrbisFiber) <= 256);
struct OrbisFiberInfo {
u64 size;
OrbisFiberEntry entry;
u64 arg_on_initialize;
void* addr_context;
u64 size_context;
char name[ORBIS_FIBER_MAX_NAME_LENGTH + 1];
u64 size_context_margin;
u8 pad[48];
};
static_assert(sizeof(OrbisFiberInfo) == 128);
struct OrbisFiberOptParam {
u32 magic;
};
static_assert(sizeof(OrbisFiberOptParam) <= 128);
s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry,
u64 arg_on_initialize, void* addr_context, u64 size_context,
const OrbisFiberOptParam* opt_param, u32 build_version);
s32 PS4_SYSV_ABI sceFiberOptParamInitialize(OrbisFiberOptParam* opt_param);
s32 PS4_SYSV_ABI sceFiberFinalize(OrbisFiber* fiber);
s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return);
s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run);
s32 PS4_SYSV_ABI sceFiberGetSelf(OrbisFiber** fiber);
s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 arg_on_return, u64* arg_on_run);
s32 PS4_SYSV_ABI sceFiberGetInfo(OrbisFiber* fiber, OrbisFiberInfo* fiber_info);
s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags);
s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck(void);
s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name);
void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Fiber

View File

@ -0,0 +1,121 @@
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
.global _sceFiberSetJmp
_sceFiberSetJmp:
movq %rax, 0x0(%rdi)
movq (%rsp), %rdx
movq %rdx, 0x10(%rdi)
movq %rcx, 0x08(%rdi)
movq %rbx, 0x18(%rdi)
movq %rsp, 0x20(%rdi)
movq %rbp, 0x28(%rdi)
movq %r8, 0x30(%rdi)
movq %r9, 0x38(%rdi)
movq %r10, 0x40(%rdi)
movq %r11, 0x48(%rdi)
movq %r12, 0x50(%rdi)
movq %r13, 0x58(%rdi)
movq %r14, 0x60(%rdi)
movq %r15, 0x68(%rdi)
fnstcw 0x70(%rdi)
stmxcsr 0x72(%rdi)
xor %eax, %eax
ret
.global _sceFiberLongJmp
_sceFiberLongJmp:
# MXCSR = (MXCSR & 0x3f) ^ (ctx->mxcsr & ~0x3f)
stmxcsr -0x4(%rsp)
movl 0x72(%rdi), %eax
andl $0xffffffc0, %eax
movl -0x4(%rsp), %ecx
andl $0x3f, %ecx
xorl %eax, %ecx
movl %ecx, -0x4(%rsp)
ldmxcsr -0x4(%rsp)
movq 0x00(%rdi), %rax
movq 0x08(%rdi), %rcx
movq 0x10(%rdi), %rdx
movq 0x18(%rdi), %rbx
movq 0x20(%rdi), %rsp
movq 0x28(%rdi), %rbp
movq 0x30(%rdi), %r8
movq 0x38(%rdi), %r9
movq 0x40(%rdi), %r10
movq 0x48(%rdi), %r11
movq 0x50(%rdi), %r12
movq 0x58(%rdi), %r13
movq 0x60(%rdi), %r14
movq 0x68(%rdi), %r15
fldcw 0x70(%rdi)
# Make the jump and return 1
movq %rdx, 0x00(%rsp)
movl $0x1, %eax
ret
.global _sceFiberSwitchEntry
_sceFiberSwitchEntry:
mov %rdi, %r11
# Set stack address to provided stack
movq 0x18(%r11), %rsp
xorl %ebp, %ebp
movq 0x20(%r11), %r10 # data->state
# Set previous fiber state to Idle
test %r10, %r10
jz .clear_regs
movl $2, (%r10)
.clear_regs:
test %esi, %esi
jz .skip_fpu_regs
ldmxcsr 0x2c(%r11)
fldcw 0x28(%r11)
.skip_fpu_regs:
movq 0x08(%r11), %rdi # data->arg_on_initialize
movq 0x10(%r11), %rsi # data->arg_on_run_to
movq 0x00(%r11), %r11 # data->entry
xorl %eax, %eax
xorl %ebx, %ebx
xorl %ecx, %ecx
xorl %edx, %edx
xorq %r8, %r8
xorq %r9, %r9
xorq %r10, %r10
xorq %r12, %r12
xorq %r13, %r13
xorq %r14, %r14
xorq %r15, %r15
pxor %mm0, %mm0
pxor %mm1, %mm1
pxor %mm2, %mm2
pxor %mm3, %mm3
pxor %mm4, %mm4
pxor %mm5, %mm5
pxor %mm6, %mm6
pxor %mm7, %mm7
emms
vzeroall
# Call the fiber's entry function: entry(arg_on_initialize, arg_on_run_to)
call *%r11
# Fiber returned, not good
movl $1, %edi
call _sceFiberForceQuit
ret

View File

@ -1,183 +1,183 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
#include "core/libraries/rtc/rtc.h"
enum class OrbisImeType : u32 {
Default = 0,
BasicLatin = 1,
Url = 2,
Mail = 3,
Number = 4,
};
enum class OrbisImeHorizontalAlignment : u32 {
Left = 0,
Center = 1,
Right = 2,
};
enum class OrbisImeVerticalAlignment : u32 {
Top = 0,
Center = 1,
Bottom = 2,
};
enum class OrbisImeEnterLabel : u32 {
Default = 0,
Send = 1,
Search = 2,
Go = 3,
};
enum class OrbisImeInputMethod : u32 {
Default = 0,
};
enum class OrbisImeEventId : u32 {
Open = 0,
UpdateText = 1,
UpdateCaret = 2,
PressClose = 4,
PressEnter = 5,
Abort = 6,
CandidateListStart = 7,
CandidateListEnd = 8,
CandidateWord = 9,
CandidateIndex = 10,
CandidateDone = 11,
CandidateCancel = 12,
ChangeDevice = 14,
ChangeInputMethodState = 18,
KeyboardOpen = 256,
KeyboardKeycodeDoen = 257,
KeyboardKeycodeUp = 258,
KeyboardKeycodeRepeat = 259,
KeyboardConnection = 260,
KeyboardDisconnection = 261,
KeyboardAbort = 262,
};
enum class OrbisImeKeyboardType : u32 {
NONE = 0,
DANISH = 1,
GERMAN = 2,
GERMAN_SW = 3,
ENGLISH_US = 4,
ENGLISH_GB = 5,
SPANISH = 6,
SPANISH_LA = 7,
FINNISH = 8,
FRENCH = 9,
FRENCH_BR = 10,
FRENCH_CA = 11,
FRENCH_SW = 12,
ITALIAN = 13,
DUTCH = 14,
NORWEGIAN = 15,
POLISH = 16,
PORTUGUESE_BR = 17,
PORTUGUESE_PT = 18,
RUSSIAN = 19,
SWEDISH = 20,
TURKISH = 21,
JAPANESE_ROMAN = 22,
JAPANESE_KANA = 23,
KOREAN = 24,
SM_CHINESE = 25,
TR_CHINESE_ZY = 26,
TR_CHINESE_PY_HK = 27,
TR_CHINESE_PY_TW = 28,
TR_CHINESE_CG = 29,
ARABIC_AR = 30,
THAI = 31,
CZECH = 32,
GREEK = 33,
INDONESIAN = 34,
VIETNAMESE = 35,
ROMANIAN = 36,
HUNGARIAN = 37,
};
enum class OrbisImeDeviceType : u32 {
None = 0,
Controller = 1,
ExtKeyboard = 2,
RemoteOsk = 3,
};
struct OrbisImeRect {
f32 x;
f32 y;
u32 width;
u32 height;
};
struct OrbisImeTextAreaProperty {
u32 mode; // OrbisImeTextAreaMode
u32 index;
s32 length;
};
struct OrbisImeEditText {
char16_t* str;
u32 caret_index;
u32 area_num;
OrbisImeTextAreaProperty text_area[4];
};
struct OrbisImeKeycode {
u16 keycode;
char16_t character;
u32 status;
OrbisImeKeyboardType type;
s32 user_id;
u32 resource_id;
Libraries::Rtc::OrbisRtcTick timestamp;
};
struct OrbisImeKeyboardResourceIdArray {
s32 userId;
u32 resourceId[5];
};
enum class OrbisImeCaretMovementDirection : u32 {
Still = 0,
Left = 1,
Right = 2,
Up = 3,
Down = 4,
Home = 5,
End = 6,
PageUp = 7,
PageDown = 8,
Top = 9,
Bottom = 10,
};
union OrbisImeEventParam {
OrbisImeRect rect;
OrbisImeEditText text;
OrbisImeCaretMovementDirection caret_move;
OrbisImeKeycode keycode;
OrbisImeKeyboardResourceIdArray resource_id_array;
char16_t* candidate_word;
s32 candidate_index;
OrbisImeDeviceType device_type;
u32 input_method_state;
s8 reserved[64];
};
struct OrbisImeEvent {
OrbisImeEventId id;
OrbisImeEventParam param;
};
using OrbisImeTextFilter = PS4_SYSV_ABI int (*)(char16_t* outText, u32* outTextLength,
const char16_t* srcText, u32 srcTextLength);
using OrbisImeEventHandler = PS4_SYSV_ABI void (*)(void* arg, const OrbisImeEvent* e);
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
#include "core/libraries/rtc/rtc.h"
enum class OrbisImeType : u32 {
Default = 0,
BasicLatin = 1,
Url = 2,
Mail = 3,
Number = 4,
};
enum class OrbisImeHorizontalAlignment : u32 {
Left = 0,
Center = 1,
Right = 2,
};
enum class OrbisImeVerticalAlignment : u32 {
Top = 0,
Center = 1,
Bottom = 2,
};
enum class OrbisImeEnterLabel : u32 {
Default = 0,
Send = 1,
Search = 2,
Go = 3,
};
enum class OrbisImeInputMethod : u32 {
Default = 0,
};
enum class OrbisImeEventId : u32 {
Open = 0,
UpdateText = 1,
UpdateCaret = 2,
PressClose = 4,
PressEnter = 5,
Abort = 6,
CandidateListStart = 7,
CandidateListEnd = 8,
CandidateWord = 9,
CandidateIndex = 10,
CandidateDone = 11,
CandidateCancel = 12,
ChangeDevice = 14,
ChangeInputMethodState = 18,
KeyboardOpen = 256,
KeyboardKeycodeDoen = 257,
KeyboardKeycodeUp = 258,
KeyboardKeycodeRepeat = 259,
KeyboardConnection = 260,
KeyboardDisconnection = 261,
KeyboardAbort = 262,
};
enum class OrbisImeKeyboardType : u32 {
NONE = 0,
DANISH = 1,
GERMAN = 2,
GERMAN_SW = 3,
ENGLISH_US = 4,
ENGLISH_GB = 5,
SPANISH = 6,
SPANISH_LA = 7,
FINNISH = 8,
FRENCH = 9,
FRENCH_BR = 10,
FRENCH_CA = 11,
FRENCH_SW = 12,
ITALIAN = 13,
DUTCH = 14,
NORWEGIAN = 15,
POLISH = 16,
PORTUGUESE_BR = 17,
PORTUGUESE_PT = 18,
RUSSIAN = 19,
SWEDISH = 20,
TURKISH = 21,
JAPANESE_ROMAN = 22,
JAPANESE_KANA = 23,
KOREAN = 24,
SM_CHINESE = 25,
TR_CHINESE_ZY = 26,
TR_CHINESE_PY_HK = 27,
TR_CHINESE_PY_TW = 28,
TR_CHINESE_CG = 29,
ARABIC_AR = 30,
THAI = 31,
CZECH = 32,
GREEK = 33,
INDONESIAN = 34,
VIETNAMESE = 35,
ROMANIAN = 36,
HUNGARIAN = 37,
};
enum class OrbisImeDeviceType : u32 {
None = 0,
Controller = 1,
ExtKeyboard = 2,
RemoteOsk = 3,
};
struct OrbisImeRect {
f32 x;
f32 y;
u32 width;
u32 height;
};
struct OrbisImeTextAreaProperty {
u32 mode; // OrbisImeTextAreaMode
u32 index;
s32 length;
};
struct OrbisImeEditText {
char16_t* str;
u32 caret_index;
u32 area_num;
OrbisImeTextAreaProperty text_area[4];
};
struct OrbisImeKeycode {
u16 keycode;
char16_t character;
u32 status;
OrbisImeKeyboardType type;
s32 user_id;
u32 resource_id;
Libraries::Rtc::OrbisRtcTick timestamp;
};
struct OrbisImeKeyboardResourceIdArray {
s32 userId;
u32 resourceId[5];
};
enum class OrbisImeCaretMovementDirection : u32 {
Still = 0,
Left = 1,
Right = 2,
Up = 3,
Down = 4,
Home = 5,
End = 6,
PageUp = 7,
PageDown = 8,
Top = 9,
Bottom = 10,
};
union OrbisImeEventParam {
OrbisImeRect rect;
OrbisImeEditText text;
OrbisImeCaretMovementDirection caret_move;
OrbisImeKeycode keycode;
OrbisImeKeyboardResourceIdArray resource_id_array;
char16_t* candidate_word;
s32 candidate_index;
OrbisImeDeviceType device_type;
u32 input_method_state;
s8 reserved[64];
};
struct OrbisImeEvent {
OrbisImeEventId id;
OrbisImeEventParam param;
};
using OrbisImeTextFilter = PS4_SYSV_ABI int (*)(char16_t* outText, u32* outTextLength,
const char16_t* srcText, u32 srcTextLength);
using OrbisImeEventHandler = PS4_SYSV_ABI void (*)(void* arg, const OrbisImeEvent* e);

View File

@ -1,253 +1,253 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "ime_ui.h"
#include "imgui/imgui_std.h"
namespace Libraries::Ime {
using namespace ImGui;
static constexpr ImVec2 BUTTON_SIZE{100.0f, 30.0f};
ImeState::ImeState(const OrbisImeParam* param) {
if (!param) {
return;
}
work_buffer = param->work;
text_buffer = param->inputTextBuffer;
std::size_t text_len = std::char_traits<char16_t>::length(text_buffer);
if (!ConvertOrbisToUTF8(text_buffer, text_len, current_text.begin(),
ORBIS_IME_MAX_TEXT_LENGTH * 4)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert text to utf8 encoding");
}
}
ImeState::ImeState(ImeState&& other) noexcept
: work_buffer(other.work_buffer), text_buffer(other.text_buffer),
current_text(std::move(other.current_text)), event_queue(std::move(other.event_queue)) {
other.text_buffer = nullptr;
}
ImeState& ImeState::operator=(ImeState&& other) noexcept {
if (this != &other) {
work_buffer = other.work_buffer;
text_buffer = other.text_buffer;
current_text = std::move(other.current_text);
event_queue = std::move(other.event_queue);
other.text_buffer = nullptr;
}
return *this;
}
void ImeState::SendEvent(OrbisImeEvent* event) {
std::unique_lock lock{queue_mutex};
event_queue.push(*event);
}
void ImeState::SendEnterEvent() {
OrbisImeEvent enterEvent{};
enterEvent.id = OrbisImeEventId::PressEnter;
SendEvent(&enterEvent);
}
void ImeState::SendCloseEvent() {
OrbisImeEvent closeEvent{};
closeEvent.id = OrbisImeEventId::PressClose;
closeEvent.param.text.str = reinterpret_cast<char16_t*>(work_buffer);
SendEvent(&closeEvent);
}
void ImeState::SetText(const char16_t* text, u32 length) {}
void ImeState::SetCaret(u32 position) {}
bool ImeState::ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len,
char* utf8_text, std::size_t utf8_text_len) {
std::fill(utf8_text, utf8_text + utf8_text_len, '\0');
const ImWchar* orbis_text_ptr = reinterpret_cast<const ImWchar*>(orbis_text);
ImTextStrToUtf8(utf8_text, utf8_text_len, orbis_text_ptr, orbis_text_ptr + orbis_text_len);
return true;
}
bool ImeState::ConvertUTF8ToOrbis(const char* utf8_text, std::size_t utf8_text_len,
char16_t* orbis_text, std::size_t orbis_text_len) {
std::fill(orbis_text, orbis_text + orbis_text_len, u'\0');
ImTextStrFromUtf8(reinterpret_cast<ImWchar*>(orbis_text), orbis_text_len, utf8_text, nullptr);
return true;
}
ImeUi::ImeUi(ImeState* state, const OrbisImeParam* param) : state(state), ime_param(param) {
if (param) {
AddLayer(this);
}
}
ImeUi::~ImeUi() {
std::scoped_lock lock(draw_mutex);
Free();
}
ImeUi& ImeUi::operator=(ImeUi&& other) {
std::scoped_lock lock(draw_mutex, other.draw_mutex);
Free();
state = other.state;
ime_param = other.ime_param;
first_render = other.first_render;
other.state = nullptr;
other.ime_param = nullptr;
AddLayer(this);
return *this;
}
void ImeUi::Draw() {
std::unique_lock lock{draw_mutex};
if (!state) {
return;
}
const auto& ctx = *GetCurrentContext();
const auto& io = ctx.IO;
// TODO: Figure out how to properly translate the positions -
// for example, if a game wants to center the IME panel,
// we have to translate the panel position in a way that it
// still becomes centered, as the game normally calculates
// the position assuming a it's running on a 1920x1080 screen,
// whereas we are running on a 1280x720 window size (by default).
//
// e.g. Panel position calculation from a game:
// param.posx = (1920 / 2) - (panelWidth / 2);
// param.posy = (1080 / 2) - (panelHeight / 2);
const auto size = GetIO().DisplaySize;
f32 pos_x = (ime_param->posx / 1920.0f * (float)size.x);
f32 pos_y = (ime_param->posy / 1080.0f * (float)size.y);
ImVec2 window_pos = {pos_x, pos_y};
ImVec2 window_size = {500.0f, 100.0f};
// SetNextWindowPos(window_pos);
SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f),
ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
SetNextWindowSize(window_size);
SetNextWindowCollapsed(false);
if (first_render || !io.NavActive) {
SetNextWindowFocus();
}
if (Begin("IME##Ime", nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoSavedSettings)) {
DrawPrettyBackground();
DrawInputText();
SetCursorPosY(GetCursorPosY() + 10.0f);
const char* button_text;
button_text = "Done##ImeDone";
float button_spacing = 10.0f;
float total_button_width = BUTTON_SIZE.x * 2 + button_spacing;
float button_start_pos = (window_size.x - total_button_width) / 2.0f;
SetCursorPosX(button_start_pos);
if (Button(button_text, BUTTON_SIZE) || (IsKeyPressed(ImGuiKey_Enter))) {
state->SendEnterEvent();
}
SameLine(0.0f, button_spacing);
if (Button("Close##ImeClose", BUTTON_SIZE)) {
state->SendCloseEvent();
}
}
End();
first_render = false;
}
void ImeUi::DrawInputText() {
ImVec2 input_size = {GetWindowWidth() - 40.0f, 0.0f};
SetCursorPosX(20.0f);
if (first_render) {
SetKeyboardFocusHere();
}
if (InputTextEx("##ImeInput", nullptr, state->current_text.begin(), ime_param->maxTextLength,
input_size, ImGuiInputTextFlags_CallbackAlways, InputTextCallback, this)) {
}
}
int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
ImeUi* ui = static_cast<ImeUi*>(data->UserData);
ASSERT(ui);
static std::string lastText;
std::string currentText(data->Buf, data->BufTextLen);
if (currentText != lastText) {
OrbisImeEditText eventParam{};
eventParam.str = reinterpret_cast<char16_t*>(ui->ime_param->work);
eventParam.caret_index = data->CursorPos;
eventParam.area_num = 1;
eventParam.text_area[0].mode = 1; // Edit mode
eventParam.text_area[0].index = data->CursorPos;
eventParam.text_area[0].length = data->BufTextLen;
if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, eventParam.str,
ui->ime_param->maxTextLength)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8");
return 0;
}
if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen,
ui->ime_param->inputTextBuffer,
ui->ime_param->maxTextLength)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8");
return 0;
}
OrbisImeEvent event{};
event.id = OrbisImeEventId::UpdateText;
event.param.text = eventParam;
lastText = currentText;
ui->state->SendEvent(&event);
}
static int lastCaretPos = -1;
if (lastCaretPos == -1) {
lastCaretPos = data->CursorPos;
} else if (data->CursorPos != lastCaretPos) {
OrbisImeCaretMovementDirection caretDirection = OrbisImeCaretMovementDirection::Still;
if (data->CursorPos < lastCaretPos) {
caretDirection = OrbisImeCaretMovementDirection::Left;
} else if (data->CursorPos > lastCaretPos) {
caretDirection = OrbisImeCaretMovementDirection::Right;
}
OrbisImeEvent event{};
event.id = OrbisImeEventId::UpdateCaret;
event.param.caret_move = caretDirection;
lastCaretPos = data->CursorPos;
ui->state->SendEvent(&event);
}
return 0;
}
void ImeUi::Free() {
RemoveLayer(this);
}
}; // namespace Libraries::Ime
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "ime_ui.h"
#include "imgui/imgui_std.h"
namespace Libraries::Ime {
using namespace ImGui;
static constexpr ImVec2 BUTTON_SIZE{100.0f, 30.0f};
ImeState::ImeState(const OrbisImeParam* param) {
if (!param) {
return;
}
work_buffer = param->work;
text_buffer = param->inputTextBuffer;
std::size_t text_len = std::char_traits<char16_t>::length(text_buffer);
if (!ConvertOrbisToUTF8(text_buffer, text_len, current_text.begin(),
ORBIS_IME_MAX_TEXT_LENGTH * 4)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert text to utf8 encoding");
}
}
ImeState::ImeState(ImeState&& other) noexcept
: work_buffer(other.work_buffer), text_buffer(other.text_buffer),
current_text(std::move(other.current_text)), event_queue(std::move(other.event_queue)) {
other.text_buffer = nullptr;
}
ImeState& ImeState::operator=(ImeState&& other) noexcept {
if (this != &other) {
work_buffer = other.work_buffer;
text_buffer = other.text_buffer;
current_text = std::move(other.current_text);
event_queue = std::move(other.event_queue);
other.text_buffer = nullptr;
}
return *this;
}
void ImeState::SendEvent(OrbisImeEvent* event) {
std::unique_lock lock{queue_mutex};
event_queue.push(*event);
}
void ImeState::SendEnterEvent() {
OrbisImeEvent enterEvent{};
enterEvent.id = OrbisImeEventId::PressEnter;
SendEvent(&enterEvent);
}
void ImeState::SendCloseEvent() {
OrbisImeEvent closeEvent{};
closeEvent.id = OrbisImeEventId::PressClose;
closeEvent.param.text.str = reinterpret_cast<char16_t*>(work_buffer);
SendEvent(&closeEvent);
}
void ImeState::SetText(const char16_t* text, u32 length) {}
void ImeState::SetCaret(u32 position) {}
bool ImeState::ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len,
char* utf8_text, std::size_t utf8_text_len) {
std::fill(utf8_text, utf8_text + utf8_text_len, '\0');
const ImWchar* orbis_text_ptr = reinterpret_cast<const ImWchar*>(orbis_text);
ImTextStrToUtf8(utf8_text, utf8_text_len, orbis_text_ptr, orbis_text_ptr + orbis_text_len);
return true;
}
bool ImeState::ConvertUTF8ToOrbis(const char* utf8_text, std::size_t utf8_text_len,
char16_t* orbis_text, std::size_t orbis_text_len) {
std::fill(orbis_text, orbis_text + orbis_text_len, u'\0');
ImTextStrFromUtf8(reinterpret_cast<ImWchar*>(orbis_text), orbis_text_len, utf8_text, nullptr);
return true;
}
ImeUi::ImeUi(ImeState* state, const OrbisImeParam* param) : state(state), ime_param(param) {
if (param) {
AddLayer(this);
}
}
ImeUi::~ImeUi() {
std::scoped_lock lock(draw_mutex);
Free();
}
ImeUi& ImeUi::operator=(ImeUi&& other) {
std::scoped_lock lock(draw_mutex, other.draw_mutex);
Free();
state = other.state;
ime_param = other.ime_param;
first_render = other.first_render;
other.state = nullptr;
other.ime_param = nullptr;
AddLayer(this);
return *this;
}
void ImeUi::Draw() {
std::unique_lock lock{draw_mutex};
if (!state) {
return;
}
const auto& ctx = *GetCurrentContext();
const auto& io = ctx.IO;
// TODO: Figure out how to properly translate the positions -
// for example, if a game wants to center the IME panel,
// we have to translate the panel position in a way that it
// still becomes centered, as the game normally calculates
// the position assuming a it's running on a 1920x1080 screen,
// whereas we are running on a 1280x720 window size (by default).
//
// e.g. Panel position calculation from a game:
// param.posx = (1920 / 2) - (panelWidth / 2);
// param.posy = (1080 / 2) - (panelHeight / 2);
const auto size = GetIO().DisplaySize;
f32 pos_x = (ime_param->posx / 1920.0f * (float)size.x);
f32 pos_y = (ime_param->posy / 1080.0f * (float)size.y);
ImVec2 window_pos = {pos_x, pos_y};
ImVec2 window_size = {500.0f, 100.0f};
// SetNextWindowPos(window_pos);
SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f),
ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
SetNextWindowSize(window_size);
SetNextWindowCollapsed(false);
if (first_render || !io.NavActive) {
SetNextWindowFocus();
}
if (Begin("IME##Ime", nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoSavedSettings)) {
DrawPrettyBackground();
DrawInputText();
SetCursorPosY(GetCursorPosY() + 10.0f);
const char* button_text;
button_text = "Done##ImeDone";
float button_spacing = 10.0f;
float total_button_width = BUTTON_SIZE.x * 2 + button_spacing;
float button_start_pos = (window_size.x - total_button_width) / 2.0f;
SetCursorPosX(button_start_pos);
if (Button(button_text, BUTTON_SIZE) || (IsKeyPressed(ImGuiKey_Enter))) {
state->SendEnterEvent();
}
SameLine(0.0f, button_spacing);
if (Button("Close##ImeClose", BUTTON_SIZE)) {
state->SendCloseEvent();
}
}
End();
first_render = false;
}
void ImeUi::DrawInputText() {
ImVec2 input_size = {GetWindowWidth() - 40.0f, 0.0f};
SetCursorPosX(20.0f);
if (first_render) {
SetKeyboardFocusHere();
}
if (InputTextEx("##ImeInput", nullptr, state->current_text.begin(), ime_param->maxTextLength,
input_size, ImGuiInputTextFlags_CallbackAlways, InputTextCallback, this)) {
}
}
int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
ImeUi* ui = static_cast<ImeUi*>(data->UserData);
ASSERT(ui);
static std::string lastText;
std::string currentText(data->Buf, data->BufTextLen);
if (currentText != lastText) {
OrbisImeEditText eventParam{};
eventParam.str = reinterpret_cast<char16_t*>(ui->ime_param->work);
eventParam.caret_index = data->CursorPos;
eventParam.area_num = 1;
eventParam.text_area[0].mode = 1; // Edit mode
eventParam.text_area[0].index = data->CursorPos;
eventParam.text_area[0].length = data->BufTextLen;
if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, eventParam.str,
ui->ime_param->maxTextLength)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8");
return 0;
}
if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen,
ui->ime_param->inputTextBuffer,
ui->ime_param->maxTextLength)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8");
return 0;
}
OrbisImeEvent event{};
event.id = OrbisImeEventId::UpdateText;
event.param.text = eventParam;
lastText = currentText;
ui->state->SendEvent(&event);
}
static int lastCaretPos = -1;
if (lastCaretPos == -1) {
lastCaretPos = data->CursorPos;
} else if (data->CursorPos != lastCaretPos) {
OrbisImeCaretMovementDirection caretDirection = OrbisImeCaretMovementDirection::Still;
if (data->CursorPos < lastCaretPos) {
caretDirection = OrbisImeCaretMovementDirection::Left;
} else if (data->CursorPos > lastCaretPos) {
caretDirection = OrbisImeCaretMovementDirection::Right;
}
OrbisImeEvent event{};
event.id = OrbisImeEventId::UpdateCaret;
event.param.caret_move = caretDirection;
lastCaretPos = data->CursorPos;
ui->state->SendEvent(&event);
}
return 0;
}
void ImeUi::Free() {
RemoveLayer(this);
}
}; // namespace Libraries::Ime

View File

@ -1,76 +1,76 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include <imgui.h>
#include <queue>
#include "imgui/imgui_layer.h"
#include "common/cstring.h"
#include "common/types.h"
#include "ime.h"
namespace Libraries::Ime {
class ImeHandler;
class ImeUi;
class ImeState {
friend class ImeHandler;
friend class ImeUi;
void* work_buffer{};
char16_t* text_buffer{};
// A character can hold up to 4 bytes in UTF-8
Common::CString<ORBIS_IME_MAX_TEXT_LENGTH * 4> current_text;
std::queue<OrbisImeEvent> event_queue;
std::mutex queue_mutex;
public:
ImeState(const OrbisImeParam* param = nullptr);
ImeState(ImeState&& other) noexcept;
ImeState& operator=(ImeState&& other) noexcept;
void SendEvent(OrbisImeEvent* event);
void SendEnterEvent();
void SendCloseEvent();
void SetText(const char16_t* text, u32 length);
void SetCaret(u32 position);
private:
bool ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, char* utf8_text,
std::size_t native_text_len);
bool ConvertUTF8ToOrbis(const char* native_text, std::size_t utf8_text_len,
char16_t* orbis_text, std::size_t orbis_text_len);
};
class ImeUi : public ImGui::Layer {
ImeState* state{};
const OrbisImeParam* ime_param{};
bool first_render = true;
std::mutex draw_mutex;
public:
explicit ImeUi(ImeState* state = nullptr, const OrbisImeParam* param = nullptr);
~ImeUi() override;
ImeUi(const ImeUi& other) = delete;
ImeUi& operator=(ImeUi&& other);
void Draw() override;
private:
void Free();
void DrawInputText();
static int InputTextCallback(ImGuiInputTextCallbackData* data);
};
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include <imgui.h>
#include <queue>
#include "imgui/imgui_layer.h"
#include "common/cstring.h"
#include "common/types.h"
#include "ime.h"
namespace Libraries::Ime {
class ImeHandler;
class ImeUi;
class ImeState {
friend class ImeHandler;
friend class ImeUi;
void* work_buffer{};
char16_t* text_buffer{};
// A character can hold up to 4 bytes in UTF-8
Common::CString<ORBIS_IME_MAX_TEXT_LENGTH * 4> current_text;
std::queue<OrbisImeEvent> event_queue;
std::mutex queue_mutex;
public:
ImeState(const OrbisImeParam* param = nullptr);
ImeState(ImeState&& other) noexcept;
ImeState& operator=(ImeState&& other) noexcept;
void SendEvent(OrbisImeEvent* event);
void SendEnterEvent();
void SendCloseEvent();
void SetText(const char16_t* text, u32 length);
void SetCaret(u32 position);
private:
bool ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, char* utf8_text,
std::size_t native_text_len);
bool ConvertUTF8ToOrbis(const char* native_text, std::size_t utf8_text_len,
char16_t* orbis_text, std::size_t orbis_text_len);
};
class ImeUi : public ImGui::Layer {
ImeState* state{};
const OrbisImeParam* ime_param{};
bool first_render = true;
std::mutex draw_mutex;
public:
explicit ImeUi(ImeState* state = nullptr, const OrbisImeParam* param = nullptr);
~ImeUi() override;
ImeUi(const ImeUi& other) = delete;
ImeUi& operator=(ImeUi&& other);
void Draw() override;
private:
void Free();
void DrawInputText();
static int InputTextCallback(ImGuiInputTextCallbackData* data);
};
}; // namespace Libraries::Ime

View File

@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <thread>
#include "common/assert.h"
#include "common/debug.h"
#include "common/logging/log.h"

View File

@ -1,52 +1,52 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "mutex.h"
#include "common/assert.h"
namespace Libraries::Kernel {
TimedMutex::TimedMutex() {
#ifdef _WIN64
mtx = CreateMutex(nullptr, false, nullptr);
ASSERT(mtx);
#endif
}
TimedMutex::~TimedMutex() {
#ifdef _WIN64
CloseHandle(mtx);
#endif
}
void TimedMutex::lock() {
#ifdef _WIN64
for (;;) {
u64 res = WaitForSingleObjectEx(mtx, INFINITE, true);
if (res == WAIT_OBJECT_0) {
return;
}
}
#else
mtx.lock();
#endif
}
bool TimedMutex::try_lock() {
#ifdef _WIN64
return WaitForSingleObjectEx(mtx, 0, true) == WAIT_OBJECT_0;
#else
return mtx.try_lock();
#endif
}
void TimedMutex::unlock() {
#ifdef _WIN64
ReleaseMutex(mtx);
#else
mtx.unlock();
#endif
}
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "mutex.h"
#include "common/assert.h"
namespace Libraries::Kernel {
TimedMutex::TimedMutex() {
#ifdef _WIN64
mtx = CreateMutex(nullptr, false, nullptr);
ASSERT(mtx);
#endif
}
TimedMutex::~TimedMutex() {
#ifdef _WIN64
CloseHandle(mtx);
#endif
}
void TimedMutex::lock() {
#ifdef _WIN64
for (;;) {
u64 res = WaitForSingleObjectEx(mtx, INFINITE, true);
if (res == WAIT_OBJECT_0) {
return;
}
}
#else
mtx.lock();
#endif
}
bool TimedMutex::try_lock() {
#ifdef _WIN64
return WaitForSingleObjectEx(mtx, 0, true) == WAIT_OBJECT_0;
#else
return mtx.try_lock();
#endif
}
void TimedMutex::unlock() {
#ifdef _WIN64
ReleaseMutex(mtx);
#else
mtx.unlock();
#endif
}
} // namespace Libraries::Kernel

View File

@ -1,80 +1,80 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <chrono>
#include "common/types.h"
#ifdef _WIN64
#include <windows.h>
#else
#include <mutex>
#endif
namespace Libraries::Kernel {
class TimedMutex {
public:
TimedMutex();
~TimedMutex();
void lock();
bool try_lock();
void unlock();
template <class Rep, class Period>
bool try_lock_for(const std::chrono::duration<Rep, Period>& rel_time) {
#ifdef _WIN64
constexpr auto zero = std::chrono::duration<Rep, Period>::zero();
const auto now = std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point abs_time = now;
if (rel_time > zero) {
constexpr auto max = (std::chrono::steady_clock::time_point::max)();
if (abs_time < max - rel_time) {
abs_time += rel_time;
} else {
abs_time = max;
}
}
return try_lock_until(abs_time);
#else
return mtx.try_lock_for(rel_time);
#endif
}
template <class Clock, class Duration>
bool try_lock_until(const std::chrono::time_point<Clock, Duration>& abs_time) {
#ifdef _WIN64
for (;;) {
const auto now = Clock::now();
if (abs_time <= now) {
return false;
}
const auto rel_ms = std::chrono::ceil<std::chrono::milliseconds>(abs_time - now);
u64 res = WaitForSingleObjectEx(mtx, static_cast<u64>(rel_ms.count()), true);
if (res == WAIT_OBJECT_0) {
return true;
} else if (res == WAIT_TIMEOUT) {
return false;
}
}
#else
return mtx.try_lock_until(abs_time);
#endif
}
private:
#ifdef _WIN64
HANDLE mtx;
#else
std::timed_mutex mtx;
#endif
};
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <chrono>
#include "common/types.h"
#ifdef _WIN64
#include <windows.h>
#else
#include <mutex>
#endif
namespace Libraries::Kernel {
class TimedMutex {
public:
TimedMutex();
~TimedMutex();
void lock();
bool try_lock();
void unlock();
template <class Rep, class Period>
bool try_lock_for(const std::chrono::duration<Rep, Period>& rel_time) {
#ifdef _WIN64
constexpr auto zero = std::chrono::duration<Rep, Period>::zero();
const auto now = std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point abs_time = now;
if (rel_time > zero) {
constexpr auto max = (std::chrono::steady_clock::time_point::max)();
if (abs_time < max - rel_time) {
abs_time += rel_time;
} else {
abs_time = max;
}
}
return try_lock_until(abs_time);
#else
return mtx.try_lock_for(rel_time);
#endif
}
template <class Clock, class Duration>
bool try_lock_until(const std::chrono::time_point<Clock, Duration>& abs_time) {
#ifdef _WIN64
for (;;) {
const auto now = Clock::now();
if (abs_time <= now) {
return false;
}
const auto rel_ms = std::chrono::ceil<std::chrono::milliseconds>(abs_time - now);
u64 res = WaitForSingleObjectEx(mtx, static_cast<u64>(rel_ms.count()), true);
if (res == WAIT_OBJECT_0) {
return true;
} else if (res == WAIT_TIMEOUT) {
return false;
}
}
#else
return mtx.try_lock_until(abs_time);
#endif
}
private:
#ifdef _WIN64
HANDLE mtx;
#else
std::timed_mutex mtx;
#endif
};
} // namespace Libraries::Kernel

View File

@ -1,167 +1,164 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <chrono>
#include "common/assert.h"
#include "common/types.h"
#ifdef _WIN64
#include <windows.h>
#elif defined(__APPLE__)
#include <dispatch/dispatch.h>
#else
#include <semaphore>
#endif
namespace Libraries::Kernel {
template <s64 max>
class Semaphore {
public:
Semaphore(s32 initialCount)
#if !defined(_WIN64) && !defined(__APPLE__)
: sem{initialCount}
#endif
{
#ifdef _WIN64
sem = CreateSemaphore(nullptr, initialCount, max, nullptr);
ASSERT(sem);
#elif defined(__APPLE__)
sem = dispatch_semaphore_create(initialCount);
ASSERT(sem);
#endif
}
~Semaphore() {
#ifdef _WIN64
CloseHandle(sem);
#elif defined(__APPLE__)
dispatch_release(sem);
#endif
}
void release() {
#ifdef _WIN64
ReleaseSemaphore(sem, 1, nullptr);
#elif defined(__APPLE__)
dispatch_semaphore_signal(sem);
#else
sem.release();
#endif
}
void acquire() {
#ifdef _WIN64
for (;;) {
u64 res = WaitForSingleObjectEx(sem, INFINITE, true);
if (res == WAIT_OBJECT_0) {
return;
}
}
#elif defined(__APPLE__)
for (;;) {
const auto res = dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
if (res == 0) {
return;
}
}
#else
sem.acquire();
#endif
}
bool try_acquire() {
#ifdef _WIN64
return WaitForSingleObjectEx(sem, 0, true) == WAIT_OBJECT_0;
#elif defined(__APPLE__)
return dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW) == 0;
#else
return sem.try_acquire();
#endif
}
template <class Rep, class Period>
bool try_acquire_for(const std::chrono::duration<Rep, Period>& rel_time) {
#ifdef _WIN64
const auto start_time = std::chrono::high_resolution_clock::now();
auto rel_time_ms = std::chrono::ceil<std::chrono::milliseconds>(rel_time);
while (rel_time_ms.count() > 0) {
u64 timeout_ms = static_cast<u64>(rel_time_ms.count());
u64 res = WaitForSingleObjectEx(sem, timeout_ms, true);
if (res == WAIT_OBJECT_0) {
return true;
} else if (res == WAIT_IO_COMPLETION) {
auto elapsed_time = std::chrono::high_resolution_clock::now() - start_time;
rel_time_ms -= std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time);
} else {
return false;
}
}
return false;
#elif defined(__APPLE__)
const auto rel_time_ns = std::chrono::ceil<std::chrono::nanoseconds>(rel_time).count();
const auto timeout = dispatch_time(DISPATCH_TIME_NOW, rel_time_ns);
return dispatch_semaphore_wait(sem, timeout) == 0;
#else
return sem.try_acquire_for(rel_time);
#endif
}
template <class Clock, class Duration>
bool try_acquire_until(const std::chrono::time_point<Clock, Duration>& abs_time) {
#ifdef _WIN64
const auto start_time = Clock::now();
if (start_time >= abs_time) {
return false;
}
auto rel_time = std::chrono::ceil<std::chrono::milliseconds>(abs_time - start_time);
while (rel_time.count() > 0) {
u64 timeout_ms = static_cast<u64>(rel_time.count());
u64 res = WaitForSingleObjectEx(sem, timeout_ms, true);
if (res == WAIT_OBJECT_0) {
return true;
} else if (res == WAIT_IO_COMPLETION) {
auto elapsed_time = Clock::now() - start_time;
rel_time -= std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time);
} else {
return false;
}
}
return false;
#elif defined(__APPLE__)
auto abs_s = std::chrono::time_point_cast<std::chrono::seconds>(abs_time);
auto abs_ns = std::chrono::time_point_cast<std::chrono::nanoseconds>(abs_time) -
std::chrono::time_point_cast<std::chrono::nanoseconds>(abs_s);
const timespec abs_timespec = {
.tv_sec = abs_s.time_since_epoch().count(),
.tv_nsec = abs_ns.count(),
};
const auto timeout = dispatch_walltime(&abs_timespec, 0);
return dispatch_semaphore_wait(sem, timeout) == 0;
#else
return sem.try_acquire_until(abs_time);
#endif
}
private:
#ifdef _WIN64
HANDLE sem;
#elif defined(__APPLE__)
dispatch_semaphore_t sem;
#else
std::counting_semaphore<max> sem;
#endif
};
using BinarySemaphore = Semaphore<1>;
using CountingSemaphore = Semaphore<0x7FFFFFFF /*ORBIS_KERNEL_SEM_VALUE_MAX*/>;
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <chrono>
#include "common/assert.h"
#include "common/types.h"
#ifdef _WIN64
#include <windows.h>
#elif defined(__APPLE__)
#include <dispatch/dispatch.h>
#else
#include <semaphore>
#endif
namespace Libraries::Kernel {
template <s64 max>
class Semaphore {
public:
Semaphore(s32 initialCount)
#if !defined(_WIN64) && !defined(__APPLE__)
: sem{initialCount}
#endif
{
#ifdef _WIN64
sem = CreateSemaphore(nullptr, initialCount, max, nullptr);
ASSERT(sem);
#elif defined(__APPLE__)
sem = dispatch_semaphore_create(initialCount);
ASSERT(sem);
#endif
}
~Semaphore() {
#ifdef _WIN64
CloseHandle(sem);
#elif defined(__APPLE__)
dispatch_release(sem);
#endif
}
void release() {
#ifdef _WIN64
ReleaseSemaphore(sem, 1, nullptr);
#elif defined(__APPLE__)
dispatch_semaphore_signal(sem);
#else
sem.release();
#endif
}
void acquire() {
#ifdef _WIN64
for (;;) {
u64 res = WaitForSingleObjectEx(sem, INFINITE, true);
if (res == WAIT_OBJECT_0) {
return;
}
}
#elif defined(__APPLE__)
for (;;) {
const auto res = dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
if (res == 0) {
return;
}
}
#else
sem.acquire();
#endif
}
bool try_acquire() {
#ifdef _WIN64
return WaitForSingleObjectEx(sem, 0, true) == WAIT_OBJECT_0;
#elif defined(__APPLE__)
return dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW) == 0;
#else
return sem.try_acquire();
#endif
}
template <class Rep, class Period>
bool try_acquire_for(const std::chrono::duration<Rep, Period>& rel_time) {
#ifdef _WIN64
const auto start_time = std::chrono::high_resolution_clock::now();
auto rel_time_ms = std::chrono::ceil<std::chrono::milliseconds>(rel_time);
do {
u64 timeout_ms = static_cast<u64>(rel_time_ms.count());
u64 res = WaitForSingleObjectEx(sem, timeout_ms, true);
if (res == WAIT_OBJECT_0) {
return true;
} else if (res == WAIT_IO_COMPLETION) {
auto elapsed_time = std::chrono::high_resolution_clock::now() - start_time;
rel_time_ms -= std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time);
} else {
return false;
}
} while (rel_time_ms.count() > 0);
return false;
#elif defined(__APPLE__)
const auto rel_time_ns = std::chrono::ceil<std::chrono::nanoseconds>(rel_time).count();
const auto timeout = dispatch_time(DISPATCH_TIME_NOW, rel_time_ns);
return dispatch_semaphore_wait(sem, timeout) == 0;
#else
return sem.try_acquire_for(rel_time);
#endif
}
template <class Clock, class Duration>
bool try_acquire_until(const std::chrono::time_point<Clock, Duration>& abs_time) {
#ifdef _WIN64
const auto start_time = Clock::now();
auto rel_time = std::chrono::ceil<std::chrono::milliseconds>(abs_time - start_time);
do {
u64 timeout_ms = static_cast<u64>(rel_time.count());
u64 res = WaitForSingleObjectEx(sem, timeout_ms, true);
if (res == WAIT_OBJECT_0) {
return true;
} else if (res == WAIT_IO_COMPLETION) {
auto elapsed_time = Clock::now() - start_time;
rel_time -= std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time);
} else {
return false;
}
} while (rel_time.count() > 0);
return false;
#elif defined(__APPLE__)
auto abs_s = std::chrono::time_point_cast<std::chrono::seconds>(abs_time);
auto abs_ns = std::chrono::time_point_cast<std::chrono::nanoseconds>(abs_time) -
std::chrono::time_point_cast<std::chrono::nanoseconds>(abs_s);
const timespec abs_timespec = {
.tv_sec = abs_s.time_since_epoch().count(),
.tv_nsec = abs_ns.count(),
};
const auto timeout = dispatch_walltime(&abs_timespec, 0);
return dispatch_semaphore_wait(sem, timeout) == 0;
#else
return sem.try_acquire_until(abs_time);
#endif
}
private:
#ifdef _WIN64
HANDLE sem;
#elif defined(__APPLE__)
dispatch_semaphore_t sem;
#else
std::counting_semaphore<max> sem;
#endif
};
using BinarySemaphore = Semaphore<1>;
using CountingSemaphore = Semaphore<0x7FFFFFFF /*ORBIS_KERNEL_SEM_VALUE_MAX*/>;
} // namespace Libraries::Kernel

View File

@ -22,7 +22,7 @@ void SigactionHandler(int signum, siginfo_t* inf, ucontext_t* raw_context) {
if (handler) {
auto ctx = Ucontext{};
#ifdef __APPLE__
auto& regs = raw_context->uc_mcontext->__ss;
const auto& regs = raw_context->uc_mcontext->__ss;
ctx.uc_mcontext.mc_r8 = regs.__r8;
ctx.uc_mcontext.mc_r9 = regs.__r9;
ctx.uc_mcontext.mc_r10 = regs.__r10;
@ -42,7 +42,7 @@ void SigactionHandler(int signum, siginfo_t* inf, ucontext_t* raw_context) {
ctx.uc_mcontext.mc_fs = regs.__fs;
ctx.uc_mcontext.mc_gs = regs.__gs;
#else
auto& regs = raw_context->uc_mcontext.gregs;
const auto& regs = raw_context->uc_mcontext.gregs;
ctx.uc_mcontext.mc_r8 = regs[REG_R8];
ctx.uc_mcontext.mc_r9 = regs[REG_R9];
ctx.uc_mcontext.mc_r10 = regs[REG_R10];
@ -131,8 +131,12 @@ int PS4_SYSV_ABI sceKernelRaiseException(PthreadT thread, int signum) {
LOG_WARNING(Lib_Kernel, "Raising exception on thread '{}'", thread->name);
ASSERT_MSG(signum == POSIX_SIGUSR1, "Attempting to raise non user defined signal!");
#ifndef _WIN64
pthread_t pthr = *reinterpret_cast<pthread_t*>(thread->native_thr.GetHandle());
pthread_kill(pthr, SIGUSR2);
const auto pthr = reinterpret_cast<pthread_t>(thread->native_thr.GetHandle());
const auto ret = pthread_kill(pthr, SIGUSR2);
if (ret != 0) {
LOG_ERROR(Kernel, "Failed to send exception signal to thread '{}': {}", thread->name,
strerror(ret));
}
#else
USER_APC_OPTION option;
option.UserApcFlags = QueueUserApcFlagsSpecialUserApc;

View File

@ -53,6 +53,7 @@ Core::Tcb* TcbCtor(Pthread* thread, int initial) {
if (tcb) {
tcb->tcb_thread = thread;
tcb->tcb_fiber = nullptr;
}
return tcb;
}

View File

@ -5,6 +5,7 @@
#include <mutex>
#include <imgui.h>
#include "common/assert.h"
#include "common/config.h"
#include "common/singleton.h"
#include "imgui/imgui_std.h"
#include "trophy_ui.h"
@ -82,7 +83,10 @@ void TrophyUI::Draw() {
void AddTrophyToQueue(const std::filesystem::path& trophyIconPath, const std::string& trophyName) {
std::lock_guard<std::mutex> lock(queueMtx);
if (current_trophy_ui.has_value()) {
if (Config::getisTrophyPopupDisabled()) {
return;
} else if (current_trophy_ui.has_value()) {
TrophyInfo new_trophy;
new_trophy.trophy_icon_path = trophyIconPath;
new_trophy.trophy_name = trophyName;

View File

@ -1,199 +1,199 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/libraries/libs.h"
#include "core/libraries/videodec/videodec2.h"
#include "core/libraries/videodec/videodec2_impl.h"
#include "core/libraries/videodec/videodec_error.h"
namespace Libraries::Vdec2 {
static constexpr u64 kMinimumMemorySize = 32_MB; ///> Fake minimum memory size for querying
s32 PS4_SYSV_ABI
sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo) {
LOG_INFO(Lib_Vdec2, "called");
if (!computeMemInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (computeMemInfo->thisSize != sizeof(OrbisVideodec2ComputeMemoryInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
computeMemInfo->cpuGpuMemory = nullptr;
computeMemInfo->cpuGpuMemorySize = kMinimumMemorySize;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI
sceVideodec2AllocateComputeQueue(const OrbisVideodec2ComputeConfigInfo* computeCfgInfo,
const OrbisVideodec2ComputeMemoryInfo* computeMemInfo,
OrbisVideodec2ComputeQueue* computeQueue) {
LOG_INFO(Lib_Vdec2, "called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2ReleaseComputeQueue(OrbisVideodec2ComputeQueue computeQueue) {
LOG_INFO(Lib_Vdec2, "called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI
sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
OrbisVideodec2DecoderMemoryInfo* decoderMemInfo) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoderCfgInfo || !decoderMemInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) ||
decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
decoderMemInfo->cpuMemory = nullptr;
decoderMemInfo->gpuMemory = nullptr;
decoderMemInfo->cpuGpuMemory = nullptr;
decoderMemInfo->cpuGpuMemorySize = kMinimumMemorySize;
decoderMemInfo->cpuMemorySize = kMinimumMemorySize;
decoderMemInfo->gpuMemorySize = kMinimumMemorySize;
decoderMemInfo->maxFrameBufferSize = kMinimumMemorySize;
decoderMemInfo->frameBufferAlignment = 0x100;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
const OrbisVideodec2DecoderMemoryInfo* decoderMemInfo,
OrbisVideodec2Decoder* decoder) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoderCfgInfo || !decoderMemInfo || !decoder) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) ||
decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
*decoder = new VdecDecoder(*decoderCfgInfo, *decoderMemInfo);
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
delete decoder;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder,
const OrbisVideodec2InputData* inputData,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo) {
LOG_TRACE(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
if (!inputData || !frameBuffer || !outputInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (inputData->thisSize != sizeof(OrbisVideodec2InputData) ||
frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
return decoder->Decode(*inputData, *frameBuffer, *outputInfo);
}
s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
if (!frameBuffer || !outputInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer) ||
outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
return decoder->Flush(*frameBuffer, *outputInfo);
}
s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
return decoder->Reset();
}
s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outputInfo,
void* p1stPictureInfoOut, void* p2ndPictureInfoOut) {
LOG_TRACE(Lib_Vdec2, "called");
if (!outputInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
if (outputInfo->pictureCount == 0 || gPictureInfos.empty()) {
return ORBIS_OK;
}
if (p1stPictureInfoOut) {
OrbisVideodec2AvcPictureInfo* picInfo =
static_cast<OrbisVideodec2AvcPictureInfo*>(p1stPictureInfoOut);
if (picInfo->thisSize != sizeof(OrbisVideodec2AvcPictureInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
*picInfo = gPictureInfos.back();
}
if (outputInfo->pictureCount > 1) {
UNREACHABLE();
}
return ORBIS_OK;
}
void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("RnDibcGCPKw", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2QueryComputeMemoryInfo);
LIB_FUNCTION("eD+X2SmxUt4", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2AllocateComputeQueue);
LIB_FUNCTION("UvtA3FAiF4Y", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2ReleaseComputeQueue);
LIB_FUNCTION("qqMCwlULR+E", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2QueryDecoderMemoryInfo);
LIB_FUNCTION("CNNRoRYd8XI", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2CreateDecoder);
LIB_FUNCTION("jwImxXRGSKA", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2DeleteDecoder);
LIB_FUNCTION("852F5+q6+iM", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Decode);
LIB_FUNCTION("l1hXwscLuCY", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Flush);
LIB_FUNCTION("wJXikG6QFN8", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Reset);
LIB_FUNCTION("NtXRa3dRzU0", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2GetPictureInfo);
}
} // namespace Libraries::Vdec2
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/libraries/libs.h"
#include "core/libraries/videodec/videodec2.h"
#include "core/libraries/videodec/videodec2_impl.h"
#include "core/libraries/videodec/videodec_error.h"
namespace Libraries::Vdec2 {
static constexpr u64 kMinimumMemorySize = 32_MB; ///> Fake minimum memory size for querying
s32 PS4_SYSV_ABI
sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo) {
LOG_INFO(Lib_Vdec2, "called");
if (!computeMemInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (computeMemInfo->thisSize != sizeof(OrbisVideodec2ComputeMemoryInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
computeMemInfo->cpuGpuMemory = nullptr;
computeMemInfo->cpuGpuMemorySize = kMinimumMemorySize;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI
sceVideodec2AllocateComputeQueue(const OrbisVideodec2ComputeConfigInfo* computeCfgInfo,
const OrbisVideodec2ComputeMemoryInfo* computeMemInfo,
OrbisVideodec2ComputeQueue* computeQueue) {
LOG_INFO(Lib_Vdec2, "called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2ReleaseComputeQueue(OrbisVideodec2ComputeQueue computeQueue) {
LOG_INFO(Lib_Vdec2, "called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI
sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
OrbisVideodec2DecoderMemoryInfo* decoderMemInfo) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoderCfgInfo || !decoderMemInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) ||
decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
decoderMemInfo->cpuMemory = nullptr;
decoderMemInfo->gpuMemory = nullptr;
decoderMemInfo->cpuGpuMemory = nullptr;
decoderMemInfo->cpuGpuMemorySize = kMinimumMemorySize;
decoderMemInfo->cpuMemorySize = kMinimumMemorySize;
decoderMemInfo->gpuMemorySize = kMinimumMemorySize;
decoderMemInfo->maxFrameBufferSize = kMinimumMemorySize;
decoderMemInfo->frameBufferAlignment = 0x100;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
const OrbisVideodec2DecoderMemoryInfo* decoderMemInfo,
OrbisVideodec2Decoder* decoder) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoderCfgInfo || !decoderMemInfo || !decoder) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) ||
decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
*decoder = new VdecDecoder(*decoderCfgInfo, *decoderMemInfo);
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
delete decoder;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder,
const OrbisVideodec2InputData* inputData,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo) {
LOG_TRACE(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
if (!inputData || !frameBuffer || !outputInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (inputData->thisSize != sizeof(OrbisVideodec2InputData) ||
frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
return decoder->Decode(*inputData, *frameBuffer, *outputInfo);
}
s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
if (!frameBuffer || !outputInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer) ||
outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
return decoder->Flush(*frameBuffer, *outputInfo);
}
s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
return decoder->Reset();
}
s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outputInfo,
void* p1stPictureInfoOut, void* p2ndPictureInfoOut) {
LOG_TRACE(Lib_Vdec2, "called");
if (!outputInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
if (outputInfo->pictureCount == 0 || gPictureInfos.empty()) {
return ORBIS_OK;
}
if (p1stPictureInfoOut) {
OrbisVideodec2AvcPictureInfo* picInfo =
static_cast<OrbisVideodec2AvcPictureInfo*>(p1stPictureInfoOut);
if (picInfo->thisSize != sizeof(OrbisVideodec2AvcPictureInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
*picInfo = gPictureInfos.back();
}
if (outputInfo->pictureCount > 1) {
UNREACHABLE();
}
return ORBIS_OK;
}
void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("RnDibcGCPKw", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2QueryComputeMemoryInfo);
LIB_FUNCTION("eD+X2SmxUt4", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2AllocateComputeQueue);
LIB_FUNCTION("UvtA3FAiF4Y", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2ReleaseComputeQueue);
LIB_FUNCTION("qqMCwlULR+E", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2QueryDecoderMemoryInfo);
LIB_FUNCTION("CNNRoRYd8XI", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2CreateDecoder);
LIB_FUNCTION("jwImxXRGSKA", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2DeleteDecoder);
LIB_FUNCTION("852F5+q6+iM", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Decode);
LIB_FUNCTION("l1hXwscLuCY", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Flush);
LIB_FUNCTION("wJXikG6QFN8", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Reset);
LIB_FUNCTION("NtXRa3dRzU0", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2GetPictureInfo);
}
} // namespace Libraries::Vdec2

View File

@ -1,139 +1,139 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
#include "videodec2_avc.h"
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::Vdec2 {
class VdecDecoder;
using OrbisVideodec2Decoder = VdecDecoder*;
using OrbisVideodec2ComputeQueue = void*;
struct OrbisVideodec2DecoderConfigInfo {
u64 thisSize;
u32 resourceType;
u32 codecType;
u32 profile;
u32 maxLevel;
s32 maxFrameWidth;
s32 maxFrameHeight;
s32 maxDpbFrameCount;
u32 decodePipelineDepth;
OrbisVideodec2ComputeQueue computeQueue;
u64 cpuAffinityMask;
s32 cpuThreadPriority;
bool optimizeProgressiveVideo;
bool checkMemoryType;
u8 reserved0;
u8 reserved1;
void* extraConfigInfo;
};
static_assert(sizeof(OrbisVideodec2DecoderConfigInfo) == 0x48);
struct OrbisVideodec2DecoderMemoryInfo {
u64 thisSize;
u64 cpuMemorySize;
void* cpuMemory;
u64 gpuMemorySize;
void* gpuMemory;
u64 cpuGpuMemorySize;
void* cpuGpuMemory;
u64 maxFrameBufferSize;
u32 frameBufferAlignment;
u32 reserved0;
};
static_assert(sizeof(OrbisVideodec2DecoderMemoryInfo) == 0x48);
struct OrbisVideodec2InputData {
u64 thisSize;
void* auData;
u64 auSize;
u64 ptsData;
u64 dtsData;
u64 attachedData;
};
static_assert(sizeof(OrbisVideodec2InputData) == 0x30);
struct OrbisVideodec2OutputInfo {
u64 thisSize;
bool isValid;
bool isErrorFrame;
u8 pictureCount;
u32 codecType;
u32 frameWidth;
u32 framePitch;
u32 frameHeight;
void* frameBuffer;
u64 frameBufferSize;
};
static_assert(sizeof(OrbisVideodec2OutputInfo) == 0x30);
struct OrbisVideodec2FrameBuffer {
u64 thisSize;
void* frameBuffer;
u64 frameBufferSize;
bool isAccepted;
};
static_assert(sizeof(OrbisVideodec2FrameBuffer) == 0x20);
struct OrbisVideodec2ComputeMemoryInfo {
u64 thisSize;
u64 cpuGpuMemorySize;
void* cpuGpuMemory;
};
static_assert(sizeof(OrbisVideodec2ComputeMemoryInfo) == 0x18);
struct OrbisVideodec2ComputeConfigInfo {
u64 thisSize;
u16 computePipeId;
u16 computeQueueId;
bool checkMemoryType;
u8 reserved0;
u16 reserved1;
};
static_assert(sizeof(OrbisVideodec2ComputeConfigInfo) == 0x10);
s32 PS4_SYSV_ABI
sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo);
s32 PS4_SYSV_ABI
sceVideodec2AllocateComputeQueue(const OrbisVideodec2ComputeConfigInfo* computeCfgInfo,
const OrbisVideodec2ComputeMemoryInfo* computeMemInfo,
OrbisVideodec2ComputeQueue* computeQueue);
s32 PS4_SYSV_ABI sceVideodec2ReleaseComputeQueue(OrbisVideodec2ComputeQueue computeQueue);
s32 PS4_SYSV_ABI
sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
OrbisVideodec2DecoderMemoryInfo* decoderMemInfo);
s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
const OrbisVideodec2DecoderMemoryInfo* decoderMemInfo,
OrbisVideodec2Decoder* decoder);
s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder);
s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder,
const OrbisVideodec2InputData* inputData,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo);
s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo);
s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder);
s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outputInfo,
void* p1stPictureInfo, void* p2ndPictureInfo);
void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym);
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
#include "videodec2_avc.h"
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::Vdec2 {
class VdecDecoder;
using OrbisVideodec2Decoder = VdecDecoder*;
using OrbisVideodec2ComputeQueue = void*;
struct OrbisVideodec2DecoderConfigInfo {
u64 thisSize;
u32 resourceType;
u32 codecType;
u32 profile;
u32 maxLevel;
s32 maxFrameWidth;
s32 maxFrameHeight;
s32 maxDpbFrameCount;
u32 decodePipelineDepth;
OrbisVideodec2ComputeQueue computeQueue;
u64 cpuAffinityMask;
s32 cpuThreadPriority;
bool optimizeProgressiveVideo;
bool checkMemoryType;
u8 reserved0;
u8 reserved1;
void* extraConfigInfo;
};
static_assert(sizeof(OrbisVideodec2DecoderConfigInfo) == 0x48);
struct OrbisVideodec2DecoderMemoryInfo {
u64 thisSize;
u64 cpuMemorySize;
void* cpuMemory;
u64 gpuMemorySize;
void* gpuMemory;
u64 cpuGpuMemorySize;
void* cpuGpuMemory;
u64 maxFrameBufferSize;
u32 frameBufferAlignment;
u32 reserved0;
};
static_assert(sizeof(OrbisVideodec2DecoderMemoryInfo) == 0x48);
struct OrbisVideodec2InputData {
u64 thisSize;
void* auData;
u64 auSize;
u64 ptsData;
u64 dtsData;
u64 attachedData;
};
static_assert(sizeof(OrbisVideodec2InputData) == 0x30);
struct OrbisVideodec2OutputInfo {
u64 thisSize;
bool isValid;
bool isErrorFrame;
u8 pictureCount;
u32 codecType;
u32 frameWidth;
u32 framePitch;
u32 frameHeight;
void* frameBuffer;
u64 frameBufferSize;
};
static_assert(sizeof(OrbisVideodec2OutputInfo) == 0x30);
struct OrbisVideodec2FrameBuffer {
u64 thisSize;
void* frameBuffer;
u64 frameBufferSize;
bool isAccepted;
};
static_assert(sizeof(OrbisVideodec2FrameBuffer) == 0x20);
struct OrbisVideodec2ComputeMemoryInfo {
u64 thisSize;
u64 cpuGpuMemorySize;
void* cpuGpuMemory;
};
static_assert(sizeof(OrbisVideodec2ComputeMemoryInfo) == 0x18);
struct OrbisVideodec2ComputeConfigInfo {
u64 thisSize;
u16 computePipeId;
u16 computeQueueId;
bool checkMemoryType;
u8 reserved0;
u16 reserved1;
};
static_assert(sizeof(OrbisVideodec2ComputeConfigInfo) == 0x10);
s32 PS4_SYSV_ABI
sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo);
s32 PS4_SYSV_ABI
sceVideodec2AllocateComputeQueue(const OrbisVideodec2ComputeConfigInfo* computeCfgInfo,
const OrbisVideodec2ComputeMemoryInfo* computeMemInfo,
OrbisVideodec2ComputeQueue* computeQueue);
s32 PS4_SYSV_ABI sceVideodec2ReleaseComputeQueue(OrbisVideodec2ComputeQueue computeQueue);
s32 PS4_SYSV_ABI
sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
OrbisVideodec2DecoderMemoryInfo* decoderMemInfo);
s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
const OrbisVideodec2DecoderMemoryInfo* decoderMemInfo,
OrbisVideodec2Decoder* decoder);
s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder);
s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder,
const OrbisVideodec2InputData* inputData,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo);
s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo);
s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder);
s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outputInfo,
void* p1stPictureInfo, void* p2ndPictureInfo);
void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Vdec2

View File

@ -1,60 +1,60 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
namespace Libraries::Vdec2 {
struct OrbisVideodec2AvcPictureInfo {
u64 thisSize;
bool isValid;
u64 ptsData;
u64 dtsData;
u64 attachedData;
u8 idrPictureflag;
u8 profile_idc;
u8 level_idc;
u32 pic_width_in_mbs_minus1;
u32 pic_height_in_map_units_minus1;
u8 frame_mbs_only_flag;
u8 frame_cropping_flag;
u32 frameCropLeftOffset;
u32 frameCropRightOffset;
u32 frameCropTopOffset;
u32 frameCropBottomOffset;
u8 aspect_ratio_info_present_flag;
u8 aspect_ratio_idc;
u16 sar_width;
u16 sar_height;
u8 video_signal_type_present_flag;
u8 video_format;
u8 video_full_range_flag;
u8 colour_description_present_flag;
u8 colour_primaries;
u8 transfer_characteristics;
u8 matrix_coefficients;
u8 timing_info_present_flag;
u32 num_units_in_tick;
u32 time_scale;
u8 fixed_frame_rate_flag;
u8 bitstream_restriction_flag;
u8 max_dec_frame_buffering;
u8 pic_struct_present_flag;
u8 pic_struct;
u8 field_pic_flag;
u8 bottom_field_flag;
};
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
namespace Libraries::Vdec2 {
struct OrbisVideodec2AvcPictureInfo {
u64 thisSize;
bool isValid;
u64 ptsData;
u64 dtsData;
u64 attachedData;
u8 idrPictureflag;
u8 profile_idc;
u8 level_idc;
u32 pic_width_in_mbs_minus1;
u32 pic_height_in_map_units_minus1;
u8 frame_mbs_only_flag;
u8 frame_cropping_flag;
u32 frameCropLeftOffset;
u32 frameCropRightOffset;
u32 frameCropTopOffset;
u32 frameCropBottomOffset;
u8 aspect_ratio_info_present_flag;
u8 aspect_ratio_idc;
u16 sar_width;
u16 sar_height;
u8 video_signal_type_present_flag;
u8 video_format;
u8 video_full_range_flag;
u8 colour_description_present_flag;
u8 colour_primaries;
u8 transfer_characteristics;
u8 matrix_coefficients;
u8 timing_info_present_flag;
u32 num_units_in_tick;
u32 time_scale;
u8 fixed_frame_rate_flag;
u8 bitstream_restriction_flag;
u8 max_dec_frame_buffering;
u8 pic_struct_present_flag;
u8 pic_struct;
u8 field_pic_flag;
u8 bottom_field_flag;
};
} // namespace Libraries::Vdec2

View File

@ -1,229 +1,229 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "videodec2_impl.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/libraries/videodec/videodec_error.h"
#include "common/support/avdec.h"
namespace Libraries::Vdec2 {
std::vector<OrbisVideodec2AvcPictureInfo> gPictureInfos;
static inline void CopyNV12Data(u8* dst, const AVFrame& src) {
std::memcpy(dst, src.data[0], src.width * src.height);
std::memcpy(dst + (src.width * src.height), src.data[1], (src.width * src.height) / 2);
}
VdecDecoder::VdecDecoder(const OrbisVideodec2DecoderConfigInfo& configInfo,
const OrbisVideodec2DecoderMemoryInfo& memoryInfo) {
ASSERT(configInfo.codecType == 1); /* AVC */
const AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264);
ASSERT(codec);
mCodecContext = avcodec_alloc_context3(codec);
ASSERT(mCodecContext);
mCodecContext->width = configInfo.maxFrameWidth;
mCodecContext->height = configInfo.maxFrameHeight;
avcodec_open2(mCodecContext, codec, nullptr);
}
VdecDecoder::~VdecDecoder() {
avcodec_free_context(&mCodecContext);
sws_freeContext(mSwsContext);
gPictureInfos.clear();
}
s32 VdecDecoder::Decode(const OrbisVideodec2InputData& inputData,
OrbisVideodec2FrameBuffer& frameBuffer,
OrbisVideodec2OutputInfo& outputInfo) {
frameBuffer.isAccepted = false;
outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo);
outputInfo.isValid = false;
outputInfo.isErrorFrame = true;
outputInfo.pictureCount = 0;
if (!inputData.auData) {
return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER;
}
if (inputData.auSize == 0) {
return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_SIZE;
}
AVPacket* packet = av_packet_alloc();
if (!packet) {
LOG_ERROR(Lib_Vdec2, "Failed to allocate packet");
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
packet->data = (u8*)inputData.auData;
packet->size = inputData.auSize;
packet->pts = inputData.ptsData;
packet->dts = inputData.dtsData;
int ret = avcodec_send_packet(mCodecContext, packet);
if (ret < 0) {
LOG_ERROR(Lib_Vdec2, "Error sending packet to decoder: {}", ret);
av_packet_free(&packet);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
AVFrame* frame = av_frame_alloc();
if (frame == nullptr) {
LOG_ERROR(Lib_Vdec2, "Failed to allocate frame");
av_packet_free(&packet);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
while (true) {
ret = avcodec_receive_frame(mCodecContext, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
LOG_ERROR(Lib_Vdec2, "Error receiving frame from decoder: {}", ret);
av_packet_free(&packet);
av_frame_free(&frame);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
if (frame->format != AV_PIX_FMT_NV12) {
AVFrame* nv12_frame = ConvertNV12Frame(*frame);
ASSERT(nv12_frame);
av_frame_free(&frame);
frame = nv12_frame;
}
CopyNV12Data((u8*)frameBuffer.frameBuffer, *frame);
frameBuffer.isAccepted = true;
outputInfo.codecType = 1; // FIXME: Hardcoded to AVC
outputInfo.frameWidth = frame->width;
outputInfo.frameHeight = frame->height;
outputInfo.framePitch = frame->linesize[0];
outputInfo.frameBufferSize = frameBuffer.frameBufferSize;
outputInfo.frameBuffer = frameBuffer.frameBuffer;
outputInfo.isValid = true;
outputInfo.isErrorFrame = false;
outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video
if (outputInfo.isValid) {
OrbisVideodec2AvcPictureInfo pictureInfo = {};
pictureInfo.thisSize = sizeof(OrbisVideodec2AvcPictureInfo);
pictureInfo.isValid = true;
pictureInfo.ptsData = inputData.ptsData;
pictureInfo.dtsData = inputData.dtsData;
pictureInfo.attachedData = inputData.attachedData;
pictureInfo.frameCropLeftOffset = frame->crop_left;
pictureInfo.frameCropRightOffset = frame->crop_right;
pictureInfo.frameCropTopOffset = frame->crop_top;
pictureInfo.frameCropBottomOffset = frame->crop_bottom;
gPictureInfos.push_back(pictureInfo);
}
}
av_packet_free(&packet);
av_frame_free(&frame);
return ORBIS_OK;
}
s32 VdecDecoder::Flush(OrbisVideodec2FrameBuffer& frameBuffer,
OrbisVideodec2OutputInfo& outputInfo) {
frameBuffer.isAccepted = false;
outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo);
outputInfo.isValid = false;
outputInfo.isErrorFrame = true;
outputInfo.pictureCount = 0;
AVFrame* frame = av_frame_alloc();
if (!frame) {
LOG_ERROR(Lib_Vdec2, "Failed to allocate frame");
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
while (true) {
int ret = avcodec_receive_frame(mCodecContext, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
LOG_ERROR(Lib_Vdec2, "Error receiving frame from decoder: {}", ret);
av_frame_free(&frame);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
if (frame->format != AV_PIX_FMT_NV12) {
AVFrame* nv12_frame = ConvertNV12Frame(*frame);
ASSERT(nv12_frame);
av_frame_free(&frame);
frame = nv12_frame;
}
CopyNV12Data((u8*)frameBuffer.frameBuffer, *frame);
frameBuffer.isAccepted = true;
outputInfo.codecType = 1; // FIXME: Hardcoded to AVC
outputInfo.frameWidth = frame->width;
outputInfo.frameHeight = frame->height;
outputInfo.framePitch = frame->linesize[0];
outputInfo.frameBufferSize = frameBuffer.frameBufferSize;
outputInfo.frameBuffer = frameBuffer.frameBuffer;
outputInfo.isValid = true;
outputInfo.isErrorFrame = false;
outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video
// FIXME: Should we add picture info here too?
}
av_frame_free(&frame);
return ORBIS_OK;
}
s32 VdecDecoder::Reset() {
avcodec_flush_buffers(mCodecContext);
gPictureInfos.clear();
return ORBIS_OK;
}
AVFrame* VdecDecoder::ConvertNV12Frame(AVFrame& frame) {
AVFrame* nv12_frame = av_frame_alloc();
nv12_frame->pts = frame.pts;
nv12_frame->pkt_dts = frame.pkt_dts < 0 ? 0 : frame.pkt_dts;
nv12_frame->format = AV_PIX_FMT_NV12;
nv12_frame->width = frame.width;
nv12_frame->height = frame.height;
nv12_frame->sample_aspect_ratio = frame.sample_aspect_ratio;
nv12_frame->crop_top = frame.crop_top;
nv12_frame->crop_bottom = frame.crop_bottom;
nv12_frame->crop_left = frame.crop_left;
nv12_frame->crop_right = frame.crop_right;
av_frame_get_buffer(nv12_frame, 0);
if (mSwsContext == nullptr) {
mSwsContext = sws_getContext(frame.width, frame.height, AVPixelFormat(frame.format),
nv12_frame->width, nv12_frame->height, AV_PIX_FMT_NV12,
SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
}
const auto res = sws_scale(mSwsContext, frame.data, frame.linesize, 0, frame.height,
nv12_frame->data, nv12_frame->linesize);
if (res < 0) {
LOG_ERROR(Lib_Vdec2, "Could not convert to NV12: {}", av_err2str(res));
return nullptr;
}
return nv12_frame;
}
} // namespace Libraries::Vdec2
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "videodec2_impl.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/libraries/videodec/videodec_error.h"
#include "common/support/avdec.h"
namespace Libraries::Vdec2 {
std::vector<OrbisVideodec2AvcPictureInfo> gPictureInfos;
static inline void CopyNV12Data(u8* dst, const AVFrame& src) {
std::memcpy(dst, src.data[0], src.width * src.height);
std::memcpy(dst + (src.width * src.height), src.data[1], (src.width * src.height) / 2);
}
VdecDecoder::VdecDecoder(const OrbisVideodec2DecoderConfigInfo& configInfo,
const OrbisVideodec2DecoderMemoryInfo& memoryInfo) {
ASSERT(configInfo.codecType == 1); /* AVC */
const AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264);
ASSERT(codec);
mCodecContext = avcodec_alloc_context3(codec);
ASSERT(mCodecContext);
mCodecContext->width = configInfo.maxFrameWidth;
mCodecContext->height = configInfo.maxFrameHeight;
avcodec_open2(mCodecContext, codec, nullptr);
}
VdecDecoder::~VdecDecoder() {
avcodec_free_context(&mCodecContext);
sws_freeContext(mSwsContext);
gPictureInfos.clear();
}
s32 VdecDecoder::Decode(const OrbisVideodec2InputData& inputData,
OrbisVideodec2FrameBuffer& frameBuffer,
OrbisVideodec2OutputInfo& outputInfo) {
frameBuffer.isAccepted = false;
outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo);
outputInfo.isValid = false;
outputInfo.isErrorFrame = true;
outputInfo.pictureCount = 0;
if (!inputData.auData) {
return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER;
}
if (inputData.auSize == 0) {
return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_SIZE;
}
AVPacket* packet = av_packet_alloc();
if (!packet) {
LOG_ERROR(Lib_Vdec2, "Failed to allocate packet");
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
packet->data = (u8*)inputData.auData;
packet->size = inputData.auSize;
packet->pts = inputData.ptsData;
packet->dts = inputData.dtsData;
int ret = avcodec_send_packet(mCodecContext, packet);
if (ret < 0) {
LOG_ERROR(Lib_Vdec2, "Error sending packet to decoder: {}", ret);
av_packet_free(&packet);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
AVFrame* frame = av_frame_alloc();
if (frame == nullptr) {
LOG_ERROR(Lib_Vdec2, "Failed to allocate frame");
av_packet_free(&packet);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
while (true) {
ret = avcodec_receive_frame(mCodecContext, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
LOG_ERROR(Lib_Vdec2, "Error receiving frame from decoder: {}", ret);
av_packet_free(&packet);
av_frame_free(&frame);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
if (frame->format != AV_PIX_FMT_NV12) {
AVFrame* nv12_frame = ConvertNV12Frame(*frame);
ASSERT(nv12_frame);
av_frame_free(&frame);
frame = nv12_frame;
}
CopyNV12Data((u8*)frameBuffer.frameBuffer, *frame);
frameBuffer.isAccepted = true;
outputInfo.codecType = 1; // FIXME: Hardcoded to AVC
outputInfo.frameWidth = frame->width;
outputInfo.frameHeight = frame->height;
outputInfo.framePitch = frame->linesize[0];
outputInfo.frameBufferSize = frameBuffer.frameBufferSize;
outputInfo.frameBuffer = frameBuffer.frameBuffer;
outputInfo.isValid = true;
outputInfo.isErrorFrame = false;
outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video
if (outputInfo.isValid) {
OrbisVideodec2AvcPictureInfo pictureInfo = {};
pictureInfo.thisSize = sizeof(OrbisVideodec2AvcPictureInfo);
pictureInfo.isValid = true;
pictureInfo.ptsData = inputData.ptsData;
pictureInfo.dtsData = inputData.dtsData;
pictureInfo.attachedData = inputData.attachedData;
pictureInfo.frameCropLeftOffset = frame->crop_left;
pictureInfo.frameCropRightOffset = frame->crop_right;
pictureInfo.frameCropTopOffset = frame->crop_top;
pictureInfo.frameCropBottomOffset = frame->crop_bottom;
gPictureInfos.push_back(pictureInfo);
}
}
av_packet_free(&packet);
av_frame_free(&frame);
return ORBIS_OK;
}
s32 VdecDecoder::Flush(OrbisVideodec2FrameBuffer& frameBuffer,
OrbisVideodec2OutputInfo& outputInfo) {
frameBuffer.isAccepted = false;
outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo);
outputInfo.isValid = false;
outputInfo.isErrorFrame = true;
outputInfo.pictureCount = 0;
AVFrame* frame = av_frame_alloc();
if (!frame) {
LOG_ERROR(Lib_Vdec2, "Failed to allocate frame");
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
while (true) {
int ret = avcodec_receive_frame(mCodecContext, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
LOG_ERROR(Lib_Vdec2, "Error receiving frame from decoder: {}", ret);
av_frame_free(&frame);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
if (frame->format != AV_PIX_FMT_NV12) {
AVFrame* nv12_frame = ConvertNV12Frame(*frame);
ASSERT(nv12_frame);
av_frame_free(&frame);
frame = nv12_frame;
}
CopyNV12Data((u8*)frameBuffer.frameBuffer, *frame);
frameBuffer.isAccepted = true;
outputInfo.codecType = 1; // FIXME: Hardcoded to AVC
outputInfo.frameWidth = frame->width;
outputInfo.frameHeight = frame->height;
outputInfo.framePitch = frame->linesize[0];
outputInfo.frameBufferSize = frameBuffer.frameBufferSize;
outputInfo.frameBuffer = frameBuffer.frameBuffer;
outputInfo.isValid = true;
outputInfo.isErrorFrame = false;
outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video
// FIXME: Should we add picture info here too?
}
av_frame_free(&frame);
return ORBIS_OK;
}
s32 VdecDecoder::Reset() {
avcodec_flush_buffers(mCodecContext);
gPictureInfos.clear();
return ORBIS_OK;
}
AVFrame* VdecDecoder::ConvertNV12Frame(AVFrame& frame) {
AVFrame* nv12_frame = av_frame_alloc();
nv12_frame->pts = frame.pts;
nv12_frame->pkt_dts = frame.pkt_dts < 0 ? 0 : frame.pkt_dts;
nv12_frame->format = AV_PIX_FMT_NV12;
nv12_frame->width = frame.width;
nv12_frame->height = frame.height;
nv12_frame->sample_aspect_ratio = frame.sample_aspect_ratio;
nv12_frame->crop_top = frame.crop_top;
nv12_frame->crop_bottom = frame.crop_bottom;
nv12_frame->crop_left = frame.crop_left;
nv12_frame->crop_right = frame.crop_right;
av_frame_get_buffer(nv12_frame, 0);
if (mSwsContext == nullptr) {
mSwsContext = sws_getContext(frame.width, frame.height, AVPixelFormat(frame.format),
nv12_frame->width, nv12_frame->height, AV_PIX_FMT_NV12,
SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
}
const auto res = sws_scale(mSwsContext, frame.data, frame.linesize, 0, frame.height,
nv12_frame->data, nv12_frame->linesize);
if (res < 0) {
LOG_ERROR(Lib_Vdec2, "Could not convert to NV12: {}", av_err2str(res));
return nullptr;
}
return nv12_frame;
}
} // namespace Libraries::Vdec2

View File

@ -1,39 +1,39 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <vector>
#include "videodec2.h"
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
namespace Libraries::Vdec2 {
extern std::vector<OrbisVideodec2AvcPictureInfo> gPictureInfos;
class VdecDecoder {
public:
VdecDecoder(const OrbisVideodec2DecoderConfigInfo& configInfo,
const OrbisVideodec2DecoderMemoryInfo& memoryInfo);
~VdecDecoder();
s32 Decode(const OrbisVideodec2InputData& inputData, OrbisVideodec2FrameBuffer& frameBuffer,
OrbisVideodec2OutputInfo& outputInfo);
s32 Flush(OrbisVideodec2FrameBuffer& frameBuffer, OrbisVideodec2OutputInfo& outputInfo);
s32 Reset();
private:
AVFrame* ConvertNV12Frame(AVFrame& frame);
private:
AVCodecContext* mCodecContext = nullptr;
SwsContext* mSwsContext = nullptr;
};
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <vector>
#include "videodec2.h"
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
namespace Libraries::Vdec2 {
extern std::vector<OrbisVideodec2AvcPictureInfo> gPictureInfos;
class VdecDecoder {
public:
VdecDecoder(const OrbisVideodec2DecoderConfigInfo& configInfo,
const OrbisVideodec2DecoderMemoryInfo& memoryInfo);
~VdecDecoder();
s32 Decode(const OrbisVideodec2InputData& inputData, OrbisVideodec2FrameBuffer& frameBuffer,
OrbisVideodec2OutputInfo& outputInfo);
s32 Flush(OrbisVideodec2FrameBuffer& frameBuffer, OrbisVideodec2OutputInfo& outputInfo);
s32 Reset();
private:
AVFrame* ConvertNV12Frame(AVFrame& frame);
private:
AVCodecContext* mCodecContext = nullptr;
SwsContext* mSwsContext = nullptr;
};
} // namespace Libraries::Vdec2

View File

@ -1,151 +1,152 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/alignment.h"
#include "core/libraries/kernel/threads/pthread.h"
#include "thread.h"
#ifdef _WIN64
#include <windows.h>
#include "common/ntapi.h"
#else
#include <csignal>
#include <pthread.h>
#endif
namespace Core {
#ifdef _WIN64
#define KGDT64_R3_DATA (0x28)
#define KGDT64_R3_CODE (0x30)
#define KGDT64_R3_CMTEB (0x50)
#define RPL_MASK (0x03)
#define INITIAL_FPUCW (0x037f)
#define INITIAL_MXCSR_MASK (0xffbf)
#define EFLAGS_INTERRUPT_MASK (0x200)
void InitializeTeb(INITIAL_TEB* teb, const ::Libraries::Kernel::PthreadAttr* attr) {
teb->StackBase = (void*)((u64)attr->stackaddr_attr + attr->stacksize_attr);
teb->StackLimit = nullptr;
teb->StackAllocationBase = attr->stackaddr_attr;
}
void InitializeContext(CONTEXT* ctx, ThreadFunc func, void* arg,
const ::Libraries::Kernel::PthreadAttr* attr) {
/* Note: The stack has to be reversed */
ctx->Rsp = (u64)attr->stackaddr_attr + attr->stacksize_attr;
ctx->Rbp = (u64)attr->stackaddr_attr + attr->stacksize_attr;
ctx->Rcx = (u64)arg;
ctx->Rip = (u64)func;
ctx->SegGs = KGDT64_R3_DATA | RPL_MASK;
ctx->SegEs = KGDT64_R3_DATA | RPL_MASK;
ctx->SegDs = KGDT64_R3_DATA | RPL_MASK;
ctx->SegCs = KGDT64_R3_CODE | RPL_MASK;
ctx->SegSs = KGDT64_R3_DATA | RPL_MASK;
ctx->SegFs = KGDT64_R3_CMTEB | RPL_MASK;
ctx->EFlags = 0x3000 | EFLAGS_INTERRUPT_MASK;
ctx->MxCsr = INITIAL_MXCSR;
ctx->FltSave.ControlWord = INITIAL_FPUCW;
ctx->FltSave.MxCsr = INITIAL_MXCSR;
ctx->FltSave.MxCsr_Mask = INITIAL_MXCSR_MASK;
ctx->ContextFlags =
CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT;
}
#endif
NativeThread::NativeThread() : native_handle{0} {}
NativeThread::~NativeThread() {}
int NativeThread::Create(ThreadFunc func, void* arg, const ::Libraries::Kernel::PthreadAttr* attr) {
#ifndef _WIN64
pthread_t* pthr = reinterpret_cast<pthread_t*>(&native_handle);
pthread_attr_t pattr;
pthread_attr_init(&pattr);
pthread_attr_setstack(&pattr, attr->stackaddr_attr, attr->stacksize_attr);
return pthread_create(pthr, &pattr, (PthreadFunc)func, arg);
#else
CLIENT_ID clientId{};
INITIAL_TEB teb{};
CONTEXT ctx{};
clientId.UniqueProcess = GetCurrentProcess();
clientId.UniqueThread = GetCurrentThread();
InitializeTeb(&teb, attr);
InitializeContext(&ctx, func, arg, attr);
return NtCreateThread(&native_handle, THREAD_ALL_ACCESS, nullptr, GetCurrentProcess(),
&clientId, &ctx, &teb, false);
#endif
}
void NativeThread::Exit() {
if (!native_handle) {
return;
}
tid = 0;
#ifdef _WIN64
NtClose(native_handle);
native_handle = nullptr;
/* The Windows kernel will free the stack
given at thread creation via INITIAL_TEB
(StackAllocationBase) upon thread termination.
In earlier Windows versions (NT4 to Windows Server 2003),
you could get around this via disabling FreeStackOnTermination
on the TEB. This has been removed since then.
To avoid this, we must forcefully set the TEB
deallocation stack pointer to NULL so ZwFreeVirtualMemory fails
in the kernel and our stack is not freed.
*/
auto* teb = reinterpret_cast<TEB*>(NtCurrentTeb());
teb->DeallocationStack = nullptr;
NtTerminateThread(nullptr, 0);
#else
// Disable and free the signal stack.
constexpr stack_t sig_stack = {
.ss_flags = SS_DISABLE,
};
sigaltstack(&sig_stack, nullptr);
if (sig_stack_ptr) {
free(sig_stack_ptr);
sig_stack_ptr = nullptr;
}
pthread_exit(nullptr);
#endif
}
void NativeThread::Initialize() {
#if _WIN64
tid = GetCurrentThreadId();
#else
tid = (u64)pthread_self();
// Set up an alternate signal handler stack to avoid overflowing small thread stacks.
const size_t page_size = getpagesize();
const size_t sig_stack_size = Common::AlignUp(std::max<size_t>(64_KB, MINSIGSTKSZ), page_size);
ASSERT_MSG(posix_memalign(&sig_stack_ptr, page_size, sig_stack_size) == 0,
"Failed to allocate signal stack: {}", errno);
stack_t sig_stack;
sig_stack.ss_sp = sig_stack_ptr;
sig_stack.ss_size = sig_stack_size;
sig_stack.ss_flags = 0;
ASSERT_MSG(sigaltstack(&sig_stack, nullptr) == 0, "Failed to set signal stack: {}", errno);
#endif
}
} // namespace Core
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/alignment.h"
#include "core/libraries/kernel/threads/pthread.h"
#include "thread.h"
#ifdef _WIN64
#include <windows.h>
#include "common/ntapi.h"
#else
#include <csignal>
#include <pthread.h>
#include <unistd.h>
#endif
namespace Core {
#ifdef _WIN64
#define KGDT64_R3_DATA (0x28)
#define KGDT64_R3_CODE (0x30)
#define KGDT64_R3_CMTEB (0x50)
#define RPL_MASK (0x03)
#define INITIAL_FPUCW (0x037f)
#define INITIAL_MXCSR_MASK (0xffbf)
#define EFLAGS_INTERRUPT_MASK (0x200)
void InitializeTeb(INITIAL_TEB* teb, const ::Libraries::Kernel::PthreadAttr* attr) {
teb->StackBase = (void*)((u64)attr->stackaddr_attr + attr->stacksize_attr);
teb->StackLimit = nullptr;
teb->StackAllocationBase = attr->stackaddr_attr;
}
void InitializeContext(CONTEXT* ctx, ThreadFunc func, void* arg,
const ::Libraries::Kernel::PthreadAttr* attr) {
/* Note: The stack has to be reversed */
ctx->Rsp = (u64)attr->stackaddr_attr + attr->stacksize_attr;
ctx->Rbp = (u64)attr->stackaddr_attr + attr->stacksize_attr;
ctx->Rcx = (u64)arg;
ctx->Rip = (u64)func;
ctx->SegGs = KGDT64_R3_DATA | RPL_MASK;
ctx->SegEs = KGDT64_R3_DATA | RPL_MASK;
ctx->SegDs = KGDT64_R3_DATA | RPL_MASK;
ctx->SegCs = KGDT64_R3_CODE | RPL_MASK;
ctx->SegSs = KGDT64_R3_DATA | RPL_MASK;
ctx->SegFs = KGDT64_R3_CMTEB | RPL_MASK;
ctx->EFlags = 0x3000 | EFLAGS_INTERRUPT_MASK;
ctx->MxCsr = INITIAL_MXCSR;
ctx->FltSave.ControlWord = INITIAL_FPUCW;
ctx->FltSave.MxCsr = INITIAL_MXCSR;
ctx->FltSave.MxCsr_Mask = INITIAL_MXCSR_MASK;
ctx->ContextFlags =
CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT;
}
#endif
NativeThread::NativeThread() : native_handle{0} {}
NativeThread::~NativeThread() {}
int NativeThread::Create(ThreadFunc func, void* arg, const ::Libraries::Kernel::PthreadAttr* attr) {
#ifndef _WIN64
pthread_t* pthr = reinterpret_cast<pthread_t*>(&native_handle);
pthread_attr_t pattr;
pthread_attr_init(&pattr);
pthread_attr_setstack(&pattr, attr->stackaddr_attr, attr->stacksize_attr);
return pthread_create(pthr, &pattr, (PthreadFunc)func, arg);
#else
CLIENT_ID clientId{};
INITIAL_TEB teb{};
CONTEXT ctx{};
clientId.UniqueProcess = GetCurrentProcess();
clientId.UniqueThread = GetCurrentThread();
InitializeTeb(&teb, attr);
InitializeContext(&ctx, func, arg, attr);
return NtCreateThread(&native_handle, THREAD_ALL_ACCESS, nullptr, GetCurrentProcess(),
&clientId, &ctx, &teb, false);
#endif
}
void NativeThread::Exit() {
if (!native_handle) {
return;
}
tid = 0;
#ifdef _WIN64
NtClose(native_handle);
native_handle = nullptr;
/* The Windows kernel will free the stack
given at thread creation via INITIAL_TEB
(StackAllocationBase) upon thread termination.
In earlier Windows versions (NT4 to Windows Server 2003),
you could get around this via disabling FreeStackOnTermination
on the TEB. This has been removed since then.
To avoid this, we must forcefully set the TEB
deallocation stack pointer to NULL so ZwFreeVirtualMemory fails
in the kernel and our stack is not freed.
*/
auto* teb = reinterpret_cast<TEB*>(NtCurrentTeb());
teb->DeallocationStack = nullptr;
NtTerminateThread(nullptr, 0);
#else
// Disable and free the signal stack.
constexpr stack_t sig_stack = {
.ss_flags = SS_DISABLE,
};
sigaltstack(&sig_stack, nullptr);
if (sig_stack_ptr) {
free(sig_stack_ptr);
sig_stack_ptr = nullptr;
}
pthread_exit(nullptr);
#endif
}
void NativeThread::Initialize() {
#if _WIN64
tid = GetCurrentThreadId();
#else
tid = (u64)pthread_self();
// Set up an alternate signal handler stack to avoid overflowing small thread stacks.
const size_t page_size = getpagesize();
const size_t sig_stack_size = Common::AlignUp(std::max<size_t>(64_KB, MINSIGSTKSZ), page_size);
ASSERT_MSG(posix_memalign(&sig_stack_ptr, page_size, sig_stack_size) == 0,
"Failed to allocate signal stack: {}", errno);
stack_t sig_stack;
sig_stack.ss_sp = sig_stack_ptr;
sig_stack.ss_size = sig_stack_size;
sig_stack.ss_flags = 0;
ASSERT_MSG(sigaltstack(&sig_stack, nullptr) == 0, "Failed to set signal stack: {}", errno);
#endif
}
} // namespace Core

View File

@ -1,45 +1,45 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
namespace Libraries::Kernel {
struct PthreadAttr;
} // namespace Libraries::Kernel
namespace Core {
using ThreadFunc = void (*)(void*);
using PthreadFunc = void* (*)(void*);
class NativeThread {
public:
NativeThread();
~NativeThread();
int Create(ThreadFunc func, void* arg, const ::Libraries::Kernel::PthreadAttr* attr);
void Exit();
void Initialize();
uintptr_t GetHandle() {
return reinterpret_cast<uintptr_t>(native_handle);
}
u64 GetTid() {
return tid;
}
private:
#ifdef _WIN64
void* native_handle;
#else
uintptr_t native_handle;
void* sig_stack_ptr;
#endif
u64 tid;
};
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
namespace Libraries::Kernel {
struct PthreadAttr;
} // namespace Libraries::Kernel
namespace Core {
using ThreadFunc = void (*)(void*);
using PthreadFunc = void* (*)(void*);
class NativeThread {
public:
NativeThread();
~NativeThread();
int Create(ThreadFunc func, void* arg, const ::Libraries::Kernel::PthreadAttr* attr);
void Exit();
void Initialize();
uintptr_t GetHandle() {
return reinterpret_cast<uintptr_t>(native_handle);
}
u64 GetTid() {
return tid;
}
private:
#ifdef _WIN64
void* native_handle;
#else
uintptr_t native_handle;
void* sig_stack_ptr;
#endif
u64 tid;
};
} // namespace Core

View File

@ -9,6 +9,10 @@ namespace Xbyak {
class CodeGenerator;
}
namespace Libraries::Fiber {
struct OrbisFiberContext;
}
namespace Core {
union DtvEntry {
@ -20,6 +24,7 @@ struct Tcb {
Tcb* tcb_self;
DtvEntry* tcb_dtv;
void* tcb_thread;
::Libraries::Fiber::OrbisFiberContext* tcb_fiber;
};
#ifdef _WIN32

View File

@ -15,6 +15,7 @@
#include <QNetworkRequest>
#include <QProcess>
#include <QPushButton>
#include <QStandardPaths>
#include <QString>
#include <QStringList>
#include <QTextEdit>
@ -212,9 +213,9 @@ void CheckUpdate::setupUI(const QString& downloadUrl, const QString& latestDate,
// Don't show changelog button if:
// The current version is a pre-release and the version to be downloaded is a release.
bool current_isRelease = currentRev.startsWith('v', Qt::CaseInsensitive);
bool latest_isRelease = latestRev.startsWith('v', Qt::CaseInsensitive);
if (!current_isRelease && latest_isRelease) {
bool current_isWIP = currentRev.endsWith("WIP", Qt::CaseInsensitive);
bool latest_isWIP = latestRev.endsWith("WIP", Qt::CaseInsensitive);
if (current_isWIP && !latest_isWIP) {
} else {
QTextEdit* textField = new QTextEdit(this);
textField->setReadOnly(true);
@ -348,7 +349,9 @@ void CheckUpdate::DownloadUpdate(const QString& url) {
QString userPath;
Common::FS::PathToQString(userPath, Common::FS::GetUserPath(Common::FS::PathType::UserDir));
#ifdef Q_OS_WIN
QString tempDownloadPath = QString(getenv("LOCALAPPDATA")) + "/Temp/temp_download_update";
QString tempDownloadPath =
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
"/Temp/temp_download_update";
#else
QString tempDownloadPath = userPath + "/temp_download_update";
#endif
@ -397,10 +400,11 @@ void CheckUpdate::Install() {
QString processCommand;
#ifdef Q_OS_WIN
// On windows, overwrite tempDirPath with AppData/Local/Temp folder
// On windows, overwrite tempDirPath with AppData/Roaming/shadps4/Temp folder
// due to PowerShell Expand-Archive not being able to handle correctly
// paths in square brackets (ie: ./[shadps4])
tempDirPath = QString(getenv("LOCALAPPDATA")) + "/Temp/temp_download_update";
tempDirPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
"/Temp/temp_download_update";
// Windows Batch Script
scriptFileName = tempDirPath + "/update.ps1";
@ -536,6 +540,7 @@ void CheckUpdate::Install() {
QFile scriptFile(scriptFileName);
if (scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&scriptFile);
scriptFile.write("\xEF\xBB\xBF");
#ifdef Q_OS_WIN
out << scriptContent.arg(binaryStartingUpdate).arg(tempDirPath).arg(rootPath);
#endif

View File

@ -4,6 +4,7 @@
#include <QFileInfo>
#include <QMessageBox>
#include <QProgressDialog>
#include <QTimer>
#include "common/path_util.h"
#include "compatibility_info.h"
@ -17,12 +18,14 @@ CompatibilityInfoClass::CompatibilityInfoClass()
};
CompatibilityInfoClass::~CompatibilityInfoClass() = default;
void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent) {
if (LoadCompatibilityFile())
return;
void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent, bool forced) {
if (!forced)
if (LoadCompatibilityFile())
return;
QNetworkReply* reply = FetchPage(1);
WaitForReply(reply);
if (!WaitForReply(reply))
return;
QProgressDialog dialog(tr("Fetching compatibility data, please wait"), tr("Cancel"), 0, 0,
parent);
@ -43,7 +46,8 @@ void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent) {
QMessageBox::critical(parent, tr("Error"),
tr("Unable to update compatibility data! Try again later."));
// Try loading compatibility_file.json again
LoadCompatibilityFile();
if (!forced)
LoadCompatibilityFile();
return;
}
@ -57,12 +61,17 @@ void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent) {
}
future_watcher.setFuture(QtConcurrent::map(replies, WaitForReply));
connect(&future_watcher, &QFutureWatcher<QByteArray>::finished, [&]() {
connect(&future_watcher, &QFutureWatcher<void>::finished, [&]() {
for (int i = 0; i < remaining_pages; i++) {
if (replies[i]->error() == QNetworkReply::NoError) {
ExtractCompatibilityInfo(replies[i]->readAll());
if (replies[i]->bytesAvailable()) {
if (replies[i]->error() == QNetworkReply::NoError) {
ExtractCompatibilityInfo(replies[i]->readAll());
}
replies[i]->deleteLater();
} else {
// This means the request timed out
return;
}
replies[i]->deleteLater();
}
QFile compatibility_file(m_compatibility_filename);
@ -83,6 +92,16 @@ void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent) {
dialog.reset();
});
connect(&future_watcher, &QFutureWatcher<void>::canceled, [&]() {
// Cleanup if user cancels pulling data
for (int i = 0; i < remaining_pages; i++) {
if (!replies[i]->bytesAvailable()) {
replies[i]->deleteLater();
} else if (!replies[i]->isFinished()) {
replies[i]->abort();
}
}
});
connect(&dialog, &QProgressDialog::canceled, &future_watcher, &QFutureWatcher<void>::cancel);
dialog.setRange(0, remaining_pages);
connect(&future_watcher, &QFutureWatcher<void>::progressValueChanged, &dialog,
@ -105,20 +124,34 @@ QNetworkReply* CompatibilityInfoClass::FetchPage(int page_num) {
return reply;
}
void CompatibilityInfoClass::WaitForReply(QNetworkReply* reply) {
bool CompatibilityInfoClass::WaitForReply(QNetworkReply* reply) {
// Returns true if reply succeeded, false if reply timed out
QTimer timer;
timer.setSingleShot(true);
QEventLoop loop;
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
timer.start(5000);
loop.exec();
return;
if (timer.isActive()) {
timer.stop();
return true;
} else {
disconnect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
reply->abort();
return false;
}
};
CompatibilityEntry CompatibilityInfoClass::GetCompatibilityInfo(const std::string& serial) {
QString title_id = QString::fromStdString(serial);
if (m_compatibility_database.contains(title_id)) {
{
QJsonObject compatibility_obj = m_compatibility_database[title_id].toObject();
for (int os_int = 0; os_int != static_cast<int>(OSType::Last); os_int++) {
QString os_string = OSTypeToString.at(static_cast<OSType>(os_int));
QJsonObject compatibility_obj = m_compatibility_database[title_id].toObject();
if (compatibility_obj.contains(os_string)) {
QJsonObject compatibility_entry_obj = compatibility_obj[os_string].toObject();
CompatibilityEntry compatibility_entry{
@ -133,7 +166,9 @@ CompatibilityEntry CompatibilityInfoClass::GetCompatibilityInfo(const std::strin
}
}
}
return CompatibilityEntry{CompatibilityStatus::Unknown};
return CompatibilityEntry{CompatibilityStatus::Unknown, "", QDateTime::currentDateTime(), "",
0};
}
bool CompatibilityInfoClass::LoadCompatibilityFile() {

View File

@ -57,6 +57,7 @@ class CompatibilityInfoClass : public QObject {
public:
// Please think of a better alternative
inline static const std::unordered_map<QString, CompatibilityStatus> LabelToCompatStatus = {
{QStringLiteral("status-unknown"), CompatibilityStatus::Unknown},
{QStringLiteral("status-nothing"), CompatibilityStatus::Nothing},
{QStringLiteral("status-boots"), CompatibilityStatus::Boots},
{QStringLiteral("status-menus"), CompatibilityStatus::Menus},
@ -83,11 +84,11 @@ public:
CompatibilityInfoClass();
~CompatibilityInfoClass();
void UpdateCompatibilityDatabase(QWidget* parent = nullptr);
void UpdateCompatibilityDatabase(QWidget* parent = nullptr, bool forced = false);
bool LoadCompatibilityFile();
CompatibilityEntry GetCompatibilityInfo(const std::string& serial);
void ExtractCompatibilityInfo(QByteArray response);
static void WaitForReply(QNetworkReply* reply);
static bool WaitForReply(QNetworkReply* reply);
QNetworkReply* FetchPage(int page_num);
private:

View File

@ -3,9 +3,12 @@
#include "common/path_util.h"
#include "game_grid_frame.h"
#include "qt_gui/compatibility_info.h"
GameGridFrame::GameGridFrame(std::shared_ptr<GameInfoClass> game_info_get, QWidget* parent)
: QTableWidget(parent), m_game_info(game_info_get) {
GameGridFrame::GameGridFrame(std::shared_ptr<GameInfoClass> game_info_get,
std::shared_ptr<CompatibilityInfoClass> compat_info_get,
QWidget* parent)
: QTableWidget(parent), m_game_info(game_info_get), m_compat_info(compat_info_get) {
icon_size = Config::getIconSizeGrid();
windowWidth = parent->width();
this->setShowGrid(false);
@ -29,7 +32,7 @@ GameGridFrame::GameGridFrame(std::shared_ptr<GameInfoClass> game_info_get, QWidg
connect(this->horizontalScrollBar(), &QScrollBar::valueChanged, this,
&GameGridFrame::RefreshGridBackgroundImage);
connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) {
m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, this, false);
m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, m_compat_info, this, false);
});
}

View File

@ -10,6 +10,7 @@
#include "game_info.h"
#include "game_list_utils.h"
#include "gui_context_menus.h"
#include "qt_gui/compatibility_info.h"
class GameGridFrame : public QTableWidget {
Q_OBJECT
@ -29,11 +30,14 @@ private:
GameListUtils m_game_list_utils;
GuiContextMenus m_gui_context_menus;
std::shared_ptr<GameInfoClass> m_game_info;
std::shared_ptr<CompatibilityInfoClass> m_compat_info;
std::shared_ptr<QVector<GameInfo>> m_games_shared;
bool validCellSelected = false;
public:
explicit GameGridFrame(std::shared_ptr<GameInfoClass> game_info_get, QWidget* parent = nullptr);
explicit GameGridFrame(std::shared_ptr<GameInfoClass> game_info_get,
std::shared_ptr<CompatibilityInfoClass> compat_info_get,
QWidget* parent = nullptr);
void PopulateGameGrid(QVector<GameInfo> m_games, bool fromSearch);
bool IsValidCellSelected();

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <QToolTip>
#include "common/config.h"
#include "common/logging/log.h"
#include "common/path_util.h"
#include "common/string_util.h"
@ -72,7 +73,7 @@ GameListFrame::GameListFrame(std::shared_ptr<GameInfoClass> game_info_get,
});
connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) {
m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, this, true);
m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, m_compat_info, this, true);
});
connect(this, &QTableWidget::cellClicked, this, [=, this](int row, int column) {
@ -103,6 +104,8 @@ void GameListFrame::PlayBackgroundMusic(QTableWidgetItem* item) {
}
void GameListFrame::PopulateGameList() {
// Do not show status column if it is not enabled
this->setColumnHidden(2, !Config::getCompatibilityEnabled());
this->setRowCount(m_game_info->m_games.size());
ResizeIcons(icon_size);
@ -236,7 +239,7 @@ void GameListFrame::SetCompatibilityItem(int row, int column, CompatibilityEntry
break;
case CompatibilityStatus::Nothing:
color = QStringLiteral("#212121");
status_explanation = tr("Games does not initialize properly / crashes the emulator");
status_explanation = tr("Game does not initialize properly / crashes the emulator");
break;
case CompatibilityStatus::Boots:
color = QStringLiteral("#828282");

View File

@ -11,6 +11,9 @@
#include <QTreeWidgetItem>
#include "cheats_patches.h"
#include "common/config.h"
#include "common/version.h"
#include "compatibility_info.h"
#include "game_info.h"
#include "trophy_viewer.h"
@ -27,8 +30,9 @@
class GuiContextMenus : public QObject {
Q_OBJECT
public:
void RequestGameMenu(const QPoint& pos, QVector<GameInfo> m_games, QTableWidget* widget,
bool isList) {
void RequestGameMenu(const QPoint& pos, QVector<GameInfo> m_games,
std::shared_ptr<CompatibilityInfoClass> m_compat_info,
QTableWidget* widget, bool isList) {
QPoint global_pos = widget->viewport()->mapToGlobal(pos);
int itemID = 0;
if (isList) {
@ -91,6 +95,21 @@ public:
menu.addMenu(deleteMenu);
// Compatibility submenu.
QMenu* compatibilityMenu = new QMenu(tr("Compatibility..."), widget);
QAction* updateCompatibility = new QAction(tr("Update database"), widget);
QAction* viewCompatibilityReport = new QAction(tr("View report"), widget);
QAction* submitCompatibilityReport = new QAction(tr("Submit a report"), widget);
compatibilityMenu->addAction(updateCompatibility);
compatibilityMenu->addAction(viewCompatibilityReport);
compatibilityMenu->addAction(submitCompatibilityReport);
menu.addMenu(compatibilityMenu);
compatibilityMenu->setEnabled(Config::getCompatibilityEnabled());
viewCompatibilityReport->setEnabled(!m_games[itemID].compatibility.url.isEmpty());
// Show menu.
auto selected = menu.exec(global_pos);
if (!selected) {
@ -360,6 +379,31 @@ public:
}
}
}
if (selected == updateCompatibility) {
m_compat_info->UpdateCompatibilityDatabase(widget, true);
}
if (selected == viewCompatibilityReport) {
if (!m_games[itemID].compatibility.url.isEmpty())
QDesktopServices::openUrl(QUrl(m_games[itemID].compatibility.url));
}
if (selected == submitCompatibilityReport) {
QUrl url = QUrl("https://github.com/shadps4-emu/shadps4-game-compatibility/issues/new");
QUrlQuery query;
query.addQueryItem("template", QString("game_compatibility.yml"));
query.addQueryItem(
"title", QString("%1 - %2").arg(QString::fromStdString(m_games[itemID].serial),
QString::fromStdString(m_games[itemID].name)));
query.addQueryItem("game-name", QString::fromStdString(m_games[itemID].name));
query.addQueryItem("game-code", QString::fromStdString(m_games[itemID].serial));
query.addQueryItem("game-version", QString::fromStdString(m_games[itemID].version));
query.addQueryItem("emulator-version", QString(Common::VERSION));
url.setQuery(query);
QDesktopServices::openUrl(url);
}
}
int GetRowIndex(QTreeWidget* treeWidget, QTreeWidgetItem* item) {

View File

@ -144,7 +144,7 @@ void MainWindow::CreateDockWindows() {
m_dock_widget.reset(new QDockWidget(tr("Game List"), this));
m_game_list_frame.reset(new GameListFrame(m_game_info, m_compat_info, this));
m_game_list_frame->setObjectName("gamelist");
m_game_grid_frame.reset(new GameGridFrame(m_game_info, this));
m_game_grid_frame.reset(new GameGridFrame(m_game_info, m_compat_info, this));
m_game_grid_frame->setObjectName("gamegridlist");
m_elf_viewer.reset(new ElfViewer(this));
m_elf_viewer->setObjectName("elflist");
@ -189,7 +189,9 @@ void MainWindow::CreateDockWindows() {
void MainWindow::LoadGameLists() {
// Update compatibility database
m_compat_info->UpdateCompatibilityDatabase(this);
if (Config::getCheckCompatibilityOnStartup()) {
m_compat_info->UpdateCompatibilityDatabase(this);
}
// Get game info from game folders.
m_game_info->GetGameInfo(this);
if (isTableList) {
@ -255,20 +257,26 @@ void MainWindow::CreateConnects() {
&MainWindow::StartGame);
connect(ui->configureAct, &QAction::triggered, this, [this]() {
auto settingsDialog = new SettingsDialog(m_physical_devices, this);
auto settingsDialog = new SettingsDialog(m_physical_devices, m_compat_info, this);
connect(settingsDialog, &SettingsDialog::LanguageChanged, this,
&MainWindow::OnLanguageChanged);
connect(settingsDialog, &SettingsDialog::CompatibilityChanged, this,
&MainWindow::RefreshGameTable);
settingsDialog->exec();
});
connect(ui->settingsButton, &QPushButton::clicked, this, [this]() {
auto settingsDialog = new SettingsDialog(m_physical_devices, this);
auto settingsDialog = new SettingsDialog(m_physical_devices, m_compat_info, this);
connect(settingsDialog, &SettingsDialog::LanguageChanged, this,
&MainWindow::OnLanguageChanged);
connect(settingsDialog, &SettingsDialog::CompatibilityChanged, this,
&MainWindow::RefreshGameTable);
settingsDialog->exec();
});
@ -564,7 +572,6 @@ void MainWindow::CreateConnects() {
}
void MainWindow::StartGame() {
isGameRunning = true;
BackgroundMusicPlayer::getInstance().stopMusic();
QString gamePath = "";
int table_mode = Config::getTableMode();
@ -587,13 +594,12 @@ void MainWindow::StartGame() {
}
if (gamePath != "") {
AddRecentFiles(gamePath);
Core::Emulator emulator;
const auto path = Common::FS::PathFromQString(gamePath);
if (!std::filesystem::exists(path)) {
QMessageBox::critical(nullptr, tr("Run Game"), QString(tr("Eboot.bin file not found")));
return;
}
emulator.Run(path);
StartEmulator(path);
}
}
@ -690,13 +696,12 @@ void MainWindow::BootGame() {
QString(tr("Only one file can be selected!")));
} else {
std::filesystem::path path = Common::FS::PathFromQString(fileNames[0]);
Core::Emulator emulator;
if (!std::filesystem::exists(path)) {
QMessageBox::critical(nullptr, tr("Run Game"),
QString(tr("Eboot.bin file not found")));
return;
}
emulator.Run(path);
StartEmulator(path);
}
}
}
@ -1050,12 +1055,11 @@ void MainWindow::CreateRecentGameActions() {
connect(m_recent_files_group, &QActionGroup::triggered, this, [this](QAction* action) {
auto gamePath = Common::FS::PathFromQString(action->text());
AddRecentFiles(action->text()); // Update the list.
Core::Emulator emulator;
if (!std::filesystem::exists(gamePath)) {
QMessageBox::critical(nullptr, tr("Run Game"), QString(tr("Eboot.bin file not found")));
return;
}
emulator.Run(gamePath);
StartEmulator(gamePath);
});
}
@ -1103,4 +1107,23 @@ bool MainWindow::eventFilter(QObject* obj, QEvent* event) {
}
}
return QMainWindow::eventFilter(obj, event);
}
}
void MainWindow::StartEmulator(std::filesystem::path path) {
if (isGameRunning) {
QMessageBox::critical(nullptr, tr("Run Game"), QString(tr("Game is already running!")));
return;
}
isGameRunning = true;
#ifdef __APPLE__
// SDL on macOS requires main thread.
Core::Emulator emulator;
emulator.Run(path);
#else
std::thread emulator_thread([=] {
Core::Emulator emulator;
emulator.Run(path);
});
emulator_thread.detach();
#endif
}

View File

@ -69,6 +69,7 @@ private:
void LoadTranslation();
void PlayBackgroundMusic();
QIcon RecolorIcon(const QIcon& icon, bool isWhite);
void StartEmulator(std::filesystem::path);
bool isIconBlack = false;
bool isTableList = true;
bool isGameRunning = false;

View File

@ -6,6 +6,8 @@
#include <QHoverEvent>
#include <common/version.h>
#include "common/config.h"
#include "qt_gui/compatibility_info.h"
#ifdef ENABLE_DISCORD_RPC
#include "common/discord_rpc_handler.h"
#endif
@ -54,7 +56,9 @@ QStringList languageNames = {"Arabic",
const QVector<int> languageIndexes = {21, 23, 14, 6, 18, 1, 12, 22, 2, 4, 25, 24, 29, 5, 0, 9,
15, 16, 17, 7, 26, 8, 11, 20, 3, 13, 27, 10, 19, 30, 28};
SettingsDialog::SettingsDialog(std::span<const QString> physical_devices, QWidget* parent)
SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
std::shared_ptr<CompatibilityInfoClass> m_compat_info,
QWidget* parent)
: QDialog(parent), ui(new Ui::SettingsDialog) {
ui->setupUi(this);
ui->tabWidgetSettings->setUsesScrollButtons(false);
@ -140,6 +144,16 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices, QWidge
ui->updaterGroupBox->setVisible(false);
ui->GUIgroupBox->setMaximumSize(265, 16777215);
#endif
connect(ui->updateCompatibilityButton, &QPushButton::clicked, this,
[this, parent, m_compat_info]() {
m_compat_info->UpdateCompatibilityDatabase(this, true);
emit CompatibilityChanged();
});
connect(ui->enableCompatibilityCheckBox, &QCheckBox::stateChanged, this, [this](int state) {
Config::setCompatibilityEnabled(state);
emit CompatibilityChanged();
});
}
// Input TAB
@ -194,6 +208,10 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices, QWidge
ui->updaterGroupBox->installEventFilter(this);
#endif
ui->GUIgroupBox->installEventFilter(this);
ui->disableTrophycheckBox->installEventFilter(this);
ui->enableCompatibilityCheckBox->installEventFilter(this);
ui->checkCompatibilityOnStartupCheckBox->installEventFilter(this);
ui->updateCompatibilityButton->installEventFilter(this);
// Input
ui->hideCursorGroupBox->installEventFilter(this);
@ -263,6 +281,8 @@ void SettingsDialog::LoadValuesFromConfig() {
ui->dumpShadersCheckBox->setChecked(toml::find_or<bool>(data, "GPU", "dumpShaders", false));
ui->nullGpuCheckBox->setChecked(toml::find_or<bool>(data, "GPU", "nullGpu", false));
ui->playBGMCheckBox->setChecked(toml::find_or<bool>(data, "General", "playBGM", false));
ui->disableTrophycheckBox->setChecked(
toml::find_or<bool>(data, "General", "isTrophyPopupDisabled", false));
ui->BGMVolumeSlider->setValue(toml::find_or<int>(data, "General", "BGMvolume", 50));
ui->discordRPCCheckbox->setChecked(
toml::find_or<bool>(data, "General", "enableDiscordRPC", true));
@ -282,6 +302,10 @@ void SettingsDialog::LoadValuesFromConfig() {
ui->vkSyncValidationCheckBox->setChecked(
toml::find_or<bool>(data, "Vulkan", "validation_sync", false));
ui->rdocCheckBox->setChecked(toml::find_or<bool>(data, "Vulkan", "rdocEnable", false));
ui->enableCompatibilityCheckBox->setChecked(
toml::find_or<bool>(data, "General", "compatibilityEnabled", false));
ui->checkCompatibilityOnStartupCheckBox->setChecked(
toml::find_or<bool>(data, "General", "checkCompatibilityOnStartup", false));
#ifdef ENABLE_UPDATER
ui->updateCheckBox->setChecked(toml::find_or<bool>(data, "General", "autoUpdate", false));
@ -397,6 +421,14 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) {
#endif
} else if (elementName == "GUIgroupBox") {
text = tr("GUIgroupBox");
} else if (elementName == "disableTrophycheckBox") {
text = tr("disableTrophycheckBox");
} else if (elementName == "enableCompatibilityCheckBox") {
text = tr("enableCompatibilityCheckBox");
} else if (elementName == "checkCompatibilityOnStartupCheckBox") {
text = tr("checkCompatibilityOnStartupCheckBox");
} else if (elementName == "updateCompatibilityButton") {
text = tr("updateCompatibilityButton");
}
// Input
@ -485,6 +517,7 @@ void SettingsDialog::UpdateSettings() {
Config::setBackButtonBehavior(TouchPadIndex[ui->backButtonBehaviorComboBox->currentIndex()]);
Config::setNeoMode(ui->ps4proCheckBox->isChecked());
Config::setFullscreenMode(ui->fullscreenCheckBox->isChecked());
Config::setisTrophyPopupDisabled(ui->disableTrophycheckBox->isChecked());
Config::setPlayBGM(ui->playBGMCheckBox->isChecked());
Config::setNeoMode(ui->ps4proCheckBox->isChecked());
Config::setLogType(ui->logTypeComboBox->currentText().toStdString());
@ -509,6 +542,8 @@ void SettingsDialog::UpdateSettings() {
Config::setRdocEnabled(ui->rdocCheckBox->isChecked());
Config::setAutoUpdate(ui->updateCheckBox->isChecked());
Config::setUpdateChannel(ui->updateComboBox->currentText().toStdString());
Config::setCompatibilityEnabled(ui->enableCompatibilityCheckBox->isChecked());
Config::setCheckCompatibilityOnStartup(ui->checkCompatibilityOnStartupCheckBox->isChecked());
#ifdef ENABLE_DISCORD_RPC
auto* rpc = Common::Singleton<DiscordRPCHandler::RPC>::Instance();
@ -549,4 +584,4 @@ void SettingsDialog::ResetInstallFolders() {
}
Config::setGameInstallDirs(settings_install_dirs_config);
}
}
}

View File

@ -3,6 +3,7 @@
#pragma once
#include <memory>
#include <span>
#include <QDialog>
#include <QGroupBox>
@ -10,6 +11,7 @@
#include "common/config.h"
#include "common/path_util.h"
#include "qt_gui/compatibility_info.h"
namespace Ui {
class SettingsDialog;
@ -18,7 +20,9 @@ class SettingsDialog;
class SettingsDialog : public QDialog {
Q_OBJECT
public:
explicit SettingsDialog(std::span<const QString> physical_devices, QWidget* parent = nullptr);
explicit SettingsDialog(std::span<const QString> physical_devices,
std::shared_ptr<CompatibilityInfoClass> m_compat_info,
QWidget* parent = nullptr);
~SettingsDialog();
bool eventFilter(QObject* obj, QEvent* event) override;
@ -28,6 +32,7 @@ public:
signals:
void LanguageChanged(const std::string& locale);
void CompatibilityChanged();
private:
void LoadValuesFromConfig();

View File

@ -11,7 +11,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>854</width>
<width>900</width>
<height>660</height>
</rect>
</property>
@ -59,9 +59,9 @@
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>832</width>
<height>431</height>
<y>-97</y>
<width>815</width>
<height>618</height>
</rect>
</property>
<property name="sizePolicy">
@ -306,7 +306,7 @@
</property>
<property name="minimumSize">
<size>
<width>275</width>
<width>265</width>
<height>0</height>
</size>
</property>
@ -469,6 +469,13 @@
<property name="bottomMargin">
<number>11</number>
</property>
<item>
<widget class="QCheckBox" name="disableTrophycheckBox">
<property name="text">
<string>Disable Trophy Pop-ups</string>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="GUIMusicLayout">
<property name="topMargin">
@ -561,41 +568,94 @@
</item>
</layout>
</item>
<item>
<widget class="QWidget" name="GUIwidgetSpacer" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>61</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="EmptyTabLayoutRight">
<item>
<spacer name="emptyHorizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
<layout class="QVBoxLayout" name="CompatTabLayoutRight" stretch="0">
<item alignment="Qt::AlignmentFlag::AlignTop">
<widget class="QGroupBox" name="CompatgroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeHint" stdset="0">
<property name="minimumSize">
<size>
<width>40</width>
<height>20</height>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
<property name="title">
<string>Game Compatibility</string>
</property>
<layout class="QVBoxLayout" name="CompatLayout">
<property name="topMargin">
<number>1</number>
</property>
<property name="bottomMargin">
<number>11</number>
</property>
<item>
<widget class="QCheckBox" name="enableCompatibilityCheckBox">
<property name="text">
<string>Display Compatibility Data</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkCompatibilityOnStartupCheckBox">
<property name="text">
<string>Update Compatibility Database On Startup</string>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="UpdateCompatLayout">
<property name="topMargin">
<number>1</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="updateCompatibilityButton">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>197</width>
<height>28</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Update Compatibility Database</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="inputTab">
<attribute name="title">
@ -696,7 +756,7 @@
<property name="bottomMargin">
<number>5</number>
</property>
<item alignment="Qt::AlignmentFlag::AlignHCenter">
<item>
<widget class="QSpinBox" name="idleTimeoutSpinBox">
<property name="enabled">
<bool>true</bool>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>إعدادات الواجهة</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>تشغيل موسيقى العنوان</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>تشغيل موسيقى العنوان:\nإذا كانت اللعبة تدعم ذلك، قم بتمكين تشغيل موسيقى خاصة عند اختيار اللعبة في واجهة المستخدم.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>سلوك زر العودة:\nيضبط زر العودة في وحدة التحكم ليحاكي الضغط على الموضع المحدد على لوحة اللمس في PS4.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>سيريال</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>GUI-Indstillinger</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Afspil titelsang</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Titelsmusikafspilning:\nHvis spillet understøtter det, aktiver speciel musik, når spillet vælges i brugergrænsefladen.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Tilbageknap Adfærd:\nIndstiller controllerens tilbageknap til at efterligne tryk den angivne position PS4 berøringsflade.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Seriel</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>GUI-Einstellungen</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Titelmusik abspielen</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Wiedergabe der Titelmusik:\nWenn das Spiel dies unterstützt, wird beim Auswählen des Spiels in der Benutzeroberfläche spezielle Musik abgespielt.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Zurück-Button Verhalten:\nStellt die Zurück-Taste des Controllers so ein, dass sie das Antippen der angegebenen Position auf dem PS4-Touchpad emuliert.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Seriennummer</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>Ρυθμίσεις GUI</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Αναπαραγωγή μουσικής τίτλου</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Αναπαραγωγή Μουσικής Τίτλων:\nΕάν το παιχνίδι το υποστηρίζει, ενεργοποιεί ειδική μουσική κατά την επιλογή του παιχνιδιού από τη διεπαφή χρήστη.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Συμπεριφορά Κουμπιού Επιστροφής:\nΟρίζει το κουμπί επιστροφής του ελεγκτή να προσομοιώνει το πάτημα της καθορισμένης θέσης στην οθόνη αφής PS4.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Σειριακός αριθμός</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>GUI Settings</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Play title music</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Serial</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>Configuraciones de la Interfaz</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Reproducir la música de apertura</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Reproducir Música del Título:\nSi un juego lo admite, habilita la reproducción de música especial al seleccionar el juego en la interfaz gráfica.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Comportamiento del Botón Atrás:\nEstablece el botón atrás del controlador para emular el toque en la posición especificada en el touchpad del PS4.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Numero de serie</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>تنظیمات رابط کاربری</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>پخش موسیقی عنوان</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>رفتار دکمه برگشت:\nدکمه برگشت کنترلر را طوری تنظیم می کند که ضربه زدن روی موقعیت مشخص شده روی صفحه لمسی PS4 را شبیه سازی کند.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>سریال</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>GUI-Asetukset</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Soita otsikkomusiikkia</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Soita Otsikkomusiikkia:\nJos peli tukee sitä, ota käyttöön erityisen musiikin soittaminen pelin valinnan yhteydessä käyttöliittymässä.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Takaisin-napin käyttäytyminen:\nAsettaa ohjaimen takaisin-napin jäljittelemään kosketusta PS4:n kosketuslevyn määritettyyn kohtaan.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Sarjanumero</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>Paramètres de l'interface</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Lire la musique du titre</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Jouer de la musique de titre:\nSi le jeu le prend en charge, cela active la musique spéciale lorsque vous sélectionnez le jeu dans l'interface utilisateur.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Comportement du bouton retour:\nDéfinit le bouton de retour de la manette pour imiter le toucher de la position spécifiée sur le pavé tactile PS4.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Série</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Jamais joué</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>GUI Beállítások</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Címzene lejátszása</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Játék címzene lejátszása:\nHa a játék támogatja, engedélyezze egy speciális zene lejátszását, amikor a játékot kiválasztja a GUI-ban.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Vissza gomb viselkedés:\nBeállítja a vezérlő vissza gombját, hogy utánozza a PS4 érintőpadján megadott pozíció megérintését.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Sorozatszám</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>
@ -1493,4 +1573,4 @@
<translation>A frissítési szkript fájl létrehozása nem sikerült</translation>
</message>
</context>
</TS>
</TS>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>Pengaturan GUI</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Putar musik judul</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Putar Musik Judul Permainan:\nJika permainan mendukungnya, aktifkan pemutaran musik khusus saat memilih permainan di GUI.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Perilaku Tombol Kembali:\nMengatur tombol kembali pada pengontrol untuk meniru ketukan di posisi yang ditentukan di touchpad PS4.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Serial</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>Impostazioni GUI</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Riproduci musica del titolo</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Riproduci Musica del Titolo:\nSe un gioco lo supporta, attiva la riproduzione di musica speciale quando selezioni il gioco nell'interfaccia grafica.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Comportamento del pulsante Indietro:\nImposta il pulsante Indietro del controller per emulare il tocco sulla posizione specificata sul touchpad PS4.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Seriale</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>
@ -1493,4 +1573,4 @@
<translation>Impossibile creare il file di script di aggiornamento</translation>
</message>
</context>
</TS>
</TS>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>GUI設定</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation></translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>:\nゲームがそれをサポートしている場合GUIでゲームを選択したときに特別な音楽を再生することを有効にします</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>:\nコントローラーの戻るボタンをPS4のタッチパッドの指定された位置をタッチするように設定します</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation></translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>GUI Settings</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Play title music</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Serial</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>GUI Nustatymai</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Groti antraštės muziką</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Groti antraščių muziką:\nJei žaidimas tai palaiko, įjungia specialios muzikos grojimą, kai pasirinkite žaidimą GUI.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Atgal mygtuko elgesys:\nNustato valdiklio atgal mygtuką imituoti paspaudimą nurodytoje vietoje PS4 jutiklinėje plokštėje.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Serijinis numeris</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>GUI-Innstillinger</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Spill tittelmusikk</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Spille tittelmusikk:\nHvis et spill støtter det, aktiveres det spesiell musikk når du velger spillet i menyen.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Atferd for tilbaketasten:\nSetter tilbaketasten kontrolleren til å imitere et trykk den angitte posisjonen PS4s berøringsplate.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Serienummer</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>
@ -1493,4 +1573,4 @@
<translation>Kunne ikke opprette oppdateringsskriptfilen</translation>
</message>
</context>
</TS>
</TS>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>GUI-Instellingen</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Titelmuziek afspelen</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Speel titelsong:\nAls een game dit ondersteunt, wordt speciale muziek afgespeeld wanneer je het spel in de GUI selecteert.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Gedrag van de terugknop:\nStelt de terugknop van de controller in om een aanraking op de opgegeven positie op de PS4-touchpad na te bootsen.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Serienummer</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>Ustawienia Interfejsu</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Odtwórz muzykę tytułową</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Odtwórz muzykę tytułową:\nJeśli gra to obsługuje, aktywuje odtwarzanie specjalnej muzyki podczas wybierania gry w GUI.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Zachowanie przycisku Wstecz:\nUstawia przycisk Wstecz kontrolera tak, aby emulował dotknięcie określonego miejsca na panelu dotykowym PS4.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Numer seryjny</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>Configurações da Interface</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Desabilitar Pop-ups dos Troféus</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Reproduzir música de abertura</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Atualizar Compatibilidade ao Inicializar</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Compatibilidade dos Jogos</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Exibir Dados de Compatibilidade</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Atualizar Lista de Compatibilidade</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Reproduzir música de abertura:\nSe o jogo suportar, ativa a reprodução de uma música especial ao selecionar o jogo na interface do menu.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Desabilitar pop-ups dos troféus:\nDesabilite notificações de troféus no jogo. O progresso do troféu ainda pode ser rastreado usando o Trophy Viewer (clique com o botão direito do mouse no jogo na janela principal).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Comportamento do botão Voltar:\nDefine o botão Voltar do controle para emular o toque na posição especificada no touchpad do PS4.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Exibir Dados de Compatibilidade:\nExibe informações de compatibilidade dos jogos na janela principal.\nHabilitar "Atualizar Compatibilidade ao Inicializar" para obter informações atualizadas.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Atualizar Compatibilidade ao inicializar:\nAtualiza automaticamente o banco de dados de compatibilidade quando o SHADPS4 é iniciado.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Atualizar Lista de Compatibilidade:\nAtualizar imediatamente o banco de dados de compatibilidade.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Serial</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibilidade</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="36"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Nunca jogado</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibilidade não testada</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Jogo não inicializa corretamente / trava o emulador</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>O jogo inicializa, mas exibe apenas uma tela vazia</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Jogo exibe imagem mas não passa do menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>O jogo tem falhas que interrompem o jogo ou desempenho injogável</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>O jogo pode ser concluído com desempenho jogável e sem grandes falhas</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>Setări GUI</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Redă muzica titlului</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Redă muzica titlului:\nDacă un joc o suportă, activează redarea muzicii speciale când selectezi jocul în GUI.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Comportamentul butonului înapoi:\nSetează butonul înapoi al controlerului imite atingerea poziției specificate pe touchpad-ul PS4.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Serie</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>Интерфейс</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Играть заглавную музыку</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Играть заглавную музыку:\nВключает воспроизведение специальной музыки при выборе игры в списке, если она это поддерживает.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Поведение кнопки «Назад»:\nНастраивает кнопку «Назад» контроллера на эмуляцию нажатия на указанную область на сенсорной панели контроллера PS4.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Серийный номер</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>Cilësimet e GUI</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Luaj muzikën e titullit</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Luaj muzikën e titullit:\nNëse një lojë e mbështet, aktivizohet luajtja e muzikës veçantë kur zgjidhësh lojën GUI.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Sjellja e butonit mbrapa:\nLejon përcaktohet se cilën pjesë tastierës prekëse do imitojë një prekje butoni mprapa.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Seriku</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>
@ -1493,4 +1573,4 @@
<translation>Krijimi i skedarit skript përditësimit dështoi</translation>
</message>
</context>
</TS>
</TS>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>GUI Ayarları</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Başlık müziğini çal</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Başlık Müziklerini Çal:\nEğer bir oyun bunu destekliyorsa, GUI'de oyunu seçtiğinizde özel müziklerin çalmasını etkinleştirir.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Geri düğmesi davranışı:\nKontrol cihazındaki geri düğmesini, PS4'ün dokunmatik panelindeki belirlenen noktaya dokunmak için ayarlar.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Seri Numarası</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>
@ -1493,4 +1573,4 @@
<translation>Güncelleme betiği dosyası oluşturulamadı</translation>
</message>
</context>
</TS>
</TS>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>Інтерфейс</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Програвати заголовну музику</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Грати заголовну музику:\nВмикає відтворення спеціальної музики під час вибору гри в списку, якщо вона це підтримує.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Поведінка кнопки «Назад»:\nНалаштовує кнопку «Назад» контролера на емуляцію натискання на зазначену область на сенсорній панелі контролера PS4.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Серійний номер</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation>Cài đt GUI</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation>Phát nhạc tiêu đ</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>Phát nhạc tiêu đ trò chơi:\nNếu một trò chơi hỗ trợ điều này, hãy kích hoạt phát nhạc đc biệt khi bạn chọn trò chơi trong GUI.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>Hành vi nút quay lại:\nĐặt nút quay lại của tay cầm đ phỏng việc chạm vào vị trí đã chỉ đnh trên touchpad của PS4.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation>Số seri</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation></translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation></translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>:\n如果游戏支持</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>:\n设置控制器的返回按钮以模拟在 PS4 </translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation></translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>
@ -1493,4 +1573,4 @@
<translation></translation>
</message>
</context>
</TS>
</TS>

View File

@ -672,11 +672,36 @@
<source>GUI Settings</source>
<translation></translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="475"/>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="375"/>
<source>Play title music</source>
<translation></translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database On Startup</source>
<translation>Update Compatibility Database On Startup</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Game Compatibility</source>
<translation>Game Compatibility</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Display Compatibility Data</source>
<translation>Display Compatibility Data</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Update Compatibility Database</source>
<translation>Update Compatibility Database</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="394"/>
<source>Volume</source>
@ -1201,6 +1226,11 @@
<source>GUIgroupBox</source>
<translation>:\n如果遊戲支持GUI中選擇遊戲時播放特殊音樂</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="267"/>
<source>disableTrophycheckBox</source>
<translation>Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window).</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="450"/>
<source>hideCursorGroupBox</source>
@ -1216,6 +1246,21 @@
<source>backButtonBehaviorGroupBox</source>
<translation>:\n設定控制器的返回按鈕模擬在 PS4 </translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>enableCompatibilityCheckBox</source>
<translation>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>checkCompatibilityOnStartupCheckBox</source>
<translation>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>updateCompatibilityButton</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="70"/>
<source>Never</source>
@ -1329,6 +1374,11 @@
<source>Serial</source>
<translation></translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility</source>
<translation>Compatibility</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
@ -1364,6 +1414,36 @@
<source>Never Played</source>
<translation>Never Played</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Compatibility is untested</source>
<translation>Compatibility is untested</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game does not initialize properly / crashes the emulator</source>
<translation>Game does not initialize properly / crashes the emulator</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game boots, but only displays a blank screen</source>
<translation>Game boots, but only displays a blank screen</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game displays an image but does not go past the menu</source>
<translation>Game displays an image but does not go past the menu</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game has game-breaking glitches or unplayable performance</source>
<translation>Game has game-breaking glitches or unplayable performance</translation>
</message>
<message>
<location filename="../game_list_frame.cpp"/>
<source>Game can be completed with playable performance and no major glitches</source>
<translation>Game can be completed with playable performance and no major glitches</translation>
</message>
</context>
<context>
<name>CheckUpdate</name>

View File

@ -1,5 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <span>
#include <type_traits>
#include <utility>

View File

@ -168,22 +168,6 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords,
return texture.is_integer ? ctx.OpBitcast(ctx.F32[4], texels) : texels;
}
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod,
const IR::Value& offset, Id ms) {
const auto& texture = ctx.images[handle & 0xFFFF];
const Id image = ctx.OpLoad(texture.image_type, texture.id);
const Id result_type = texture.data_types->Get(4);
ImageOperands operands;
operands.AddOffset(ctx, offset);
operands.Add(spv::ImageOperandsMask::Lod, lod);
operands.Add(spv::ImageOperandsMask::Sample, ms);
const Id texel =
texture.is_storage
? ctx.OpImageRead(result_type, image, coords, operands.mask, operands.operands)
: ctx.OpImageFetch(result_type, image, coords, operands.mask, operands.operands);
return texture.is_integer ? ctx.OpBitcast(ctx.F32[4], texel) : texel;
}
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, u32 handle, Id lod, bool has_mips) {
const auto& texture = ctx.images[handle & 0xFFFF];
const Id image = ctx.OpLoad(texture.image_type, texture.id);
@ -236,15 +220,34 @@ Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id
return texture.is_integer ? ctx.OpBitcast(ctx.F32[4], sample) : sample;
}
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id lod) {
UNREACHABLE_MSG("SPIR-V Instruction");
}
void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, Id color) {
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, Id ms) {
const auto& texture = ctx.images[handle & 0xFFFF];
const Id image = ctx.OpLoad(texture.image_type, texture.id);
const Id color_type = texture.data_types->Get(4);
ImageOperands operands;
operands.Add(spv::ImageOperandsMask::Sample, ms);
Id texel;
if (!texture.is_storage) {
operands.Add(spv::ImageOperandsMask::Lod, lod);
texel = ctx.OpImageFetch(color_type, image, coords, operands.mask, operands.operands);
} else {
if (ctx.profile.supports_image_load_store_lod) {
operands.Add(spv::ImageOperandsMask::Lod, lod);
} else if (Sirit::ValidId(lod)) {
LOG_WARNING(Render, "Image read with LOD not supported by driver");
}
texel = ctx.OpImageRead(color_type, image, coords, operands.mask, operands.operands);
}
return !texture.is_integer ? ctx.OpBitcast(ctx.U32[4], texel) : texel;
}
void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, Id ms,
Id color) {
const auto& texture = ctx.images[handle & 0xFFFF];
const Id image = ctx.OpLoad(texture.image_type, texture.id);
const Id color_type = texture.data_types->Get(4);
ImageOperands operands;
operands.Add(spv::ImageOperandsMask::Sample, ms);
if (ctx.profile.supports_image_load_store_lod) {
operands.Add(spv::ImageOperandsMask::Lod, lod);
} else if (Sirit::ValidId(lod)) {

View File

@ -395,14 +395,13 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords,
const IR::Value& offset);
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords,
const IR::Value& offset, Id dref);
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod,
const IR::Value& offset, Id ms);
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, u32 handle, Id lod, bool skip_mips);
Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords);
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id derivatives_dx,
Id derivatives_dy, const IR::Value& offset, const IR::Value& lod_clamp);
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id lod);
void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, Id color);
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, Id ms);
void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, Id ms,
Id color);
Id EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);

View File

@ -0,0 +1,329 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <sirit/sirit.h>
#include "shader_recompiler/backend/spirv/emit_spirv_quad_rect.h"
#include "shader_recompiler/runtime_info.h"
namespace Shader::Backend::SPIRV {
using Sirit::Id;
constexpr u32 SPIRV_VERSION_1_5 = 0x00010500;
struct QuadRectListEmitter : public Sirit::Module {
explicit QuadRectListEmitter(const FragmentRuntimeInfo& fs_info_)
: Sirit::Module{SPIRV_VERSION_1_5}, fs_info{fs_info_}, inputs{fs_info_.num_inputs},
outputs{fs_info_.num_inputs} {
void_id = TypeVoid();
bool_id = TypeBool();
float_id = TypeFloat(32);
uint_id = TypeUInt(32U);
int_id = TypeInt(32U, true);
bvec2_id = TypeVector(bool_id, 2);
vec2_id = TypeVector(float_id, 2);
vec3_id = TypeVector(float_id, 3);
vec4_id = TypeVector(float_id, 4);
float_one = Constant(float_id, 1.0f);
float_min_one = Constant(float_id, -1.0f);
int_zero = Constant(int_id, 0);
const Id float_arr{TypeArray(float_id, Constant(uint_id, 1U))};
gl_per_vertex_type = TypeStruct(vec4_id, float_id, float_arr, float_arr);
Decorate(gl_per_vertex_type, spv::Decoration::Block);
MemberDecorate(gl_per_vertex_type, 0U, spv::Decoration::BuiltIn,
static_cast<u32>(spv::BuiltIn::Position));
MemberDecorate(gl_per_vertex_type, 1U, spv::Decoration::BuiltIn,
static_cast<u32>(spv::BuiltIn::PointSize));
MemberDecorate(gl_per_vertex_type, 2U, spv::Decoration::BuiltIn,
static_cast<u32>(spv::BuiltIn::ClipDistance));
MemberDecorate(gl_per_vertex_type, 3U, spv::Decoration::BuiltIn,
static_cast<u32>(spv::BuiltIn::CullDistance));
}
/// Emits tessellation control shader for interpolating the 4th vertex of rectange primitive
void EmitRectListTCS() {
DefineEntry(spv::ExecutionModel::TessellationControl);
// Set passthrough tessellation factors
const Id output_float_id{TypePointer(spv::StorageClass::Output, float_id)};
for (int i = 0; i < 4; i++) {
const Id ptr{OpAccessChain(output_float_id, gl_tess_level_outer, Int(i))};
OpStore(ptr, float_one);
}
for (int i = 0; i < 2; i++) {
const Id ptr{OpAccessChain(output_float_id, gl_tess_level_inner, Int(i))};
OpStore(ptr, float_one);
}
const Id input_vec4{TypePointer(spv::StorageClass::Input, vec4_id)};
const Id output_vec4{TypePointer(spv::StorageClass::Output, vec4_id)};
// Emit interpolation block of the 4th vertex in rect.
// Load positions
std::array<Id, 3> pos;
for (int i = 0; i < 3; i++) {
pos[i] = OpLoad(vec4_id, OpAccessChain(input_vec4, gl_in, Int(i), int_zero));
}
std::array<Id, 3> point_coord_equal;
for (int i = 0; i < 3; i++) {
// point_coord_equal[i] = equal(gl_in[i].gl_Position.xy, gl_in[(i + 1) %
// 3].gl_Position.xy);
const Id pos_l_xy{OpVectorShuffle(vec2_id, pos[i], pos[i], 0, 1)};
const Id pos_r_xy{OpVectorShuffle(vec2_id, pos[(i + 1) % 3], pos[(i + 1) % 3], 0, 1)};
point_coord_equal[i] = OpFOrdEqual(bvec2_id, pos_l_xy, pos_r_xy);
}
std::array<Id, 3> bary_coord;
std::array<Id, 3> is_edge_vertex;
for (int i = 0; i < 3; i++) {
// bool xy_equal = point_coord_equal[i].x && point_coord_equal[(i + 2) % 3].y;
const Id xy_equal{
OpLogicalAnd(bool_id, OpCompositeExtract(bool_id, point_coord_equal[i], 0),
OpCompositeExtract(bool_id, point_coord_equal[(i + 2) % 3], 1))};
// bool yx_equal = point_coord_equal[i].y && point_coord_equal[(i + 2) % 3].x;
const Id yx_equal{
OpLogicalAnd(bool_id, OpCompositeExtract(bool_id, point_coord_equal[i], 1),
OpCompositeExtract(bool_id, point_coord_equal[(i + 2) % 3], 0))};
// bary_coord[i] = (xy_equal || yx_equal) ? -1.f : 1.f;
is_edge_vertex[i] = OpLogicalOr(bool_id, xy_equal, yx_equal);
bary_coord[i] = OpSelect(float_id, is_edge_vertex[i], float_min_one, float_one);
}
const auto interpolate = [&](Id v0, Id v1, Id v2) {
// return v0 * bary_coord.x + v1 * bary_coord.y + v2 * bary_coord.z;
const Id p0{OpVectorTimesScalar(vec4_id, v0, bary_coord[0])};
const Id p1{OpVectorTimesScalar(vec4_id, v1, bary_coord[1])};
const Id p2{OpVectorTimesScalar(vec4_id, v2, bary_coord[2])};
return OpFAdd(vec4_id, p0, OpFAdd(vec4_id, p1, p2));
};
// int vertex_index_id = is_edge_vertex[1] ? 1 : (is_edge_vertex[2] ? 2 : 0);
Id vertex_index{OpSelect(int_id, is_edge_vertex[2], Int(2), Int(0))};
vertex_index = OpSelect(int_id, is_edge_vertex[1], Int(1), vertex_index);
// int index = (vertex_index_id + gl_InvocationID) % 3;
const Id invocation_id{OpLoad(int_id, gl_invocation_id)};
const Id invocation_3{OpIEqual(bool_id, invocation_id, Int(3))};
const Id index{OpSMod(int_id, OpIAdd(int_id, vertex_index, invocation_id), Int(3))};
// gl_out[gl_InvocationID].gl_Position = gl_InvocationID == 3 ? pos3 :
// gl_in[index].gl_Position;
const Id pos3{interpolate(pos[0], pos[1], pos[2])};
const Id in_ptr{OpAccessChain(input_vec4, gl_in, index, Int(0))};
const Id position{OpSelect(vec4_id, invocation_3, pos3, OpLoad(vec4_id, in_ptr))};
OpStore(OpAccessChain(output_vec4, gl_out, invocation_id, Int(0)), position);
// Set attributes
for (int i = 0; i < inputs.size(); i++) {
// vec4 in_paramN3 = interpolate(bary_coord, in_paramN[0], in_paramN[1], in_paramN[2]);
const Id v0{OpLoad(vec4_id, OpAccessChain(input_vec4, inputs[i], Int(0)))};
const Id v1{OpLoad(vec4_id, OpAccessChain(input_vec4, inputs[i], Int(1)))};
const Id v2{OpLoad(vec4_id, OpAccessChain(input_vec4, inputs[i], Int(2)))};
const Id in_param3{interpolate(v0, v1, v2)};
// out_paramN[gl_InvocationID] = gl_InvocationID == 3 ? in_paramN3 : in_paramN[index];
const Id in_param{OpLoad(vec4_id, OpAccessChain(input_vec4, inputs[i], index))};
const Id out_param{OpSelect(vec4_id, invocation_3, in_param3, in_param)};
OpStore(OpAccessChain(output_vec4, outputs[i], invocation_id), out_param);
}
OpReturn();
OpFunctionEnd();
}
/// Emits a passthrough quad tessellation control shader that outputs 4 control points.
void EmitQuadListTCS() {
DefineEntry(spv::ExecutionModel::TessellationControl);
const Id array_type{TypeArray(int_id, Int(4))};
const Id values{ConstantComposite(array_type, Int(1), Int(2), Int(0), Int(3))};
const Id indices{AddLocalVariable(TypePointer(spv::StorageClass::Function, array_type),
spv::StorageClass::Function, values)};
// Set passthrough tessellation factors
const Id output_float{TypePointer(spv::StorageClass::Output, float_id)};
for (int i = 0; i < 4; i++) {
const Id ptr{OpAccessChain(output_float, gl_tess_level_outer, Int(i))};
OpStore(ptr, float_one);
}
for (int i = 0; i < 2; i++) {
const Id ptr{OpAccessChain(output_float, gl_tess_level_inner, Int(i))};
OpStore(ptr, float_one);
}
const Id input_vec4{TypePointer(spv::StorageClass::Input, vec4_id)};
const Id output_vec4{TypePointer(spv::StorageClass::Output, vec4_id)};
const Id func_int{TypePointer(spv::StorageClass::Function, int_id)};
const Id invocation_id{OpLoad(int_id, gl_invocation_id)};
const Id index{OpLoad(int_id, OpAccessChain(func_int, indices, invocation_id))};
// gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
const Id in_position{OpLoad(vec4_id, OpAccessChain(input_vec4, gl_in, index, Int(0)))};
OpStore(OpAccessChain(output_vec4, gl_out, invocation_id, Int(0)), in_position);
for (int i = 0; i < inputs.size(); i++) {
// out_paramN[gl_InvocationID] = in_paramN[gl_InvocationID];
const Id in_param{OpLoad(vec4_id, OpAccessChain(input_vec4, inputs[i], index))};
OpStore(OpAccessChain(output_vec4, outputs[i], invocation_id), in_param);
}
OpReturn();
OpFunctionEnd();
}
/// Emits a passthrough quad tessellation evaluation shader that outputs 4 control points.
void EmitPassthroughTES() {
DefineEntry(spv::ExecutionModel::TessellationEvaluation);
// const int index = int(gl_TessCoord.y) * 2 + int(gl_TessCoord.x);
const Id input_float{TypePointer(spv::StorageClass::Input, float_id)};
const Id tess_coord_x{OpLoad(float_id, OpAccessChain(input_float, gl_tess_coord, Int(0)))};
const Id tess_coord_y{OpLoad(float_id, OpAccessChain(input_float, gl_tess_coord, Int(1)))};
const Id index{OpIAdd(int_id, OpIMul(int_id, OpConvertFToS(int_id, tess_coord_y), Int(2)),
OpConvertFToS(int_id, tess_coord_x))};
// gl_Position = gl_in[index].gl_Position;
const Id input_vec4{TypePointer(spv::StorageClass::Input, vec4_id)};
const Id output_vec4{TypePointer(spv::StorageClass::Output, vec4_id)};
const Id position{OpLoad(vec4_id, OpAccessChain(input_vec4, gl_in, index, Int(0)))};
OpStore(OpAccessChain(output_vec4, gl_per_vertex, Int(0)), position);
// out_paramN = in_paramN[index];
for (int i = 0; i < inputs.size(); i++) {
const Id param{OpLoad(vec4_id, OpAccessChain(input_vec4, inputs[i], index))};
OpStore(outputs[i], param);
}
OpReturn();
OpFunctionEnd();
}
private:
Id Int(s32 value) {
return Constant(int_id, value);
}
Id AddInput(Id type) {
const Id input{AddGlobalVariable(TypePointer(spv::StorageClass::Input, type),
spv::StorageClass::Input)};
interfaces.push_back(input);
return input;
}
Id AddOutput(Id type) {
const Id output{AddGlobalVariable(TypePointer(spv::StorageClass::Output, type),
spv::StorageClass::Output)};
interfaces.push_back(output);
return output;
}
void DefineEntry(spv::ExecutionModel model) {
AddCapability(spv::Capability::Shader);
AddCapability(spv::Capability::Tessellation);
const Id void_function{TypeFunction(void_id)};
main = OpFunction(void_id, spv::FunctionControlMask::MaskNone, void_function);
if (model == spv::ExecutionModel::TessellationControl) {
AddExecutionMode(main, spv::ExecutionMode::OutputVertices, 4U);
} else {
AddExecutionMode(main, spv::ExecutionMode::Quads);
AddExecutionMode(main, spv::ExecutionMode::SpacingEqual);
AddExecutionMode(main, spv::ExecutionMode::VertexOrderCw);
}
DefineInputs(model);
DefineOutputs(model);
AddEntryPoint(model, main, "main", interfaces);
AddLabel(OpLabel());
}
void DefineOutputs(spv::ExecutionModel model) {
if (model == spv::ExecutionModel::TessellationControl) {
const Id gl_per_vertex_array{TypeArray(gl_per_vertex_type, Constant(uint_id, 4U))};
gl_out = AddOutput(gl_per_vertex_array);
const Id arr2_id{TypeArray(float_id, Constant(uint_id, 2U))};
gl_tess_level_inner = AddOutput(arr2_id);
Decorate(gl_tess_level_inner, spv::Decoration::BuiltIn, spv::BuiltIn::TessLevelInner);
Decorate(gl_tess_level_inner, spv::Decoration::Patch);
const Id arr4_id{TypeArray(float_id, Constant(uint_id, 4U))};
gl_tess_level_outer = AddOutput(arr4_id);
Decorate(gl_tess_level_outer, spv::Decoration::BuiltIn, spv::BuiltIn::TessLevelOuter);
Decorate(gl_tess_level_outer, spv::Decoration::Patch);
} else {
gl_per_vertex = AddOutput(gl_per_vertex_type);
}
for (int i = 0; i < fs_info.num_inputs; i++) {
outputs[i] = AddOutput(model == spv::ExecutionModel::TessellationControl
? TypeArray(vec4_id, Int(4))
: vec4_id);
Decorate(outputs[i], spv::Decoration::Location, fs_info.inputs[i].param_index);
}
}
void DefineInputs(spv::ExecutionModel model) {
if (model == spv::ExecutionModel::TessellationEvaluation) {
gl_tess_coord = AddInput(vec3_id);
Decorate(gl_tess_coord, spv::Decoration::BuiltIn, spv::BuiltIn::TessCoord);
} else {
gl_invocation_id = AddInput(int_id);
Decorate(gl_invocation_id, spv::Decoration::BuiltIn, spv::BuiltIn::InvocationId);
}
const Id gl_per_vertex_array{TypeArray(gl_per_vertex_type, Constant(uint_id, 32U))};
gl_in = AddInput(gl_per_vertex_array);
const Id float_arr{TypeArray(vec4_id, Int(32))};
for (int i = 0; i < fs_info.num_inputs; i++) {
inputs[i] = AddInput(float_arr);
Decorate(inputs[i], spv::Decoration::Location, fs_info.inputs[i].param_index);
}
}
private:
FragmentRuntimeInfo fs_info;
Id main;
Id void_id;
Id bool_id;
Id float_id;
Id uint_id;
Id int_id;
Id bvec2_id;
Id vec2_id;
Id vec3_id;
Id vec4_id;
Id float_one;
Id float_min_one;
Id int_zero;
Id gl_per_vertex_type;
Id gl_in;
union {
Id gl_out;
Id gl_per_vertex;
};
Id gl_tess_level_inner;
Id gl_tess_level_outer;
union {
Id gl_tess_coord;
Id gl_invocation_id;
};
std::vector<Id> inputs;
std::vector<Id> outputs;
std::vector<Id> interfaces;
};
std::vector<u32> EmitAuxilaryTessShader(AuxShaderType type, const FragmentRuntimeInfo& fs_info) {
QuadRectListEmitter ctx{fs_info};
switch (type) {
case AuxShaderType::RectListTCS:
ctx.EmitRectListTCS();
break;
case AuxShaderType::QuadListTCS:
ctx.EmitQuadListTCS();
break;
case AuxShaderType::PassthroughTES:
ctx.EmitPassthroughTES();
break;
}
return ctx.Assemble();
}
} // namespace Shader::Backend::SPIRV

View File

@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <vector>
#include "common/types.h"
namespace Shader {
struct FragmentRuntimeInfo;
}
namespace Shader::Backend::SPIRV {
enum class AuxShaderType : u32 {
RectListTCS,
QuadListTCS,
PassthroughTES,
};
[[nodiscard]] std::vector<u32> EmitAuxilaryTessShader(AuxShaderType type,
const FragmentRuntimeInfo& fs_info);
} // namespace Shader::Backend::SPIRV

View File

@ -772,7 +772,7 @@ Id ImageType(EmitContext& ctx, const ImageResource& desc, Id sampled_type) {
const auto image = desc.GetSharp(ctx.info);
const auto format = desc.is_atomic ? GetFormat(image) : spv::ImageFormat::Unknown;
const auto type = image.GetBoundType();
const u32 sampled = desc.is_storage ? 2 : 1;
const u32 sampled = desc.IsStorage(image) ? 2 : 1;
switch (type) {
case AmdGpu::ImageType::Color1D:
return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, false, false, sampled, format);
@ -800,6 +800,7 @@ void EmitContext::DefineImagesAndSamplers() {
const auto sharp = image_desc.GetSharp(info);
const auto nfmt = sharp.GetNumberFmt();
const bool is_integer = AmdGpu::IsInteger(nfmt);
const bool is_storage = image_desc.IsStorage(sharp);
const VectorIds& data_types = GetAttributeType(*this, nfmt);
const Id sampled_type = data_types[1];
const Id image_type{ImageType(*this, image_desc, sampled_type)};
@ -811,11 +812,11 @@ void EmitContext::DefineImagesAndSamplers() {
images.push_back({
.data_types = &data_types,
.id = id,
.sampled_type = image_desc.is_storage ? sampled_type : TypeSampledImage(image_type),
.sampled_type = is_storage ? sampled_type : TypeSampledImage(image_type),
.pointer_type = pointer_type,
.image_type = image_type,
.is_integer = is_integer,
.is_storage = image_desc.is_storage,
.is_storage = is_storage,
});
interfaces.push_back(id);
}

View File

@ -1,7 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "shader_recompiler/frontend/translate/translate.h"
#include "shader_recompiler/ir/reg.h"
#include "shader_recompiler/profile.h"
#include "shader_recompiler/runtime_info.h"
namespace Shader::Gcn {
@ -203,6 +205,7 @@ void Translator::DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool strid
addr, ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0)));
ir.WriteShared(bit_size, ir.GetVectorReg(data0), addr0);
}
emit_ds_read_barrier = true;
}
void Translator::DS_SWIZZLE_B32(const GcnInst& inst) {
@ -219,6 +222,11 @@ void Translator::DS_SWIZZLE_B32(const GcnInst& inst) {
void Translator::DS_READ(int bit_size, bool is_signed, bool is_pair, bool stride64,
const GcnInst& inst) {
if (emit_ds_read_barrier && profile.needs_lds_barriers) {
ir.Barrier();
emit_ds_read_barrier = false;
}
const IR::U32 addr{ir.GetVectorReg(IR::VectorReg(inst.src[0].code))};
IR::VectorReg dst_reg{inst.dst[0].code};
if (is_pair) {

View File

@ -306,6 +306,7 @@ private:
const RuntimeInfo& runtime_info;
const Profile& profile;
bool opcode_missing = false;
bool emit_ds_read_barrier = false;
};
void Translate(IR::Block* block, u32 block_base, std::span<const GcnInst> inst_list, Info& info,

View File

@ -420,13 +420,13 @@ void Translator::IMAGE_LOAD(bool has_mip, const GcnInst& inst) {
IR::TextureInstInfo info{};
info.has_lod.Assign(has_mip);
const IR::Value texel = ir.ImageFetch(handle, body, {}, {}, {}, info);
const IR::Value texel = ir.ImageRead(handle, body, {}, {}, info);
for (u32 i = 0; i < 4; i++) {
if (((mimg.dmask >> i) & 1) == 0) {
continue;
}
IR::F32 value = IR::F32{ir.CompositeExtract(texel, i)};
IR::U32 value = IR::U32{ir.CompositeExtract(texel, i)};
ir.SetVectorReg(dest_reg++, value);
}
}
@ -454,7 +454,7 @@ void Translator::IMAGE_STORE(bool has_mip, const GcnInst& inst) {
comps.push_back(ir.GetVectorReg<IR::F32>(data_reg++));
}
const IR::Value value = ir.CompositeConstruct(comps[0], comps[1], comps[2], comps[3]);
ir.ImageWrite(handle, body, {}, value, info);
ir.ImageWrite(handle, body, {}, {}, value, info);
}
void Translator::IMAGE_GET_RESINFO(const GcnInst& inst) {

View File

@ -49,11 +49,11 @@ struct BufferResource {
u8 instance_attrib{};
bool is_written{};
bool IsStorage(AmdGpu::Buffer buffer) const noexcept {
[[nodiscard]] bool IsStorage(const AmdGpu::Buffer& buffer) const noexcept {
return buffer.GetSize() > MaxUboSize || is_written || is_gds_buffer;
}
constexpr AmdGpu::Buffer GetSharp(const Info& info) const noexcept;
[[nodiscard]] constexpr AmdGpu::Buffer GetSharp(const Info& info) const noexcept;
};
using BufferResourceList = boost::container::small_vector<BufferResource, 16>;
@ -61,18 +61,24 @@ struct TextureBufferResource {
u32 sharp_idx;
bool is_written{};
constexpr AmdGpu::Buffer GetSharp(const Info& info) const noexcept;
[[nodiscard]] constexpr AmdGpu::Buffer GetSharp(const Info& info) const noexcept;
};
using TextureBufferResourceList = boost::container::small_vector<TextureBufferResource, 16>;
struct ImageResource {
u32 sharp_idx;
bool is_storage{};
bool is_depth{};
bool is_atomic{};
bool is_array{};
bool is_read{};
bool is_written{};
constexpr AmdGpu::Image GetSharp(const Info& info) const noexcept;
[[nodiscard]] bool IsStorage(const AmdGpu::Image& image) const noexcept {
// Need cube as storage when used with ImageRead.
return is_written || (is_read && image.GetBoundType() == AmdGpu::ImageType::Cube);
}
[[nodiscard]] constexpr AmdGpu::Image GetSharp(const Info& info) const noexcept;
};
using ImageResourceList = boost::container::small_vector<ImageResource, 16>;

View File

@ -1,74 +1,74 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <optional>
#include <type_traits>
#include <boost/container/small_vector.hpp>
#include <queue>
#include "shader_recompiler/ir/value.h"
namespace Shader::IR {
// Use typename Instruction so the function can be used to return either const or mutable
// Insts depending on the context.
template <typename Instruction, typename Pred>
auto BreadthFirstSearch(Instruction* inst,
Pred&& pred) -> std::invoke_result_t<Pred, Instruction*> {
// Most often case the instruction is the desired already.
if (std::optional result = pred(inst)) {
return result;
}
// Breadth-first search visiting the right most arguments first
boost::container::small_vector<Instruction*, 2> visited;
std::queue<Instruction*> queue;
queue.push(inst);
while (!queue.empty()) {
// Pop one instruction from the queue
Instruction* inst{queue.front()};
queue.pop();
if (std::optional result = pred(inst)) {
// This is the instruction we were looking for
return result;
}
// Visit the right most arguments first
for (size_t arg = inst->NumArgs(); arg--;) {
Value arg_value{inst->Arg(arg)};
if (arg_value.IsImmediate()) {
continue;
}
// Queue instruction if it hasn't been visited
Instruction* arg_inst{arg_value.InstRecursive()};
if (std::ranges::find(visited, arg_inst) == visited.end()) {
visited.push_back(arg_inst);
queue.push(arg_inst);
}
}
}
// SSA tree has been traversed and the result hasn't been found
return std::nullopt;
}
template <typename Pred>
auto BreadthFirstSearch(const Value& value,
Pred&& pred) -> std::invoke_result_t<Pred, const Inst*> {
if (value.IsImmediate()) {
// Nothing to do with immediates
return std::nullopt;
}
return BreadthFirstSearch(value.InstRecursive(), pred);
}
template <typename Pred>
auto BreadthFirstSearch(Value value, Pred&& pred) -> std::invoke_result_t<Pred, Inst*> {
if (value.IsImmediate()) {
// Nothing to do with immediates
return std::nullopt;
}
return BreadthFirstSearch(value.InstRecursive(), pred);
}
} // namespace Shader::IR
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <optional>
#include <type_traits>
#include <boost/container/small_vector.hpp>
#include <queue>
#include "shader_recompiler/ir/value.h"
namespace Shader::IR {
// Use typename Instruction so the function can be used to return either const or mutable
// Insts depending on the context.
template <typename Instruction, typename Pred>
auto BreadthFirstSearch(Instruction* inst,
Pred&& pred) -> std::invoke_result_t<Pred, Instruction*> {
// Most often case the instruction is the desired already.
if (std::optional result = pred(inst)) {
return result;
}
// Breadth-first search visiting the right most arguments first
boost::container::small_vector<Instruction*, 2> visited;
std::queue<Instruction*> queue;
queue.push(inst);
while (!queue.empty()) {
// Pop one instruction from the queue
Instruction* inst{queue.front()};
queue.pop();
if (std::optional result = pred(inst)) {
// This is the instruction we were looking for
return result;
}
// Visit the right most arguments first
for (size_t arg = inst->NumArgs(); arg--;) {
Value arg_value{inst->Arg(arg)};
if (arg_value.IsImmediate()) {
continue;
}
// Queue instruction if it hasn't been visited
Instruction* arg_inst{arg_value.InstRecursive()};
if (std::ranges::find(visited, arg_inst) == visited.end()) {
visited.push_back(arg_inst);
queue.push(arg_inst);
}
}
}
// SSA tree has been traversed and the result hasn't been found
return std::nullopt;
}
template <typename Pred>
auto BreadthFirstSearch(const Value& value,
Pred&& pred) -> std::invoke_result_t<Pred, const Inst*> {
if (value.IsImmediate()) {
// Nothing to do with immediates
return std::nullopt;
}
return BreadthFirstSearch(value.InstRecursive(), pred);
}
template <typename Pred>
auto BreadthFirstSearch(Value value, Pred&& pred) -> std::invoke_result_t<Pred, Inst*> {
if (value.IsImmediate()) {
// Nothing to do with immediates
return std::nullopt;
}
return BreadthFirstSearch(value.InstRecursive(), pred);
}
} // namespace Shader::IR

View File

@ -1630,11 +1630,6 @@ Value IREmitter::ImageGatherDref(const Value& handle, const Value& coords, const
return Inst(Opcode::ImageGatherDref, Flags{info}, handle, coords, offset, dref);
}
Value IREmitter::ImageFetch(const Value& handle, const Value& coords, const U32& lod,
const Value& offset, const U32& multisampling, TextureInstInfo info) {
return Inst(Opcode::ImageFetch, Flags{info}, handle, coords, lod, offset, multisampling);
}
Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod,
const IR::U1& skip_mips) {
return Inst(Opcode::ImageQueryDimensions, handle, lod, skip_mips);
@ -1657,13 +1652,13 @@ Value IREmitter::ImageGradient(const Value& handle, const Value& coords,
}
Value IREmitter::ImageRead(const Value& handle, const Value& coords, const U32& lod,
TextureInstInfo info) {
return Inst(Opcode::ImageRead, Flags{info}, handle, coords, lod);
const U32& multisampling, TextureInstInfo info) {
return Inst(Opcode::ImageRead, Flags{info}, handle, coords, lod, multisampling);
}
void IREmitter::ImageWrite(const Value& handle, const Value& coords, const U32& lod,
const Value& color, TextureInstInfo info) {
Inst(Opcode::ImageWrite, Flags{info}, handle, coords, lod, color);
const U32& multisampling, const Value& color, TextureInstInfo info) {
Inst(Opcode::ImageWrite, Flags{info}, handle, coords, lod, multisampling, color);
}
// Debug print maps to SPIRV's NonSemantic DebugPrintf instruction

View File

@ -325,17 +325,14 @@ public:
TextureInstInfo info);
[[nodiscard]] Value ImageGatherDref(const Value& handle, const Value& coords,
const Value& offset, const F32& dref, TextureInstInfo info);
[[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const U32& lod,
const Value& offset, const U32& multisampling,
TextureInstInfo info);
[[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords,
const Value& derivatives_dx, const Value& derivatives_dy,
const Value& offset, const F32& lod_clamp,
TextureInstInfo info);
[[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, const U32& lod,
TextureInstInfo info);
void ImageWrite(const Value& handle, const Value& coords, const U32& lod, const Value& color,
TextureInstInfo info);
const U32& multisampling, TextureInstInfo info);
void ImageWrite(const Value& handle, const Value& coords, const U32& lod,
const U32& multisampling, const Value& color, TextureInstInfo info);
void EmitVertex();
void EmitPrimitive();

View File

@ -338,12 +338,11 @@ OPCODE(ImageSampleDrefImplicitLod, F32x4, Opaq
OPCODE(ImageSampleDrefExplicitLod, F32x4, Opaque, Opaque, F32, F32, Opaque, )
OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, )
OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, F32, )
OPCODE(ImageFetch, F32x4, Opaque, Opaque, U32, Opaque, Opaque, )
OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, U1, )
OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, )
OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, F32, )
OPCODE(ImageRead, U32x4, Opaque, Opaque, U32, )
OPCODE(ImageWrite, Void, Opaque, Opaque, U32, U32x4, )
OPCODE(ImageRead, U32x4, Opaque, Opaque, U32, U32, )
OPCODE(ImageWrite, Void, Opaque, Opaque, U32, U32, U32x4, )
// Image atomic operations
OPCODE(ImageAtomicIAdd32, U32, Opaque, Opaque, U32, )

View File

@ -115,25 +115,16 @@ bool IsImageAtomicInstruction(const IR::Inst& inst) {
}
}
bool IsImageStorageInstruction(const IR::Inst& inst) {
switch (inst.GetOpcode()) {
case IR::Opcode::ImageWrite:
case IR::Opcode::ImageRead:
return true;
default:
return IsImageAtomicInstruction(inst);
}
}
bool IsImageInstruction(const IR::Inst& inst) {
switch (inst.GetOpcode()) {
case IR::Opcode::ImageFetch:
case IR::Opcode::ImageRead:
case IR::Opcode::ImageWrite:
case IR::Opcode::ImageQueryDimensions:
case IR::Opcode::ImageQueryLod:
case IR::Opcode::ImageSampleRaw:
return true;
default:
return IsImageStorageInstruction(inst);
return IsImageAtomicInstruction(inst);
}
}
@ -201,7 +192,8 @@ public:
return desc.sharp_idx == existing.sharp_idx;
})};
auto& image = image_resources[index];
image.is_storage |= desc.is_storage;
image.is_read |= desc.is_read;
image.is_written |= desc.is_written;
return index;
}
@ -429,9 +421,9 @@ void PatchTextureBufferInstruction(IR::Block& block, IR::Inst& inst, Info& info,
}
IR::Value PatchCubeCoord(IR::IREmitter& ir, const IR::Value& s, const IR::Value& t,
const IR::Value& z, bool is_storage, bool is_array) {
const IR::Value& z, bool is_written, bool is_array) {
// When cubemap is written with imageStore it is treated like 2DArray.
if (is_storage) {
if (is_written) {
return ir.CompositeConstruct(s, t, z);
}
@ -684,15 +676,16 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip
image = AmdGpu::Image::Null();
}
ASSERT(image.GetType() != AmdGpu::ImageType::Invalid);
const bool is_storage = IsImageStorageInstruction(inst);
const bool is_read = inst.GetOpcode() == IR::Opcode::ImageRead;
const bool is_written = inst.GetOpcode() == IR::Opcode::ImageWrite;
// Patch image instruction if image is FMask.
if (image.IsFmask()) {
ASSERT_MSG(!is_storage, "FMask storage instructions are not supported");
ASSERT_MSG(!is_written, "FMask storage instructions are not supported");
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
switch (inst.GetOpcode()) {
case IR::Opcode::ImageFetch:
case IR::Opcode::ImageRead:
case IR::Opcode::ImageSampleRaw: {
IR::F32 fmaskx = ir.BitCast<IR::F32>(ir.Imm32(0x76543210));
IR::F32 fmasky = ir.BitCast<IR::F32>(ir.Imm32(0xfedcba98));
@ -721,10 +714,11 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip
u32 image_binding = descriptors.Add(ImageResource{
.sharp_idx = tsharp,
.is_storage = is_storage,
.is_depth = bool(inst_info.is_depth),
.is_atomic = IsImageAtomicInstruction(inst),
.is_array = bool(inst_info.is_array),
.is_read = is_read,
.is_written = is_written,
});
// Sample instructions must be resolved into a new instruction using address register data.
@ -762,7 +756,7 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip
case AmdGpu::ImageType::Color3D: // x, y, z, [lod]
return {ir.CompositeConstruct(body->Arg(0), body->Arg(1), body->Arg(2)), body->Arg(3)};
case AmdGpu::ImageType::Cube: // x, y, face, [lod]
return {PatchCubeCoord(ir, body->Arg(0), body->Arg(1), body->Arg(2), is_storage,
return {PatchCubeCoord(ir, body->Arg(0), body->Arg(1), body->Arg(2), is_written,
inst_info.is_array),
body->Arg(3)};
default:
@ -772,19 +766,20 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip
inst.SetArg(1, coords);
if (inst.GetOpcode() == IR::Opcode::ImageWrite) {
inst.SetArg(3, SwizzleVector(ir, image, inst.Arg(3)));
inst.SetArg(4, SwizzleVector(ir, image, inst.Arg(4)));
}
if (inst_info.has_lod) {
ASSERT(inst.GetOpcode() == IR::Opcode::ImageFetch ||
inst.GetOpcode() == IR::Opcode::ImageRead ||
ASSERT(inst.GetOpcode() == IR::Opcode::ImageRead ||
inst.GetOpcode() == IR::Opcode::ImageWrite);
ASSERT(image.GetType() != AmdGpu::ImageType::Color2DMsaa &&
image.GetType() != AmdGpu::ImageType::Color2DMsaaArray);
inst.SetArg(2, arg);
} else if (image.GetType() == AmdGpu::ImageType::Color2DMsaa ||
image.GetType() == AmdGpu::ImageType::Color2DMsaaArray) {
inst.SetArg(4, arg);
} else if ((image.GetType() == AmdGpu::ImageType::Color2DMsaa ||
image.GetType() == AmdGpu::ImageType::Color2DMsaaArray) &&
(inst.GetOpcode() == IR::Opcode::ImageRead ||
inst.GetOpcode() == IR::Opcode::ImageWrite)) {
inst.SetArg(3, arg);
}
}

View File

@ -227,7 +227,7 @@ struct RuntimeInfo {
ComputeRuntimeInfo cs_info;
};
RuntimeInfo(Stage stage_) {
void Initialize(Stage stage_) {
memset(this, 0, sizeof(*this));
stage = stage_;
}

View File

@ -39,10 +39,12 @@ struct TextureBufferSpecialization {
struct ImageSpecialization {
AmdGpu::ImageType type = AmdGpu::ImageType::Color2D;
bool is_integer = false;
bool is_storage = false;
u32 dst_select = 0;
bool operator==(const ImageSpecialization& other) const {
return type == other.type && is_integer == other.is_integer &&
is_storage == other.is_storage &&
(dst_select != 0 ? dst_select == other.dst_select : true);
}
};
@ -114,7 +116,8 @@ struct StageSpecialization {
[](auto& spec, const auto& desc, AmdGpu::Image sharp) {
spec.type = sharp.GetBoundType();
spec.is_integer = AmdGpu::IsInteger(sharp.GetNumberFmt());
if (desc.is_storage) {
spec.is_storage = desc.IsStorage(sharp);
if (spec.is_storage) {
spec.dst_select = sharp.DstSelect();
}
});

View File

@ -428,6 +428,14 @@ struct Liverpool {
BitField<0, 22, u32> tile_max;
} depth_slice;
bool DepthValid() const {
return Address() != 0 && z_info.format != ZFormat::Invalid;
}
bool StencilValid() const {
return Address() != 0 && stencil_info.format != StencilFormat::Invalid;
}
u32 Pitch() const {
return (depth_size.pitch_tile_max + 1) << 3;
}
@ -1275,6 +1283,26 @@ struct Liverpool {
return nullptr;
}
u32 NumSamples() const {
// It seems that the number of samples > 1 set in the AA config doesn't mean we're
// always rendering with MSAA, so we need to derive MS ratio from the CB and DB
// settings.
u32 num_samples = 1u;
if (color_control.mode != ColorControl::OperationMode::Disable) {
for (auto cb = 0u; cb < NumColorBuffers; ++cb) {
const auto& col_buf = color_buffers[cb];
if (!col_buf) {
continue;
}
num_samples = std::max(num_samples, col_buf.NumSamples());
}
}
if (depth_buffer.DepthValid() || depth_buffer.StencilValid()) {
num_samples = std::max(num_samples, depth_buffer.NumSamples());
}
return num_samples;
}
void SetDefaults();
};

View File

@ -126,6 +126,7 @@ enum class TilingMode : u32 {
Display_MacroTiled = 0xAu,
Texture_MicroTiled = 0xDu,
Texture_MacroTiled = 0xEu,
Texture_Volume = 0x13u,
};
constexpr std::string_view NameOf(TilingMode type) {
@ -140,6 +141,8 @@ constexpr std::string_view NameOf(TilingMode type) {
return "Texture_MicroTiled";
case TilingMode::Texture_MacroTiled:
return "Texture_MacroTiled";
case TilingMode::Texture_Volume:
return "Texture_Volume";
default:
return "Unknown";
}
@ -294,9 +297,6 @@ struct Image {
return tiling_index == 5 ? TilingMode::Texture_MicroTiled
: TilingMode::Depth_MacroTiled;
}
if (tiling_index == 0x13) {
return TilingMode::Texture_MicroTiled;
}
return static_cast<TilingMode>(tiling_index);
}

Some files were not shown because too many files have changed in this diff Show More