mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-04 08:22:32 +00:00
Merge remote-tracking branch 'upstream/main' - update to 0.4.0
This commit is contained in:
commit
e3408a22cf
62
.github/workflows/build.yml
vendored
62
.github/workflows/build.yml
vendored
@ -43,6 +43,7 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
date: ${{ steps.vars.outputs.date }}
|
date: ${{ steps.vars.outputs.date }}
|
||||||
shorthash: ${{ steps.vars.outputs.shorthash }}
|
shorthash: ${{ steps.vars.outputs.shorthash }}
|
||||||
|
fullhash: ${{ steps.vars.outputs.fullhash }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Get date and git hash
|
- name: Get date and git hash
|
||||||
@ -50,8 +51,10 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
|
echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
|
||||||
echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||||
|
echo "fullhash=$(git rev-parse HEAD)" >> $GITHUB_ENV
|
||||||
echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||||
echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||||
|
echo "fullhash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
windows-sdl:
|
windows-sdl:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
@ -287,7 +290,7 @@ jobs:
|
|||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential libasound2-dev libpulse-dev libopenal-dev
|
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev
|
||||||
|
|
||||||
- name: Cache CMake Configuration
|
- name: Cache CMake Configuration
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
@ -343,7 +346,7 @@ jobs:
|
|||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev
|
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev
|
||||||
|
|
||||||
- name: Cache CMake Configuration
|
- name: Cache CMake Configuration
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
@ -420,9 +423,62 @@ jobs:
|
|||||||
tag: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}"
|
tag: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}"
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: true
|
prerelease: true
|
||||||
body: "Full Changelog: [${{ env.last_release_tag }}...${{ needs.get-info.outputs.shorthash }}](https://github.com/shadps4-emu/shadPS4/compare/${{ env.last_release_tag }}...${{ needs.get-info.outputs.shorthash }})"
|
body: "Full Changelog: [${{ env.last_release_tag }}...${{ needs.get-info.outputs.shorthash }}](https://github.com/shadps4-emu/shadPS4/compare/${{ env.last_release_tag }}...${{ needs.get-info.outputs.fullhash }})"
|
||||||
artifacts: ./artifacts/*.zip
|
artifacts: ./artifacts/*.zip
|
||||||
|
|
||||||
|
- name: Publish to Release Repository
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.SHADPS4_TOKEN_REPO }}
|
||||||
|
run: |
|
||||||
|
ARTIFACTS_DIR=./artifacts
|
||||||
|
REPO_WINDOWS="shadps4-emu/shadps4-binaries-Windows"
|
||||||
|
REPO_LINUX="shadps4-emu/shadps4-binaries-Linux"
|
||||||
|
REPO_MAC="shadps4-emu/shadps4-binaries-Mac"
|
||||||
|
|
||||||
|
for file in "$ARTIFACTS_DIR"/*.zip; do
|
||||||
|
filename=$(basename "$file")
|
||||||
|
REPO=""
|
||||||
|
|
||||||
|
# Determine repository based on file name
|
||||||
|
if [[ "$filename" == *"win64"* ]]; then
|
||||||
|
REPO=$REPO_WINDOWS
|
||||||
|
elif [[ "$filename" == *"linux"* ]] || [[ "$filename" == *"ubuntu64"* ]]; then
|
||||||
|
REPO=$REPO_LINUX
|
||||||
|
elif [[ "$filename" == *"macos"* ]]; then
|
||||||
|
REPO=$REPO_MAC
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If REPO is empty, skip file
|
||||||
|
if [[ -z "$REPO" ]]; then
|
||||||
|
echo "No matching repository for $filename"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if release already exists and get ID
|
||||||
|
release_id=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
|
||||||
|
"https://api.github.com/repos/$REPO/releases/tags/Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}" | jq -r '.id')
|
||||||
|
|
||||||
|
if [[ "$release_id" == "null" ]]; then
|
||||||
|
echo "Creating release in $REPO for $filename"
|
||||||
|
release_id=$(curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
|
||||||
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
|
-d '{
|
||||||
|
"tag_name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}",
|
||||||
|
"name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}",
|
||||||
|
"draft": false,
|
||||||
|
"prerelease": true,
|
||||||
|
"body": "Commit: [${{ needs.get-info.outputs.fullhash }}](https://github.com/shadps4-emu/shadPS4/commit/${{ needs.get-info.outputs.fullhash }})"
|
||||||
|
}' "https://api.github.com/repos/$REPO/releases" | jq -r '.id')
|
||||||
|
else
|
||||||
|
echo "Release already exists in $REPO with ID $release_id"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Artifact upload
|
||||||
|
echo "Uploading $filename to release $release_id in $REPO"
|
||||||
|
upload_url="https://uploads.github.com/repos/$REPO/releases/$release_id/assets?name=$filename"
|
||||||
|
curl -X POST -H "Authorization: token $GITHUB_TOKEN" -H "Content-Type: application/octet-stream" --data-binary @"$file" "$upload_url"
|
||||||
|
done
|
||||||
|
|
||||||
- name: Get current pre-release information
|
- name: Get current pre-release information
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.SHADPS4_TOKEN_REPO }}
|
GITHUB_TOKEN: ${{ secrets.SHADPS4_TOKEN_REPO }}
|
||||||
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -382,10 +382,10 @@ FodyWeavers.xsd
|
|||||||
|
|
||||||
# VS Code files for those working on multiple tools
|
# VS Code files for those working on multiple tools
|
||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/settings.json
|
.vscode/settings.json
|
||||||
!.vscode/tasks.json
|
.vscode/tasks.json
|
||||||
!.vscode/launch.json
|
.vscode/launch.json
|
||||||
!.vscode/extensions.json
|
.vscode/extensions.json
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
/CMakeUserPresets.json
|
/CMakeUserPresets.json
|
||||||
/compile_commands.json
|
/compile_commands.json
|
||||||
|
@ -290,8 +290,6 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
|
|||||||
src/core/libraries/audio3d/audio3d_error.h
|
src/core/libraries/audio3d/audio3d_error.h
|
||||||
src/core/libraries/audio3d/audio3d_impl.cpp
|
src/core/libraries/audio3d/audio3d_impl.cpp
|
||||||
src/core/libraries/audio3d/audio3d_impl.h
|
src/core/libraries/audio3d/audio3d_impl.h
|
||||||
src/core/libraries/ime/ime.cpp
|
|
||||||
src/core/libraries/ime/ime.h
|
|
||||||
src/core/libraries/game_live_streaming/gamelivestreaming.cpp
|
src/core/libraries/game_live_streaming/gamelivestreaming.cpp
|
||||||
src/core/libraries/game_live_streaming/gamelivestreaming.h
|
src/core/libraries/game_live_streaming/gamelivestreaming.h
|
||||||
src/core/libraries/remote_play/remoteplay.cpp
|
src/core/libraries/remote_play/remoteplay.cpp
|
||||||
@ -311,13 +309,17 @@ set(LIBC_SOURCES src/core/libraries/libc_internal/libc_internal.cpp
|
|||||||
src/core/libraries/libc_internal/libc_internal.h
|
src/core/libraries/libc_internal/libc_internal.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(DIALOGS_LIB src/core/libraries/dialogs/error_dialog.cpp
|
set(IME_LIB src/core/libraries/ime/error_dialog.cpp
|
||||||
src/core/libraries/dialogs/error_dialog.h
|
src/core/libraries/ime/error_dialog.h
|
||||||
src/core/libraries/dialogs/ime_dialog_ui.cpp
|
src/core/libraries/ime/ime_common.h
|
||||||
src/core/libraries/dialogs/ime_dialog_ui.h
|
src/core/libraries/ime/ime_dialog_ui.cpp
|
||||||
src/core/libraries/dialogs/ime_dialog.cpp
|
src/core/libraries/ime/ime_dialog_ui.h
|
||||||
src/core/libraries/dialogs/ime_dialog.h
|
src/core/libraries/ime/ime_dialog.cpp
|
||||||
src/core/libraries/dialogs/error_codes.h
|
src/core/libraries/ime/ime_dialog.h
|
||||||
|
src/core/libraries/ime/ime_ui.cpp
|
||||||
|
src/core/libraries/ime/ime_ui.h
|
||||||
|
src/core/libraries/ime/ime.cpp
|
||||||
|
src/core/libraries/ime/ime.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(PAD_LIB src/core/libraries/pad/pad.cpp
|
set(PAD_LIB src/core/libraries/pad/pad.cpp
|
||||||
@ -346,6 +348,13 @@ set(FIBER_LIB src/core/libraries/fiber/fiber.cpp
|
|||||||
src/core/libraries/fiber/fiber.h
|
src/core/libraries/fiber/fiber.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp
|
||||||
|
src/core/libraries/videodec/videodec2_impl.h
|
||||||
|
src/core/libraries/videodec/videodec2.cpp
|
||||||
|
src/core/libraries/videodec/videodec2.h
|
||||||
|
src/core/libraries/videodec/videodec2_avc.h
|
||||||
|
)
|
||||||
|
|
||||||
set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp
|
set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp
|
||||||
src/core/libraries/np_manager/np_manager.h
|
src/core/libraries/np_manager/np_manager.h
|
||||||
src/core/libraries/np_score/np_score.cpp
|
src/core/libraries/np_score/np_score.cpp
|
||||||
@ -499,8 +508,9 @@ set(CORE src/core/aerolib/stubs.cpp
|
|||||||
${RANDOM_LIB}
|
${RANDOM_LIB}
|
||||||
${USBD_LIB}
|
${USBD_LIB}
|
||||||
${MISC_LIBS}
|
${MISC_LIBS}
|
||||||
${DIALOGS_LIB}
|
${IME_LIB}
|
||||||
${FIBER_LIB}
|
${FIBER_LIB}
|
||||||
|
${VDEC_LIB}
|
||||||
${DEV_TOOLS}
|
${DEV_TOOLS}
|
||||||
src/core/debug_state.cpp
|
src/core/debug_state.cpp
|
||||||
src/core/debug_state.h
|
src/core/debug_state.h
|
||||||
|
@ -119,6 +119,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
|||||||
SUB(Lib, Remoteplay) \
|
SUB(Lib, Remoteplay) \
|
||||||
SUB(Lib, SharePlay) \
|
SUB(Lib, SharePlay) \
|
||||||
SUB(Lib, Fiber) \
|
SUB(Lib, Fiber) \
|
||||||
|
SUB(Lib, Vdec2) \
|
||||||
CLS(Frontend) \
|
CLS(Frontend) \
|
||||||
CLS(Render) \
|
CLS(Render) \
|
||||||
SUB(Render, Vulkan) \
|
SUB(Render, Vulkan) \
|
||||||
|
@ -86,6 +86,7 @@ enum class Class : u8 {
|
|||||||
Lib_Remoteplay, ///< The LibSceRemotePlay implementation
|
Lib_Remoteplay, ///< The LibSceRemotePlay implementation
|
||||||
Lib_SharePlay, ///< The LibSceSharePlay implemenation
|
Lib_SharePlay, ///< The LibSceSharePlay implemenation
|
||||||
Lib_Fiber, ///< The LibSceFiber implementation.
|
Lib_Fiber, ///< The LibSceFiber implementation.
|
||||||
|
Lib_Vdec2, ///< The LibSceVideodec2 implementation.
|
||||||
Frontend, ///< Emulator UI
|
Frontend, ///< Emulator UI
|
||||||
Render, ///< Video Core
|
Render, ///< Video Core
|
||||||
Render_Vulkan, ///< Vulkan backend
|
Render_Vulkan, ///< Vulkan backend
|
||||||
|
@ -86,30 +86,27 @@ static std::filesystem::path GetBundleParentDirectory() {
|
|||||||
|
|
||||||
static auto UserPaths = [] {
|
static auto UserPaths = [] {
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
// Start by assuming the base directory is the bundle's parent directory.
|
// Set the current path to the directory containing the app bundle.
|
||||||
std::filesystem::path base_dir = GetBundleParentDirectory();
|
std::filesystem::current_path(GetBundleParentDirectory());
|
||||||
std::filesystem::path user_dir = base_dir / PORTABLE_DIR;
|
#endif
|
||||||
// Check if the "user" directory exists in the current path:
|
|
||||||
|
// Try the portable user directory first.
|
||||||
|
auto user_dir = std::filesystem::current_path() / PORTABLE_DIR;
|
||||||
if (!std::filesystem::exists(user_dir)) {
|
if (!std::filesystem::exists(user_dir)) {
|
||||||
// If it doesn't exist, use the new hardcoded path:
|
// If it doesn't exist, use the standard path for the platform instead.
|
||||||
|
// NOTE: On Windows we currently just create the portable directory instead.
|
||||||
|
#ifdef __APPLE__
|
||||||
user_dir =
|
user_dir =
|
||||||
std::filesystem::path(getenv("HOME")) / "Library" / "Application Support" / "shadPS4";
|
std::filesystem::path(getenv("HOME")) / "Library" / "Application Support" / "shadPS4";
|
||||||
}
|
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
auto user_dir = std::filesystem::current_path() / PORTABLE_DIR;
|
|
||||||
// Check if the "user" directory exists in the current path:
|
|
||||||
if (!std::filesystem::exists(user_dir)) {
|
|
||||||
// If it doesn't exist, use XDG_DATA_HOME if it is set, and provide a standard default
|
|
||||||
const char* xdg_data_home = getenv("XDG_DATA_HOME");
|
const char* xdg_data_home = getenv("XDG_DATA_HOME");
|
||||||
if (xdg_data_home != nullptr && strlen(xdg_data_home) > 0) {
|
if (xdg_data_home != nullptr && strlen(xdg_data_home) > 0) {
|
||||||
user_dir = std::filesystem::path(xdg_data_home) / "shadPS4";
|
user_dir = std::filesystem::path(xdg_data_home) / "shadPS4";
|
||||||
} else {
|
} else {
|
||||||
user_dir = std::filesystem::path(getenv("HOME")) / ".local" / "share" / "shadPS4";
|
user_dir = std::filesystem::path(getenv("HOME")) / ".local" / "share" / "shadPS4";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#else
|
|
||||||
const auto user_dir = std::filesystem::current_path() / PORTABLE_DIR;
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
std::unordered_map<PathType, fs::path> paths;
|
std::unordered_map<PathType, fs::path> paths;
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
constexpr char VERSION[] = "0.3.1 WIP";
|
constexpr char VERSION[] = "0.4.1 WIP";
|
||||||
constexpr bool isRelease = false;
|
constexpr bool isRelease = false;
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
@ -19,6 +19,7 @@ using namespace Core::Devtools;
|
|||||||
using L = Core::Devtools::Layer;
|
using L = Core::Devtools::Layer;
|
||||||
|
|
||||||
static bool show_simple_fps = false;
|
static bool show_simple_fps = false;
|
||||||
|
static bool visibility_toggled = false;
|
||||||
|
|
||||||
static float fps_scale = 1.0f;
|
static float fps_scale = 1.0f;
|
||||||
static bool show_advanced_debug = false;
|
static bool show_advanced_debug = false;
|
||||||
@ -296,20 +297,24 @@ void L::Draw() {
|
|||||||
const auto fn = DebugState.flip_frame_count.load();
|
const auto fn = DebugState.flip_frame_count.load();
|
||||||
frame_graph.AddFrame(fn, io.DeltaTime);
|
frame_graph.AddFrame(fn, io.DeltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsKeyPressed(ImGuiKey_F10, false)) {
|
if (IsKeyPressed(ImGuiKey_F10, false)) {
|
||||||
if (io.KeyCtrl) {
|
if (io.KeyCtrl) {
|
||||||
show_advanced_debug = !show_advanced_debug;
|
show_advanced_debug = !show_advanced_debug;
|
||||||
} else {
|
} else {
|
||||||
show_simple_fps = !show_simple_fps;
|
show_simple_fps = !show_simple_fps;
|
||||||
}
|
}
|
||||||
|
visibility_toggled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (show_simple_fps) {
|
if (show_simple_fps) {
|
||||||
if (Begin("Video Info", nullptr,
|
if (Begin("Video Info", nullptr,
|
||||||
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration |
|
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration |
|
||||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking)) {
|
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking)) {
|
||||||
SetWindowPos("Video Info", {999999.0f, 0.0f}, ImGuiCond_FirstUseEver);
|
// Set window position to top left if it was toggled on
|
||||||
|
if (visibility_toggled) {
|
||||||
|
SetWindowPos("Video Info", {999999.0f, 0.0f}, ImGuiCond_Always);
|
||||||
|
visibility_toggled = false;
|
||||||
|
}
|
||||||
if (BeginPopupContextWindow()) {
|
if (BeginPopupContextWindow()) {
|
||||||
#define M(label, value) \
|
#define M(label, value) \
|
||||||
if (MenuItem(label, nullptr, fps_scale == value)) \
|
if (MenuItem(label, nullptr, fps_scale == value)) \
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
constexpr int ORBIS_ERROR_DIALOG_ERROR_NOT_INITIALIZED = 0x80ED0001; // not initialized
|
|
||||||
constexpr int ORBIS_ERROR_DIALOG_ERROR_ALREADY_INITIALIZED = 0x80ED0002; // already initialized
|
|
||||||
constexpr int ORBIS_ERROR_DIALOG_ERROR_PARAM_INVALID = 0x80ED0003; // Parameter is invalid
|
|
||||||
constexpr int ORBIS_ERROR_DIALOG_ERROR_UNEXPECTED_FATAL = 0x80ED0004; // Unexpected fatal error
|
|
||||||
constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_STATE = 0x80ED0005; // not in a callable state
|
|
||||||
constexpr int ORBIS_ERROR_DIALOG_ERROR_SERVICE_BUSY = 0x80ED0006; // Process is busy
|
|
||||||
constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_USER_ID = 0x80ED0007; // Invalid user ID
|
|
@ -508,3 +508,86 @@ constexpr int ORBIS_FIBER_ERROR_RANGE = 0x80590003;
|
|||||||
constexpr int ORBIS_FIBER_ERROR_INVALID = 0x80590004;
|
constexpr int ORBIS_FIBER_ERROR_INVALID = 0x80590004;
|
||||||
constexpr int ORBIS_FIBER_ERROR_PERMISSION = 0x80590005;
|
constexpr int ORBIS_FIBER_ERROR_PERMISSION = 0x80590005;
|
||||||
constexpr int ORBIS_FIBER_ERROR_STATE = 0x80590006;
|
constexpr int ORBIS_FIBER_ERROR_STATE = 0x80590006;
|
||||||
|
|
||||||
|
// ImeDialog library
|
||||||
|
constexpr int ORBIS_ERROR_DIALOG_ERROR_NOT_INITIALIZED = 0x80ED0001;
|
||||||
|
constexpr int ORBIS_ERROR_DIALOG_ERROR_ALREADY_INITIALIZED = 0x80ED0002;
|
||||||
|
constexpr int ORBIS_ERROR_DIALOG_ERROR_PARAM_INVALID = 0x80ED0003;
|
||||||
|
constexpr int ORBIS_ERROR_DIALOG_ERROR_UNEXPECTED_FATAL = 0x80ED0004;
|
||||||
|
constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_STATE = 0x80ED0005;
|
||||||
|
constexpr int ORBIS_ERROR_DIALOG_ERROR_SERVICE_BUSY = 0x80ED0006;
|
||||||
|
constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_USER_ID = 0x80ED0007;
|
||||||
|
|
||||||
|
// Ime library
|
||||||
|
constexpr int ORBIS_IME_ERROR_BUSY = 0x80BC0001;
|
||||||
|
constexpr int ORBIS_IME_ERROR_NOT_OPENED = 0x80BC0002;
|
||||||
|
constexpr int ORBIS_IME_ERROR_NO_MEMORY = 0x80BC0003;
|
||||||
|
constexpr int ORBIS_IME_ERROR_CONNECTION_FAILED = 0x80BC0004;
|
||||||
|
constexpr int ORBIS_IME_ERROR_TOO_MANY_REQUESTS = 0x80BC0005;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_TEXT = 0x80BC0006;
|
||||||
|
constexpr int ORBIS_IME_ERROR_EVENT_OVERFLOW = 0x80BC0007;
|
||||||
|
constexpr int ORBIS_IME_ERROR_NOT_ACTIVE = 0x80BC0008;
|
||||||
|
constexpr int ORBIS_IME_ERROR_IME_SUSPENDING = 0x80BC0009;
|
||||||
|
constexpr int ORBIS_IME_ERROR_DEVICE_IN_USE = 0x80BC000A;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_USER_ID = 0x80BC0010;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_TYPE = 0x80BC0011;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_SUPPORTED_LANGUAGES = 0x80BC0012;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_ENTER_LABEL = 0x80BC0013;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_INPUT_METHOD = 0x80BC0014;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_OPTION = 0x80BC0015;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_MAX_TEXT_LENGTH = 0x80BC0016;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_INPUT_TEXT_BUFFER = 0x80BC0017;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_POSX = 0x80BC0018;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_POSY = 0x80BC0019;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_HORIZONTAL_ALIGNMENT = 0x80BC001A;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_VERTICAL_ALIGNMENT = 0x80BC001B;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_EXTENDED = 0x80BC001C;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_KEYBOARD_TYPE = 0x80BC001D;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_WORK = 0x80BC0020;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_ARG = 0x80BC0021;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_HANDLER = 0x80BC0022;
|
||||||
|
constexpr int ORBIS_IME_ERROR_NO_RESOURCE_ID = 0x80BC0023;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_MODE = 0x80BC0024;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_PARAM = 0x80BC0030;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_ADDRESS = 0x80BC0031;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_RESERVED = 0x80BC0032;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INVALID_TIMING = 0x80BC0033;
|
||||||
|
constexpr int ORBIS_IME_ERROR_INTERNAL = 0x80BC00FF;
|
||||||
|
|
||||||
|
// Videodec2 library
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_API_FAIL = 0x811D0100;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE = 0x811D0101;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER = 0x811D0102;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE = 0x811D0103;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_SIZE = 0x811D0104;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_POINTER = 0x811D0105;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_SIZE = 0x811D0106;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_POINTER = 0x811D0107;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_ALIGNMENT = 0x811D0108;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_NOT_ONION_MEMORY = 0x811D0109;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_NOT_GARLIC_MEMORY = 0x811D010A;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_NOT_DIRECT_MEMORY = 0x811D010B;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_INFO = 0x811D010C;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_SIZE = 0x811D010D;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER = 0x811D010E;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_OUTPUT_INFO = 0x811D010F;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_QUEUE = 0x811D0110;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STATE = 0x811D0111;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_PRESET_VALUE = 0x811D0112;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_CONFIG_INFO = 0x811D0200;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_PIPE_ID = 0x811D0201;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_QUEUE_ID = 0x811D0202;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_RESOURCE_TYPE = 0x811D0203;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_CODEC_TYPE = 0x811D0204;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_PROFILE_LEVEL = 0x811D0205;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_PIPELINE_DEPTH = 0x811D0206;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_AFFINITY_MASK = 0x811D0207;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_THREAD_PRIORITY = 0x811D0208;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_DPB_FRAME_COUNT = 0x811D0209;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_WIDTH_HEIGHT = 0x811D020A;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_EXTRA_CONFIG_INFO = 0x811D020B;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_NEW_SEQUENCE = 0x811D0300;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT = 0x811D0301;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_OVERSIZE_DECODE = 0x811D0302;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_INVALID_SEQUENCE = 0x811D0303;
|
||||||
|
constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STREAM = 0x811D0304;
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
namespace Libraries::Fiber {
|
namespace Libraries::Fiber {
|
||||||
|
|
||||||
constexpr static u64 kFiberSignature = 0x054ad954;
|
static constexpr u64 kFiberSignature = 0x054ad954;
|
||||||
|
|
||||||
thread_local SceFiber* gCurrentFiber = nullptr;
|
thread_local SceFiber* gCurrentFiber = nullptr;
|
||||||
thread_local void* gFiberThread = nullptr;
|
thread_local void* gFiberThread = nullptr;
|
||||||
|
@ -359,12 +359,13 @@ s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) {
|
|||||||
eq->AddEvent(kernel_event);
|
eq->AddEvent(kernel_event);
|
||||||
|
|
||||||
Platform::IrqC::Instance()->Register(
|
Platform::IrqC::Instance()->Register(
|
||||||
Platform::InterruptId::GfxEop,
|
static_cast<Platform::InterruptId>(id),
|
||||||
[=](Platform::InterruptId irq) {
|
[=](Platform::InterruptId irq) {
|
||||||
ASSERT_MSG(irq == Platform::InterruptId::GfxEop,
|
ASSERT_MSG(irq == static_cast<Platform::InterruptId>(id),
|
||||||
"An unexpected IRQ occured"); // We need to convert IRQ# to event id and do
|
"An unexpected IRQ occured"); // We need to convert IRQ# to event id and do
|
||||||
// proper filtering in trigger function
|
// proper filtering in trigger function
|
||||||
eq->TriggerEvent(GnmEventIdents::GfxEop, SceKernelEvent::Filter::GraphicsCore, nullptr);
|
eq->TriggerEvent(static_cast<GnmEventIdents>(id), SceKernelEvent::Filter::GraphicsCore,
|
||||||
|
nullptr);
|
||||||
},
|
},
|
||||||
eq);
|
eq);
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
@ -468,7 +469,6 @@ int PS4_SYSV_ABI sceGnmDebugHardwareStatus() {
|
|||||||
|
|
||||||
s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id) {
|
s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id) {
|
||||||
LOG_TRACE(Lib_GnmDriver, "called");
|
LOG_TRACE(Lib_GnmDriver, "called");
|
||||||
ASSERT_MSG(id == GnmEventIdents::GfxEop);
|
|
||||||
|
|
||||||
if (!eq) {
|
if (!eq) {
|
||||||
return ORBIS_KERNEL_ERROR_EBADF;
|
return ORBIS_KERNEL_ERROR_EBADF;
|
||||||
@ -476,7 +476,7 @@ s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id) {
|
|||||||
|
|
||||||
eq->RemoveEvent(id);
|
eq->RemoveEvent(id);
|
||||||
|
|
||||||
Platform::IrqC::Instance()->Unregister(Platform::InterruptId::GfxEop, eq);
|
Platform::IrqC::Instance()->Unregister(static_cast<Platform::InterruptId>(id), eq);
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,108 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
#include "ime.h"
|
#include "ime.h"
|
||||||
|
#include "ime_ui.h"
|
||||||
|
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/singleton.h"
|
||||||
#include "core/libraries/error_codes.h"
|
#include "core/libraries/error_codes.h"
|
||||||
#include "core/libraries/libs.h"
|
#include "core/libraries/libs.h"
|
||||||
|
#include "core/linker.h"
|
||||||
|
|
||||||
namespace Libraries::Ime {
|
namespace Libraries::Ime {
|
||||||
|
|
||||||
|
static std::queue<OrbisImeEvent> g_ime_events;
|
||||||
|
static ImeState g_ime_state{};
|
||||||
|
static ImeUi g_ime_ui;
|
||||||
|
|
||||||
|
class ImeHandler {
|
||||||
|
public:
|
||||||
|
ImeHandler(const OrbisImeKeyboardParam* param) {
|
||||||
|
Init(param, false);
|
||||||
|
}
|
||||||
|
ImeHandler(const OrbisImeParam* param) {
|
||||||
|
Init(param, true);
|
||||||
|
}
|
||||||
|
~ImeHandler() = default;
|
||||||
|
|
||||||
|
void Init(const void* param, bool ime_mode) {
|
||||||
|
if (ime_mode) {
|
||||||
|
m_param.ime = *(OrbisImeParam*)param;
|
||||||
|
} else {
|
||||||
|
m_param.key = *(OrbisImeKeyboardParam*)param;
|
||||||
|
}
|
||||||
|
m_ime_mode = ime_mode;
|
||||||
|
|
||||||
|
// Open an event to let the game know the IME has started
|
||||||
|
OrbisImeEvent openEvent{};
|
||||||
|
openEvent.id = (ime_mode ? OrbisImeEventId::OPEN : OrbisImeEventId::KEYBOARD_OPEN);
|
||||||
|
|
||||||
|
if (ime_mode) {
|
||||||
|
sceImeGetPanelSize(&m_param.ime, &openEvent.param.rect.width,
|
||||||
|
&openEvent.param.rect.height);
|
||||||
|
openEvent.param.rect.x = m_param.ime.posx;
|
||||||
|
openEvent.param.rect.y = m_param.ime.posy;
|
||||||
|
} else {
|
||||||
|
openEvent.param.resourceIdArray.userId = 1;
|
||||||
|
openEvent.param.resourceIdArray.resourceId[0] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Execute(nullptr, &openEvent, true);
|
||||||
|
|
||||||
|
if (ime_mode) {
|
||||||
|
g_ime_state = ImeState(&m_param.ime);
|
||||||
|
g_ime_ui = ImeUi(&g_ime_state, &m_param.ime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 Update(OrbisImeEventHandler handler) {
|
||||||
|
std::unique_lock lock{g_ime_state.queue_mutex};
|
||||||
|
|
||||||
|
while (!g_ime_state.event_queue.empty()) {
|
||||||
|
OrbisImeEvent event = g_ime_state.event_queue.front();
|
||||||
|
g_ime_state.event_queue.pop();
|
||||||
|
Execute(handler, &event, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Execute(OrbisImeEventHandler handler, OrbisImeEvent* event, bool use_param_handler) {
|
||||||
|
const auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||||
|
|
||||||
|
if (m_ime_mode) {
|
||||||
|
OrbisImeParam param = m_param.ime;
|
||||||
|
if (use_param_handler) {
|
||||||
|
linker->ExecuteGuest(param.handler, param.arg, event);
|
||||||
|
} else {
|
||||||
|
linker->ExecuteGuest(handler, param.arg, event);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
OrbisImeKeyboardParam param = m_param.key;
|
||||||
|
if (use_param_handler) {
|
||||||
|
linker->ExecuteGuest(param.handler, param.arg, event);
|
||||||
|
} else {
|
||||||
|
linker->ExecuteGuest(handler, param.arg, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsIme() {
|
||||||
|
return m_ime_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
union ImeParam {
|
||||||
|
OrbisImeKeyboardParam key;
|
||||||
|
OrbisImeParam ime;
|
||||||
|
} m_param{};
|
||||||
|
bool m_ime_mode = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::unique_ptr<ImeHandler> g_ime_handler;
|
||||||
|
|
||||||
int PS4_SYSV_ABI FinalizeImeModule() {
|
int PS4_SYSV_ABI FinalizeImeModule() {
|
||||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
@ -34,8 +128,19 @@ int PS4_SYSV_ABI sceImeCheckUpdateTextInfo() {
|
|||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceImeClose() {
|
s32 PS4_SYSV_ABI sceImeClose() {
|
||||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
LOG_INFO(Lib_Ime, "(STUBBED) called");
|
||||||
|
|
||||||
|
if (!g_ime_handler) {
|
||||||
|
return ORBIS_IME_ERROR_NOT_OPENED;
|
||||||
|
}
|
||||||
|
if (!g_ime_handler->IsIme()) {
|
||||||
|
return ORBIS_IME_ERROR_NOT_OPENED;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_ime_handler.release();
|
||||||
|
g_ime_ui = ImeUi();
|
||||||
|
g_ime_state = ImeState();
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,13 +209,42 @@ int PS4_SYSV_ABI sceImeGetPanelPositionAndForm() {
|
|||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceImeGetPanelSize() {
|
s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height) {
|
||||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
LOG_INFO(Lib_Ime, "called");
|
||||||
|
|
||||||
|
if (!width || !height) {
|
||||||
|
return ORBIS_IME_ERROR_INVALID_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (param->type) {
|
||||||
|
case OrbisImeType::DEFAULT:
|
||||||
|
case OrbisImeType::BASIC_LATIN:
|
||||||
|
case OrbisImeType::URL:
|
||||||
|
case OrbisImeType::MAIL:
|
||||||
|
// We set our custom sizes, commented sizes are the original ones
|
||||||
|
*width = 500; // 793
|
||||||
|
*height = 100; // 408
|
||||||
|
break;
|
||||||
|
case OrbisImeType::NUMBER:
|
||||||
|
*width = 370;
|
||||||
|
*height = 402;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceImeKeyboardClose() {
|
s32 PS4_SYSV_ABI sceImeKeyboardClose(s32 userId) {
|
||||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
LOG_INFO(Lib_Ime, "(STUBBED) called");
|
||||||
|
|
||||||
|
if (!g_ime_handler) {
|
||||||
|
return ORBIS_IME_ERROR_NOT_OPENED;
|
||||||
|
}
|
||||||
|
if (g_ime_handler->IsIme()) {
|
||||||
|
return ORBIS_IME_ERROR_NOT_OPENED;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_ime_handler.release();
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,9 +258,19 @@ int PS4_SYSV_ABI sceImeKeyboardGetResourceId() {
|
|||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceImeKeyboardOpen() {
|
s32 PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param) {
|
||||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||||
return ORBIS_OK;
|
|
||||||
|
if (!param) {
|
||||||
|
return ORBIS_IME_ERROR_INVALID_ADDRESS;
|
||||||
|
}
|
||||||
|
if (g_ime_handler) {
|
||||||
|
return ORBIS_IME_ERROR_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// g_ime_handler = std::make_unique<ImeHandler>(param);
|
||||||
|
// return ORBIS_OK;
|
||||||
|
return ORBIS_IME_ERROR_CONNECTION_FAILED; // Fixup
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceImeKeyboardOpenInternal() {
|
int PS4_SYSV_ABI sceImeKeyboardOpenInternal() {
|
||||||
@ -144,8 +288,19 @@ int PS4_SYSV_ABI sceImeKeyboardUpdate() {
|
|||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceImeOpen() {
|
s32 PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const void* extended) {
|
||||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
LOG_INFO(Lib_Ime, "called");
|
||||||
|
|
||||||
|
if (!g_ime_handler) {
|
||||||
|
g_ime_handler = std::make_unique<ImeHandler>(param);
|
||||||
|
} else {
|
||||||
|
if (g_ime_handler->IsIme()) {
|
||||||
|
return ORBIS_IME_ERROR_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_ime_handler->Init((void*)param, true);
|
||||||
|
}
|
||||||
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,9 +309,15 @@ int PS4_SYSV_ABI sceImeOpenInternal() {
|
|||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceImeParamInit() {
|
void PS4_SYSV_ABI sceImeParamInit(OrbisImeParam* param) {
|
||||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
LOG_INFO(Lib_Ime, "called");
|
||||||
return ORBIS_OK;
|
|
||||||
|
if (!param) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(param, 0, sizeof(OrbisImeParam));
|
||||||
|
param->userId = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceImeSetCandidateIndex() {
|
int PS4_SYSV_ABI sceImeSetCandidateIndex() {
|
||||||
@ -164,7 +325,7 @@ int PS4_SYSV_ABI sceImeSetCandidateIndex() {
|
|||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceImeSetCaret() {
|
int PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret) {
|
||||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
@ -179,9 +340,14 @@ int PS4_SYSV_ABI sceImeSetTextGeometry() {
|
|||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceImeUpdate() {
|
s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler) {
|
||||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
LOG_TRACE(Lib_Ime, "called");
|
||||||
return ORBIS_OK;
|
|
||||||
|
if (!g_ime_handler) {
|
||||||
|
return ORBIS_IME_ERROR_NOT_OPENED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_ime_handler->Update(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceImeVshClearPreedit() {
|
int PS4_SYSV_ABI sceImeVshClearPreedit() {
|
||||||
|
@ -5,12 +5,62 @@
|
|||||||
|
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
|
||||||
|
#include "ime_common.h"
|
||||||
|
|
||||||
namespace Core::Loader {
|
namespace Core::Loader {
|
||||||
class SymbolsResolver;
|
class SymbolsResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Libraries::Ime {
|
namespace Libraries::Ime {
|
||||||
|
|
||||||
|
constexpr u32 ORBIS_IME_MAX_TEXT_LENGTH = 2048;
|
||||||
|
|
||||||
|
enum class OrbisImeKeyboardOption : u32 {
|
||||||
|
DEFAULT = 0,
|
||||||
|
REPEAT = 1,
|
||||||
|
REPEAT_EACH_KEY = 2,
|
||||||
|
ADD_OSK = 4,
|
||||||
|
EFFECTIVE_WITH_TIME = 8,
|
||||||
|
DISABLE_RESUME = 16,
|
||||||
|
DISABLE_CAPSLOCK_WITHOUT_SHIFT = 32,
|
||||||
|
};
|
||||||
|
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeKeyboardOption)
|
||||||
|
|
||||||
|
struct OrbisImeKeyboardParam {
|
||||||
|
OrbisImeKeyboardOption option;
|
||||||
|
s8 reserved1[4];
|
||||||
|
void* arg;
|
||||||
|
OrbisImeEventHandler handler;
|
||||||
|
s8 reserved2[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OrbisImeParam {
|
||||||
|
s32 userId;
|
||||||
|
OrbisImeType type;
|
||||||
|
u64 supportedLanguages;
|
||||||
|
OrbisImeEnterLabel enterLabel;
|
||||||
|
OrbisImeInputMethod inputMethod;
|
||||||
|
OrbisImeTextFilter filter;
|
||||||
|
u32 option;
|
||||||
|
u32 maxTextLength;
|
||||||
|
char16_t* inputTextBuffer;
|
||||||
|
float posx;
|
||||||
|
float posy;
|
||||||
|
OrbisImeHorizontalAlignment horizontalAlignment;
|
||||||
|
OrbisImeVerticalAlignment verticalAlignment;
|
||||||
|
void* work;
|
||||||
|
void* arg;
|
||||||
|
OrbisImeEventHandler handler;
|
||||||
|
s8 reserved[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OrbisImeCaret {
|
||||||
|
f32 x;
|
||||||
|
f32 y;
|
||||||
|
u32 height;
|
||||||
|
u32 index;
|
||||||
|
};
|
||||||
|
|
||||||
int PS4_SYSV_ABI FinalizeImeModule();
|
int PS4_SYSV_ABI FinalizeImeModule();
|
||||||
int PS4_SYSV_ABI InitializeImeModule();
|
int PS4_SYSV_ABI InitializeImeModule();
|
||||||
int PS4_SYSV_ABI sceImeCheckFilterText();
|
int PS4_SYSV_ABI sceImeCheckFilterText();
|
||||||
@ -30,22 +80,22 @@ int PS4_SYSV_ABI sceImeDisableController();
|
|||||||
int PS4_SYSV_ABI sceImeFilterText();
|
int PS4_SYSV_ABI sceImeFilterText();
|
||||||
int PS4_SYSV_ABI sceImeForTestFunction();
|
int PS4_SYSV_ABI sceImeForTestFunction();
|
||||||
int PS4_SYSV_ABI sceImeGetPanelPositionAndForm();
|
int PS4_SYSV_ABI sceImeGetPanelPositionAndForm();
|
||||||
int PS4_SYSV_ABI sceImeGetPanelSize();
|
s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height);
|
||||||
int PS4_SYSV_ABI sceImeKeyboardClose();
|
s32 PS4_SYSV_ABI sceImeKeyboardClose(s32 userId);
|
||||||
int PS4_SYSV_ABI sceImeKeyboardGetInfo();
|
int PS4_SYSV_ABI sceImeKeyboardGetInfo();
|
||||||
int PS4_SYSV_ABI sceImeKeyboardGetResourceId();
|
int PS4_SYSV_ABI sceImeKeyboardGetResourceId();
|
||||||
int PS4_SYSV_ABI sceImeKeyboardOpen();
|
s32 PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param);
|
||||||
int PS4_SYSV_ABI sceImeKeyboardOpenInternal();
|
int PS4_SYSV_ABI sceImeKeyboardOpenInternal();
|
||||||
int PS4_SYSV_ABI sceImeKeyboardSetMode();
|
int PS4_SYSV_ABI sceImeKeyboardSetMode();
|
||||||
int PS4_SYSV_ABI sceImeKeyboardUpdate();
|
int PS4_SYSV_ABI sceImeKeyboardUpdate();
|
||||||
int PS4_SYSV_ABI sceImeOpen();
|
s32 PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const void* extended);
|
||||||
int PS4_SYSV_ABI sceImeOpenInternal();
|
int PS4_SYSV_ABI sceImeOpenInternal();
|
||||||
int PS4_SYSV_ABI sceImeParamInit();
|
void PS4_SYSV_ABI sceImeParamInit(OrbisImeParam* param);
|
||||||
int PS4_SYSV_ABI sceImeSetCandidateIndex();
|
int PS4_SYSV_ABI sceImeSetCandidateIndex();
|
||||||
int PS4_SYSV_ABI sceImeSetCaret();
|
s32 PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret);
|
||||||
int PS4_SYSV_ABI sceImeSetText();
|
int PS4_SYSV_ABI sceImeSetText();
|
||||||
int PS4_SYSV_ABI sceImeSetTextGeometry();
|
int PS4_SYSV_ABI sceImeSetTextGeometry();
|
||||||
int PS4_SYSV_ABI sceImeUpdate();
|
s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler);
|
||||||
int PS4_SYSV_ABI sceImeVshClearPreedit();
|
int PS4_SYSV_ABI sceImeVshClearPreedit();
|
||||||
int PS4_SYSV_ABI sceImeVshClose();
|
int PS4_SYSV_ABI sceImeVshClose();
|
||||||
int PS4_SYSV_ABI sceImeVshConfirmPreedit();
|
int PS4_SYSV_ABI sceImeVshConfirmPreedit();
|
||||||
|
184
src/core/libraries/ime/ime_common.h
Normal file
184
src/core/libraries/ime/ime_common.h
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include "common/enum.h"
|
||||||
|
#include "common/types.h"
|
||||||
|
#include "core/libraries/rtc/rtc.h"
|
||||||
|
|
||||||
|
enum class OrbisImeType : u32 {
|
||||||
|
DEFAULT = 0,
|
||||||
|
BASIC_LATIN = 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,
|
||||||
|
UPDATE_TEXT = 1,
|
||||||
|
UPDATE_CARET = 2,
|
||||||
|
PRESS_CLOSE = 4,
|
||||||
|
PRESS_ENTER = 5,
|
||||||
|
ABORT = 6,
|
||||||
|
CANDIDATE_LIST_START = 7,
|
||||||
|
CANDIDATE_LIST_END = 8,
|
||||||
|
CANDIDATE_WORD = 9,
|
||||||
|
CANDIDATE_INDEX = 10,
|
||||||
|
CANDIDATE_DONE = 11,
|
||||||
|
CANDIDATE_CANCEL = 12,
|
||||||
|
CHANGE_DEVICE = 14,
|
||||||
|
CHANGE_INPUT_METHOD_STATE = 18,
|
||||||
|
|
||||||
|
KEYBOARD_OPEN = 256,
|
||||||
|
KEYBOARD_KEYCODE_DOWN = 257,
|
||||||
|
KEYBOARD_KEYCODE_UP = 258,
|
||||||
|
KEYBOARD_KEYCODE_REPEAT = 259,
|
||||||
|
KEYBOARD_CONNECTION = 260,
|
||||||
|
KEYBOARD_DISCONNECTION = 261,
|
||||||
|
KEYBOARD_ABORT = 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,
|
||||||
|
EXT_KEYBOARD = 2,
|
||||||
|
REMOTE_OSK = 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 caretIndex;
|
||||||
|
u32 areaNum;
|
||||||
|
OrbisImeTextAreaProperty textArea[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OrbisImeKeycode {
|
||||||
|
u16 keycode;
|
||||||
|
char16_t character;
|
||||||
|
u32 status;
|
||||||
|
OrbisImeKeyboardType type;
|
||||||
|
s32 userId;
|
||||||
|
u32 resourceId;
|
||||||
|
Libraries::Rtc::OrbisRtcTick timestamp;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OrbisImeKeyboardResourceIdArray {
|
||||||
|
s32 userId;
|
||||||
|
u32 resourceId[6];
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class OrbisImeCaretMovementDirection : u32 {
|
||||||
|
STILL = 0,
|
||||||
|
LEFT = 1,
|
||||||
|
RIGHT = 2,
|
||||||
|
UP = 3,
|
||||||
|
DOWN = 4,
|
||||||
|
HOME = 5,
|
||||||
|
END = 6,
|
||||||
|
PAGE_UP = 7,
|
||||||
|
PAGE_DOWN = 8,
|
||||||
|
TOP = 9,
|
||||||
|
BOTTOM = 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
union OrbisImeEventParam {
|
||||||
|
OrbisImeRect rect;
|
||||||
|
OrbisImeEditText text;
|
||||||
|
OrbisImeCaretMovementDirection caretMove;
|
||||||
|
OrbisImeKeycode keycode;
|
||||||
|
OrbisImeKeyboardResourceIdArray resourceIdArray;
|
||||||
|
char16_t* candidateWord;
|
||||||
|
s32 candidateIndex;
|
||||||
|
OrbisImeDeviceType deviceType;
|
||||||
|
u32 inputMethodState;
|
||||||
|
s8 reserved[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OrbisImeEvent {
|
||||||
|
OrbisImeEventId id;
|
||||||
|
OrbisImeEventParam param;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef PS4_SYSV_ABI int (*OrbisImeTextFilter)(char16_t* outText, u32* outTextLength,
|
||||||
|
const char16_t* srcText, u32 srcTextLength);
|
||||||
|
|
||||||
|
typedef PS4_SYSV_ABI void (*OrbisImeEventHandler)(void* arg, const OrbisImeEvent* e);
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "common/enum.h"
|
#include "common/enum.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
#include "ime_common.h"
|
||||||
|
|
||||||
namespace Core::Loader {
|
namespace Core::Loader {
|
||||||
class SymbolsResolver;
|
class SymbolsResolver;
|
||||||
@ -68,21 +69,6 @@ enum class OrbisImeDialogEndStatus : u32 {
|
|||||||
ABORTED = 2,
|
ABORTED = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class OrbisImeType : u32 {
|
|
||||||
DEFAULT = 0,
|
|
||||||
BASIC_LATIN = 1,
|
|
||||||
URL = 2,
|
|
||||||
MAIL = 3,
|
|
||||||
NUMBER = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class OrbisImeEnterLabel : u32 {
|
|
||||||
DEFAULT = 0,
|
|
||||||
SEND = 1,
|
|
||||||
SEARCH = 2,
|
|
||||||
GO = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class OrbisImeDialogOption : u32 {
|
enum class OrbisImeDialogOption : u32 {
|
||||||
DEFAULT = 0,
|
DEFAULT = 0,
|
||||||
MULTILINE = 1,
|
MULTILINE = 1,
|
||||||
@ -91,25 +77,8 @@ enum class OrbisImeDialogOption : u32 {
|
|||||||
// TODO: Document missing options
|
// TODO: Document missing options
|
||||||
LARGE_RESOLUTION = 1024,
|
LARGE_RESOLUTION = 1024,
|
||||||
};
|
};
|
||||||
|
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeDialogOption)
|
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeDialogOption)
|
||||||
|
|
||||||
enum class OrbisImeInputMethod : u32 {
|
|
||||||
DEFAULT = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class OrbisImeHorizontalAlignment : u32 {
|
|
||||||
LEFT = 0,
|
|
||||||
CENTER = 1,
|
|
||||||
RIGHT = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class OrbisImeVerticalAlignment : u32 {
|
|
||||||
TOP = 0,
|
|
||||||
CENTER = 1,
|
|
||||||
BOTTOM = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class OrbisImePanelPriority : u32 {
|
enum class OrbisImePanelPriority : u32 {
|
||||||
DEFAULT = 0,
|
DEFAULT = 0,
|
||||||
ALPHABET = 1,
|
ALPHABET = 1,
|
||||||
@ -117,47 +86,6 @@ enum class OrbisImePanelPriority : u32 {
|
|||||||
ACCENT = 3,
|
ACCENT = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct OrbisImeColor {
|
struct OrbisImeColor {
|
||||||
u8 r;
|
u8 r;
|
||||||
u8 g;
|
u8 g;
|
||||||
@ -180,9 +108,6 @@ struct OrbisImeKeycode {
|
|||||||
u64 timestamp;
|
u64 timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef PS4_SYSV_ABI int (*OrbisImeTextFilter)(char16_t* outText, u32* outTextLength,
|
|
||||||
const char16_t* srcText, u32 srcTextLength);
|
|
||||||
|
|
||||||
typedef PS4_SYSV_ABI int (*OrbisImeExtKeyboardFilter)(const OrbisImeKeycode* srcKeycode,
|
typedef PS4_SYSV_ABI int (*OrbisImeExtKeyboardFilter)(const OrbisImeKeycode* srcKeycode,
|
||||||
u16* outKeycode, u32* outStatus,
|
u16* outKeycode, u32* outStatus,
|
||||||
void* reserved);
|
void* reserved);
|
@ -9,8 +9,8 @@
|
|||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/singleton.h"
|
#include "common/singleton.h"
|
||||||
#include "core/libraries/dialogs/ime_dialog.h"
|
#include "core/libraries/ime/ime_dialog.h"
|
||||||
#include "core/libraries/dialogs/ime_dialog_ui.h"
|
#include "core/libraries/ime/ime_dialog_ui.h"
|
||||||
#include "core/linker.h"
|
#include "core/linker.h"
|
||||||
#include "imgui/imgui_std.h"
|
#include "imgui/imgui_std.h"
|
||||||
|
|
||||||
@ -22,8 +22,9 @@ namespace Libraries::ImeDialog {
|
|||||||
|
|
||||||
ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param,
|
ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param,
|
||||||
const OrbisImeParamExtended* extended) {
|
const OrbisImeParamExtended* extended) {
|
||||||
if (!param)
|
if (!param) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
userId = param->userId;
|
userId = param->userId;
|
||||||
is_multiLine = True(param->option & OrbisImeDialogOption::MULTILINE);
|
is_multiLine = True(param->option & OrbisImeDialogOption::MULTILINE);
|
||||||
@ -344,7 +345,6 @@ void ImeDialogUi::DrawMultiLineInputText() {
|
|||||||
|
|
||||||
int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
|
int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
|
||||||
ImeDialogUi* ui = static_cast<ImeDialogUi*>(data->UserData);
|
ImeDialogUi* ui = static_cast<ImeDialogUi*>(data->UserData);
|
||||||
|
|
||||||
ASSERT(ui);
|
ASSERT(ui);
|
||||||
|
|
||||||
// Should we filter punctuation?
|
// Should we filter punctuation?
|
@ -8,7 +8,7 @@
|
|||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include "common/cstring.h"
|
#include "common/cstring.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "core/libraries/dialogs/ime_dialog.h"
|
#include "core/libraries/ime/ime_dialog.h"
|
||||||
#include "imgui/imgui_layer.h"
|
#include "imgui/imgui_layer.h"
|
||||||
|
|
||||||
namespace Libraries::ImeDialog {
|
namespace Libraries::ImeDialog {
|
252
src/core/libraries/ime/ime_ui.cpp
Normal file
252
src/core/libraries/ime/ime_ui.cpp
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
// 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
|
||||||
|
: input_changed(other.input_changed), 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) {
|
||||||
|
input_changed = other.input_changed;
|
||||||
|
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::PRESS_ENTER;
|
||||||
|
SendEvent(&enterEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImeState::SendCloseEvent() {
|
||||||
|
OrbisImeEvent closeEvent{};
|
||||||
|
closeEvent.id = OrbisImeEventId::PRESS_CLOSE;
|
||||||
|
closeEvent.param.text.str = reinterpret_cast<char16_t*>(work_buffer);
|
||||||
|
SendEvent(&closeEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
state->input_changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
|
||||||
|
ImeUi* ui = static_cast<ImeUi*>(data->UserData);
|
||||||
|
ASSERT(ui);
|
||||||
|
|
||||||
|
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::UPDATE_CARET;
|
||||||
|
event.param.caretMove = caretDirection;
|
||||||
|
|
||||||
|
lastCaretPos = data->CursorPos;
|
||||||
|
ui->state->SendEvent(&event);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.caretIndex = data->CursorPos;
|
||||||
|
eventParam.areaNum = 1;
|
||||||
|
|
||||||
|
eventParam.textArea[0].mode = 1; // Edit mode
|
||||||
|
eventParam.textArea[0].index = data->CursorPos;
|
||||||
|
eventParam.textArea[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::UPDATE_TEXT;
|
||||||
|
event.param.text = eventParam;
|
||||||
|
|
||||||
|
lastText = currentText;
|
||||||
|
ui->state->SendEvent(&event);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImeUi::Free() {
|
||||||
|
RemoveLayer(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace Libraries::Ime
|
76
src/core/libraries/ime/ime_ui.h
Normal file
76
src/core/libraries/ime/ime_ui.h
Normal file
@ -0,0 +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;
|
||||||
|
|
||||||
|
bool input_changed = false;
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
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
|
@ -256,7 +256,7 @@ int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
|
|||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) {
|
int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) {
|
||||||
int version = Common::ElfInfo::Instance().RawFirmwareVer();
|
int version = Common::ElfInfo::Instance().RawFirmwareVer();
|
||||||
LOG_INFO(Kernel, "returned system version = {:#x}", version);
|
LOG_DEBUG(Kernel, "returned system version = {:#x}", version);
|
||||||
*ver = version;
|
*ver = version;
|
||||||
return (version > 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL;
|
return (version > 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,12 @@
|
|||||||
#include "core/libraries/audio/audioout.h"
|
#include "core/libraries/audio/audioout.h"
|
||||||
#include "core/libraries/audio3d/audio3d.h"
|
#include "core/libraries/audio3d/audio3d.h"
|
||||||
#include "core/libraries/avplayer/avplayer.h"
|
#include "core/libraries/avplayer/avplayer.h"
|
||||||
#include "core/libraries/dialogs/error_dialog.h"
|
|
||||||
#include "core/libraries/dialogs/ime_dialog.h"
|
|
||||||
#include "core/libraries/disc_map/disc_map.h"
|
#include "core/libraries/disc_map/disc_map.h"
|
||||||
#include "core/libraries/game_live_streaming/gamelivestreaming.h"
|
#include "core/libraries/game_live_streaming/gamelivestreaming.h"
|
||||||
#include "core/libraries/gnmdriver/gnmdriver.h"
|
#include "core/libraries/gnmdriver/gnmdriver.h"
|
||||||
|
#include "core/libraries/ime/error_dialog.h"
|
||||||
#include "core/libraries/ime/ime.h"
|
#include "core/libraries/ime/ime.h"
|
||||||
|
#include "core/libraries/ime/ime_dialog.h"
|
||||||
#include "core/libraries/kernel/libkernel.h"
|
#include "core/libraries/kernel/libkernel.h"
|
||||||
#include "core/libraries/libc_internal/libc_internal.h"
|
#include "core/libraries/libc_internal/libc_internal.h"
|
||||||
#include "core/libraries/libpng/pngdec.h"
|
#include "core/libraries/libpng/pngdec.h"
|
||||||
@ -41,6 +41,7 @@
|
|||||||
#include "core/libraries/system/systemservice.h"
|
#include "core/libraries/system/systemservice.h"
|
||||||
#include "core/libraries/system/userservice.h"
|
#include "core/libraries/system/userservice.h"
|
||||||
#include "core/libraries/usbd/usbd.h"
|
#include "core/libraries/usbd/usbd.h"
|
||||||
|
#include "core/libraries/videodec/videodec2.h"
|
||||||
#include "core/libraries/videoout/video_out.h"
|
#include "core/libraries/videoout/video_out.h"
|
||||||
|
|
||||||
namespace Libraries {
|
namespace Libraries {
|
||||||
@ -80,6 +81,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
|
|||||||
Libraries::ErrorDialog::RegisterlibSceErrorDialog(sym);
|
Libraries::ErrorDialog::RegisterlibSceErrorDialog(sym);
|
||||||
Libraries::ImeDialog::RegisterlibSceImeDialog(sym);
|
Libraries::ImeDialog::RegisterlibSceImeDialog(sym);
|
||||||
Libraries::AvPlayer::RegisterlibSceAvPlayer(sym);
|
Libraries::AvPlayer::RegisterlibSceAvPlayer(sym);
|
||||||
|
Libraries::Vdec2::RegisterlibSceVdec2(sym);
|
||||||
Libraries::Audio3d::RegisterlibSceAudio3d(sym);
|
Libraries::Audio3d::RegisterlibSceAudio3d(sym);
|
||||||
Libraries::Ime::RegisterlibSceIme(sym);
|
Libraries::Ime::RegisterlibSceIme(sym);
|
||||||
Libraries::GameLiveStreaming::RegisterlibSceGameLiveStreaming(sym);
|
Libraries::GameLiveStreaming::RegisterlibSceGameLiveStreaming(sym);
|
||||||
|
200
src/core/libraries/videodec/videodec2.cpp
Normal file
200
src/core/libraries/videodec/videodec2.cpp
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "videodec2.h"
|
||||||
|
#include "videodec2_impl.h"
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "core/libraries/error_codes.h"
|
||||||
|
#include "core/libraries/libs.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
|
139
src/core/libraries/videodec/videodec2.h
Normal file
139
src/core/libraries/videodec/videodec2.h
Normal file
@ -0,0 +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*;
|
||||||
|
typedef void* OrbisVideodec2ComputeQueue;
|
||||||
|
|
||||||
|
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
|
60
src/core/libraries/videodec/videodec2_avc.h
Normal file
60
src/core/libraries/videodec/videodec2_avc.h
Normal file
@ -0,0 +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;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Libraries::Vdec2
|
228
src/core/libraries/videodec/videodec2_impl.cpp
Normal file
228
src/core/libraries/videodec/videodec2_impl.cpp
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "videodec2_impl.h"
|
||||||
|
|
||||||
|
#include "common/alignment.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "core/libraries/error_codes.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
|
39
src/core/libraries/videodec/videodec2_impl.h
Normal file
39
src/core/libraries/videodec/videodec2_impl.h
Normal file
@ -0,0 +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;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Libraries::Vdec2
|
@ -106,13 +106,13 @@ s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* co
|
|||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceVideoOutSetFlipRate(s32 handle, s32 rate) {
|
s32 PS4_SYSV_ABI sceVideoOutSetFlipRate(s32 handle, s32 rate) {
|
||||||
LOG_INFO(Lib_VideoOut, "called");
|
LOG_TRACE(Lib_VideoOut, "called");
|
||||||
driver->GetPort(handle)->flip_rate = rate;
|
driver->GetPort(handle)->flip_rate = rate;
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceVideoOutIsFlipPending(s32 handle) {
|
s32 PS4_SYSV_ABI sceVideoOutIsFlipPending(s32 handle) {
|
||||||
LOG_INFO(Lib_VideoOut, "called");
|
LOG_TRACE(Lib_VideoOut, "called");
|
||||||
auto* port = driver->GetPort(handle);
|
auto* port = driver->GetPort(handle);
|
||||||
std::unique_lock lock{port->port_mutex};
|
std::unique_lock lock{port->port_mutex};
|
||||||
s32 pending = port->flip_status.flipPendingNum;
|
s32 pending = port->flip_status.flipPendingNum;
|
||||||
|
@ -17,31 +17,35 @@
|
|||||||
namespace Platform {
|
namespace Platform {
|
||||||
|
|
||||||
enum class InterruptId : u32 {
|
enum class InterruptId : u32 {
|
||||||
Compute0RelMem = 0u,
|
Compute0RelMem = 0x00,
|
||||||
Compute1RelMem = 1u,
|
Compute1RelMem = 0x01,
|
||||||
Compute2RelMem = 2u,
|
Compute2RelMem = 0x02,
|
||||||
Compute3RelMem = 3u,
|
Compute3RelMem = 0x03,
|
||||||
Compute4RelMem = 4u,
|
Compute4RelMem = 0x04,
|
||||||
Compute5RelMem = 5u,
|
Compute5RelMem = 0x05,
|
||||||
Compute6RelMem = 6u,
|
Compute6RelMem = 0x06,
|
||||||
GfxEop = 7u,
|
GfxEop = 0x40,
|
||||||
GfxFlip = 8u,
|
GfxFlip = 0x08,
|
||||||
GpuIdle = 9u,
|
GpuIdle = 0x09,
|
||||||
|
|
||||||
|
InterruptIdMax = 0x40, ///< Max possible value (GfxEop)
|
||||||
};
|
};
|
||||||
|
|
||||||
using IrqHandler = std::function<void(InterruptId)>;
|
using IrqHandler = std::function<void(InterruptId)>;
|
||||||
|
|
||||||
struct IrqController {
|
struct IrqController {
|
||||||
void RegisterOnce(InterruptId irq, IrqHandler handler) {
|
void RegisterOnce(InterruptId irq, IrqHandler handler) {
|
||||||
ASSERT_MSG(static_cast<u32>(irq) < irq_contexts.size(), "Invalid IRQ number");
|
ASSERT_MSG(static_cast<u32>(irq) <= static_cast<u32>(InterruptId::InterruptIdMax),
|
||||||
auto& ctx = irq_contexts[static_cast<u32>(irq)];
|
"Invalid IRQ number");
|
||||||
|
auto& ctx = irq_contexts.try_emplace(irq).first->second;
|
||||||
std::unique_lock lock{ctx.m_lock};
|
std::unique_lock lock{ctx.m_lock};
|
||||||
ctx.one_time_subscribers.emplace(handler);
|
ctx.one_time_subscribers.emplace(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Register(InterruptId irq, IrqHandler handler, void* uid) {
|
void Register(InterruptId irq, IrqHandler handler, void* uid) {
|
||||||
ASSERT_MSG(static_cast<u32>(irq) < irq_contexts.size(), "Invalid IRQ number");
|
ASSERT_MSG(static_cast<u32>(irq) <= static_cast<u32>(InterruptId::InterruptIdMax),
|
||||||
auto& ctx = irq_contexts[static_cast<u32>(irq)];
|
"Invalid IRQ number");
|
||||||
|
auto& ctx = irq_contexts.try_emplace(irq).first->second;
|
||||||
|
|
||||||
std::unique_lock lock{ctx.m_lock};
|
std::unique_lock lock{ctx.m_lock};
|
||||||
ASSERT_MSG(ctx.persistent_handlers.find(uid) == ctx.persistent_handlers.cend(),
|
ASSERT_MSG(ctx.persistent_handlers.find(uid) == ctx.persistent_handlers.cend(),
|
||||||
@ -50,15 +54,17 @@ struct IrqController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Unregister(InterruptId irq, void* uid) {
|
void Unregister(InterruptId irq, void* uid) {
|
||||||
ASSERT_MSG(static_cast<u32>(irq) < irq_contexts.size(), "Invalid IRQ number");
|
ASSERT_MSG(static_cast<u32>(irq) <= static_cast<u32>(InterruptId::InterruptIdMax),
|
||||||
auto& ctx = irq_contexts[static_cast<u32>(irq)];
|
"Invalid IRQ number");
|
||||||
|
auto& ctx = irq_contexts.try_emplace(irq).first->second;
|
||||||
std::unique_lock lock{ctx.m_lock};
|
std::unique_lock lock{ctx.m_lock};
|
||||||
ctx.persistent_handlers.erase(uid);
|
ctx.persistent_handlers.erase(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Signal(InterruptId irq) {
|
void Signal(InterruptId irq) {
|
||||||
ASSERT_MSG(static_cast<u32>(irq) < irq_contexts.size(), "Unexpected IRQ signaled");
|
ASSERT_MSG(static_cast<u32>(irq) <= static_cast<u32>(InterruptId::InterruptIdMax),
|
||||||
auto& ctx = irq_contexts[static_cast<u32>(irq)];
|
"Unexpected IRQ signaled");
|
||||||
|
auto& ctx = irq_contexts.try_emplace(irq).first->second;
|
||||||
std::unique_lock lock{ctx.m_lock};
|
std::unique_lock lock{ctx.m_lock};
|
||||||
|
|
||||||
LOG_TRACE(Core, "IRQ signaled: {}", magic_enum::enum_name(irq));
|
LOG_TRACE(Core, "IRQ signaled: {}", magic_enum::enum_name(irq));
|
||||||
@ -81,7 +87,7 @@ private:
|
|||||||
std::queue<IrqHandler> one_time_subscribers{};
|
std::queue<IrqHandler> one_time_subscribers{};
|
||||||
std::mutex m_lock{};
|
std::mutex m_lock{};
|
||||||
};
|
};
|
||||||
std::array<IrqContext, magic_enum::enum_count<InterruptId>()> irq_contexts{};
|
std::unordered_map<InterruptId, IrqContext> irq_contexts{};
|
||||||
};
|
};
|
||||||
|
|
||||||
using IrqC = Common::Singleton<IrqController>;
|
using IrqC = Common::Singleton<IrqController>;
|
||||||
|
@ -229,7 +229,7 @@ void Emulator::Run(const std::filesystem::path& file) {
|
|||||||
linker->LoadModule(eboot_path);
|
linker->LoadModule(eboot_path);
|
||||||
|
|
||||||
// check if we have system modules to load
|
// check if we have system modules to load
|
||||||
LoadSystemModules(eboot_path);
|
LoadSystemModules(eboot_path, game_info.game_serial);
|
||||||
|
|
||||||
// Load all prx from game's sce_module folder
|
// Load all prx from game's sce_module folder
|
||||||
std::filesystem::path sce_module_folder = file.parent_path() / "sce_module";
|
std::filesystem::path sce_module_folder = file.parent_path() / "sce_module";
|
||||||
@ -273,8 +273,8 @@ void Emulator::Run(const std::filesystem::path& file) {
|
|||||||
std::exit(0);
|
std::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::LoadSystemModules(const std::filesystem::path& file) {
|
void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string game_serial) {
|
||||||
constexpr std::array<SysModules, 13> ModulesToLoad{
|
constexpr std::array<SysModules, 12> ModulesToLoad{
|
||||||
{{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2},
|
{{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2},
|
||||||
{"libSceFiber.sprx", &Libraries::Fiber::RegisterlibSceFiber},
|
{"libSceFiber.sprx", &Libraries::Fiber::RegisterlibSceFiber},
|
||||||
{"libSceUlt.sprx", nullptr},
|
{"libSceUlt.sprx", nullptr},
|
||||||
@ -284,7 +284,6 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file) {
|
|||||||
{"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap},
|
{"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap},
|
||||||
{"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc},
|
{"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc},
|
||||||
{"libSceJpegEnc.sprx", nullptr},
|
{"libSceJpegEnc.sprx", nullptr},
|
||||||
{"libSceFont.sprx", nullptr},
|
|
||||||
{"libSceRazorCpu.sprx", nullptr},
|
{"libSceRazorCpu.sprx", nullptr},
|
||||||
{"libSceCesCs.sprx", nullptr},
|
{"libSceCesCs.sprx", nullptr},
|
||||||
{"libSceRudp.sprx", nullptr}}};
|
{"libSceRudp.sprx", nullptr}}};
|
||||||
@ -309,6 +308,14 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file) {
|
|||||||
LOG_INFO(Loader, "No HLE available for {} module", module_name);
|
LOG_INFO(Loader, "No HLE available for {} module", module_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (std::filesystem::exists(sys_module_path / game_serial)) {
|
||||||
|
for (const auto& entry :
|
||||||
|
std::filesystem::directory_iterator(sys_module_path / game_serial)) {
|
||||||
|
LOG_INFO(Loader, "Loading {} from game serial file {}", entry.path().string(),
|
||||||
|
game_serial);
|
||||||
|
linker->LoadModule(entry.path());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_QT_GUI
|
#ifdef ENABLE_QT_GUI
|
||||||
|
@ -29,7 +29,7 @@ public:
|
|||||||
void UpdatePlayTime(const std::string& serial);
|
void UpdatePlayTime(const std::string& serial);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void LoadSystemModules(const std::filesystem::path& file);
|
void LoadSystemModules(const std::filesystem::path& file, std::string game_serial);
|
||||||
|
|
||||||
Core::MemoryManager* memory;
|
Core::MemoryManager* memory;
|
||||||
Input::GameController* controller;
|
Input::GameController* controller;
|
||||||
|
@ -145,7 +145,9 @@ void CheckUpdate::CheckForUpdates(const bool showMessage) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString currentRev = QString::fromStdString(Common::g_scm_rev).left(7);
|
QString currentRev = (updateChannel == "Nightly")
|
||||||
|
? QString::fromStdString(Common::g_scm_rev).left(7)
|
||||||
|
: "v." + QString::fromStdString(Common::VERSION);
|
||||||
QString currentDate = Common::g_scm_date;
|
QString currentDate = Common::g_scm_date;
|
||||||
|
|
||||||
QDateTime dateTime = QDateTime::fromString(latestDate, Qt::ISODate);
|
QDateTime dateTime = QDateTime::fromString(latestDate, Qt::ISODate);
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../about_dialog.ui" line="78"/>
|
<location filename="../about_dialog.ui" line="78"/>
|
||||||
<source>shadPS4 is an experimental open-source emulator for the PlayStation 4.</source>
|
<source>shadPS4 is an experimental open-source emulator for the PlayStation 4.</source>
|
||||||
<translation>shadPS4 è un emulatore sperimentale open source per PlayStation 4.</translation>
|
<translation>shadPS4 è un emulatore sperimentale open-source per PlayStation 4.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../about_dialog.ui" line="99"/>
|
<location filename="../about_dialog.ui" line="99"/>
|
||||||
@ -62,7 +62,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../install_dir_select.cpp" line="37"/>
|
<location filename="../install_dir_select.cpp" line="37"/>
|
||||||
<source>Select which directory you want to install to.</source>
|
<source>Select which directory you want to install to.</source>
|
||||||
<translation>Select which directory you want to install to.</translation>
|
<translation>Seleziona in quale cartella vuoi effettuare l'installazione.</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -143,22 +143,22 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="72"/>
|
<location filename="../gui_context_menus.h" line="72"/>
|
||||||
<source>Delete...</source>
|
<source>Delete...</source>
|
||||||
<translation>Delete...</translation>
|
<translation>Elimina...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="73"/>
|
<location filename="../gui_context_menus.h" line="73"/>
|
||||||
<source>Delete Game</source>
|
<source>Delete Game</source>
|
||||||
<translation>Delete Game</translation>
|
<translation>Elimina Gioco</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="74"/>
|
<location filename="../gui_context_menus.h" line="74"/>
|
||||||
<source>Delete Update</source>
|
<source>Delete Update</source>
|
||||||
<translation>Delete Update</translation>
|
<translation>Elimina Aggiornamento</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="75"/>
|
<location filename="../gui_context_menus.h" line="75"/>
|
||||||
<source>Delete DLC</source>
|
<source>Delete DLC</source>
|
||||||
<translation>Delete DLC</translation>
|
<translation>Elimina DLC</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="195"/>
|
<location filename="../gui_context_menus.h" line="195"/>
|
||||||
@ -188,7 +188,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="299"/>
|
<location filename="../gui_context_menus.h" line="299"/>
|
||||||
<source>Game</source>
|
<source>Game</source>
|
||||||
<translation>Game</translation>
|
<translation>Gioco</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="305"/>
|
<location filename="../gui_context_menus.h" line="305"/>
|
||||||
@ -198,7 +198,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="312"/>
|
<location filename="../gui_context_menus.h" line="312"/>
|
||||||
<source>This game has no update to delete!</source>
|
<source>This game has no update to delete!</source>
|
||||||
<translation>This game has no update to delete!</translation>
|
<translation>Questo gioco non ha alcun aggiornamento da eliminare!</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="316"/>
|
<location filename="../gui_context_menus.h" line="316"/>
|
||||||
@ -208,7 +208,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="321"/>
|
<location filename="../gui_context_menus.h" line="321"/>
|
||||||
<source>This game has no DLC to delete!</source>
|
<source>This game has no DLC to delete!</source>
|
||||||
<translation>This game has no DLC to delete!</translation>
|
<translation>Questo gioco non ha alcun DLC da eliminare!</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="325"/>
|
<location filename="../gui_context_menus.h" line="325"/>
|
||||||
@ -218,12 +218,12 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="332"/>
|
<location filename="../gui_context_menus.h" line="332"/>
|
||||||
<source>Delete %1</source>
|
<source>Delete %1</source>
|
||||||
<translation>Delete %1</translation>
|
<translation>Elimina %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="333"/>
|
<location filename="../gui_context_menus.h" line="333"/>
|
||||||
<source>Are you sure you want to delete %1's %2 directory?</source>
|
<source>Are you sure you want to delete %1's %2 directory?</source>
|
||||||
<translation>Are you sure you want to delete %1's %2 directory?</translation>
|
<translation>Sei sicuro di eliminale la cartella %2 di %1?</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -336,7 +336,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../main_window_ui.h" line="343"/>
|
<location filename="../main_window_ui.h" line="343"/>
|
||||||
<source>Download Cheats/Patches</source>
|
<source>Download Cheats/Patches</source>
|
||||||
<translation>Scarica Trucchi / Patch</translation>
|
<translation>Scarica Trucchi/Patch</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../main_window_ui.h" line="345"/>
|
<location filename="../main_window_ui.h" line="345"/>
|
||||||
@ -480,7 +480,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="140"/>
|
<location filename="../settings_dialog.ui" line="140"/>
|
||||||
<source>Enable Separate Update Folder</source>
|
<source>Enable Separate Update Folder</source>
|
||||||
<translation>Enable Separate Update Folder</translation>
|
<translation>Abilità Cartella Aggiornamenti Separata</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="129"/>
|
<location filename="../settings_dialog.ui" line="129"/>
|
||||||
@ -490,7 +490,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="136"/>
|
<location filename="../settings_dialog.ui" line="136"/>
|
||||||
<source>Is PS4 Pro</source>
|
<source>Is PS4 Pro</source>
|
||||||
<translation>Modalità Ps4Pro</translation>
|
<translation>Modalità Ps4 Pro</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="154"/>
|
<location filename="../settings_dialog.ui" line="154"/>
|
||||||
@ -530,12 +530,12 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="635"/>
|
<location filename="../settings_dialog.ui" line="635"/>
|
||||||
<source>Hide Cursor</source>
|
<source>Hide Cursor</source>
|
||||||
<translation>Nascondi cursore</translation>
|
<translation>Nascondi Cursore</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="668"/>
|
<location filename="../settings_dialog.ui" line="668"/>
|
||||||
<source>Hide Cursor Idle Timeout</source>
|
<source>Hide Cursor Idle Timeout</source>
|
||||||
<translation>Timeout inattivo per nascondere il cursore</translation>
|
<translation>Timeout inattività per nascondere il cursore</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="767"/>
|
<location filename="../settings_dialog.ui" line="767"/>
|
||||||
@ -555,7 +555,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="282"/>
|
<location filename="../settings_dialog.ui" line="282"/>
|
||||||
<source>Graphics Device</source>
|
<source>Graphics Device</source>
|
||||||
<translation>Adattatore grafico</translation>
|
<translation>Scheda Grafica</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="326"/>
|
<location filename="../settings_dialog.ui" line="326"/>
|
||||||
@ -575,7 +575,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="453"/>
|
<location filename="../settings_dialog.ui" line="453"/>
|
||||||
<source>Advanced</source>
|
<source>Advanced</source>
|
||||||
<translation>Avanzato</translation>
|
<translation>Avanzate</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="462"/>
|
<location filename="../settings_dialog.ui" line="462"/>
|
||||||
@ -614,8 +614,8 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="537"/>
|
<location filename="../settings_dialog.ui" line="537"/>
|
||||||
<source>Enable Debug Dumping</source>
|
<source>Enable</source>
|
||||||
<translation>Abilita Dump Debug</translation>
|
<translation>Abilita Debug Dumping</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="560"/>
|
<location filename="../settings_dialog.ui" line="560"/>
|
||||||
@ -630,7 +630,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="574"/>
|
<location filename="../settings_dialog.ui" line="574"/>
|
||||||
<source>Enable RenderDoc Debugging</source>
|
<source>Enable RenderDoc Debugging</source>
|
||||||
<translation>Abilita Debugging RenderDoc</translation>
|
<translation>Abilita RenderDoc Debugging</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="274"/>
|
<location filename="../settings_dialog.ui" line="274"/>
|
||||||
@ -693,7 +693,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../main_window.cpp" line="363"/>
|
<location filename="../main_window.cpp" line="363"/>
|
||||||
<source>Download Complete</source>
|
<source>Download Complete</source>
|
||||||
<translation>Scaricamento completato</translation>
|
<translation>Download completato</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../main_window.cpp" line="364"/>
|
<location filename="../main_window.cpp" line="364"/>
|
||||||
@ -748,7 +748,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../main_window.cpp" line="646"/>
|
<location filename="../main_window.cpp" line="646"/>
|
||||||
<source>PKG and Game versions match: </source>
|
<source>PKG and Game versions match: </source>
|
||||||
<translation>Le versioni di PKG e del gioco corrispondono: </translation>
|
<translation>Le versioni di PKG e del Gioco corrispondono: </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../main_window.cpp" line="647"/>
|
<location filename="../main_window.cpp" line="647"/>
|
||||||
@ -1144,7 +1144,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.cpp" line="293"/>
|
<location filename="../settings_dialog.cpp" line="293"/>
|
||||||
<source>separateUpdatesCheckBox</source>
|
<source>separateUpdatesCheckBox</source>
|
||||||
<translation>Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.</translation>
|
<translation>Abilita Cartella Aggiornamenti Separata:\nAbilita l'installazione degli aggiornamenti in una cartella separata per una più facile gestione.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.cpp" line="295"/>
|
<location filename="../settings_dialog.cpp" line="295"/>
|
||||||
@ -1420,7 +1420,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../check_update.cpp" line="198"/>
|
<location filename="../check_update.cpp" line="198"/>
|
||||||
<source>Check for Updates at Startup</source>
|
<source>Check for Updates at Startup</source>
|
||||||
<translation>Verifica aggiornamenti all’avvio</translation>
|
<translation>Controlla aggiornamenti all’avvio</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../check_update.cpp" line="199"/>
|
<location filename="../check_update.cpp" line="199"/>
|
||||||
|
@ -143,27 +143,27 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="72"/>
|
<location filename="../gui_context_menus.h" line="72"/>
|
||||||
<source>Delete...</source>
|
<source>Delete...</source>
|
||||||
<translation>Delete...</translation>
|
<translation>Fshi...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="73"/>
|
<location filename="../gui_context_menus.h" line="73"/>
|
||||||
<source>Delete Game</source>
|
<source>Delete Game</source>
|
||||||
<translation>Delete Game</translation>
|
<translation>Fshi lojën</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="74"/>
|
<location filename="../gui_context_menus.h" line="74"/>
|
||||||
<source>Delete Update</source>
|
<source>Delete Update</source>
|
||||||
<translation>Delete Update</translation>
|
<translation>Fshi përditësimin</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="75"/>
|
<location filename="../gui_context_menus.h" line="75"/>
|
||||||
<source>Delete DLC</source>
|
<source>Delete DLC</source>
|
||||||
<translation>Delete DLC</translation>
|
<translation>Fshi DLC-në</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="195"/>
|
<location filename="../gui_context_menus.h" line="195"/>
|
||||||
<source>Shortcut creation</source>
|
<source>Shortcut creation</source>
|
||||||
<translation>Krijim i shkurtores</translation>
|
<translation>Krijimi i shkurtores</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="196"/>
|
<location filename="../gui_context_menus.h" line="196"/>
|
||||||
@ -188,27 +188,27 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="299"/>
|
<location filename="../gui_context_menus.h" line="299"/>
|
||||||
<source>Game</source>
|
<source>Game</source>
|
||||||
<translation>Game</translation>
|
<translation>Loja</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="305"/>
|
<location filename="../gui_context_menus.h" line="305"/>
|
||||||
<source>requiresEnableSeparateUpdateFolder_MSG</source>
|
<source>requiresEnableSeparateUpdateFolder_MSG</source>
|
||||||
<translation>This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it.</translation>
|
<translation>Kjo veçori kërkon cilësimin 'Aktivizo dosjen e ndarë të përditësimit' për të punuar. Në qoftë se do ta përdorësh këtë veçori, të lutem aktivizoje.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="312"/>
|
<location filename="../gui_context_menus.h" line="312"/>
|
||||||
<source>This game has no update to delete!</source>
|
<source>This game has no update to delete!</source>
|
||||||
<translation>This game has no update to delete!</translation>
|
<translation>Kjo lojë nuk ka përditësim për të fshirë!</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="316"/>
|
<location filename="../gui_context_menus.h" line="316"/>
|
||||||
<source>Update</source>
|
<source>Update</source>
|
||||||
<translation>Update</translation>
|
<translation>Përditësim</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="321"/>
|
<location filename="../gui_context_menus.h" line="321"/>
|
||||||
<source>This game has no DLC to delete!</source>
|
<source>This game has no DLC to delete!</source>
|
||||||
<translation>This game has no DLC to delete!</translation>
|
<translation>Kjo lojë nuk ka DLC për të fshirë!</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="325"/>
|
<location filename="../gui_context_menus.h" line="325"/>
|
||||||
@ -218,12 +218,12 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="332"/>
|
<location filename="../gui_context_menus.h" line="332"/>
|
||||||
<source>Delete %1</source>
|
<source>Delete %1</source>
|
||||||
<translation>Delete %1</translation>
|
<translation>Fshi %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="333"/>
|
<location filename="../gui_context_menus.h" line="333"/>
|
||||||
<source>Are you sure you want to delete %1's %2 directory?</source>
|
<source>Are you sure you want to delete %1's %2 directory?</source>
|
||||||
<translation>Are you sure you want to delete %1's %2 directory?</translation>
|
<translation>Je i sigurt që do të fsish dosjen %2 të %1?</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -256,7 +256,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../main_window_ui.h" line="315"/>
|
<location filename="../main_window_ui.h" line="315"/>
|
||||||
<source>Configure...</source>
|
<source>Configure...</source>
|
||||||
<translation>Formëso...</translation>
|
<translation>Konfiguro...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../main_window_ui.h" line="318"/>
|
<location filename="../main_window_ui.h" line="318"/>
|
||||||
@ -480,7 +480,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="140"/>
|
<location filename="../settings_dialog.ui" line="140"/>
|
||||||
<source>Enable Separate Update Folder</source>
|
<source>Enable Separate Update Folder</source>
|
||||||
<translation>Enable Separate Update Folder</translation>
|
<translation>Aktivizo dosjen e ndarë të përditësimit</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="129"/>
|
<location filename="../settings_dialog.ui" line="129"/>
|
||||||
@ -1026,7 +1026,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../cheats_patches.cpp" line="763"/>
|
<location filename="../cheats_patches.cpp" line="763"/>
|
||||||
<source>DownloadComplete_MSG</source>
|
<source>DownloadComplete_MSG</source>
|
||||||
<translation>Arnat u shkarkuan me sukses! Të gjitha arnat e ofruara për të gjitha lojërat janë shkarkuar, nuk ka nevojë t'i shkarkosh ato individualisht për secilën lojë siç ndodh me Mashtrimet. Nëse patch-i nuk shfaqet, mund të mos ekzistojë për numrin e serisë dhe versionin specifik të lojës.</translation>
|
<translation>Arnat u shkarkuan me sukses! Të gjitha arnat e ofruara për të gjitha lojërat janë shkarkuar, nuk ka nevojë t'i shkarkosh ato individualisht për secilën lojë siç ndodh me Mashtrimet. Nëse arna nuk shfaqet, mund të mos ekzistojë për numrin e serikut dhe versionin specifik të lojës.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../cheats_patches.cpp" line="773"/>
|
<location filename="../cheats_patches.cpp" line="773"/>
|
||||||
@ -1046,12 +1046,12 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../cheats_patches.cpp" line="851"/>
|
<location filename="../cheats_patches.cpp" line="851"/>
|
||||||
<source>The downloaded patch only works on version: %1</source>
|
<source>The downloaded patch only works on version: %1</source>
|
||||||
<translation>Patch-i i shkarkuar funksionon vetëm në versionin: %1</translation>
|
<translation>Arna e shkarkuar funksionon vetëm në versionin: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../cheats_patches.cpp" line="856"/>
|
<location filename="../cheats_patches.cpp" line="856"/>
|
||||||
<source>You may need to update your game.</source>
|
<source>You may need to update your game.</source>
|
||||||
<translation>Ju mund të duhet të përditësoni lojën tuaj.</translation>
|
<translation>Mund të duhet të përditësosh lojën tënde.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../cheats_patches.cpp" line="860"/>
|
<location filename="../cheats_patches.cpp" line="860"/>
|
||||||
@ -1096,7 +1096,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../cheats_patches.cpp" line="1163"/>
|
<location filename="../cheats_patches.cpp" line="1163"/>
|
||||||
<source>Can't apply cheats before the game is started</source>
|
<source>Can't apply cheats before the game is started</source>
|
||||||
<translation>Nuk mund të zbatohen mashtrime para se të fillojë loja.</translation>
|
<translation>Nuk mund të zbatohen mashtrime para fillimit të lojës.</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -1144,7 +1144,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.cpp" line="293"/>
|
<location filename="../settings_dialog.cpp" line="293"/>
|
||||||
<source>separateUpdatesCheckBox</source>
|
<source>separateUpdatesCheckBox</source>
|
||||||
<translation>Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.</translation>
|
<translation>Aktivizo dosjen e ndarë të përditësimit:\nAktivizon instalimin e përditësimeve të lojërave në dosje të veçanta për menaxhim më të lehtë.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.cpp" line="295"/>
|
<location filename="../settings_dialog.cpp" line="295"/>
|
||||||
|
@ -62,7 +62,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../install_dir_select.cpp" line="37"/>
|
<location filename="../install_dir_select.cpp" line="37"/>
|
||||||
<source>Select which directory you want to install to.</source>
|
<source>Select which directory you want to install to.</source>
|
||||||
<translation>Select which directory you want to install to.</translation>
|
<translation>选择你想要安装到的目录。</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -143,22 +143,22 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="72"/>
|
<location filename="../gui_context_menus.h" line="72"/>
|
||||||
<source>Delete...</source>
|
<source>Delete...</source>
|
||||||
<translation>Delete...</translation>
|
<translation>删除...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="73"/>
|
<location filename="../gui_context_menus.h" line="73"/>
|
||||||
<source>Delete Game</source>
|
<source>Delete Game</source>
|
||||||
<translation>Delete Game</translation>
|
<translation>删除游戏</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="74"/>
|
<location filename="../gui_context_menus.h" line="74"/>
|
||||||
<source>Delete Update</source>
|
<source>Delete Update</source>
|
||||||
<translation>Delete Update</translation>
|
<translation>删除更新</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="75"/>
|
<location filename="../gui_context_menus.h" line="75"/>
|
||||||
<source>Delete DLC</source>
|
<source>Delete DLC</source>
|
||||||
<translation>Delete DLC</translation>
|
<translation>删除DLC</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="195"/>
|
<location filename="../gui_context_menus.h" line="195"/>
|
||||||
@ -188,27 +188,27 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="299"/>
|
<location filename="../gui_context_menus.h" line="299"/>
|
||||||
<source>Game</source>
|
<source>Game</source>
|
||||||
<translation>Game</translation>
|
<translation>游戏</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="305"/>
|
<location filename="../gui_context_menus.h" line="305"/>
|
||||||
<source>requiresEnableSeparateUpdateFolder_MSG</source>
|
<source>requiresEnableSeparateUpdateFolder_MSG</source>
|
||||||
<translation>This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it.</translation>
|
<translation>这个功能需要‘启用单独的更新目录’配置选项才能正常运行,如果你想要使用这个功能,请启用它。</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="312"/>
|
<location filename="../gui_context_menus.h" line="312"/>
|
||||||
<source>This game has no update to delete!</source>
|
<source>This game has no update to delete!</source>
|
||||||
<translation>This game has no update to delete!</translation>
|
<translation>这个游戏没有更新可以删除!</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="316"/>
|
<location filename="../gui_context_menus.h" line="316"/>
|
||||||
<source>Update</source>
|
<source>Update</source>
|
||||||
<translation>Update</translation>
|
<translation>更新</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="321"/>
|
<location filename="../gui_context_menus.h" line="321"/>
|
||||||
<source>This game has no DLC to delete!</source>
|
<source>This game has no DLC to delete!</source>
|
||||||
<translation>This game has no DLC to delete!</translation>
|
<translation>这个游戏没有DLC可以删除!</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="325"/>
|
<location filename="../gui_context_menus.h" line="325"/>
|
||||||
@ -218,12 +218,12 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="332"/>
|
<location filename="../gui_context_menus.h" line="332"/>
|
||||||
<source>Delete %1</source>
|
<source>Delete %1</source>
|
||||||
<translation>Delete %1</translation>
|
<translation>删除 %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../gui_context_menus.h" line="333"/>
|
<location filename="../gui_context_menus.h" line="333"/>
|
||||||
<source>Are you sure you want to delete %1's %2 directory?</source>
|
<source>Are you sure you want to delete %1's %2 directory?</source>
|
||||||
<translation>Are you sure you want to delete %1's %2 directory?</translation>
|
<translation>你确定要删除 %1 的 %2 目录?</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -480,7 +480,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="140"/>
|
<location filename="../settings_dialog.ui" line="140"/>
|
||||||
<source>Enable Separate Update Folder</source>
|
<source>Enable Separate Update Folder</source>
|
||||||
<translation>Enable Separate Update Folder</translation>
|
<translation>启用单独的更新目录</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="129"/>
|
<location filename="../settings_dialog.ui" line="129"/>
|
||||||
@ -560,7 +560,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="326"/>
|
<location filename="../settings_dialog.ui" line="326"/>
|
||||||
<source>Width</source>
|
<source>Width</source>
|
||||||
<translation>宽带</translation>
|
<translation>宽度</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.ui" line="357"/>
|
<location filename="../settings_dialog.ui" line="357"/>
|
||||||
@ -1144,7 +1144,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.cpp" line="293"/>
|
<location filename="../settings_dialog.cpp" line="293"/>
|
||||||
<source>separateUpdatesCheckBox</source>
|
<source>separateUpdatesCheckBox</source>
|
||||||
<translation>Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.</translation>
|
<translation>启用单独的更新目录:\n启用安装游戏更新到一个单独的目录中以更便于管理。</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../settings_dialog.cpp" line="295"/>
|
<location filename="../settings_dialog.cpp" line="295"/>
|
||||||
|
@ -261,6 +261,13 @@ struct Image {
|
|||||||
return last_level + 1;
|
return last_level + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 NumSamples() const {
|
||||||
|
if (GetType() == ImageType::Color2DMsaa || GetType() == ImageType::Color2DMsaaArray) {
|
||||||
|
return 1u << last_level;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
ImageType GetType() const noexcept {
|
ImageType GetType() const noexcept {
|
||||||
return static_cast<ImageType>(type);
|
return static_cast<ImageType>(type);
|
||||||
}
|
}
|
||||||
|
@ -447,6 +447,10 @@ std::span<const SurfaceFormatInfo> SurfaceFormats() {
|
|||||||
vk::Format::eR8Unorm),
|
vk::Format::eR8Unorm),
|
||||||
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Snorm,
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Snorm,
|
||||||
vk::Format::eR8Snorm),
|
vk::Format::eR8Snorm),
|
||||||
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Uscaled,
|
||||||
|
vk::Format::eR8Uscaled),
|
||||||
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Sscaled,
|
||||||
|
vk::Format::eR8Sscaled),
|
||||||
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Uint,
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Uint,
|
||||||
vk::Format::eR8Uint),
|
vk::Format::eR8Uint),
|
||||||
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Sint,
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Sint,
|
||||||
@ -458,6 +462,10 @@ std::span<const SurfaceFormatInfo> SurfaceFormats() {
|
|||||||
vk::Format::eR16Unorm),
|
vk::Format::eR16Unorm),
|
||||||
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Snorm,
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Snorm,
|
||||||
vk::Format::eR16Snorm),
|
vk::Format::eR16Snorm),
|
||||||
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Uscaled,
|
||||||
|
vk::Format::eR16Uscaled),
|
||||||
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Sscaled,
|
||||||
|
vk::Format::eR16Sscaled),
|
||||||
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Uint,
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Uint,
|
||||||
vk::Format::eR16Uint),
|
vk::Format::eR16Uint),
|
||||||
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Sint,
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Sint,
|
||||||
@ -469,6 +477,10 @@ std::span<const SurfaceFormatInfo> SurfaceFormats() {
|
|||||||
vk::Format::eR8G8Unorm),
|
vk::Format::eR8G8Unorm),
|
||||||
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Snorm,
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Snorm,
|
||||||
vk::Format::eR8G8Snorm),
|
vk::Format::eR8G8Snorm),
|
||||||
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Uscaled,
|
||||||
|
vk::Format::eR8G8Uscaled),
|
||||||
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Sscaled,
|
||||||
|
vk::Format::eR8G8Sscaled),
|
||||||
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Uint,
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Uint,
|
||||||
vk::Format::eR8G8Uint),
|
vk::Format::eR8G8Uint),
|
||||||
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Sint,
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Sint,
|
||||||
@ -509,6 +521,14 @@ std::span<const SurfaceFormatInfo> SurfaceFormats() {
|
|||||||
vk::Format::eA2B10G10R10UnormPack32),
|
vk::Format::eA2B10G10R10UnormPack32),
|
||||||
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Snorm,
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Snorm,
|
||||||
vk::Format::eA2B10G10R10SnormPack32),
|
vk::Format::eA2B10G10R10SnormPack32),
|
||||||
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Uscaled,
|
||||||
|
vk::Format::eA2B10G10R10UscaledPack32),
|
||||||
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Sscaled,
|
||||||
|
vk::Format::eA2B10G10R10SscaledPack32),
|
||||||
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Uint,
|
||||||
|
vk::Format::eA2B10G10R10UintPack32),
|
||||||
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Sint,
|
||||||
|
vk::Format::eA2B10G10R10SintPack32),
|
||||||
// 8_8_8_8
|
// 8_8_8_8
|
||||||
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8_8_8, AmdGpu::NumberFormat::Unorm,
|
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8_8_8, AmdGpu::NumberFormat::Unorm,
|
||||||
vk::Format::eR8G8B8A8Unorm),
|
vk::Format::eR8G8B8A8Unorm),
|
||||||
|
@ -127,18 +127,33 @@ bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache,
|
|||||||
// we can skip the whole dispatch and update the tracked state instead. Also, it is not
|
// we can skip the whole dispatch and update the tracked state instead. Also, it is not
|
||||||
// intended to be consumed and in such rare cases (e.g. HTile introspection, CRAA) we
|
// intended to be consumed and in such rare cases (e.g. HTile introspection, CRAA) we
|
||||||
// will need its full emulation anyways. For cases of metadata read a warning will be logged.
|
// will need its full emulation anyways. For cases of metadata read a warning will be logged.
|
||||||
for (const auto& desc : info->texture_buffers) {
|
const auto IsMetaUpdate = [&](const auto& desc) {
|
||||||
const VAddr address = desc.GetSharp(*info).base_address;
|
const VAddr address = desc.GetSharp(*info).base_address;
|
||||||
if (desc.is_written) {
|
if (desc.is_written) {
|
||||||
if (texture_cache.TouchMeta(address, true)) {
|
if (texture_cache.TouchMeta(address, true)) {
|
||||||
LOG_TRACE(Render_Vulkan, "Metadata update skipped");
|
LOG_TRACE(Render_Vulkan, "Metadata update skipped");
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (texture_cache.IsMeta(address)) {
|
if (texture_cache.IsMeta(address)) {
|
||||||
LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a CS shader (buffer)");
|
LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a CS shader (buffer)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& desc : info->buffers) {
|
||||||
|
if (desc.is_gds_buffer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (IsMetaUpdate(desc)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto& desc : info->texture_buffers) {
|
||||||
|
if (IsMetaUpdate(desc)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BindBuffers(buffer_cache, texture_cache, *info, binding, push_data, set_writes,
|
BindBuffers(buffer_cache, texture_cache, *info, binding, push_data, set_writes,
|
||||||
|
@ -160,22 +160,23 @@ Instance::Instance(Frontend::WindowSDL& window, s32 physical_device_index,
|
|||||||
|
|
||||||
// Check and log format support details.
|
// Check and log format support details.
|
||||||
for (const auto& format : LiverpoolToVK::SurfaceFormats()) {
|
for (const auto& format : LiverpoolToVK::SurfaceFormats()) {
|
||||||
if (!IsFormatSupported(GetSupportedFormat(format.vk_format, format.flags), format.flags)) {
|
if (!IsFormatSupported(format.vk_format, format.flags)) {
|
||||||
LOG_WARNING(Render_Vulkan,
|
LOG_WARNING(Render_Vulkan,
|
||||||
"Surface format data_format={}, number_format={} is not fully supported "
|
"Surface format data_format={}, number_format={} is not fully supported "
|
||||||
"(vk_format={}, requested flags={})",
|
"(vk_format={}, missing features={})",
|
||||||
static_cast<u32>(format.data_format),
|
static_cast<u32>(format.data_format),
|
||||||
static_cast<u32>(format.number_format), vk::to_string(format.vk_format),
|
static_cast<u32>(format.number_format), vk::to_string(format.vk_format),
|
||||||
vk::to_string(format.flags));
|
vk::to_string(format.flags & ~GetFormatFeatureFlags(format.vk_format)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const auto& format : LiverpoolToVK::DepthFormats()) {
|
for (const auto& format : LiverpoolToVK::DepthFormats()) {
|
||||||
if (!IsFormatSupported(GetSupportedFormat(format.vk_format, format.flags), format.flags)) {
|
if (!IsFormatSupported(format.vk_format, format.flags)) {
|
||||||
LOG_WARNING(Render_Vulkan,
|
LOG_WARNING(Render_Vulkan,
|
||||||
"Depth format z_format={}, stencil_format={} is not fully supported "
|
"Depth format z_format={}, stencil_format={} is not fully supported "
|
||||||
"(vk_format={}, requested flags={})",
|
"(vk_format={}, missing features={})",
|
||||||
static_cast<u32>(format.z_format), static_cast<u32>(format.stencil_format),
|
static_cast<u32>(format.z_format), static_cast<u32>(format.stencil_format),
|
||||||
vk::to_string(format.vk_format), vk::to_string(format.flags));
|
vk::to_string(format.vk_format),
|
||||||
|
vk::to_string(format.flags & ~GetFormatFeatureFlags(format.vk_format)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -546,18 +547,21 @@ void Instance::CollectToolingInfo() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Instance::IsFormatSupported(const vk::Format format,
|
vk::FormatFeatureFlags2 Instance::GetFormatFeatureFlags(vk::Format format) const {
|
||||||
const vk::FormatFeatureFlags2 flags) const {
|
|
||||||
if (format == vk::Format::eUndefined) [[unlikely]] {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto it = format_properties.find(format);
|
const auto it = format_properties.find(format);
|
||||||
if (it == format_properties.end()) {
|
if (it == format_properties.end()) {
|
||||||
UNIMPLEMENTED_MSG("Properties of format {} have not been queried.", vk::to_string(format));
|
UNIMPLEMENTED_MSG("Properties of format {} have not been queried.", vk::to_string(format));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ((it->second.optimalTilingFeatures | it->second.bufferFeatures) & flags) == flags;
|
return it->second.optimalTilingFeatures | it->second.bufferFeatures;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Instance::IsFormatSupported(const vk::Format format,
|
||||||
|
const vk::FormatFeatureFlags2 flags) const {
|
||||||
|
if (format == vk::Format::eUndefined) [[unlikely]] {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return (GetFormatFeatureFlags(format) & flags) == flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
static vk::Format GetAlternativeFormat(const vk::Format format) {
|
static vk::Format GetAlternativeFormat(const vk::Format format) {
|
||||||
|
@ -275,6 +275,9 @@ private:
|
|||||||
void CollectDeviceParameters();
|
void CollectDeviceParameters();
|
||||||
void CollectToolingInfo();
|
void CollectToolingInfo();
|
||||||
|
|
||||||
|
/// Gets the supported feature flags for a format.
|
||||||
|
[[nodiscard]] vk::FormatFeatureFlags2 GetFormatFeatureFlags(vk::Format format) const;
|
||||||
|
|
||||||
/// Determines if a format is supported for a set of feature flags.
|
/// Determines if a format is supported for a set of feature flags.
|
||||||
[[nodiscard]] bool IsFormatSupported(vk::Format format, vk::FormatFeatureFlags2 flags) const;
|
[[nodiscard]] bool IsFormatSupported(vk::Format format, vk::FormatFeatureFlags2 flags) const;
|
||||||
|
|
||||||
|
@ -181,26 +181,6 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_,
|
|||||||
PipelineCache::~PipelineCache() = default;
|
PipelineCache::~PipelineCache() = default;
|
||||||
|
|
||||||
const GraphicsPipeline* PipelineCache::GetGraphicsPipeline() {
|
const GraphicsPipeline* PipelineCache::GetGraphicsPipeline() {
|
||||||
const auto& regs = liverpool->regs;
|
|
||||||
// Tessellation is unsupported so skip the draw to avoid locking up the driver.
|
|
||||||
if (regs.primitive_type == AmdGpu::PrimitiveType::PatchPrimitive) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
// There are several cases (e.g. FCE, FMask/HTile decompression) where we don't need to do an
|
|
||||||
// actual draw hence can skip pipeline creation.
|
|
||||||
if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::EliminateFastClear) {
|
|
||||||
LOG_TRACE(Render_Vulkan, "FCE pass skipped");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::FmaskDecompress) {
|
|
||||||
// TODO: check for a valid MRT1 to promote the draw to the resolve pass.
|
|
||||||
LOG_TRACE(Render_Vulkan, "FMask decompression pass skipped");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (regs.primitive_type == AmdGpu::PrimitiveType::None) {
|
|
||||||
LOG_TRACE(Render_Vulkan, "Primitive type 'None' skipped");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (!RefreshGraphicsKey()) {
|
if (!RefreshGraphicsKey()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -41,9 +41,43 @@ void Rasterizer::CpSync() {
|
|||||||
vk::DependencyFlagBits::eByRegion, ib_barrier, {}, {});
|
vk::DependencyFlagBits::eByRegion, ib_barrier, {}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Rasterizer::FilterDraw() {
|
||||||
|
const auto& regs = liverpool->regs;
|
||||||
|
// Tessellation is unsupported so skip the draw to avoid locking up the driver.
|
||||||
|
if (regs.primitive_type == AmdGpu::PrimitiveType::PatchPrimitive) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// There are several cases (e.g. FCE, FMask/HTile decompression) where we don't need to do an
|
||||||
|
// actual draw hence can skip pipeline creation.
|
||||||
|
if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::EliminateFastClear) {
|
||||||
|
LOG_TRACE(Render_Vulkan, "FCE pass skipped");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::FmaskDecompress) {
|
||||||
|
// TODO: check for a valid MRT1 to promote the draw to the resolve pass.
|
||||||
|
LOG_TRACE(Render_Vulkan, "FMask decompression pass skipped");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::Resolve) {
|
||||||
|
LOG_TRACE(Render_Vulkan, "Resolve pass");
|
||||||
|
Resolve();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (regs.primitive_type == AmdGpu::PrimitiveType::None) {
|
||||||
|
LOG_TRACE(Render_Vulkan, "Primitive type 'None' skipped");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void Rasterizer::Draw(bool is_indexed, u32 index_offset) {
|
void Rasterizer::Draw(bool is_indexed, u32 index_offset) {
|
||||||
RENDERER_TRACE;
|
RENDERER_TRACE;
|
||||||
|
|
||||||
|
if (!FilterDraw()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const auto cmdbuf = scheduler.CommandBuffer();
|
const auto cmdbuf = scheduler.CommandBuffer();
|
||||||
const auto& regs = liverpool->regs;
|
const auto& regs = liverpool->regs;
|
||||||
const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline();
|
const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline();
|
||||||
@ -80,6 +114,10 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) {
|
|||||||
void Rasterizer::DrawIndirect(bool is_indexed, VAddr address, u32 offset, u32 size) {
|
void Rasterizer::DrawIndirect(bool is_indexed, VAddr address, u32 offset, u32 size) {
|
||||||
RENDERER_TRACE;
|
RENDERER_TRACE;
|
||||||
|
|
||||||
|
if (!FilterDraw()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const auto cmdbuf = scheduler.CommandBuffer();
|
const auto cmdbuf = scheduler.CommandBuffer();
|
||||||
const auto& regs = liverpool->regs;
|
const auto& regs = liverpool->regs;
|
||||||
const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline();
|
const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline();
|
||||||
@ -258,6 +296,54 @@ void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline) {
|
|||||||
scheduler.BeginRendering(state);
|
scheduler.BeginRendering(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Rasterizer::Resolve() {
|
||||||
|
const auto cmdbuf = scheduler.CommandBuffer();
|
||||||
|
|
||||||
|
// Read from MRT0, average all samples, and write to MRT1, which is one-sample
|
||||||
|
const auto& mrt0_hint = liverpool->last_cb_extent[0];
|
||||||
|
const auto& mrt1_hint = liverpool->last_cb_extent[1];
|
||||||
|
VideoCore::ImageInfo mrt0_info{liverpool->regs.color_buffers[0], mrt0_hint};
|
||||||
|
VideoCore::ImageInfo mrt1_info{liverpool->regs.color_buffers[1], mrt1_hint};
|
||||||
|
auto& mrt0_image = texture_cache.GetImage(texture_cache.FindImage(mrt0_info));
|
||||||
|
auto& mrt1_image = texture_cache.GetImage(texture_cache.FindImage(mrt1_info));
|
||||||
|
|
||||||
|
VideoCore::SubresourceRange mrt0_range;
|
||||||
|
mrt0_range.base.layer = liverpool->regs.color_buffers[0].view.slice_start;
|
||||||
|
mrt0_range.extent.layers = liverpool->regs.color_buffers[0].NumSlices() - mrt0_range.base.layer;
|
||||||
|
VideoCore::SubresourceRange mrt1_range;
|
||||||
|
mrt1_range.base.layer = liverpool->regs.color_buffers[1].view.slice_start;
|
||||||
|
mrt1_range.extent.layers = liverpool->regs.color_buffers[1].NumSlices() - mrt1_range.base.layer;
|
||||||
|
|
||||||
|
vk::ImageResolve region = {
|
||||||
|
.srcSubresource =
|
||||||
|
{
|
||||||
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
.mipLevel = 0,
|
||||||
|
.baseArrayLayer = mrt0_range.base.layer,
|
||||||
|
.layerCount = mrt0_range.extent.layers,
|
||||||
|
},
|
||||||
|
.srcOffset = {0, 0, 0},
|
||||||
|
.dstSubresource =
|
||||||
|
{
|
||||||
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
.mipLevel = 0,
|
||||||
|
.baseArrayLayer = mrt1_range.base.layer,
|
||||||
|
.layerCount = mrt1_range.extent.layers,
|
||||||
|
},
|
||||||
|
.dstOffset = {0, 0, 0},
|
||||||
|
.extent = {mrt1_image.info.size.width, mrt1_image.info.size.height, 1},
|
||||||
|
};
|
||||||
|
|
||||||
|
mrt0_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead,
|
||||||
|
mrt0_range);
|
||||||
|
|
||||||
|
mrt1_image.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite,
|
||||||
|
mrt1_range);
|
||||||
|
|
||||||
|
cmdbuf.resolveImage(mrt0_image.image, vk::ImageLayout::eTransferSrcOptimal, mrt1_image.image,
|
||||||
|
vk::ImageLayout::eTransferDstOptimal, region);
|
||||||
|
}
|
||||||
|
|
||||||
void Rasterizer::InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds) {
|
void Rasterizer::InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds) {
|
||||||
buffer_cache.InlineData(address, value, num_bytes, is_gds);
|
buffer_cache.InlineData(address, value, num_bytes, is_gds);
|
||||||
}
|
}
|
||||||
|
@ -54,11 +54,14 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void BeginRendering(const GraphicsPipeline& pipeline);
|
void BeginRendering(const GraphicsPipeline& pipeline);
|
||||||
|
void Resolve();
|
||||||
|
|
||||||
void UpdateDynamicState(const GraphicsPipeline& pipeline);
|
void UpdateDynamicState(const GraphicsPipeline& pipeline);
|
||||||
void UpdateViewportScissorState();
|
void UpdateViewportScissorState();
|
||||||
void UpdateDepthStencilState();
|
void UpdateDepthStencilState();
|
||||||
|
|
||||||
|
bool FilterDraw();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Instance& instance;
|
const Instance& instance;
|
||||||
Scheduler& scheduler;
|
Scheduler& scheduler;
|
||||||
|
@ -219,6 +219,7 @@ ImageInfo::ImageInfo(const AmdGpu::Image& image, const Shader::ImageResource& de
|
|||||||
pitch = image.Pitch();
|
pitch = image.Pitch();
|
||||||
resources.levels = image.NumLevels();
|
resources.levels = image.NumLevels();
|
||||||
resources.layers = image.NumLayers(desc.is_array);
|
resources.layers = image.NumLayers(desc.is_array);
|
||||||
|
num_samples = image.NumSamples();
|
||||||
num_bits = NumBits(image.GetDataFmt());
|
num_bits = NumBits(image.GetDataFmt());
|
||||||
usage.texture = true;
|
usage.texture = true;
|
||||||
|
|
||||||
|
@ -80,7 +80,12 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageReso
|
|||||||
}
|
}
|
||||||
range.base.level = image.base_level;
|
range.base.level = image.base_level;
|
||||||
range.base.layer = image.base_array;
|
range.base.layer = image.base_array;
|
||||||
|
if (image.GetType() == AmdGpu::ImageType::Color2DMsaa ||
|
||||||
|
image.GetType() == AmdGpu::ImageType::Color2DMsaaArray) {
|
||||||
|
range.extent.levels = 1;
|
||||||
|
} else {
|
||||||
range.extent.levels = image.last_level - image.base_level + 1;
|
range.extent.levels = image.last_level - image.base_level + 1;
|
||||||
|
}
|
||||||
range.extent.layers = image.last_array - image.base_array + 1;
|
range.extent.layers = image.last_array - image.base_array + 1;
|
||||||
type = ConvertImageViewType(image.GetType());
|
type = ConvertImageViewType(image.GetType());
|
||||||
|
|
||||||
|
@ -221,7 +221,8 @@ ImageId TextureCache::FindImage(const ImageInfo& info, FindFlags flags) {
|
|||||||
!IsVulkanFormatCompatible(info.pixel_format, cache_image.info.pixel_format)) {
|
!IsVulkanFormatCompatible(info.pixel_format, cache_image.info.pixel_format)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ASSERT(cache_image.info.type == info.type || True(flags & FindFlags::RelaxFmt));
|
ASSERT((cache_image.info.type == info.type || info.size == Extent3D{1, 1, 1} ||
|
||||||
|
True(flags & FindFlags::RelaxFmt)));
|
||||||
image_id = cache_id;
|
image_id = cache_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +38,10 @@ struct Extent3D {
|
|||||||
u32 depth;
|
u32 depth;
|
||||||
|
|
||||||
auto operator<=>(const Extent3D&) const = default;
|
auto operator<=>(const Extent3D&) const = default;
|
||||||
|
|
||||||
|
bool operator==(const Extent3D& other) const {
|
||||||
|
return width == other.width && height == other.height && depth == other.depth;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SubresourceLayers {
|
struct SubresourceLayers {
|
||||||
|
Loading…
Reference in New Issue
Block a user