From 351e4861ca9648ac22b73cce369412f40c74b6ec Mon Sep 17 00:00:00 2001 From: fireph <443370+fireph@users.noreply.github.com> Date: Sun, 6 Oct 2024 03:00:35 -0700 Subject: [PATCH 01/93] Working touchpad support (#1257) * Working touchpad support Tested on PS5 controller plugged in via USB. * fix lint --- src/core/libraries/pad/pad.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index 6c3b1f56c..b671e0077 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -368,12 +368,13 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { pData->angularVelocity.x = 0.0f; pData->angularVelocity.y = 0.0f; pData->angularVelocity.z = 0.0f; - pData->touchData.touchNum = 0; - pData->touchData.touch[0].x = 0; - pData->touchData.touch[0].y = 0; + pData->touchData.touchNum = + (state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0); + pData->touchData.touch[0].x = state.touchpad[0].x; + pData->touchData.touch[0].y = state.touchpad[0].y; pData->touchData.touch[0].id = 1; - pData->touchData.touch[1].x = 0; - pData->touchData.touch[1].y = 0; + pData->touchData.touch[1].x = state.touchpad[1].x; + pData->touchData.touch[1].y = state.touchpad[1].y; pData->touchData.touch[1].id = 2; pData->timestamp = state.time; pData->connected = true; // isConnected; //TODO fix me proper From 4ce95e55e0614749057bb44c609efcf079c43932 Mon Sep 17 00:00:00 2001 From: tGecko Date: Sun, 6 Oct 2024 13:53:53 +0200 Subject: [PATCH 02/93] add log type to log file (#1260) --- src/emulator.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/emulator.cpp b/src/emulator.cpp index 721151ccc..6649b7bba 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -58,6 +58,7 @@ Emulator::Emulator() { LOG_INFO(Loader, "Branch {}", Common::g_scm_branch); LOG_INFO(Loader, "Description {}", Common::g_scm_desc); + LOG_INFO(Config, "General Logtype: {}", Config::getLogType()); LOG_INFO(Config, "General isNeo: {}", Config::isNeoMode()); LOG_INFO(Config, "GPU isNullGpu: {}", Config::nullGpu()); LOG_INFO(Config, "GPU shouldDumpShaders: {}", Config::dumpShaders()); From 310814ac716534b20fbfeb0d7983dcaebb9d161f Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Sun, 6 Oct 2024 19:43:59 +0300 Subject: [PATCH 03/93] shader_recompiler: Support for more offset layouts (#1270) --- .../backend/spirv/emit_spirv.cpp | 6 ++++-- .../backend/spirv/spirv_emit_context.cpp | 18 ++++++++++-------- src/shader_recompiler/frontend/copy_shader.cpp | 16 +++++++++------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index a585f3283..f199860af 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -27,8 +27,10 @@ static constexpr spv::ExecutionMode GetInputPrimitiveType(AmdGpu::PrimitiveType case AmdGpu::PrimitiveType::TriangleList: case AmdGpu::PrimitiveType::TriangleStrip: return spv::ExecutionMode::Triangles; + case AmdGpu::PrimitiveType::AdjTriangleList: + return spv::ExecutionMode::InputTrianglesAdjacency; default: - UNREACHABLE(); + UNREACHABLE_MSG("Unknown input primitive type {}", u32(type)); } } @@ -41,7 +43,7 @@ static constexpr spv::ExecutionMode GetOutputPrimitiveType(AmdGpu::GsOutputPrimi case AmdGpu::GsOutputPrimitiveType::TriangleStrip: return spv::ExecutionMode::OutputTriangleStrip; default: - UNREACHABLE(); + UNREACHABLE_MSG("Unknown output primitive type {}", u32(type)); } } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index f5b60d51d..c753b4a47 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -34,14 +34,17 @@ std::string_view StageName(Stage stage) { throw InvalidArgument("Invalid stage {}", u32(stage)); } -static constexpr u32 NumVertices(AmdGpu::GsOutputPrimitiveType type) { +static constexpr u32 NumVertices(AmdGpu::PrimitiveType type) { switch (type) { - case AmdGpu::GsOutputPrimitiveType::PointList: + case AmdGpu::PrimitiveType::PointList: return 1u; - case AmdGpu::GsOutputPrimitiveType::LineStrip: + case AmdGpu::PrimitiveType::LineList: return 2u; - case AmdGpu::GsOutputPrimitiveType::TriangleStrip: + case AmdGpu::PrimitiveType::TriangleList: + case AmdGpu::PrimitiveType::TriangleStrip: return 3u; + case AmdGpu::PrimitiveType::AdjTriangleList: + return 6u; default: UNREACHABLE(); } @@ -321,16 +324,15 @@ void EmitContext::DefineInputs() { MemberDecorate(gl_per_vertex, 2, spv::Decoration::BuiltIn, static_cast(spv::BuiltIn::ClipDistance)); Decorate(gl_per_vertex, spv::Decoration::Block); - const auto vertices_in = - TypeArray(gl_per_vertex, ConstU32(NumVertices(runtime_info.gs_info.out_primitive[0]))); + const auto num_verts_in = NumVertices(runtime_info.gs_info.in_primitive); + const auto vertices_in = TypeArray(gl_per_vertex, ConstU32(num_verts_in)); gl_in = Name(DefineVar(vertices_in, spv::StorageClass::Input), "gl_in"); interfaces.push_back(gl_in); const auto num_params = runtime_info.gs_info.in_vertex_data_size / 4 - 1u; for (int param_id = 0; param_id < num_params; ++param_id) { const IR::Attribute param{IR::Attribute::Param0 + param_id}; - const Id type{ - TypeArray(F32[4], ConstU32(NumVertices(runtime_info.gs_info.out_primitive[0])))}; + const Id type{TypeArray(F32[4], ConstU32(num_verts_in))}; const Id id{DefineInput(type, param_id)}; Name(id, fmt::format("in_attr{}", param_id)); input_params[param_id] = {id, input_f32, F32[1], 4}; diff --git a/src/shader_recompiler/frontend/copy_shader.cpp b/src/shader_recompiler/frontend/copy_shader.cpp index a194aec95..363c1c821 100644 --- a/src/shader_recompiler/frontend/copy_shader.cpp +++ b/src/shader_recompiler/frontend/copy_shader.cpp @@ -15,18 +15,18 @@ CopyShaderData ParseCopyShader(const std::span& code) { ASSERT_MSG(code[0] == token_mov_vcchi, "First instruction is not s_mov_b32 vcc_hi, #imm"); std::array offsets{}; - std::fill(offsets.begin(), offsets.end(), -1); + offsets.fill(-1); + + std::array sources{}; + sources.fill(-1); CopyShaderData data{}; - Gcn::OperandField sgpr{}; auto last_attr{IR::Attribute::Position0}; - s32 soffset{0}; while (!code_slice.atEnd()) { auto inst = decoder.decodeInstruction(code_slice); switch (inst.opcode) { case Gcn::Opcode::S_MOVK_I32: { - sgpr = inst.dst[0].field; - soffset = inst.control.sopk.simm; + sources[inst.dst[0].code] = inst.control.sopk.simm; break; } case Gcn::Opcode::EXP: { @@ -46,8 +46,9 @@ CopyShaderData ParseCopyShader(const std::span& code) { case Gcn::Opcode::BUFFER_LOAD_DWORD: { offsets[inst.src[1].code] = inst.control.mubuf.offset; if (inst.src[3].field != Gcn::OperandField::ConstZero) { - ASSERT(inst.src[3].field == sgpr); - offsets[inst.src[1].code] += soffset; + const u32 index = inst.src[3].code; + ASSERT(sources[index] != -1); + offsets[inst.src[1].code] += sources[index]; } break; } @@ -59,6 +60,7 @@ CopyShaderData ParseCopyShader(const std::span& code) { if (last_attr != IR::Attribute::Position0) { data.num_attrs = static_cast(last_attr) - static_cast(IR::Attribute::Param0) + 1; } + return data; } From 3c0255b9537201bd179240b99b6719302e24014e Mon Sep 17 00:00:00 2001 From: baggins183 Date: Sun, 6 Oct 2024 12:34:40 -0700 Subject: [PATCH 04/93] DebugPrintf in shaders (#1252) * Add shader debug print opcode that uses NonSemantic DebugPrintf extension * small correction for flags in Inst * Fix IR Debug Print. Add StringLiteral op * add missing microinstruction changes for debugprint * cleanup. delete vaarg stuff. Smuggle format string in Info and flags * more cleanup * more * (dont merge??) update sirit submodule * fix num args 4 -> 5 * add notes about DebugPrint IR op * use NumArgsOf again * copyright * update sirit submodule * fix clangformat * add new Value variant for string literal. Use arg0 for fmt string * remove string pool changes * Update src/shader_recompiler/ir/value.cpp Co-authored-by: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> --------- Co-authored-by: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> --- externals/sirit | 2 +- .../backend/spirv/emit_spirv.cpp | 2 + .../backend/spirv/emit_spirv_instructions.h | 1 + .../backend/spirv/emit_spirv_special.cpp | 8 ++++ .../backend/spirv/spirv_emit_context.cpp | 2 + src/shader_recompiler/info.h | 1 + src/shader_recompiler/ir/debug_print.h | 21 +++++++++++ src/shader_recompiler/ir/ir_emitter.cpp | 37 +++++++++++++++++++ src/shader_recompiler/ir/ir_emitter.h | 2 + src/shader_recompiler/ir/microinstruction.cpp | 1 + src/shader_recompiler/ir/opcodes.h | 3 +- src/shader_recompiler/ir/opcodes.inc | 1 + src/shader_recompiler/ir/type.cpp | 7 ++-- src/shader_recompiler/ir/type.h | 1 + src/shader_recompiler/ir/value.cpp | 5 +++ src/shader_recompiler/ir/value.h | 11 ++++++ 16 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 src/shader_recompiler/ir/debug_print.h diff --git a/externals/sirit b/externals/sirit index 37090c74c..6cecb95d6 160000 --- a/externals/sirit +++ b/externals/sirit @@ -1 +1 @@ -Subproject commit 37090c74cc6e680f2bc334cac8fd182f7634a1f6 +Subproject commit 6cecb95d679c82c413d1f989e0b7ad9af130600d diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index f199860af..f90e9db77 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -70,6 +70,8 @@ ArgType Arg(EmitContext& ctx, const IR::Value& arg) { return arg.ScalarReg(); } else if constexpr (std::is_same_v) { return arg.VectorReg(); + } else if constexpr (std::is_same_v) { + return arg.StringLiteral(); } } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index ec86e5cc9..6ae1ef24a 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -48,6 +48,7 @@ void EmitPrologue(EmitContext& ctx); void EmitEpilogue(EmitContext& ctx); void EmitDiscard(EmitContext& ctx); void EmitDiscardCond(EmitContext& ctx, Id condition); +void EmitDebugPrint(EmitContext& ctx, IR::Inst* inst, Id arg0, Id arg1, Id arg2, Id arg3, Id arg4); void EmitBarrier(EmitContext& ctx); void EmitWorkgroupMemoryBarrier(EmitContext& ctx); void EmitDeviceMemoryBarrier(EmitContext& ctx); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp index c12e4997f..e9ffdcce8 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp @@ -3,6 +3,7 @@ #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" #include "shader_recompiler/backend/spirv/spirv_emit_context.h" +#include "shader_recompiler/ir/debug_print.h" namespace Shader::Backend::SPIRV { @@ -57,4 +58,11 @@ void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) { throw NotImplementedException("Geometry streams"); } +void EmitDebugPrint(EmitContext& ctx, IR::Inst* inst, Id fmt, Id arg0, Id arg1, Id arg2, Id arg3) { + IR::DebugPrintFlags flags = inst->Flags(); + std::array fmt_args = {arg0, arg1, arg2, arg3}; + auto fmt_args_span = std::span(fmt_args.begin(), fmt_args.begin() + flags.num_args); + ctx.OpDebugPrintf(fmt, fmt_args_span); +} + } // namespace Shader::Backend::SPIRV diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index c753b4a47..5eee656dd 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -91,6 +91,8 @@ Id EmitContext::Def(const IR::Value& value) { return ConstF32(value.F32()); case IR::Type::F64: return Constant(F64[1], value.F64()); + case IR::Type::StringLiteral: + return String(value.StringLiteral()); default: throw NotImplementedException("Immediate type {}", value.Type()); } diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index 7fc7be753..78a6805fd 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include #include #include "common/assert.h" diff --git a/src/shader_recompiler/ir/debug_print.h b/src/shader_recompiler/ir/debug_print.h new file mode 100644 index 000000000..1ab1575de --- /dev/null +++ b/src/shader_recompiler/ir/debug_print.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/bit_field.h" +#include "shader_recompiler/ir/opcodes.h" +#include "src/common/types.h" + +#pragma once + +namespace Shader::IR { + +constexpr size_t DEBUGPRINT_NUM_FORMAT_ARGS = NumArgsOf(IR::Opcode::DebugPrint) - 1; + +union DebugPrintFlags { + u32 raw; + // For now, only flag is the number of variadic format args actually used + // So bitfield not really needed + BitField<0, 32, u32> num_args; +}; + +} // namespace Shader::IR \ No newline at end of file diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index 01336c567..4f5eb5c33 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -1,10 +1,15 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include +#include +#include "common/assert.h" #include "shader_recompiler/exception.h" +#include "shader_recompiler/ir/debug_print.h" #include "shader_recompiler/ir/ir_emitter.h" +#include "shader_recompiler/ir/opcodes.h" #include "shader_recompiler/ir/value.h" namespace Shader::IR { @@ -1553,6 +1558,38 @@ void IREmitter::ImageWrite(const Value& handle, const Value& coords, const Value Inst(Opcode::ImageWrite, Flags{info}, handle, coords, color); } +// Debug print maps to SPIRV's NonSemantic DebugPrintf instruction +// Renderdoc will hook in its own implementation of the SPIRV instruction +// Renderdoc accepts format specifiers, e.g. %u, listed here: +// https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/main/docs/debug_printf.md +// +// fmt must be a string literal (pointer is shallow copied into a Value) +// Example usage: +// ir.DebugPrint("invocation xyz: (%u, %u, %u)", +// {ir.GetVectorReg(IR::VectorReg::V0), +// ir.GetVectorReg(IR::VectorReg::V1), +// ir.GetVectorReg(IR::VectorReg::V2)}); +void IREmitter::DebugPrint(const char* fmt, boost::container::small_vector format_args) { + std::array args; + + ASSERT_MSG(format_args.size() < DEBUGPRINT_NUM_FORMAT_ARGS, + "DebugPrint only supports up to {} format args", DEBUGPRINT_NUM_FORMAT_ARGS); + + for (int i = 0; i < format_args.size(); i++) { + args[i] = format_args[i]; + } + + for (int i = format_args.size(); i < DEBUGPRINT_NUM_FORMAT_ARGS; i++) { + args[i] = Inst(Opcode::Void); + } + + IR::Value fmt_val{fmt}; + + DebugPrintFlags flags; + flags.num_args.Assign(format_args.size()); + Inst(Opcode::DebugPrint, Flags{flags}, fmt_val, args[0], args[1], args[2], args[3]); +} + void IREmitter::EmitVertex() { Inst(Opcode::EmitVertex); } diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index 8657c430b..2ebac037e 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -6,6 +6,7 @@ #include #include +#include "shader_recompiler/info.h" #include "shader_recompiler/ir/attribute.h" #include "shader_recompiler/ir/basic_block.h" #include "shader_recompiler/ir/condition.h" @@ -43,6 +44,7 @@ public: void Epilogue(); void Discard(); void Discard(const U1& cond); + void DebugPrint(const char* fmt, boost::container::small_vector args); void Barrier(); void WorkgroupMemoryBarrier(); diff --git a/src/shader_recompiler/ir/microinstruction.cpp b/src/shader_recompiler/ir/microinstruction.cpp index 8d606a6cc..f0b4882b3 100644 --- a/src/shader_recompiler/ir/microinstruction.cpp +++ b/src/shader_recompiler/ir/microinstruction.cpp @@ -89,6 +89,7 @@ bool Inst::MayHaveSideEffects() const noexcept { case Opcode::ImageAtomicOr32: case Opcode::ImageAtomicXor32: case Opcode::ImageAtomicExchange32: + case Opcode::DebugPrint: case Opcode::EmitVertex: case Opcode::EmitPrimitive: return true; diff --git a/src/shader_recompiler/ir/opcodes.h b/src/shader_recompiler/ir/opcodes.h index 06f1a6117..2cea70090 100644 --- a/src/shader_recompiler/ir/opcodes.h +++ b/src/shader_recompiler/ir/opcodes.h @@ -51,6 +51,7 @@ constexpr Type F32x4{Type::F32x4}; constexpr Type F64x2{Type::F64x2}; constexpr Type F64x3{Type::F64x3}; constexpr Type F64x4{Type::F64x4}; +constexpr Type StringLiteral{Type::StringLiteral}; constexpr OpcodeMeta META_TABLE[]{ #define OPCODE(name_token, type_token, ...) \ @@ -81,7 +82,7 @@ constexpr u8 NUM_ARGS[]{ } /// Get the number of arguments an opcode accepts -[[nodiscard]] inline size_t NumArgsOf(Opcode op) noexcept { +[[nodiscard]] constexpr inline size_t NumArgsOf(Opcode op) noexcept { return static_cast(Detail::NUM_ARGS[static_cast(op)]); } diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index c69dc90a5..41e94ab13 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -14,6 +14,7 @@ OPCODE(Prologue, Void, OPCODE(Epilogue, Void, ) OPCODE(Discard, Void, ) OPCODE(DiscardCond, Void, U1, ) +OPCODE(DebugPrint, Void, StringLiteral, Opaque, Opaque, Opaque, Opaque, ) // Constant memory operations OPCODE(ReadConst, U32, U32x2, U32, ) diff --git a/src/shader_recompiler/ir/type.cpp b/src/shader_recompiler/ir/type.cpp index 9d303b4db..08157f108 100644 --- a/src/shader_recompiler/ir/type.cpp +++ b/src/shader_recompiler/ir/type.cpp @@ -9,10 +9,9 @@ namespace Shader::IR { std::string NameOf(Type type) { static constexpr std::array names{ - "Opaque", "Label", "Reg", "Pred", "Attribute", "U1", "U8", "U16", "U32", - "U64", "F16", "F32", "F64", "U32x2", "U32x3", "U32x4", "F16x2", "F16x3", - "F16x4", "F32x2", "F32x3", "F32x4", "F64x2", "F64x3", "F64x4", - }; + "Opaque", "Label", "Reg", "Pred", "Attribute", "U1", "U8", "U16", "U32", + "U64", "F16", "F32", "F64", "U32x2", "U32x3", "U32x4", "F16x2", "F16x3", + "F16x4", "F32x2", "F32x3", "F32x4", "F64x2", "F64x3", "F64x4", "StringLiteral"}; const size_t bits{static_cast(type)}; if (bits == 0) { return "Void"; diff --git a/src/shader_recompiler/ir/type.h b/src/shader_recompiler/ir/type.h index d7f47e1de..ec855a77e 100644 --- a/src/shader_recompiler/ir/type.h +++ b/src/shader_recompiler/ir/type.h @@ -36,6 +36,7 @@ enum class Type { F64x2 = 1 << 22, F64x3 = 1 << 23, F64x4 = 1 << 24, + StringLiteral = 1 << 25, }; DECLARE_ENUM_FLAG_OPERATORS(Type) diff --git a/src/shader_recompiler/ir/value.cpp b/src/shader_recompiler/ir/value.cpp index 86e5dd141..cf7a70f76 100644 --- a/src/shader_recompiler/ir/value.cpp +++ b/src/shader_recompiler/ir/value.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include "shader_recompiler/ir/value.h" namespace Shader::IR { @@ -27,6 +28,8 @@ Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {} Value::Value(f64 value) noexcept : type{Type::F64}, imm_f64{value} {} +Value::Value(const char* value) noexcept : type{Type::StringLiteral}, string_literal{value} {} + IR::Type Value::Type() const noexcept { if (IsPhi()) { // The type of a phi node is stored in its flags @@ -69,6 +72,8 @@ bool Value::operator==(const Value& other) const { case Type::U64: case Type::F64: return imm_u64 == other.imm_u64; + case Type::StringLiteral: + return std::string_view(string_literal) == other.string_literal; case Type::U32x2: case Type::U32x3: case Type::U32x4: diff --git a/src/shader_recompiler/ir/value.h b/src/shader_recompiler/ir/value.h index db939eaa5..060b9d2bb 100644 --- a/src/shader_recompiler/ir/value.h +++ b/src/shader_recompiler/ir/value.h @@ -39,6 +39,7 @@ public: explicit Value(f32 value) noexcept; explicit Value(u64 value) noexcept; explicit Value(f64 value) noexcept; + explicit Value(const char* value) noexcept; [[nodiscard]] bool IsIdentity() const noexcept; [[nodiscard]] bool IsPhi() const noexcept; @@ -60,6 +61,7 @@ public: [[nodiscard]] f32 F32() const; [[nodiscard]] u64 U64() const; [[nodiscard]] f64 F64() const; + [[nodiscard]] const char* StringLiteral() const; [[nodiscard]] bool operator==(const Value& other) const; [[nodiscard]] bool operator!=(const Value& other) const; @@ -78,6 +80,7 @@ private: f32 imm_f32; u64 imm_u64; f64 imm_f64; + const char* string_literal; }; }; static_assert(static_cast(IR::Type::Void) == 0, "memset relies on IR::Type being zero"); @@ -348,6 +351,14 @@ inline f64 Value::F64() const { return imm_f64; } +inline const char* Value::StringLiteral() const { + if (IsIdentity()) { + return inst->Arg(0).StringLiteral(); + } + DEBUG_ASSERT(type == Type::StringLiteral); + return string_literal; +} + [[nodiscard]] inline bool IsPhi(const Inst& inst) { return inst.GetOpcode() == Opcode::Phi; } From 75c92a7cd1f60fcef09c0a2a36da3090b9aa5bf3 Mon Sep 17 00:00:00 2001 From: Dzmitry Dubrova Date: Sun, 6 Oct 2024 22:34:55 +0300 Subject: [PATCH 05/93] net: Stub sceNetErrnoLoc (#1271) --- src/core/libraries/network/net.cpp | 6 ++++-- src/core/libraries/network/net.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp index 2c03dde3e..35d5851ea 100644 --- a/src/core/libraries/network/net.cpp +++ b/src/core/libraries/network/net.cpp @@ -18,6 +18,8 @@ namespace Libraries::Net { +static thread_local int32_t net_errno = 0; + int PS4_SYSV_ABI in6addr_any() { LOG_ERROR(Lib_Net, "(STUBBED) called"); return ORBIS_OK; @@ -563,9 +565,9 @@ int PS4_SYSV_ABI sceNetEpollWait() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetErrnoLoc() { +int* PS4_SYSV_ABI sceNetErrnoLoc() { LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; + return &net_errno; } int PS4_SYSV_ABI sceNetEtherNtostr() { diff --git a/src/core/libraries/network/net.h b/src/core/libraries/network/net.h index eababdb67..3f010f685 100644 --- a/src/core/libraries/network/net.h +++ b/src/core/libraries/network/net.h @@ -136,7 +136,7 @@ int PS4_SYSV_ABI sceNetEpollControl(); int PS4_SYSV_ABI sceNetEpollCreate(); int PS4_SYSV_ABI sceNetEpollDestroy(); int PS4_SYSV_ABI sceNetEpollWait(); -int PS4_SYSV_ABI sceNetErrnoLoc(); +int* PS4_SYSV_ABI sceNetErrnoLoc(); int PS4_SYSV_ABI sceNetEtherNtostr(); int PS4_SYSV_ABI sceNetEtherStrton(); int PS4_SYSV_ABI sceNetEventCallbackCreate(); From d7e5b5f13f3ef95ee9808dedb0c6c7293b4a360a Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Mon, 7 Oct 2024 13:04:09 +0300 Subject: [PATCH 06/93] updated sdl to fix touchpad issue (#1275) --- externals/date | 2 +- externals/sdl3 | 2 +- src/audio_core/sdl_audio.cpp | 2 +- src/imgui/renderer/imgui_impl_sdl3.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/externals/date b/externals/date index 51ce7e131..dd8affc6d 160000 --- a/externals/date +++ b/externals/date @@ -1 +1 @@ -Subproject commit 51ce7e131079c061533d741be5fe7cca57f2faac +Subproject commit dd8affc6de5755e07638bf0a14382d29549d6ee9 diff --git a/externals/sdl3 b/externals/sdl3 index 0548050fc..54e622c2e 160000 --- a/externals/sdl3 +++ b/externals/sdl3 @@ -1 +1 @@ -Subproject commit 0548050fc5a4edf1f47c3633c2cd06d8762b5532 +Subproject commit 54e622c2e6af456bfef382fae44c17682d5ac88a diff --git a/src/audio_core/sdl_audio.cpp b/src/audio_core/sdl_audio.cpp index 894f5da55..7fed42a44 100644 --- a/src/audio_core/sdl_audio.cpp +++ b/src/audio_core/sdl_audio.cpp @@ -103,7 +103,7 @@ s32 SDLAudio::AudioOutOutput(s32 handle, const void* ptr) { const size_t data_size = port.samples_num * port.sample_size * port.channels_num; - SDL_bool result = SDL_PutAudioStreamData(port.stream, ptr, data_size); + bool result = SDL_PutAudioStreamData(port.stream, ptr, data_size); lock.unlock(); // Unlock only after necessary operations diff --git a/src/imgui/renderer/imgui_impl_sdl3.cpp b/src/imgui/renderer/imgui_impl_sdl3.cpp index bb194bff7..cecf504a7 100644 --- a/src/imgui/renderer/imgui_impl_sdl3.cpp +++ b/src/imgui/renderer/imgui_impl_sdl3.cpp @@ -581,7 +581,7 @@ static void UpdateMouseData() { // (below) // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries // shouldn't e.g. trigger other operations outside - SDL_CaptureMouse((bd->mouse_buttons_down != 0) ? SDL_TRUE : SDL_FALSE); + SDL_CaptureMouse((bd->mouse_buttons_down != 0) ? true : false); SDL_Window* focused_window = SDL_GetKeyboardFocus(); const bool is_app_focused = (bd->window == focused_window); From 20db37f2351a0670ba666fa247ba0d158f299912 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Mon, 7 Oct 2024 08:52:38 -0300 Subject: [PATCH 07/93] fix update linux console (#1238) --- src/qt_gui/check_update.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/qt_gui/check_update.cpp b/src/qt_gui/check_update.cpp index 023c6e7bb..48adbb88a 100644 --- a/src/qt_gui/check_update.cpp +++ b/src/qt_gui/check_update.cpp @@ -454,6 +454,10 @@ void CheckUpdate::Install() { " sleep 2\n" " extract_file\n" " sleep 2\n" + " if pgrep -f \"Shadps4-qt.AppImage\" > /dev/null; then\n" + " pkill -f \"Shadps4-qt.AppImage\"\n" + " sleep 2\n" + " fi\n" " cp -r \"%2/\"* \"%3/\"\n" " sleep 2\n" " rm \"%3/update.sh\"\n" @@ -525,4 +529,4 @@ void CheckUpdate::Install() { this, tr("Error"), QString(tr("Failed to create the update script file") + ":\n" + scriptFileName)); } -} \ No newline at end of file +} From 7389cf3e897bdf489f9260cfdef6fdddee88f72c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Mon, 7 Oct 2024 15:29:07 +0200 Subject: [PATCH 08/93] PM4 old removed + fixes (#1259) --- src/qt_gui/settings_dialog.cpp | 4 +--- src/qt_gui/settings_dialog.h | 2 +- src/qt_gui/translations/ar.ts | 10 ---------- src/qt_gui/translations/da_DK.ts | 10 ---------- src/qt_gui/translations/de.ts | 10 ---------- src/qt_gui/translations/el.ts | 10 ---------- src/qt_gui/translations/en.ts | 10 ---------- src/qt_gui/translations/es_ES.ts | 10 ---------- src/qt_gui/translations/fa_IR.ts | 10 ---------- src/qt_gui/translations/fi.ts | 10 ---------- src/qt_gui/translations/fr.ts | 10 ---------- src/qt_gui/translations/hu_HU.ts | 10 ---------- src/qt_gui/translations/id.ts | 10 ---------- src/qt_gui/translations/it.ts | 10 ---------- src/qt_gui/translations/ja_JP.ts | 10 ---------- src/qt_gui/translations/ko_KR.ts | 10 ---------- src/qt_gui/translations/lt_LT.ts | 10 ---------- src/qt_gui/translations/nb.ts | 10 ---------- src/qt_gui/translations/nl.ts | 10 ---------- src/qt_gui/translations/pl_PL.ts | 10 ---------- src/qt_gui/translations/pt_BR.ts | 10 ---------- src/qt_gui/translations/ro_RO.ts | 10 ---------- src/qt_gui/translations/ru_RU.ts | 10 ---------- src/qt_gui/translations/sq.ts | 10 ---------- src/qt_gui/translations/tr_TR.ts | 10 ---------- src/qt_gui/translations/vi_VN.ts | 10 ---------- src/qt_gui/translations/zh_CN.ts | 10 ---------- src/qt_gui/translations/zh_TW.ts | 10 ---------- 28 files changed, 2 insertions(+), 264 deletions(-) diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 4a42e1961..1c0b0bc07 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -334,8 +334,6 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("dumpShadersCheckBox"); } else if (elementName == "nullGpuCheckBox") { text = tr("nullGpuCheckBox"); - } else if (elementName == "dumpPM4CheckBox") { - text = tr("dumpPM4CheckBox"); } // Debug @@ -352,7 +350,7 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { ui->descriptionText->setText(text.replace("\\n", "\n")); } -bool SettingsDialog::override(QObject* obj, QEvent* event) { +bool SettingsDialog::eventFilter(QObject* obj, QEvent* event) { if (event->type() == QEvent::Enter || event->type() == QEvent::Leave) { if (qobject_cast(obj)) { bool hovered = (event->type() == QEvent::Enter); diff --git a/src/qt_gui/settings_dialog.h b/src/qt_gui/settings_dialog.h index 71307d398..85ae39aaa 100644 --- a/src/qt_gui/settings_dialog.h +++ b/src/qt_gui/settings_dialog.h @@ -21,7 +21,7 @@ public: explicit SettingsDialog(std::span physical_devices, QWidget* parent = nullptr); ~SettingsDialog(); - bool override(QObject* obj, QEvent* event); + bool eventFilter(QObject* obj, QEvent* event) override; void updateNoteTextEdit(const QString& groupName); int exec() override; diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index b385b491b..ca23620bb 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -474,11 +474,6 @@ Enable NULL GPU تمكين وحدة معالجة الرسومات الفارغة - - - Enable PM4 Dumping - PM4 تمكين تفريغ - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox تمكين GPU الافتراضية:\nلأغراض تصحيح الأخطاء التقنية، يقوم بتعطيل عرض اللعبة كما لو لم يكن هناك بطاقة رسومات. - - - dumpPM4CheckBox - تمكين تفريغ PM4:\nلأغراض تصحيح الأخطاء التقنية، يحفظ بيانات تعليمات GPU الأولية في مجلد أثناء معالجتها. - debugDump diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 73bc37189..0d81e2357 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -474,11 +474,6 @@ Enable NULL GPU Enable NULL GPU - - - Enable PM4 Dumping - Enable PM4 Dumping - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Aktiver virtuel GPU:\nTil teknisk fejlfinding deaktiverer det spilvisning, som om der ikke var et grafikkort. - - - dumpPM4CheckBox - Aktiver dumping af PM4:\nTil teknisk fejlfinding gemmer det rå GPU-kommandoer i en mappe under behandling. - debugDump diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index 771d3bf8e..9e31afd6e 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -474,11 +474,6 @@ Enable NULL GPU NULL GPU aktivieren - - - Enable PM4 Dumping - PM4-Dumping aktivieren - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Virtuelle GPU aktivieren:\nFür das technische Debugging deaktiviert es die Spielanzeige, als ob keine Grafikkarte vorhanden wäre. - - - dumpPM4CheckBox - PM4-Dumping aktivieren:\nZum technischen Debuggen speichert es rohe GPU-Befehlsdaten in einem Ordner während der Verarbeitung. - debugDump diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index f2265c7d7..de10fc3a8 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -474,11 +474,6 @@ Enable NULL GPU Enable NULL GPU - - - Enable PM4 Dumping - Enable PM4 Dumping - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Ενεργοποίηση Εικονικής GPU:\nΓια τεχνικό εντοπισμό σφαλμάτων, απενεργοποιεί την εμφάνιση του παιχνιδιού σαν να μην υπάρχει κάρτα γραφικών. - - - dumpPM4CheckBox - Ενεργοποίηση Καταγραφής PM4:\nΓια τεχνικό εντοπισμό σφαλμάτων, αποθηκεύει τις ακατέργαστες εντολές της GPU σε φάκελο κατά την επεξεργασία. - debugDump diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index e45ac475a..4052b4ef5 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -474,11 +474,6 @@ Enable NULL GPU Enable NULL GPU - - - Enable PM4 Dumping - Enable PM4 Dumping - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card. - - - dumpPM4CheckBox - Enable PM4 Dumping:\nFor the sake of technical debugging, saves raw GPU instruction data to a folder as the emulator processes it. - debugDump diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 4242bc91e..86a3e1adc 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -474,11 +474,6 @@ Enable NULL GPU Habilitar GPU NULL - - - Enable PM4 Dumping - Habilitar volcado de PM4 - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Habilitar GPU Nula:\nPor el bien de la depuración técnica, desactiva el renderizado del juego como si no hubiera tarjeta gráfica. - - - dumpPM4CheckBox - Habilitar la Volcadura de PM4:\nPor el bien de la depuración técnica, guarda los datos de instrucciones crudas de GPU en una carpeta a medida que el emulador los procesa. - debugDump diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 15e07c886..a613af70b 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -474,11 +474,6 @@ Enable NULL GPU NULL GPU فعال کردن - - - Enable PM4 Dumping - PM4 Dumping فعال کردن - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card. - - - dumpPM4CheckBox - Enable PM4 Dumping:\nFor the sake of technical debugging, saves raw GPU instruction data to a folder as the emulator processes it. - debugDump diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index a2b319280..e4010b872 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -474,11 +474,6 @@ Enable NULL GPU Enable NULL GPU - - - Enable PM4 Dumping - Enable PM4 Dumping - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Ota Null GPU käyttöön:\nTeknistä vianetsintää varten pelin renderöinti estetään niin, että ikään kuin grafiikkakorttia ei olisi. - - - dumpPM4CheckBox - Ota PM4 dumpaus käyttöön:\nTeknistä vianetsintää varten raakoja GPU-ohjeita tallennetaan kansioon emulaattorin käsitellessä sitä. - debugDump diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index 05c70f195..b11b585c9 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -474,11 +474,6 @@ Enable NULL GPU NULL GPU - - - Enable PM4 Dumping - Dumper le PM4 - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Activer le GPU nul :\nPour le débogage technique, désactive le rendu du jeu comme s'il n'y avait pas de carte graphique. - - - dumpPM4CheckBox - Activer l'exportation PM4 :\nPour le débogage technique, enregistre les données brutes des instructions GPU dans un dossier pendant que l'émulateur les traite. - debugDump diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index fb348ee29..5986193c4 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -474,11 +474,6 @@ Enable NULL GPU NULL GPU Engedélyezése - - - Enable PM4 Dumping - PM4 Dumpolás Engedélyezése - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Null GPU engedélyezése:\nMűszaki hibaelhárítás céljából letiltja a játék renderelését, mintha nem lenne grafikus kártya. - - - dumpPM4CheckBox - PM4 dumpolás engedélyezése:\nMűszaki hibaelhárítás céljából a nyers GPU utasítási adatokat elmenti egy mappába, ahogy az emulátor feldolgozza őket. - debugDump diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index 86c58c6ce..274029bd0 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -474,11 +474,6 @@ Enable NULL GPU Enable NULL GPU - - - Enable PM4 Dumping - Enable PM4 Dumping - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Aktifkan GPU Null:\nUntuk tujuan debugging teknis, menonaktifkan rendering permainan seolah-olah tidak ada kartu grafis. - - - dumpPM4CheckBox - Aktifkan Pembuangan PM4:\nUntuk tujuan debugging teknis, menyimpan data instruksi GPU mentah ke folder saat emulator memprosesnya. - debugDump diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index f5ea9a65c..07976865c 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -474,11 +474,6 @@ Enable NULL GPU Abilita NULL GPU - - - Enable PM4 Dumping - Abilita Dump PM4 - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Abilita GPU Null:\nPer scopi di debug tecnico, disabilita il rendering del gioco come se non ci fosse alcuna scheda grafica. - - - dumpPM4CheckBox - Abilita Pompaggio PM4:\nPer scopi di debug tecnico, salva i dati delle istruzioni GPU grezze in una cartella mentre l'emulatore li elabora. - debugDump diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index 166100db6..c3444906b 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -474,11 +474,6 @@ Enable NULL GPU NULL GPUを有効にする - - - Enable PM4 Dumping - PM4ダンプを有効にする - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Null GPUを有効にする:\n技術的なデバッグの目的で、グラフィックスカードがないかのようにゲームのレンダリングを無効にします。 - - - dumpPM4CheckBox - PM4ダンプを有効にする:\n技術的なデバッグの目的で、エミュレーターが処理している間に生のGPU命令データをフォルダーに保存します。 - debugDump diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index 93ceb9e35..e816e2f4b 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -474,11 +474,6 @@ Enable NULL GPU Enable NULL GPU - - - Enable PM4 Dumping - Enable PM4 Dumping - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card. - - - dumpPM4CheckBox - Enable PM4 Dumping:\nFor the sake of technical debugging, saves raw GPU instruction data to a folder as the emulator processes it. - debugDump diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index cac8ebbfc..d5b4ff3ad 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -474,11 +474,6 @@ Enable NULL GPU Enable NULL GPU - - - Enable PM4 Dumping - Enable PM4 Dumping - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Įjungti tuščią GPU:\nTechninio derinimo tikslais išjungia žaidimo renderiavimą, tarsi nebūtų grafikos plokštės. - - - dumpPM4CheckBox - Įjungti PM4 išmetimą:\nTechninio derinimo tikslais saugo žalius GPU nurodymų duomenis į aplanką, kai emuliatorius juos apdoroja. - debugDump diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index e0bc1a550..334600950 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -474,11 +474,6 @@ Enable NULL GPU Enable NULL GPU - - - Enable PM4 Dumping - Enable PM4 Dumping - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Aktiver Null GPU:\nFor teknisk feilsøking deaktiverer spillrendering som om det ikke var noe grafikkort. - - - dumpPM4CheckBox - Aktiver PM4 dumping:\nFor teknisk feilsøking lagrer rå GPU-instruksjonsdata i en mappe mens emulatoren behandler dem. - debugDump diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index 29a60ecc8..6e49d4640 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -474,11 +474,6 @@ Enable NULL GPU Enable NULL GPU - - - Enable PM4 Dumping - Enable PM4 Dumping - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Null GPU inschakelen:\nVoor technische foutopsporing schakelt de game-rendering uit alsof er geen grafische kaart is. - - - dumpPM4CheckBox - PM4 dumpen inschakelen:\nVoor technische foutopsporing slaat het ruwe GPU-instructiegegevens op in een map terwijl de emulator ze verwerkt. - debugDump diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index bd772b0fe..e95b8ba0c 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -474,11 +474,6 @@ Enable NULL GPU Wyłącz kartę graficzną - - - Enable PM4 Dumping - Włącz zgrywanie PM4 - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Włącz Null GPU:\nDla technicznego debugowania dezaktywuje renderowanie gry tak, jakby nie było karty graficznej. - - - dumpPM4CheckBox - Włącz zrzucanie PM4:\nDla technicznego debugowania zapisuje surowe dane instrukcji GPU w folderze, gdy emulator je przetwarza. - debugDump diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index 38836b1d0..e1570b2cd 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -474,11 +474,6 @@ Enable NULL GPU Ativar GPU NULA - - - Enable PM4 Dumping - Ativar Dumping de PM4 - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Ativar GPU NULA:\nDesativa a renderização do jogo para fins de depuração técnica, como se não houvesse nenhuma placa gráfica. - - - dumpPM4CheckBox - Ativar Dumping de PM4:\nArmazena os dados de instrução bruta da GPU em uma pasta enquanto o emulador os processa, para fins de depuração técnica. Recomendado deixar desativado. - debugDump diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index a5c3783bb..e98b7e5c9 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -474,11 +474,6 @@ Enable NULL GPU Enable NULL GPU - - - Enable PM4 Dumping - Enable PM4 Dumping - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Activează GPU Null:\nÎn scopuri de depanare tehnică, dezactivează redarea jocului ca și cum nu ar exista o placă grafică. - - - dumpPM4CheckBox - Activează salvarea PM4:\nÎn scopuri de depanare tehnică, salvează datele brute ale instrucțiunilor GPU într-un folder pe măsură ce emulatorul le procesează. - debugDump diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 13cc49f20..aaff66ef9 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -474,11 +474,6 @@ Enable NULL GPU Включить NULL GPU - - - Enable PM4 Dumping - Включить дамп PM4 - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Включить NULL GPU:\nДля технической отладки отключает рендеринг игры так, как будто графической карты нет. - - - dumpPM4CheckBox - Включить дамп PM4:\nДля технической отладки сохраняет необработанные данные инструкций GPU в папку, пока эмулятор их обрабатывает. - debugDump diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index d9f1d3859..c6e4c018d 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -474,11 +474,6 @@ Enable NULL GPU Aktivizo GPU-në NULL - - - Enable PM4 Dumping - Aktivizo Zbrazjen PM4 - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Aktivizo GPU Null:\nPër qëllime të debugimit teknik, deaktivizon renditjen e lojës sikur nuk do të kishte një kartë grafike. - - - dumpPM4CheckBox - Aktivizo dump-in e PM4:\nPër qëllime të debugimit teknik, ruan të dhënat e instruksioneve të GPU-së në një folder ndërsa emulatori i përpunon ato. - debugDump diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 32e5c4bd6..5cd71af34 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -474,11 +474,6 @@ Enable NULL GPU NULL GPU'yu Etkinleştir - - - Enable PM4 Dumping - PM4 Kaydını Etkinleştir - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Null GPU'yu Etkinleştir:\nTeknik hata ayıklama amacıyla, oyunun render edilmesini grafik kartı yokmuş gibi devre dışı bırakır. - - - dumpPM4CheckBox - PM4 Dışa Aktarmayı Etkinleştir:\nTeknik hata ayıklama amacıyla, emülatör bunları işlerken GPU komut verilerini bir klasöre kaydeder. - debugDump diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index 8f377f2a7..523683621 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -474,11 +474,6 @@ Enable NULL GPU Enable NULL GPU - - - Enable PM4 Dumping - Enable PM4 Dumping - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox Bật GPU Null:\nĐể mục đích gỡ lỗi kỹ thuật, vô hiệu hóa việc kết xuất trò chơi như thể không có card đồ họa. - - - dumpPM4CheckBox - Bật xuất PM4:\nĐể mục đích gỡ lỗi kỹ thuật, lưu dữ liệu lệnh GPU vào một thư mục khi trình giả lập xử lý chúng. - debugDump diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 0eae5a5bd..f2abc4f4b 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -474,11 +474,6 @@ Enable NULL GPU 启用 NULL GPU - - - Enable PM4 Dumping - 启用 PM4 转储 - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox 启用空 GPU:\n为了技术调试,将游戏渲染禁用,仿佛没有图形卡。 - - - dumpPM4CheckBox - 启用 PM4 转储:\n为了技术调试,在模拟器处理时将原始 GPU 指令数据保存到文件夹中。 - debugDump diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index cead22ecf..2d34f8d12 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -474,11 +474,6 @@ Enable NULL GPU Enable NULL GPU - - - Enable PM4 Dumping - Enable PM4 Dumping - Debug @@ -1053,11 +1048,6 @@ nullGpuCheckBox 啟用空GPU:\n為了技術調試,禁用遊戲渲染,彷彿沒有顯示卡。 - - - dumpPM4CheckBox - 啟用PM4轉儲:\n為了技術調試,將原始GPU指令數據在模擬器處理時保存到文件夾中。 - debugDump From ce53c412050c39c161a2ce2f6e18bdf6a9554a27 Mon Sep 17 00:00:00 2001 From: fireph <443370+fireph@users.noreply.github.com> Date: Mon, 7 Oct 2024 23:15:30 -0700 Subject: [PATCH 09/93] Add support to click touchpad using back button on non PS4/5 controllers (#1258) * Working touchpad support Tested on PS5 controller plugged in via USB. * fix lint * Add support to click touchpad using back button on other controllers Takes the back button and allows the user to change the behavior of how it clicks the touchpad. The current options are left, right, center, and none. * add description text * make more generic so translations can be supported in combobox * fix lint * linter again * support back button to touchpad for spacebar as well * linter at it again --- README.md | 2 +- src/common/config.cpp | 12 ++++++++ src/common/config.h | 2 ++ src/qt_gui/settings_dialog.cpp | 24 ++++++++++++++++ src/qt_gui/settings_dialog.ui | 52 +++++++++++++++++++++++++--------- src/qt_gui/translations/en.ts | 35 +++++++++++++++++++++++ src/sdl_window.cpp | 26 +++++++++++++++-- 7 files changed, 136 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index da01833e5..95428dfd0 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ PAD DOWN | DOWN | PAD LEFT | LEFT | PAD RIGHT | RIGHT | OPTIONS | RETURN | -TOUCH PAD | SPACE | +BACK BUTTON / TOUCH PAD | SPACE | L1 | Q | R1 | U | L2 | E | diff --git a/src/common/config.cpp b/src/common/config.cpp index 8ac3c694b..8c1c4f79d 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -41,6 +41,7 @@ static std::string logFilter; static std::string logType = "async"; static std::string userName = "shadPS4"; static std::string updateChannel; +static std::string backButtonBehavior = "left"; static bool useSpecialPad = false; static int specialPadClass = 1; static bool isDebugDump = false; @@ -123,6 +124,10 @@ std::string getUpdateChannel() { return updateChannel; } +std::string getBackButtonBehavior() { + return backButtonBehavior; +} + bool getUseSpecialPad() { return useSpecialPad; } @@ -275,6 +280,10 @@ void setUpdateChannel(const std::string& type) { updateChannel = type; } +void setBackButtonBehavior(const std::string& type) { + backButtonBehavior = type; +} + void setUseSpecialPad(bool use) { useSpecialPad = use; } @@ -435,6 +444,7 @@ void load(const std::filesystem::path& path) { } isShowSplash = toml::find_or(general, "showSplash", true); isAutoUpdate = toml::find_or(general, "autoUpdate", false); + backButtonBehavior = toml::find_or(general, "backButtonBehavior", "left"); } if (data.contains("Input")) { @@ -533,6 +543,7 @@ void save(const std::filesystem::path& path) { data["General"]["updateChannel"] = updateChannel; data["General"]["showSplash"] = isShowSplash; data["General"]["autoUpdate"] = isAutoUpdate; + data["General"]["backButtonBehavior"] = backButtonBehavior; data["Input"]["useSpecialPad"] = useSpecialPad; data["Input"]["specialPadClass"] = specialPadClass; data["GPU"]["screenWidth"] = screenWidth; @@ -591,6 +602,7 @@ void setDefaultValues() { } else { updateChannel = "Nightly"; } + backButtonBehavior = "left"; useSpecialPad = false; specialPadClass = 1; isDebugDump = false; diff --git a/src/common/config.h b/src/common/config.h index daee9e078..4aad7bede 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -20,6 +20,7 @@ std::string getLogFilter(); std::string getLogType(); std::string getUserName(); std::string getUpdateChannel(); +std::string getBackButtonBehavior(); bool getUseSpecialPad(); int getSpecialPadClass(); @@ -54,6 +55,7 @@ void setLanguage(u32 language); void setNeoMode(bool enable); void setUserName(const std::string& type); void setUpdateChannel(const std::string& type); +void setBackButtonBehavior(const std::string& type); void setUseSpecialPad(bool use); void setSpecialPadClass(int type); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 1c0b0bc07..bcf5762f2 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -98,6 +98,15 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setText(tr("Restore Defaults")); ui->buttonBox->button(QDialogButtonBox::Close)->setText(tr("Close")); + ui->backButtonBehaviorComboBox->addItem(tr("Touchpad Left"), "left"); + ui->backButtonBehaviorComboBox->addItem(tr("Touchpad Center"), "center"); + ui->backButtonBehaviorComboBox->addItem(tr("Touchpad Right"), "right"); + ui->backButtonBehaviorComboBox->addItem(tr("None"), "none"); + + QString currentBackButtonBehavior = QString::fromStdString(Config::getBackButtonBehavior()); + int index = ui->backButtonBehaviorComboBox->findData(currentBackButtonBehavior); + ui->backButtonBehaviorComboBox->setCurrentIndex(index != -1 ? index : 0); + connect(ui->tabWidgetSettings, &QTabWidget::currentChanged, this, [this]() { ui->buttonBox->button(QDialogButtonBox::Close)->setFocus(); }); @@ -151,6 +160,14 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge Config::setBGMvolume(val); BackgroundMusicPlayer::getInstance().setVolume(val); }); + + connect(ui->backButtonBehaviorComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, [this](int index) { + if (index >= 0 && index < ui->backButtonBehaviorComboBox->count()) { + QString data = ui->backButtonBehaviorComboBox->itemData(index).toString(); + Config::setBackButtonBehavior(data.toStdString()); + } + }); } // GPU TAB @@ -204,6 +221,7 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge ui->logFilter->installEventFilter(this); ui->updaterGroupBox->installEventFilter(this); ui->GUIgroupBox->installEventFilter(this); + ui->backButtonBehaviorGroupBox->installEventFilter(this); // Graphics ui->graphicsAdapterGroupBox->installEventFilter(this); @@ -258,6 +276,10 @@ void SettingsDialog::LoadValuesFromConfig() { } } ui->updateComboBox->setCurrentText(QString::fromStdString(updateChannel)); + + QString backButtonBehavior = QString::fromStdString(Config::getBackButtonBehavior()); + int index = ui->backButtonBehaviorComboBox->findData(backButtonBehavior); + ui->backButtonBehaviorComboBox->setCurrentIndex(index != -1 ? index : 0); } void SettingsDialog::InitializeEmulatorLanguages() { @@ -319,6 +341,8 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("updaterGroupBox"); } else if (elementName == "GUIgroupBox") { text = tr("GUIgroupBox"); + } else if (elementName == "backButtonBehaviorGroupBox") { + text = tr("backButtonBehaviorGroupBox"); } // Graphics diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 4edec9e1b..a6264af6f 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -442,20 +442,44 @@ - - - Qt::Orientation::Horizontal - - - QSizePolicy::Policy::Expanding - - - - 0 - 0 - - - + + + + + + 0 + 0 + + + + Controller Settings + + + + + 12 + 30 + 241 + 65 + + + + Back Button Behavior + + + + + 12 + 30 + 217 + 28 + + + + + + + diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 4052b4ef5..18c54428f 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -534,6 +534,16 @@ Volume Volume + + + Controller Settings + Controller Settings + + + + Back Button Behavior + Back Button Behavior + MainWindow @@ -1023,6 +1033,31 @@ GUIgroupBox Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. + + + backButtonBehaviorGroupBox + Back Button Behavior:\nAllows setting which part of the touchpad the back button will emulate a touch on. + + + + Touchpad Left + Touchpad Left + + + + Touchpad Right + Touchpad Right + + + + Touchpad Center + Touchpad Center + + + + None + None + graphicsAdapterGroupBox diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 4a4020a42..470f12de3 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -145,6 +145,7 @@ void WindowSDL::onKeyPress(const SDL_Event* event) { Input::Axis axis = Input::Axis::AxisMax; int axisvalue = 0; int ax = 0; + std::string backButtonBehavior = Config::getBackButtonBehavior(); switch (event->key.key) { case SDLK_UP: button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP; @@ -278,7 +279,15 @@ void WindowSDL::onKeyPress(const SDL_Event* event) { ax = Input::GetAxis(0, 0x80, axisvalue); break; case SDLK_SPACE: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD; + if (backButtonBehavior != "none") { + float x = backButtonBehavior == "left" ? 0.25f + : (backButtonBehavior == "right" ? 0.75f : 0.5f); + // trigger a touchpad event so that the touchpad emulation for back button works + controller->SetTouchpadState(0, true, x, 0.5f); + button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD; + } else { + button = 0; + } break; case SDLK_F11: if (event->type == SDL_EVENT_KEY_DOWN) { @@ -330,7 +339,20 @@ void WindowSDL::onGamepadEvent(const SDL_Event* event) { case SDL_EVENT_GAMEPAD_BUTTON_UP: button = sdlGamepadToOrbisButton(event->gbutton.button); if (button != 0) { - controller->CheckButton(0, button, event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN); + if (event->gbutton.button == SDL_GAMEPAD_BUTTON_BACK) { + std::string backButtonBehavior = Config::getBackButtonBehavior(); + if (backButtonBehavior != "none") { + float x = backButtonBehavior == "left" + ? 0.25f + : (backButtonBehavior == "right" ? 0.75f : 0.5f); + // trigger a touchpad event so that the touchpad emulation for back button works + controller->SetTouchpadState(0, true, x, 0.5f); + controller->CheckButton(0, button, + event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN); + } + } else { + controller->CheckButton(0, button, event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN); + } } if (SDL_GetCursor() != NULL) { SDL_HideCursor(); From 01fb320f4e6b47a6caacb9769ebb9ff8cb573df2 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Tue, 8 Oct 2024 03:15:50 -0300 Subject: [PATCH 10/93] Fixes encoding in update text (#1283) * Fixes encoding in update text * + --- src/qt_gui/check_update.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/qt_gui/check_update.cpp b/src/qt_gui/check_update.cpp index 48adbb88a..ca6009ca3 100644 --- a/src/qt_gui/check_update.cpp +++ b/src/qt_gui/check_update.cpp @@ -374,11 +374,17 @@ void CheckUpdate::Install() { QString userPath; Common::FS::PathToQString(userPath, Common::FS::GetUserPath(Common::FS::PathType::UserDir)); - QString startingUpdate = tr("Starting Update..."); - QString tempDirPath = userPath + "/temp_download_update"; QString rootPath; Common::FS::PathToQString(rootPath, std::filesystem::current_path()); + QString tempDirPath = userPath + "/temp_download_update"; + QString startingUpdate = tr("Starting Update..."); + + QString binaryStartingUpdate; + for (QChar c : startingUpdate) { + binaryStartingUpdate.append(QString::number(c.unicode(), 2).rightJustified(16, '0')); + } + QString scriptContent; QString scriptFileName; QStringList arguments; @@ -389,7 +395,13 @@ void CheckUpdate::Install() { scriptFileName = tempDirPath + "/update.ps1"; scriptContent = QStringLiteral( "Set-ExecutionPolicy Bypass -Scope Process -Force\n" - "Write-Output '%1'\n" + "$binaryStartingUpdate = '%1'\n" + "$chars = @()\n" + "for ($i = 0; $i -lt $binaryStartingUpdate.Length; $i += 16) {\n" + " $chars += [char]([convert]::ToInt32($binaryStartingUpdate.Substring($i, 16), 2))\n" + "}\n" + "$startingUpdate = -join $chars\n" + "Write-Output $startingUpdate\n" "Expand-Archive -Path '%2\\temp_download_update.zip' -DestinationPath '%2' -Force\n" "Start-Sleep -Seconds 3\n" "Copy-Item -Recurse -Force '%2\\*' '%3\\'\n" @@ -512,7 +524,12 @@ void CheckUpdate::Install() { QFile scriptFile(scriptFileName); if (scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&scriptFile); +#ifdef Q_OS_WIN + out << scriptContent.arg(binaryStartingUpdate).arg(tempDirPath).arg(rootPath); +#endif +#if defined(Q_OS_LINUX) || defined(Q_OS_MAC) out << scriptContent.arg(startingUpdate).arg(tempDirPath).arg(rootPath); +#endif scriptFile.close(); // Make the script executable on Unix-like systems From 96344873d649663cd4c35c4f3079a6f2415968eb Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Tue, 8 Oct 2024 03:16:06 -0300 Subject: [PATCH 11/93] SaveDataLib: use param.sfo to store max_blocks instead of txt (#1287) --- src/core/file_format/psf.cpp | 6 ++++ src/core/file_format/psf.h | 1 + .../libraries/save_data/save_instance.cpp | 32 ++++++++----------- src/core/libraries/save_data/save_instance.h | 2 +- src/core/libraries/save_data/savedata.cpp | 7 ++-- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/core/file_format/psf.cpp b/src/core/file_format/psf.cpp index a5e502f98..0502f29d2 100644 --- a/src/core/file_format/psf.cpp +++ b/src/core/file_format/psf.cpp @@ -227,6 +227,12 @@ void PSF::AddBinary(std::string key, std::vector value, bool update) { map_binaries.emplace(entry_list.size() - 1, std::move(value)); } +void PSF::AddBinary(std::string key, uint64_t value, bool update) { + std::vector data(8); + std::memcpy(data.data(), &value, 8); + return AddBinary(std::move(key), std::move(data), update); +} + void PSF::AddString(std::string key, std::string value, bool update) { auto [it, index] = FindEntry(key); bool exist = it != entry_list.end(); diff --git a/src/core/file_format/psf.h b/src/core/file_format/psf.h index d25b79eec..6f35fa69a 100644 --- a/src/core/file_format/psf.h +++ b/src/core/file_format/psf.h @@ -67,6 +67,7 @@ public: std::optional GetInteger(std::string_view key) const; void AddBinary(std::string key, std::vector value, bool update = false); + void AddBinary(std::string key, uint64_t value, bool update = false); // rsv4 format void AddString(std::string key, std::string value, bool update = false); void AddInteger(std::string key, s32 value, bool update = false); diff --git a/src/core/libraries/save_data/save_instance.cpp b/src/core/libraries/save_data/save_instance.cpp index 1127a5452..0d6c5173c 100644 --- a/src/core/libraries/save_data/save_instance.cpp +++ b/src/core/libraries/save_data/save_instance.cpp @@ -12,9 +12,9 @@ #include "core/file_sys/fs.h" #include "save_instance.h" -constexpr u32 OrbisSaveDataBlocksMax = 32768; // 1 GiB +constexpr auto OrbisSaveDataBlocksMin2 = 96; // 3MiB +constexpr auto OrbisSaveDataBlocksMax = 32768; // 1 GiB constexpr std::string_view sce_sys = "sce_sys"; // system folder inside save -constexpr std::string_view max_block_file_name = "max_block.txt"; static Core::FileSys::MntPoints* g_mnt = Common::Singleton::Instance(); @@ -58,18 +58,13 @@ std::filesystem::path SaveInstance::MakeDirSavePath(OrbisUserServiceUserId user_ game_serial / dir_name; } -int SaveInstance::GetMaxBlocks(const std::filesystem::path& save_path) { - Common::FS::IOFile max_blocks_file{save_path / sce_sys / max_block_file_name, - Common::FS::FileAccessMode::Read}; - int max_blocks = 0; - if (max_blocks_file.IsOpen()) { - max_blocks = std::atoi(max_blocks_file.ReadString(16).c_str()); +uint64_t SaveInstance::GetMaxBlockFromSFO(const PSF& psf) { + const auto vec = psf.GetBinary(std::string{SaveParams::SAVEDATA_BLOCKS}); + if (!vec.has_value()) { + return OrbisSaveDataBlocksMax; } - if (max_blocks <= 0) { - max_blocks = OrbisSaveDataBlocksMax; - } - - return max_blocks; + auto value = vec.value(); + return *(uint64_t*)value.data(); } std::filesystem::path SaveInstance::GetParamSFOPath(const std::filesystem::path& dir_path) { @@ -92,13 +87,15 @@ void SaveInstance::SetupDefaultParamSFO(PSF& param_sfo, std::string dir_name, P(String, SaveParams::SAVEDATA_DIRECTORY, std::move(dir_name)); P(Integer, SaveParams::SAVEDATA_LIST_PARAM, 0); P(String, SaveParams::TITLE_ID, std::move(game_serial)); + P(Binary, SaveParams::SAVEDATA_BLOCKS, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); #undef P } SaveInstance::SaveInstance(int slot_num, OrbisUserServiceUserId user_id, std::string _game_serial, std::string_view _dir_name, int max_blocks) : slot_num(slot_num), user_id(user_id), game_serial(std::move(_game_serial)), - dir_name(_dir_name), max_blocks(max_blocks) { + dir_name(_dir_name), + max_blocks(std::clamp(max_blocks, OrbisSaveDataBlocksMin2, OrbisSaveDataBlocksMax)) { ASSERT(slot_num >= 0 && slot_num < 16); save_path = MakeDirSavePath(user_id, game_serial, dir_name); @@ -187,7 +184,7 @@ void SaveInstance::SetupAndMount(bool read_only, bool copy_icon, bool ignore_cor } } - max_blocks = GetMaxBlocks(save_path); + max_blocks = static_cast(GetMaxBlockFromSFO(param_sfo)); g_mnt->Mount(save_path, mount_point, read_only); mounted = true; @@ -217,16 +214,13 @@ void SaveInstance::CreateFiles() { fs::create_directories(sce_sys_dir); SetupDefaultParamSFO(param_sfo, dir_name, game_serial); + param_sfo.AddBinary(std::string{SaveParams::SAVEDATA_BLOCKS}, max_blocks, true); const bool ok = param_sfo.Encode(param_sfo_path); if (!ok) { throw std::filesystem::filesystem_error("Failed to write param.sfo", param_sfo_path, std::make_error_code(std::errc::permission_denied)); } - - Common::FS::IOFile max_block{sce_sys_dir / max_block_file_name, - Common::FS::FileAccessMode::Write}; - max_block.WriteString(std::to_string(max_blocks == 0 ? OrbisSaveDataBlocksMax : max_blocks)); } } // namespace Libraries::SaveData \ No newline at end of file diff --git a/src/core/libraries/save_data/save_instance.h b/src/core/libraries/save_data/save_instance.h index f07011047..3be5c4595 100644 --- a/src/core/libraries/save_data/save_instance.h +++ b/src/core/libraries/save_data/save_instance.h @@ -62,7 +62,7 @@ public: std::string_view game_serial, std::string_view dir_name); - static int GetMaxBlocks(const std::filesystem::path& save_path); + static uint64_t GetMaxBlockFromSFO(const PSF& psf); // Get param.sfo path from a dir_path generated by MakeDirSavePath static std::filesystem::path GetParamSFOPath(const std::filesystem::path& dir_path); diff --git a/src/core/libraries/save_data/savedata.cpp b/src/core/libraries/save_data/savedata.cpp index a2af2f159..93b3c20a9 100644 --- a/src/core/libraries/save_data/savedata.cpp +++ b/src/core/libraries/save_data/savedata.cpp @@ -445,7 +445,7 @@ static Error saveDataMount(const OrbisSaveDataMount2* mount_info, fs::create_directories(root_save); const auto available = fs::space(root_save).available; - auto requested_size = mount_info->blocks * OrbisSaveDataBlockSize; + auto requested_size = save_instance.GetMaxBlocks() * OrbisSaveDataBlockSize; if (requested_size > available) { mount_result->required_blocks = (requested_size - available) / OrbisSaveDataBlockSize; return Error::NO_SPACE_FS; @@ -830,10 +830,11 @@ Error PS4_SYSV_ABI sceSaveDataDirNameSearch(const OrbisSaveDataDirNameSearchCond LOG_ERROR(Lib_SaveData, "Failed to read SFO: {}", fmt::UTF(sfo_path.u8string())); ASSERT_MSG(false, "Failed to read SFO"); } - map_dir_sfo.emplace(dir_name, std::move(sfo)); size_t size = Common::FS::GetDirectorySize(dir_path); - size_t total = SaveInstance::GetMaxBlocks(dir_path); + size_t total = SaveInstance::GetMaxBlockFromSFO(sfo); + + map_dir_sfo.emplace(dir_name, std::move(sfo)); map_free_size.emplace(dir_name, total - size / OrbisSaveDataBlockSize); map_max_blocks.emplace(dir_name, total); } From f139762c6450c5f4fb961e5f4757c35680a5cec0 Mon Sep 17 00:00:00 2001 From: Exhigh Date: Tue, 8 Oct 2024 10:47:42 +0400 Subject: [PATCH 12/93] imgui/renderer: Hide Cursor on Idle Implementation (#1266) Implement hide cursor on idle w/ idle timeout duration (configurable via GUI). While at it add always and never to hide the cursor options as well. * Revert commit #1211 as to not interfere with the cursor states. * Make hide cursor on idle as the default setting w/ timeout duration of 5 seconds to hide. * Add an input tab in the settings page to add the hide cursor setting, with hiding the idle timeout box with respect to the cursor hide option. Co-authored-by: georgemoralis --- src/common/config.cpp | 24 +++ src/common/config.h | 8 + src/imgui/renderer/imgui_impl_sdl3.cpp | 42 +++++- src/qt_gui/settings_dialog.cpp | 31 ++++ src/qt_gui/settings_dialog.h | 1 + src/qt_gui/settings_dialog.ui | 196 ++++++++++++++++++++++++- src/sdl_window.cpp | 6 - 7 files changed, 296 insertions(+), 12 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 8c1c4f79d..a7d01f53b 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -57,6 +57,8 @@ static bool vkValidationGpu = false; static bool rdocEnable = false; static bool vkMarkers = false; static bool vkCrashDiagnostic = false; +static s16 cursorState = HideCursorState::Idle; +static int cursorHideTimeout = 5; // 5 seconds (default) // Gui std::filesystem::path settings_install_dir = {}; @@ -96,6 +98,14 @@ int getBGMvolume() { return BGMvolume; } +s16 getCursorState() { + return cursorState; +} + +int getCursorHideTimeout() { + return cursorHideTimeout; +} + u32 getScreenWidth() { return screenWidth; } @@ -256,6 +266,14 @@ void setBGMvolume(int volume) { BGMvolume = volume; } +void setCursorState(s16 newCursorState) { + cursorState = newCursorState; +} + +void setCursorHideTimeout(int newcursorHideTimeout) { + cursorHideTimeout = newcursorHideTimeout; +} + void setLanguage(u32 language) { m_language = language; } @@ -450,6 +468,8 @@ void load(const std::filesystem::path& path) { if (data.contains("Input")) { const toml::value& input = data.at("Input"); + cursorState = toml::find_or(input, "cursorState", HideCursorState::Idle); + cursorHideTimeout = toml::find_or(input, "cursorHideTimeout", 5); useSpecialPad = toml::find_or(input, "useSpecialPad", false); specialPadClass = toml::find_or(input, "specialPadClass", 1); } @@ -543,6 +563,8 @@ void save(const std::filesystem::path& path) { data["General"]["updateChannel"] = updateChannel; data["General"]["showSplash"] = isShowSplash; data["General"]["autoUpdate"] = isAutoUpdate; + data["Input"]["cursorState"] = cursorState; + data["Input"]["cursorHideTimeout"] = cursorHideTimeout; data["General"]["backButtonBehavior"] = backButtonBehavior; data["Input"]["useSpecialPad"] = useSpecialPad; data["Input"]["specialPadClass"] = specialPadClass; @@ -592,6 +614,8 @@ void setDefaultValues() { isFullscreen = false; playBGM = false; BGMvolume = 50; + cursorState = HideCursorState::Idle; + cursorHideTimeout = 5; screenWidth = 1280; screenHeight = 720; logFilter = ""; diff --git a/src/common/config.h b/src/common/config.h index 4aad7bede..32cfc605e 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -8,6 +8,9 @@ #include "types.h" namespace Config { + +enum HideCursorState : s16 { Never, Idle, Always }; + void load(const std::filesystem::path& path); void save(const std::filesystem::path& path); @@ -16,6 +19,9 @@ bool isFullscreenMode(); bool getPlayBGM(); int getBGMvolume(); +s16 getCursorState(); +int getCursorHideTimeout(); + std::string getLogFilter(); std::string getLogType(); std::string getUserName(); @@ -51,6 +57,8 @@ void setScreenHeight(u32 height); void setFullscreenMode(bool enable); void setPlayBGM(bool enable); void setBGMvolume(int volume); +void setCursorState(s16 cursorState); +void setCursorHideTimeout(int newcursorHideTimeout); void setLanguage(u32 language); void setNeoMode(bool enable); void setUserName(const std::string& type); diff --git a/src/imgui/renderer/imgui_impl_sdl3.cpp b/src/imgui/renderer/imgui_impl_sdl3.cpp index cecf504a7..230d396f0 100644 --- a/src/imgui/renderer/imgui_impl_sdl3.cpp +++ b/src/imgui/renderer/imgui_impl_sdl3.cpp @@ -4,6 +4,7 @@ // Based on imgui_impl_sdl3.cpp from Dear ImGui repository #include +#include "common/config.h" #include "imgui_impl_sdl3.h" // SDL @@ -36,6 +37,8 @@ struct SdlData { SDL_Cursor* mouse_cursors[ImGuiMouseCursor_COUNT]{}; SDL_Cursor* mouse_last_cursor{}; int mouse_pending_leave_frame{}; + ImVec2 prev_mouse_pos{0, 0}; + Uint64 lastCursorMoveTime{}; // Gamepad handling ImVector gamepads{}; @@ -371,6 +374,13 @@ bool ProcessEvent(const SDL_Event* event) { ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); io.AddMousePosEvent(mouse_pos.x, mouse_pos.y); + if (mouse_pos.x != bd->prev_mouse_pos.x || mouse_pos.y != bd->prev_mouse_pos.y) { + bd->prev_mouse_pos.x = mouse_pos.x; + bd->prev_mouse_pos.y = mouse_pos.y; + if (Config::getCursorState() == Config::HideCursorState::Idle) { + bd->lastCursorMoveTime = bd->time; + } + } return true; } case SDL_EVENT_MOUSE_WHEEL: { @@ -447,6 +457,7 @@ bool ProcessEvent(const SDL_Event* event) { return false; bd->mouse_window_id = event->window.windowID; bd->mouse_pending_leave_frame = 0; + bd->lastCursorMoveTime = bd->time; return true; } // - In some cases, when detaching a window from main viewport SDL may send @@ -459,6 +470,7 @@ bool ProcessEvent(const SDL_Event* event) { if (GetViewportForWindowId(event->window.windowID) == NULL) return false; bd->mouse_pending_leave_frame = ImGui::GetFrameCount() + 1; + bd->lastCursorMoveTime = bd->time; return true; } case SDL_EVENT_WINDOW_FOCUS_GAINED: @@ -600,7 +612,18 @@ static void UpdateMouseData() { int window_x, window_y; SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global); SDL_GetWindowPosition(focused_window, &window_x, &window_y); - io.AddMousePosEvent(mouse_x_global - (float)window_x, mouse_y_global - (float)window_y); + mouse_x_global -= (float)window_x; + mouse_y_global -= (float)window_y; + io.AddMousePosEvent(mouse_x_global, mouse_y_global); + // SDL_EVENT_MOUSE_MOTION isn't triggered before the first frame is rendered + // force update the prev_cursor coords + if (mouse_x_global != bd->prev_mouse_pos.x || mouse_y_global != bd->prev_mouse_pos.y && + bd->prev_mouse_pos.y == 0 && + bd->prev_mouse_pos.x == 0) { + bd->prev_mouse_pos.x = mouse_x_global; + bd->prev_mouse_pos.y = mouse_y_global; + bd->lastCursorMoveTime = bd->time; + } } } } @@ -611,10 +634,25 @@ static void UpdateMouseCursor() { return; SdlData* bd = GetBackendData(); + s16 cursorState = Config::getCursorState(); ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); - if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) { + if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None || + cursorState == Config::HideCursorState::Always) { // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor SDL_HideCursor(); + + } else if (cursorState == Config::HideCursorState::Idle && + bd->time - bd->lastCursorMoveTime >= + Config::getCursorHideTimeout() * SDL_GetPerformanceFrequency()) { + + bool wasCursorVisible = SDL_CursorVisible(); + SDL_HideCursor(); + + if (wasCursorVisible) { + SDL_WarpMouseInWindow(SDL_GetKeyboardFocus(), bd->prev_mouse_pos.x, + bd->prev_mouse_pos.y); // Force refresh the cursor state + } + } else { // Show OS mouse cursor SDL_Cursor* expected_cursor = bd->mouse_cursors[imgui_cursor] diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index bcf5762f2..3845ab0c1 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -47,6 +47,8 @@ QStringList languageNames = {"Arabic", const QVector languageIndexes = {21, 23, 14, 6, 18, 1, 12, 22, 2, 4, 25, 24, 29, 5, 0, 9, 15, 16, 17, 7, 26, 8, 11, 20, 3, 13, 27, 10, 19, 28}; +QStringList hideCursorStates = {"Never", "Idle", "Always"}; + SettingsDialog::SettingsDialog(std::span physical_devices, QWidget* parent) : QDialog(parent), ui(new Ui::SettingsDialog) { ui->setupUi(this); @@ -67,6 +69,8 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge completer->setCaseSensitivity(Qt::CaseInsensitive); ui->consoleLanguageComboBox->setCompleter(completer); + ui->hideCursorComboBox->addItems(hideCursorStates); + InitializeEmulatorLanguages(); LoadValuesFromConfig(); @@ -170,6 +174,18 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge }); } + // Input TAB + { + connect(ui->hideCursorComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, + [this](s16 index) { + Config::setCursorState(index); + OnCursorStateChanged(index); + }); + + connect(ui->idleTimeoutSpinBox, &QSpinBox::valueChanged, this, + [](int index) { Config::setCursorHideTimeout(index); }); + } + // GPU TAB { // First options is auto selection -1, so gpuId on the GUI will always have to subtract 1 @@ -246,6 +262,9 @@ void SettingsDialog::LoadValuesFromConfig() { std::find(languageIndexes.begin(), languageIndexes.end(), Config::GetLanguage())) % languageIndexes.size()); ui->emulatorLanguageComboBox->setCurrentIndex(languages[Config::getEmulatorLanguage()]); + ui->hideCursorComboBox->setCurrentIndex(Config::getCursorState()); + OnCursorStateChanged(Config::getCursorState()); + ui->idleTimeoutSpinBox->setValue(Config::getCursorHideTimeout()); ui->graphicsAdapterBox->setCurrentIndex(Config::getGpuId() + 1); ui->widthSpinBox->setValue(Config::getScreenWidth()); ui->heightSpinBox->setValue(Config::getScreenHeight()); @@ -311,6 +330,18 @@ void SettingsDialog::OnLanguageChanged(int index) { emit LanguageChanged(ui->emulatorLanguageComboBox->itemData(index).toString().toStdString()); } +void SettingsDialog::OnCursorStateChanged(s16 index) { + if (index == -1) + return; + if (index == Config::HideCursorState::Idle) { + ui->idleTimeoutGroupBox->show(); + } else { + if (!ui->idleTimeoutGroupBox->isHidden()) { + ui->idleTimeoutGroupBox->hide(); + } + } +} + int SettingsDialog::exec() { return QDialog::exec(); } diff --git a/src/qt_gui/settings_dialog.h b/src/qt_gui/settings_dialog.h index 85ae39aaa..d09617ec3 100644 --- a/src/qt_gui/settings_dialog.h +++ b/src/qt_gui/settings_dialog.h @@ -33,6 +33,7 @@ private: void LoadValuesFromConfig(); void InitializeEmulatorLanguages(); void OnLanguageChanged(int index); + void OnCursorStateChanged(s16 index); std::unique_ptr ui; diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index a6264af6f..b686c3f8b 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -34,9 +34,18 @@ :/images/shadps4.ico:/images/shadps4.ico + + false + + + false + + + true + QFrame::Shape::NoFrame @@ -51,8 +60,8 @@ 0 0 - 836 - 446 + 832 + 431 @@ -62,7 +71,7 @@ - 0 + 1 @@ -369,7 +378,7 @@ 10 30 241 - 71 + 92 @@ -485,6 +494,185 @@ + + + Input + + + + + + + + + + Cursor + + + + + + Hide Cursor + + + + + + + + + + + + true + + + + 0 + 85 + + + + Hide Cursor Idle Timeout + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + + + false + + + + 70 + + + 11 + + + + + true + + + + 0 + 0 + + + + + 80 + 30 + + + + + 16777215 + 16777215 + + + + Qt::LayoutDirection::LeftToRight + + + false + + + Qt::AlignmentFlag::AlignCenter + + + QAbstractSpinBox::ButtonSymbols::UpDownArrows + + + + + + 3600 + + + 5 + + + 10 + + + + + + + s + + + + + + + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::MinimumExpanding + + + + 20 + 20 + + + + + + + + Graphics diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 470f12de3..bf29b37f6 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -313,9 +313,6 @@ void WindowSDL::onKeyPress(const SDL_Event* event) { if (axis != Input::Axis::AxisMax) { controller->Axis(0, axis, ax); } - if (SDL_GetCursor() != NULL) { - SDL_HideCursor(); - } } void WindowSDL::onGamepadEvent(const SDL_Event* event) { @@ -354,9 +351,6 @@ void WindowSDL::onGamepadEvent(const SDL_Event* event) { controller->CheckButton(0, button, event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN); } } - if (SDL_GetCursor() != NULL) { - SDL_HideCursor(); - } break; case SDL_EVENT_GAMEPAD_AXIS_MOTION: axis = event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFTX ? Input::Axis::LeftX From 20915eb5b87ea7186fbcaa389100b99223b851ba Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 8 Oct 2024 00:29:05 -0700 Subject: [PATCH 13/93] core: Add support for Neo mode memory size. (#1196) --- src/core/address_space.cpp | 2 +- src/core/libraries/kernel/memory_management.h | 2 ++ src/core/memory.cpp | 5 ++++- src/video_core/page_manager.cpp | 3 +++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/core/address_space.cpp b/src/core/address_space.cpp index 48748e4c3..b2cb95a54 100644 --- a/src/core/address_space.cpp +++ b/src/core/address_space.cpp @@ -25,7 +25,7 @@ asm(".zerofill GUEST_SYSTEM,GUEST_SYSTEM,__guest_system,0xFBFC00000"); namespace Core { -static constexpr size_t BackingSize = SCE_KERNEL_MAIN_DMEM_SIZE; +static constexpr size_t BackingSize = SCE_KERNEL_MAIN_DMEM_SIZE_PRO; #ifdef _WIN32 diff --git a/src/core/libraries/kernel/memory_management.h b/src/core/libraries/kernel/memory_management.h index 38898aa57..6e90204cf 100644 --- a/src/core/libraries/kernel/memory_management.h +++ b/src/core/libraries/kernel/memory_management.h @@ -7,6 +7,8 @@ #include "common/types.h" constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE = 5056_MB; // ~ 5GB +// TODO: Confirm this value on hardware. +constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE_PRO = 5568_MB; // ~ 5.5GB namespace Libraries::Kernel { diff --git a/src/core/memory.cpp b/src/core/memory.cpp index d21ebae83..dd63522fc 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -3,6 +3,7 @@ #include "common/alignment.h" #include "common/assert.h" +#include "common/config.h" #include "common/debug.h" #include "core/libraries/error_codes.h" #include "core/libraries/kernel/memory_management.h" @@ -39,8 +40,10 @@ MemoryManager::MemoryManager() { MemoryManager::~MemoryManager() = default; void MemoryManager::SetupMemoryRegions(u64 flexible_size) { + const auto total_size = + Config::isNeoMode() ? SCE_KERNEL_MAIN_DMEM_SIZE_PRO : SCE_KERNEL_MAIN_DMEM_SIZE; total_flexible_size = flexible_size; - total_direct_size = SCE_KERNEL_MAIN_DMEM_SIZE - flexible_size; + total_direct_size = total_size - flexible_size; // Insert an area that covers direct memory physical block. // Note that this should never be called after direct memory allocations have been made. diff --git a/src/video_core/page_manager.cpp b/src/video_core/page_manager.cpp index fb09e70f2..a49fff43a 100644 --- a/src/video_core/page_manager.cpp +++ b/src/video_core/page_manager.cpp @@ -142,6 +142,9 @@ struct PageManager::Impl { } void Protect(VAddr address, size_t size, bool allow_write) { + ASSERT_MSG(owned_ranges.find(address) != owned_ranges.end(), + "Attempted to track non-GPU memory at address {:#x}, size {:#x}.", address, + size); #ifdef _WIN32 DWORD prot = allow_write ? PAGE_READWRITE : PAGE_READONLY; DWORD old_prot{}; From aba803bd04575c2016d15b8edeaef01856ad1724 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Tue, 8 Oct 2024 02:35:07 -0500 Subject: [PATCH 14/93] IMAGE_GATHER4_C_LZ_O (#1274) Used by Crash Team Racing Nitro Fueled. --- src/shader_recompiler/frontend/format.cpp | 4 ++-- src/shader_recompiler/frontend/translate/vector_memory.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/shader_recompiler/frontend/format.cpp b/src/shader_recompiler/frontend/format.cpp index 7b3ad00ba..4f0922e2e 100644 --- a/src/shader_recompiler/frontend/format.cpp +++ b/src/shader_recompiler/frontend/format.cpp @@ -3642,8 +3642,8 @@ constexpr std::array InstructionFormatMIMG = {{ {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, ScalarType::Undefined}, // 95 = IMAGE_GATHER4_C_LZ_O - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Float32}, // 96 = IMAGE_GET_LOD {InstClass::VectorMemImgUt, InstCategory::VectorMemory, 4, 1, ScalarType::Float32, ScalarType::Float32}, diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index 0419f7628..e76ba6d8a 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -147,6 +147,7 @@ void Translator::EmitVectorMemory(const GcnInst& inst) { case Opcode::IMAGE_GATHER4_C_O: case Opcode::IMAGE_GATHER4_C_LZ: case Opcode::IMAGE_GATHER4_LZ_O: + case Opcode::IMAGE_GATHER4_C_LZ_O: return IMAGE_GATHER(inst); // Image misc operations From 3e7137cc6b0b9b25bc55fd4d52c97e1406166ccf Mon Sep 17 00:00:00 2001 From: delledev <67817505+delledev@users.noreply.github.com> Date: Tue, 8 Oct 2024 18:14:37 +0300 Subject: [PATCH 15/93] added discord rpc (#1178) * added discord rpc * linting issues * Revert "linting issues" This reverts commit 55f4e39506361c816c391828b6dbd46cfbc1df89. * fix clang-format issues * removed wrong rpc submodule * added correct rpc submodule * Moved cmake instructions to correct files. * added minor suggestions from pr * Added an option to enable/disable RPC, added rpc to emulator.cpp making it work on nonqt builds * typo & minor stuff * Changed getInstance implementation with Singleton class. * Update discord_rpc_handler.cpp discord id * fixed ci clangformat errors --------- Co-authored-by: georgemoralis --- .gitmodules | 5 +- CMakeLists.txt | 7 ++- externals/CMakeLists.txt | 5 ++ externals/discord-rpc | 1 + src/common/config.cpp | 12 ++++ src/common/config.h | 2 + src/common/discord.cpp | 43 -------------- src/common/discord_rpc_handler.cpp | 57 +++++++++++++++++++ .../{discord.h => discord_rpc_handler.h} | 13 +++-- src/emulator.cpp | 10 ++++ src/qt_gui/main_window.cpp | 8 +++ src/qt_gui/main_window.h | 1 + src/qt_gui/settings_dialog.cpp | 12 ++++ src/qt_gui/settings_dialog.ui | 7 +++ 14 files changed, 133 insertions(+), 50 deletions(-) create mode 160000 externals/discord-rpc delete mode 100644 src/common/discord.cpp create mode 100644 src/common/discord_rpc_handler.cpp rename src/common/{discord.h => discord_rpc_handler.h} (53%) diff --git a/.gitmodules b/.gitmodules index e3dda94fd..6e4eac2b4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -94,4 +94,7 @@ [submodule "externals/pugixml"] path = externals/pugixml url = https://github.com/zeux/pugixml.git - shallow = true \ No newline at end of file + shallow = true +[submodule "externals/discord-rpc"] + path = externals/discord-rpc + url = https://github.com/shadps4-emu/ext-discord-rpc diff --git a/CMakeLists.txt b/CMakeLists.txt index 59f15add6..2db263b3a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -374,6 +374,8 @@ set(COMMON src/common/logging/backend.cpp src/common/debug.h src/common/decoder.cpp src/common/decoder.h + src/common/discord_rpc_handler.cpp + src/common/discord_rpc_handler.h src/common/elf_info.h src/common/endian.h src/common/enum.h @@ -857,4 +859,7 @@ if (UNIX AND NOT APPLE) find_package(OpenSSL REQUIRED) target_link_libraries(shadps4 PRIVATE ${OPENSSL_LIBRARIES}) endif() -endif() \ No newline at end of file +endif() + +# Discord RPC +target_link_libraries(shadps4 PRIVATE discord-rpc) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 37c0f6899..a528eaedb 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -184,5 +184,10 @@ if (NOT TARGET pugixml::pugixml) add_subdirectory(pugixml) endif() +# Discord RPC +set(BUILD_EXAMPLES OFF) +add_subdirectory(discord-rpc/) +target_include_directories(discord-rpc INTERFACE discord-rpc/include) + # GCN Headers add_subdirectory(gcn) \ No newline at end of file diff --git a/externals/discord-rpc b/externals/discord-rpc new file mode 160000 index 000000000..80d35b5f8 --- /dev/null +++ b/externals/discord-rpc @@ -0,0 +1 @@ +Subproject commit 80d35b5f86adc6557d0384771119cf167495dbae diff --git a/src/common/config.cpp b/src/common/config.cpp index a7d01f53b..40be5ebec 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -34,6 +34,7 @@ static bool isNeo = false; static bool isFullscreen = false; static bool playBGM = false; static int BGMvolume = 50; +static bool enableDiscordRPC = false; static u32 screenWidth = 1280; static u32 screenHeight = 720; static s32 gpuId = -1; // Vulkan physical device index. Set to negative for auto select @@ -98,6 +99,10 @@ int getBGMvolume() { return BGMvolume; } +bool getEnableDiscordRPC() { + return enableDiscordRPC; +} + s16 getCursorState() { return cursorState; } @@ -266,6 +271,10 @@ void setBGMvolume(int volume) { BGMvolume = volume; } +void setEnableDiscordRPC(bool enable) { + enableDiscordRPC = enable; +} + void setCursorState(s16 newCursorState) { cursorState = newCursorState; } @@ -452,6 +461,7 @@ void load(const std::filesystem::path& path) { isFullscreen = toml::find_or(general, "Fullscreen", false); playBGM = toml::find_or(general, "playBGM", false); BGMvolume = toml::find_or(general, "BGMvolume", 50); + enableDiscordRPC = toml::find_or(general, "enableDiscordRPC", true); logFilter = toml::find_or(general, "logFilter", ""); logType = toml::find_or(general, "logType", "sync"); userName = toml::find_or(general, "userName", "shadPS4"); @@ -557,6 +567,7 @@ void save(const std::filesystem::path& path) { data["General"]["Fullscreen"] = isFullscreen; data["General"]["playBGM"] = playBGM; data["General"]["BGMvolume"] = BGMvolume; + data["General"]["enableDiscordRPC"] = enableDiscordRPC; data["General"]["logFilter"] = logFilter; data["General"]["logType"] = logType; data["General"]["userName"] = userName; @@ -614,6 +625,7 @@ void setDefaultValues() { isFullscreen = false; playBGM = false; BGMvolume = 50; + enableDiscordRPC = true; cursorState = HideCursorState::Idle; cursorHideTimeout = 5; screenWidth = 1280; diff --git a/src/common/config.h b/src/common/config.h index 32cfc605e..90ebdb58a 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -18,6 +18,7 @@ bool isNeoMode(); bool isFullscreenMode(); bool getPlayBGM(); int getBGMvolume(); +bool getEnableDiscordRPC(); s16 getCursorState(); int getCursorHideTimeout(); @@ -57,6 +58,7 @@ void setScreenHeight(u32 height); void setFullscreenMode(bool enable); void setPlayBGM(bool enable); void setBGMvolume(int volume); +void setEnableDiscordRPC(bool enable); void setCursorState(s16 cursorState); void setCursorHideTimeout(int newcursorHideTimeout); void setLanguage(u32 language); diff --git a/src/common/discord.cpp b/src/common/discord.cpp deleted file mode 100644 index cce799a32..000000000 --- a/src/common/discord.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include "common/discord.h" - -namespace Discord { - -void RPC::init() { - DiscordEventHandlers handlers{}; - Discord_Initialize("1139939140494971051", &handlers, 1, nullptr); - - startTimestamp = time(nullptr); - enabled = true; -} - -void RPC::update(Discord::RPCStatus status, const std::string& game) { - DiscordRichPresence rpc{}; - - if (status == Discord::RPCStatus::Playing) { - rpc.details = "Playing a game"; - rpc.state = game.c_str(); - } else { - rpc.details = "Idle"; - } - - rpc.largeImageKey = "shadps4"; - rpc.largeImageText = "ShadPS4 is a PS4 emulator"; - rpc.startTimestamp = startTimestamp; - - Discord_UpdatePresence(&rpc); -} - -void RPC::stop() { - if (enabled) { - enabled = false; - Discord_ClearPresence(); - Discord_Shutdown(); - } -} - -} // namespace Discord diff --git a/src/common/discord_rpc_handler.cpp b/src/common/discord_rpc_handler.cpp new file mode 100644 index 000000000..8b6e78c5d --- /dev/null +++ b/src/common/discord_rpc_handler.cpp @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include "src/common/discord_rpc_handler.h" + +namespace DiscordRPCHandler { + +void RPC::init() { + DiscordEventHandlers handlers{}; + + Discord_Initialize("1138176975865909360", &handlers, 1, nullptr); + startTimestamp = time(nullptr); + rpcEnabled = true; +} + +void RPC::setStatusIdling() { + DiscordRichPresence rpc{}; + rpc.largeImageKey = "https://github.com/shadps4-emu/shadPS4/raw/main/.github/shadps4.png"; + rpc.largeImageText = "shadPS4 is a PS4 emulator"; + rpc.startTimestamp = startTimestamp; + rpc.details = "Idle"; + + status = RPCStatus::Idling; + Discord_UpdatePresence(&rpc); +} + +void RPC::setStatusPlaying(const std::string& game_name, const std::string& game_id) { + DiscordRichPresence rpc{}; + + rpc.details = "Playing"; + rpc.state = game_name.c_str(); + std::string largeImageUrl = + "https://store.playstation.com/store/api/chihiro/00_09_000/titlecontainer/US/en/999/" + + game_id + "_00/image"; + rpc.largeImageKey = largeImageUrl.c_str(); + rpc.largeImageText = game_name.c_str(); + rpc.startTimestamp = startTimestamp; + + status = RPCStatus::Playing; + Discord_UpdatePresence(&rpc); +} + +void RPC::shutdown() { + if (rpcEnabled) { + rpcEnabled = false; + Discord_ClearPresence(); + Discord_Shutdown(); + } +} + +bool RPC::getRPCEnabled() { + return rpcEnabled; +} + +} // namespace DiscordRPCHandler diff --git a/src/common/discord.h b/src/common/discord_rpc_handler.h similarity index 53% rename from src/common/discord.h rename to src/common/discord_rpc_handler.h index 54aa6c17c..1e451e181 100644 --- a/src/common/discord.h +++ b/src/common/discord_rpc_handler.h @@ -7,7 +7,7 @@ #include #include -namespace Discord { +namespace DiscordRPCHandler { enum class RPCStatus { Idling, @@ -16,12 +16,15 @@ enum class RPCStatus { class RPC { std::uint64_t startTimestamp; - bool enabled = false; + bool rpcEnabled = false; + RPCStatus status; public: void init(); - void update(RPCStatus status, const std::string& title); - void stop(); + void setStatusIdling(); + void setStatusPlaying(const std::string& game_name, const std::string& game_id); + void shutdown(); + bool getRPCEnabled(); }; -} // namespace Discord +} // namespace DiscordRPCHandler diff --git a/src/emulator.cpp b/src/emulator.cpp index 6649b7bba..a9d0f6de2 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -11,6 +11,7 @@ #include "common/memory_patcher.h" #endif #include "common/assert.h" +#include "common/discord_rpc_handler.h" #include "common/elf_info.h" #include "common/ntapi.h" #include "common/path_util.h" @@ -210,6 +211,15 @@ void Emulator::Run(const std::filesystem::path& file) { } } + // Discord RPC + if (Config::getEnableDiscordRPC()) { + auto* rpc = Common::Singleton::Instance(); + if (rpc->getRPCEnabled() == false) { + rpc->init(); + } + rpc->setStatusPlaying(game_info.title, id); + } + // start execution std::jthread mainthread = std::jthread([this](std::stop_token stop_token) { linker->Execute(); }); diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 8d8e17177..f93bdb069 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -69,6 +69,14 @@ bool MainWindow::Init() { QString statusMessage = "Games: " + QString::number(numGames) + " (" + QString::number(duration.count()) + "ms)"; statusBar->showMessage(statusMessage); + + // Initialize Discord RPC + if (Config::getEnableDiscordRPC()) { + auto* rpc = Common::Singleton::Instance(); + rpc->init(); + rpc->setStatusIdling(); + } + return true; } diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index a428f4317..6264978aa 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -9,6 +9,7 @@ #include "background_music_player.h" #include "common/config.h" +#include "common/discord_rpc_handler.h" #include "common/path_util.h" #include "core/file_format/psf.h" #include "core/file_sys/fs.h" diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 3845ab0c1..efc438455 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -165,6 +165,17 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge BackgroundMusicPlayer::getInstance().setVolume(val); }); + connect(ui->discordRPCCheckbox, &QCheckBox::stateChanged, this, [](int val) { + Config::setEnableDiscordRPC(val); + auto* rpc = Common::Singleton::Instance(); + if (val == Qt::Checked) { + rpc->init(); + rpc->setStatusIdling(); + } else { + rpc->shutdown(); + } + }); + connect(ui->backButtonBehaviorComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) { if (index >= 0 && index < ui->backButtonBehaviorComboBox->count()) { @@ -273,6 +284,7 @@ void SettingsDialog::LoadValuesFromConfig() { ui->nullGpuCheckBox->setChecked(Config::nullGpu()); ui->playBGMCheckBox->setChecked(Config::getPlayBGM()); ui->BGMVolumeSlider->setValue((Config::getBGMvolume())); + ui->discordRPCCheckbox->setChecked(Config::getEnableDiscordRPC()); ui->fullscreenCheckBox->setChecked(Config::isFullscreenMode()); ui->showSplashCheckBox->setChecked(Config::showSplash()); ui->ps4proCheckBox->setChecked(Config::isNeoMode()); diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index b686c3f8b..ad9cf5cef 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -148,6 +148,13 @@ + + + + Enable Discord Rich Presence + + + From 4fa55b7aa2ef2ac9fb5fbba25a1efa3fe66676ba Mon Sep 17 00:00:00 2001 From: RDN000 <109141852+RDN000@users.noreply.github.com> Date: Tue, 8 Oct 2024 17:58:57 +0200 Subject: [PATCH 16/93] Update sq translation (#1276) --- src/qt_gui/translations/sq.ts | 48 +++++++++++++++++------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index c6e4c018d..dddc694a4 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -417,7 +417,7 @@ Username - Nofka + Përdoruesi @@ -512,7 +512,7 @@ Update Channel - Kanali i Përditësimit + Kanali i përditësimit @@ -522,7 +522,7 @@ GUI Settings - Parametrat e GUI + Cilësimet e GUI @@ -532,7 +532,7 @@ Volume - Volumi + Vëllimi i zërit @@ -971,12 +971,12 @@ Point your mouse at an option to display its description. - Hidhni mouse-in mbi një opsion për të shfaqur përshkrimin e tij. + Vendos miun mbi një rregullim për të shfaqur përshkrimin e tij. consoleLanguageGroupBox - Gjuha e konsolës:\nPërcakton gjuhën që përdor loja PS4.\nRrekomandohet të vendosni këtë në një gjuhë që loja mbështet, e cila do të ndryshojë sipas rajonit. + Gjuha e konsolës:\nPërcakton gjuhën që përdor loja PS4.\nKëshillohet të caktosh një gjuhë që loja mbështet, e cila do të ndryshojë sipas rajonit. @@ -986,17 +986,17 @@ fullscreenCheckBox - Aktivizo ekranin e plotë:\nAutomatikisht vendos dritaren e lojës në modalitetin e ekranit të plotë.\nKjo mund të aktivizohet duke shtypur çelësin F11. + Aktivizo ekranin e plotë:\nVendos automatikisht dritaren e lojës në mënyrën e ekranit të plotë.\nKjo mund të aktivizohet duke shtypur tastin F11. showSplashCheckBox - Shfaq ekranin e ngarkesës:\nShfaq ekranin e ngarkesës së lojës (një imazh special) gjatë fillimit të lojës. + Shfaq ekranin e ngarkesës:\nShfaq ekranin e ngarkesës së lojës (një pamje e veçantë) gjatë fillimit të lojës. ps4proCheckBox - Është PS4 Pro:\nBën që emulatori të veprojë si një PS4 PRO, i cili mund të aktivizojë karakteristika speciale në lojrat që e mbështesin atë. + Është PS4 Pro:\nBën që emulatori të veprojë si një PS4 PRO, gjë që mund të aktivizojë veçori të veçanta në lojrat që e mbështesin. @@ -1006,67 +1006,67 @@ logTypeGroupBox - Tipi i logut:\nPërcakton nëse të sinkronizoni daljen e dritares së logut për performancën. Mund të ketë efekte të këqija në emulim. + Lloji i ditarit:\nPërcakton nëse të sinkronizohet dalja e dritares së ditarit për performancë. Mund të ketë efekte të këqija në emulim. logFilter - Filtri i logut: Filtron logun për të printuar vetëm informacione specifike. Shembuj: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Nivelet: Trace, Debug, Info, Warning, Error, Critical - në këtë rend, një nivel specifik hesht të gjitha nivelet përpara në listë dhe regjistron çdo nivel pas saj. + Filtri i ditarit: Filtron ditarin për të shfaqur vetëm informacione specifike. Shembuj: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Nivelet: Trace, Debug, Info, Warning, Error, Critical - në këtë rend, një nivel specifik hesht të gjitha nivelet përpara në listë dhe regjistron çdo nivel pas atij. updaterGroupBox - Aktualizimi:\nRelease: Versionet zyrtare të lëshuara çdo muaj që mund të jenë shumë të vjetra, por janë më të besueshme dhe të testuara.\nNightly: Versionet e zhvillimit që kanë të gjitha veçoritë dhe rregullimet më të fundit, por mund të përmbajnë gabime dhe janë më pak të qëndrueshme. + Aktualizimi:\nRelease: Versionet zyrtare të lëshuara çdo muaj që mund të jenë shumë të vjetra, por janë më të besueshme dhe të provuara.\nNightly: Versionet e zhvillimit që kanë të gjitha veçoritë dhe rregullimet më të fundit, por mund të përmbajnë gabime dhe janë më pak të qëndrueshme. GUIgroupBox - Lojë muzikë titulli:\nNëse një lojë e mbështet, aktivizoja luajtjen e muzikës speciale kur të zgjidhni lojën në GUI. + Luaj muzikën e titullit:\nNëse një lojë e mbështet, aktivizohet luajtja e muzikës të veçantë kur të zgjidhësh lojën në GUI. graphicsAdapterGroupBox - Dispositivi grafik:\nNë sistemet me GPU të shumëfishta, zgjidhni GPU-në që do të përdorë emulatori nga lista e rënies,\nor zgjidhni "Auto Select" për ta përcaktuar automatikisht. + Pajisja grafike:\nNë sistemet me GPU të shumëfishta, zgjidh GPU-në që do të përdorë emulatori nga lista rënëse,\nose zgjidh "Auto Select" për ta përcaktuar automatikisht. resolutionLayout - Gjerësia/ Lartësia:\nPërcakton madhësinë e dritares së emulatorit në nisje, e cila mund të rregullohet gjatë lojës.\nKjo është ndryshe nga rezolucioni në lojë. + Gjerësia/Lartësia:\nPërcakton madhësinë e dritares së emulatorit në nisje, e cila mund të rregullohet gjatë lojës.\nKjo është ndryshe nga rezolucioni në lojë. heightDivider - Pjesëtari Vblank:\nShpejtësia e kuadrit me të cilën refreshohet emulatori është shumëzuar me këtë numër. Ndryshimi i këtij mund të ketë efekte të këqija, si rritja e shpejtësisë së lojës ose shkatërrimi i funksionalitetit kritik të lojës që nuk e pret këtë të ndryshojë! + Ndarësi Vblank:\nFrekuenca pamore me të cilën rifreskohet emulatori shumëzohet me këtë numër. Ndryshimi i këtij mund të ketë efekte të këqija, si rritja e shpejtësisë së lojës ose prishja e punimit thelbësor të lojës që nuk e pret këtë ndryshim! dumpShadersCheckBox - Aktivizo dump-in e shaders:\nPër qëllime të debugimit teknik, ruan shaders e lojës në një folder ndërsa ato renditen. + Aktivizo zbrazjen e shaders-ave:\nPër qëllime të korrigjimit teknik, ruan shaders-at e lojës në një dosje ndërsa ato pasqyrohen. nullGpuCheckBox - Aktivizo GPU Null:\nPër qëllime të debugimit teknik, deaktivizon renditjen e lojës sikur nuk do të kishte një kartë grafike. + Aktivizo GPU-në Null:\nPër qëllime të korrigjimit teknik, çaktivizon pasqyrimin e lojës sikur nuk ka një kartë grafike. debugDump - Aktivizo dump-in e debugimit:\nRuani simbolet e importit dhe eksportit dhe informacionin e titullit të skedarit për aplikacionin aktual PS4 që po punon në një katalog. + Aktivizo zbrazjen për korrigjim:\nRuan simbolet e importit dhe eksportit dhe informacionin e kreut të skedarit për aplikacionin PS4 që po ekzekutohet në një dosje. vkValidationCheckBox - Aktivizo stratet e validimit Vulkan:\nAktivizon një sistem që validon gjendjen e renderizuesit Vulkan dhe regjistron informacionin në lidhje me gjendjen e tij të brendshme. Kjo do të ulet performancën dhe ndoshta do të ndryshojë sjelljen e emulimit. + Aktivizo shtresat e vlefshmërisë Vulkan:\nAktivizon një sistem që vërteton gjendjen e pasqyruesit Vulkan dhe regjistron informacionin në lidhje me gjendjen e tij të brendshme. Kjo do të ul performancën dhe ndoshta do të ndryshojë sjelljen e emulimit. vkSyncValidationCheckBox - Aktivizo validimin e sinkronizimit Vulkan:\nAktivizon një sistem që validon kohën e detyrave të renderizimit Vulkan. Kjo do të ulet performancën dhe ndoshta do të ndryshojë sjelljen e emulimit. + Aktivizo vërtetimin e sinkronizimit Vulkan:\nAktivizon një sistem që vërteton kohën e detyrave të pasqyrimit Vulkan. Kjo do të ul performancën dhe ndoshta do të ndryshojë sjelljen e emulimit. rdocCheckBox - Aktivizo debugimin RenderDoc:\nNëse aktivizohet, emulatori do të ofrojë pajtueshmëri me Renderdoc për të lejuar kapjen dhe analizën e kornizës aktuale të renderizuar. + Aktivizo korrigjimin RenderDoc:\nNëse aktivizohet, emulatori do të ofrojë pajtueshmëri me Renderdoc për të lejuar kapjen dhe analizën e pamjes të pasqyruar në moment. @@ -1162,7 +1162,7 @@ Update Channel - Kanali i Përditësimit + Kanali i përditësimit @@ -1240,4 +1240,4 @@ Krijimi i skedarit skript të përditësimit dështoi - \ No newline at end of file + From 53b7c5cc7747d2dc690721e5891e4145dd8119fe Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Tue, 8 Oct 2024 19:03:37 +0300 Subject: [PATCH 17/93] Update discord_rpc_handler.cpp Add new app ID --- src/common/discord_rpc_handler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/discord_rpc_handler.cpp b/src/common/discord_rpc_handler.cpp index 8b6e78c5d..91b278a15 100644 --- a/src/common/discord_rpc_handler.cpp +++ b/src/common/discord_rpc_handler.cpp @@ -10,7 +10,7 @@ namespace DiscordRPCHandler { void RPC::init() { DiscordEventHandlers handlers{}; - Discord_Initialize("1138176975865909360", &handlers, 1, nullptr); + Discord_Initialize("1139939140494971051", &handlers, 1, nullptr); startTimestamp = time(nullptr); rpcEnabled = true; } From e45eb0da9a3851ed7a66d3b36548cf3a26cfd38e Mon Sep 17 00:00:00 2001 From: Alexandre Bouvier Date: Tue, 8 Oct 2024 16:37:20 +0000 Subject: [PATCH 18/93] discord-rpc: fix tracked commit (#1294) --- externals/discord-rpc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/discord-rpc b/externals/discord-rpc index 80d35b5f8..4ec218155 160000 --- a/externals/discord-rpc +++ b/externals/discord-rpc @@ -1 +1 @@ -Subproject commit 80d35b5f86adc6557d0384771119cf167495dbae +Subproject commit 4ec218155d73bcb8022f8f7ca72305d801f84beb From 7d4f1ce5f98f87ef94800adbe3119054c477ba8f Mon Sep 17 00:00:00 2001 From: Alexandre Bouvier Date: Wed, 9 Oct 2024 06:28:09 +0000 Subject: [PATCH 19/93] fix some warnings (#1306) --- externals/CMakeLists.txt | 11 +++++++---- src/imgui/imgui_config.h | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index a528eaedb..e5733b981 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -3,7 +3,10 @@ set(BUILD_SHARED_LIBS OFF) set(BUILD_TESTING OFF) -set_property(DIRECTORY PROPERTY EXCLUDE_FROM_ALL ON) +set_directory_properties(PROPERTIES + EXCLUDE_FROM_ALL ON + SYSTEM ON +) if (MSVC) # Silence "deprecation" warnings @@ -16,7 +19,7 @@ if (NOT TARGET Boost::headers) set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/ext-boost" CACHE STRING "") set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "") add_library(boost INTERFACE) - target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR}) + target_include_directories(boost INTERFACE ${Boost_INCLUDE_DIR}) add_library(Boost::headers ALIAS boost) endif() @@ -77,7 +80,7 @@ endif() # RenderDoc if (NOT TARGET RenderDoc::API) add_library(renderdoc INTERFACE) - target_include_directories(renderdoc SYSTEM INTERFACE ./renderdoc) + target_include_directories(renderdoc INTERFACE ./renderdoc) add_library(RenderDoc::API ALIAS renderdoc) endif() @@ -190,4 +193,4 @@ add_subdirectory(discord-rpc/) target_include_directories(discord-rpc INTERFACE discord-rpc/include) # GCN Headers -add_subdirectory(gcn) \ No newline at end of file +add_subdirectory(gcn) diff --git a/src/imgui/imgui_config.h b/src/imgui/imgui_config.h index 2094d56bc..66d2b25c1 100644 --- a/src/imgui/imgui_config.h +++ b/src/imgui/imgui_config.h @@ -29,4 +29,4 @@ extern void assert_fail_debug_msg(const char* msg); constexpr ImVec2(float _v) : x(_v), y(_v) {} #define IM_VEC4_CLASS_EXTRA \ - constexpr ImVec4(float _v) : x(_v), y(_v), z(_v), w(_v) {} \ No newline at end of file + constexpr ImVec4(float _v) : x(_v), y(_v), z(_v), w(_v) {} From 6fe26173dcb1e294560d58c099a60e1d249bb5fb Mon Sep 17 00:00:00 2001 From: voguelike <73729626+voguelike@users.noreply.github.com> Date: Wed, 9 Oct 2024 02:28:25 -0400 Subject: [PATCH 20/93] sophisticated fix for amd gpu + reshade instead of workaround (#1282) --- src/video_core/renderer_vulkan/vk_instance.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 7a1d784fa..3513f60d2 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -265,7 +265,9 @@ bool Instance::CreateDevice() { // These extensions are promoted by Vulkan 1.3, but for greater compatibility we use Vulkan 1.2 // with extensions. - tooling_info = add_extension(VK_EXT_TOOLING_INFO_EXTENSION_NAME); + if (Config ::vkValidationEnabled() || Config::isRdocEnabled()) { + tooling_info = add_extension(VK_EXT_TOOLING_INFO_EXTENSION_NAME); + } const bool maintenance4 = add_extension(VK_KHR_MAINTENANCE_4_EXTENSION_NAME); add_extension(VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME); add_extension(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); From d4eae28ce217f9ac19206bf91e8bd2bbb098036a Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Wed, 9 Oct 2024 07:30:51 -0300 Subject: [PATCH 21/93] Play Time (#1305) * Play Time * SDL * QT * Sort / Formatting * Timer 1 minute * remove the seconds removes the seconds from the screen, but in the play_time.txt file it continues to record the seconds for better accuracy, and the screen is cleaner * fixes the invisible 0 * SDL . . . * Fix Timer --- src/emulator.cpp | 94 ++++++++++++++++++++++++++++++++ src/emulator.h | 2 + src/qt_gui/game_info.h | 3 + src/qt_gui/game_list_frame.cpp | 67 +++++++++++++++++++++-- src/qt_gui/game_list_frame.h | 7 ++- src/qt_gui/game_list_utils.h | 2 + src/qt_gui/main_window.cpp | 1 + src/qt_gui/settings_dialog.ui | 4 +- src/qt_gui/translations/ar.ts | 5 ++ src/qt_gui/translations/da_DK.ts | 5 ++ src/qt_gui/translations/de.ts | 5 ++ src/qt_gui/translations/el.ts | 5 ++ src/qt_gui/translations/en.ts | 5 ++ src/qt_gui/translations/es_ES.ts | 5 ++ src/qt_gui/translations/fa_IR.ts | 5 ++ src/qt_gui/translations/fi.ts | 5 ++ src/qt_gui/translations/fr.ts | 5 ++ src/qt_gui/translations/hu_HU.ts | 5 ++ src/qt_gui/translations/id.ts | 5 ++ src/qt_gui/translations/it.ts | 5 ++ src/qt_gui/translations/ja_JP.ts | 5 ++ src/qt_gui/translations/ko_KR.ts | 5 ++ src/qt_gui/translations/lt_LT.ts | 5 ++ src/qt_gui/translations/nb.ts | 5 ++ src/qt_gui/translations/nl.ts | 5 ++ src/qt_gui/translations/pl_PL.ts | 5 ++ src/qt_gui/translations/pt_BR.ts | 5 ++ src/qt_gui/translations/ro_RO.ts | 5 ++ src/qt_gui/translations/ru_RU.ts | 5 ++ src/qt_gui/translations/sq.ts | 5 ++ src/qt_gui/translations/tr_TR.ts | 5 ++ src/qt_gui/translations/vi_VN.ts | 5 ++ src/qt_gui/translations/zh_CN.ts | 5 ++ src/qt_gui/translations/zh_TW.ts | 5 ++ 34 files changed, 303 insertions(+), 7 deletions(-) diff --git a/src/emulator.cpp b/src/emulator.cpp index a9d0f6de2..9f801fb83 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -8,6 +8,7 @@ #include "common/logging/backend.h" #include "common/logging/log.h" #ifdef ENABLE_QT_GUI +#include #include "common/memory_patcher.h" #endif #include "common/assert.h" @@ -79,6 +80,17 @@ Emulator::Emulator() { // Load renderdoc module. VideoCore::LoadRenderDoc(); + + // Start the timer (Play Time) +#ifdef ENABLE_QT_GUI + start_time = std::chrono::steady_clock::now(); + const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); + QString filePath = QString::fromStdString((user_dir / "play_time.txt").string()); + QFile file(filePath); + if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { + LOG_INFO(Loader, "Error opening or creating play_time.txt"); + } +#endif } Emulator::~Emulator() { @@ -122,6 +134,14 @@ void Emulator::Run(const std::filesystem::path& file) { } #ifdef ENABLE_QT_GUI MemoryPatcher::g_game_serial = id; + + // Timer for 'Play Time' + QTimer* timer = new QTimer(); + QObject::connect(timer, &QTimer::timeout, [this, id]() { + UpdatePlayTime(id); + start_time = std::chrono::steady_clock::now(); + }); + timer->start(60000); // 60000 ms = 1 minute #endif title = param_sfo->GetString("TITLE").value_or("Unknown title"); LOG_INFO(Loader, "Game id: {} Title: {}", id, title); @@ -228,6 +248,10 @@ void Emulator::Run(const std::filesystem::path& file) { window->waitEvent(); } +#ifdef ENABLE_QT_GUI + UpdatePlayTime(id); +#endif + std::exit(0); } @@ -269,4 +293,74 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file) { } } +#ifdef ENABLE_QT_GUI +void Emulator::UpdatePlayTime(const std::string& serial) { + const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); + QString filePath = QString::fromStdString((user_dir / "play_time.txt").string()); + + QFile file(filePath); + if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { + LOG_INFO(Loader, "Error opening play_time.txt"); + return; + } + + auto end_time = std::chrono::steady_clock::now(); + auto duration = std::chrono::duration_cast(end_time - start_time); + int totalSeconds = duration.count(); + + QTextStream in(&file); + QStringList lines; + QString content; + while (!in.atEnd()) { + content += in.readLine() + "\n"; + } + file.close(); + + QStringList existingLines = content.split('\n', Qt::SkipEmptyParts); + int accumulatedSeconds = 0; + bool found = false; + + for (const QString& line : existingLines) { + QStringList parts = line.split(' '); + if (parts.size() == 2 && parts[0] == QString::fromStdString(serial)) { + QStringList timeParts = parts[1].split(':'); + if (timeParts.size() == 3) { + int hours = timeParts[0].toInt(); + int minutes = timeParts[1].toInt(); + int seconds = timeParts[2].toInt(); + accumulatedSeconds = hours * 3600 + minutes * 60 + seconds; + found = true; + break; + } + } + } + accumulatedSeconds += totalSeconds; + int hours = accumulatedSeconds / 3600; + int minutes = (accumulatedSeconds % 3600) / 60; + int seconds = accumulatedSeconds % 60; + QString playTimeSaved = QString::number(hours) + ":" + + QString::number(minutes).rightJustified(2, '0') + ":" + + QString::number(seconds).rightJustified(2, '0'); + + if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream out(&file); + bool lineUpdated = false; + + for (const QString& line : existingLines) { + if (line.startsWith(QString::fromStdString(serial))) { + out << QString::fromStdString(serial) + " " + playTimeSaved + "\n"; + lineUpdated = true; + } else { + out << line << "\n"; + } + } + + if (!lineUpdated) { + out << QString::fromStdString(serial) + " " + playTimeSaved + "\n"; + } + } + LOG_INFO(Loader, "Playing time for {}: {}", serial, playTimeSaved.toStdString()); +} +#endif + } // namespace Core diff --git a/src/emulator.h b/src/emulator.h index 01bce7e7c..dc2959af4 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -26,6 +26,7 @@ public: ~Emulator(); void Run(const std::filesystem::path& file); + void UpdatePlayTime(const std::string& serial); private: void LoadSystemModules(const std::filesystem::path& file); @@ -34,6 +35,7 @@ private: Input::GameController* controller; Core::Linker* linker; std::unique_ptr window; + std::chrono::steady_clock::time_point start_time; }; } // namespace Core diff --git a/src/qt_gui/game_info.h b/src/qt_gui/game_info.h index 9db25482a..8f65803bf 100644 --- a/src/qt_gui/game_info.h +++ b/src/qt_gui/game_info.h @@ -60,6 +60,9 @@ public: if (auto app_ver = psf.GetString("APP_VER"); app_ver.has_value()) { game.version = *app_ver; } + if (const auto play_time = psf.GetString("PLAY_TIME"); play_time.has_value()) { + game.play_time = *play_time; + } } return game; } diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp index c2f6736b8..99628b083 100644 --- a/src/qt_gui/game_list_frame.cpp +++ b/src/qt_gui/game_list_frame.cpp @@ -24,16 +24,17 @@ GameListFrame::GameListFrame(std::shared_ptr game_info_get, QWidg this->horizontalHeader()->setSortIndicatorShown(true); this->horizontalHeader()->setStretchLastSection(true); this->setContextMenuPolicy(Qt::CustomContextMenu); - this->setColumnCount(8); + this->setColumnCount(9); this->setColumnWidth(1, 300); // Name this->setColumnWidth(2, 120); // Serial this->setColumnWidth(3, 90); // Region this->setColumnWidth(4, 90); // Firmware this->setColumnWidth(5, 90); // Size this->setColumnWidth(6, 90); // Version + this->setColumnWidth(7, 100); // Play Time QStringList headers; headers << tr("Icon") << tr("Name") << tr("Serial") << tr("Region") << tr("Firmware") - << tr("Size") << tr("Version") << tr("Path"); + << tr("Size") << tr("Version") << tr("Play Time") << tr("Path"); this->setHorizontalHeaderLabels(headers); this->horizontalHeader()->setSortIndicatorShown(true); this->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); @@ -100,9 +101,37 @@ void GameListFrame::PopulateGameList() { SetTableItem(i, 4, QString::fromStdString(m_game_info->m_games[i].fw)); SetTableItem(i, 5, QString::fromStdString(m_game_info->m_games[i].size)); SetTableItem(i, 6, QString::fromStdString(m_game_info->m_games[i].version)); + + QString playTime = GetPlayTime(m_game_info->m_games[i].serial); + if (playTime.isEmpty()) { + m_game_info->m_games[i].play_time = "0:00:00"; + SetTableItem(i, 7, "0"); + } else { + QStringList timeParts = playTime.split(':'); + int hours = timeParts[0].toInt(); + int minutes = timeParts[1].toInt(); + int seconds = timeParts[2].toInt(); + + QString formattedPlayTime; + if (hours > 0) { + formattedPlayTime += QString("%1h ").arg(hours); + } + if (minutes > 0) { + formattedPlayTime += QString("%1m ").arg(minutes); + } + + formattedPlayTime = formattedPlayTime.trimmed(); + m_game_info->m_games[i].play_time = playTime.toStdString(); + if (formattedPlayTime.isEmpty()) { + SetTableItem(i, 7, "0"); + } else { + SetTableItem(i, 7, formattedPlayTime); + } + } + QString path; Common::FS::PathToQString(path, m_game_info->m_games[i].path); - SetTableItem(i, 7, path); + SetTableItem(i, 8, path); } } @@ -171,7 +200,7 @@ void GameListFrame::ResizeIcons(int iconSize) { this->setItem(index, 0, iconItem); index++; } - this->horizontalHeader()->setSectionResizeMode(7, QHeaderView::ResizeToContents); + this->horizontalHeader()->setSectionResizeMode(8, QHeaderView::ResizeToContents); } void GameListFrame::SetTableItem(int row, int column, QString itemStr) { @@ -224,3 +253,33 @@ void GameListFrame::SetRegionFlag(int row, int column, QString itemStr) { this->setItem(row, column, item); this->setCellWidget(row, column, widget); } + +QString GameListFrame::GetPlayTime(const std::string& serial) { + QString playTime; + const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); + QString filePath = QString::fromStdString((user_dir / "play_time.txt").string()); + + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + return playTime; + } + + while (!file.atEnd()) { + QByteArray line = file.readLine(); + QString lineStr = QString::fromUtf8(line).trimmed(); + + QStringList parts = lineStr.split(' '); + if (parts.size() >= 2) { + QString fileSerial = parts[0]; + QString time = parts[1]; + + if (fileSerial == QString::fromStdString(serial)) { + playTime = time; + break; + } + } + } + + file.close(); + return playTime; +} diff --git a/src/qt_gui/game_list_frame.h b/src/qt_gui/game_list_frame.h index 957ac7318..6da2734a8 100644 --- a/src/qt_gui/game_list_frame.h +++ b/src/qt_gui/game_list_frame.h @@ -29,6 +29,7 @@ public Q_SLOTS: private: void SetTableItem(int row, int column, QString itemStr); void SetRegionFlag(int row, int column, QString itemStr); + QString GetPlayTime(const std::string& serial); QList m_columnActs; GameInfoClass* game_inf_get = nullptr; bool ListSortedAsc = true; @@ -68,6 +69,8 @@ public: case 6: return a.version < b.version; case 7: + return a.play_time < b.play_time; + case 8: return a.path < b.path; default: return false; @@ -89,9 +92,11 @@ public: case 6: return a.version > b.version; case 7: + return a.play_time > b.play_time; + case 8: return a.path > b.path; default: return false; } } -}; \ No newline at end of file +}; diff --git a/src/qt_gui/game_list_utils.h b/src/qt_gui/game_list_utils.h index f62275eff..3d710c5b7 100644 --- a/src/qt_gui/game_list_utils.h +++ b/src/qt_gui/game_list_utils.h @@ -19,6 +19,8 @@ struct GameInfo { std::string version = "Unknown"; std::string region = "Unknown"; std::string fw = "Unknown"; + + std::string play_time = "Unknown"; }; class GameListUtils { diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index f93bdb069..2323214e6 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -58,6 +58,7 @@ bool MainWindow::Init() { this->show(); // load game list LoadGameLists(); + // Check for update CheckUpdateMain(true); auto end = std::chrono::steady_clock::now(); diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index ad9cf5cef..9637c5fec 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -12,7 +12,7 @@ 0 0 854 - 570 + 605 @@ -71,7 +71,7 @@ - 1 + 0 diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index ca23620bb..ec03c04b5 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -1111,6 +1111,11 @@ Path مسار + + + Play Time + وقت اللعب + CheckUpdate diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 0d81e2357..c7edfb102 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -1111,6 +1111,11 @@ Path Sti + + + Play Time + Spilletid + CheckUpdate diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index 9e31afd6e..43e19a37f 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -1111,6 +1111,11 @@ Path Pfad + + + Play Time + Spielzeit + CheckUpdate diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index de10fc3a8..8009dfd88 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -1111,6 +1111,11 @@ Path Διαδρομή + + + Play Time + Χρόνος παιχνιδιού + CheckUpdate diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 18c54428f..8ac683804 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -1146,6 +1146,11 @@ Path Path + + + Play Time + Play Time + CheckUpdate diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 86a3e1adc..0c6591a5f 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -1111,6 +1111,11 @@ Path Ruta + + + Play Time + Tiempo de Juego + CheckUpdate diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index a613af70b..60d8be89e 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -1111,6 +1111,11 @@ Path مسیر + + + Play Time + زمان بازی + CheckUpdate diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index e4010b872..30c60af81 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -1111,6 +1111,11 @@ Path Polku + + + Play Time + Peliaika + CheckUpdate diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index b11b585c9..54e4b0e82 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -1111,6 +1111,11 @@ Path Répertoire + + + Play Time + Temps de jeu + CheckUpdate diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 5986193c4..a6481d5a2 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -1111,6 +1111,11 @@ Path Útvonal + + + Play Time + Játékidő + CheckUpdate diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index 274029bd0..2fdc9b68c 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -1111,6 +1111,11 @@ Path Jalur + + + Play Time + Waktu Bermain + CheckUpdate diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index 07976865c..aba38a290 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -1111,6 +1111,11 @@ Path Percorso + + + Play Time + Tempo di Gioco + CheckUpdate diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index c3444906b..7524250f5 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -1111,6 +1111,11 @@ Path パス + + + Play Time + プレイ時間 + CheckUpdate diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index e816e2f4b..4805a4d0e 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -1111,6 +1111,11 @@ Path Path + + + Play Time + Play Time + CheckUpdate diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index d5b4ff3ad..beaaa116a 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -1111,6 +1111,11 @@ Path Kelias + + + Play Time + Žaidimo laikas + CheckUpdate diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index 334600950..c193e83dd 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -1111,6 +1111,11 @@ Path Sti + + + Play Time + Spilletid + CheckUpdate diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index 6e49d4640..6b0befa92 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -1111,6 +1111,11 @@ Path Pad + + + Play Time + Speeltijd + CheckUpdate diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index e95b8ba0c..ba8b4f9e4 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -1111,6 +1111,11 @@ Path Ścieżka + + + Play Time + Czas gry + CheckUpdate diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index e1570b2cd..2e859f12b 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -1111,6 +1111,11 @@ Path Diretório + + + Play Time + Horas Jogadas + CheckUpdate diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index e98b7e5c9..b36647c1c 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -1111,6 +1111,11 @@ Path Drum + + + Play Time + Timp de Joacă + CheckUpdate diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index aaff66ef9..224342f34 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -1111,6 +1111,11 @@ Path Путь + + + Play Time + Время Игры + CheckUpdate diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index dddc694a4..062226369 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -1111,6 +1111,11 @@ Path Shtegu + + + Play Time + Kohë Lojë + CheckUpdate diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 5cd71af34..3e9173c7c 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -1111,6 +1111,11 @@ Path Yol + + + Play Time + Oynama Süresi + CheckUpdate diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index 523683621..af8d218a6 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -1111,6 +1111,11 @@ Path Đường dẫn + + + Play Time + Thời gian chơi + CheckUpdate diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index f2abc4f4b..5eef55641 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -1111,6 +1111,11 @@ Path 路径 + + + Play Time + 游戏时间 + CheckUpdate diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 2d34f8d12..7e3585a07 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -1111,6 +1111,11 @@ Path 路徑 + + + Play Time + 遊玩時間 + CheckUpdate From ddb0928f10fc1c63e7b852f6e5744b177137ed25 Mon Sep 17 00:00:00 2001 From: baggins183 Date: Wed, 9 Oct 2024 03:46:04 -0700 Subject: [PATCH 22/93] update boost submodule. Add boost includes in subproject instead of externals/CMakeLists.txt (#1307) --- externals/CMakeLists.txt | 5 +---- externals/ext-boost | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index e5733b981..2f9336c21 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -16,11 +16,8 @@ endif() # Boost if (NOT TARGET Boost::headers) set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/ext-boost" CACHE STRING "") - set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/ext-boost" CACHE STRING "") set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "") - add_library(boost INTERFACE) - target_include_directories(boost INTERFACE ${Boost_INCLUDE_DIR}) - add_library(Boost::headers ALIAS boost) + add_subdirectory(ext-boost) endif() # fmtlib diff --git a/externals/ext-boost b/externals/ext-boost index a04136add..f2474e1b5 160000 --- a/externals/ext-boost +++ b/externals/ext-boost @@ -1 +1 @@ -Subproject commit a04136add1e469f46d8ae8d3e8307779240a5c53 +Subproject commit f2474e1b584fb7a3ed6f85ba875e6eacd742ec8a From 873fbc469b7711b09b71bc5d5dda854b0b5e939a Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:52:56 +0200 Subject: [PATCH 23/93] Fix spacing --- src/video_core/renderer_vulkan/vk_instance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 3513f60d2..21941b8e9 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -265,7 +265,7 @@ bool Instance::CreateDevice() { // These extensions are promoted by Vulkan 1.3, but for greater compatibility we use Vulkan 1.2 // with extensions. - if (Config ::vkValidationEnabled() || Config::isRdocEnabled()) { + if (Config::vkValidationEnabled() || Config::isRdocEnabled()) { tooling_info = add_extension(VK_EXT_TOOLING_INFO_EXTENSION_NAME); } const bool maintenance4 = add_extension(VK_KHR_MAINTENANCE_4_EXTENSION_NAME); From c9f894c45a80645bc50f722e37e47a949c85f348 Mon Sep 17 00:00:00 2001 From: psucien Date: Wed, 9 Oct 2024 20:44:38 +0200 Subject: [PATCH 24/93] hot-fix: catch device loss on presentation (prevents deadlock in waiting) --- src/video_core/renderer_vulkan/renderer_vulkan.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 97e5185e5..64a483654 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -446,6 +446,8 @@ Frame* RendererVulkan::GetRenderFrame() { // Wait for the presentation to be finished so all frame resources are free while (wait() != vk::Result::eSuccess) { + ASSERT_MSG(result != vk::Result::eErrorDeviceLost, + "Device lost during waiting for a frame"); // Retry if the waiting times out if (result == vk::Result::eTimeout) { continue; From 56e8ed7833553466398d077637dc88d19f65d080 Mon Sep 17 00:00:00 2001 From: ElBread3 <92335081+ElBread3@users.noreply.github.com> Date: Thu, 10 Oct 2024 02:28:59 -0500 Subject: [PATCH 25/93] Multiple Install Folders (#1308) * multiple install folders implimentation * clang format * paths setting tab * clang format --- CMakeLists.txt | 2 + src/common/config.cpp | 33 ++++++++++--- src/common/config.h | 4 +- src/qt_gui/game_info.cpp | 16 ++++--- src/qt_gui/game_install_dialog.cpp | 8 +++- src/qt_gui/install_dir_select.cpp | 76 ++++++++++++++++++++++++++++++ src/qt_gui/install_dir_select.h | 31 ++++++++++++ src/qt_gui/main.cpp | 2 +- src/qt_gui/main_window.cpp | 8 +++- src/qt_gui/settings_dialog.cpp | 49 +++++++++++++++++++ src/qt_gui/settings_dialog.ui | 70 +++++++++++++++++++++++++++ 11 files changed, 278 insertions(+), 21 deletions(-) create mode 100644 src/qt_gui/install_dir_select.cpp create mode 100644 src/qt_gui/install_dir_select.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2db263b3a..eeca274ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -693,6 +693,8 @@ set(QT_GUI src/qt_gui/about_dialog.cpp src/qt_gui/game_grid_frame.h src/qt_gui/game_install_dialog.cpp src/qt_gui/game_install_dialog.h + src/qt_gui/install_dir_select.cpp + src/qt_gui/install_dir_select.h src/qt_gui/pkg_viewer.cpp src/qt_gui/pkg_viewer.h src/qt_gui/trophy_viewer.cpp diff --git a/src/common/config.cpp b/src/common/config.cpp index 40be5ebec..55cf29428 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -62,7 +62,7 @@ static s16 cursorState = HideCursorState::Idle; static int cursorHideTimeout = 5; // 5 seconds (default) // Gui -std::filesystem::path settings_install_dir = {}; +std::vector settings_install_dirs = {}; std::filesystem::path settings_addon_install_dir = {}; u32 main_window_geometry_x = 400; u32 main_window_geometry_y = 400; @@ -325,8 +325,9 @@ void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) { main_window_geometry_w = w; main_window_geometry_h = h; } -void setGameInstallDir(const std::filesystem::path& dir) { - settings_install_dir = dir; +void setGameInstallDirs(const std::vector& dir) { + settings_install_dirs.resize(dir.size()); + settings_install_dirs = dir; } void setAddonInstallDir(const std::filesystem::path& dir) { settings_addon_install_dir = dir; @@ -384,8 +385,8 @@ u32 getMainWindowGeometryW() { u32 getMainWindowGeometryH() { return main_window_geometry_h; } -std::filesystem::path getGameInstallDir() { - return settings_install_dir; +std::vector getGameInstallDirs() { + return settings_install_dirs; } std::filesystem::path getAddonInstallDir() { if (settings_addon_install_dir.empty()) { @@ -523,7 +524,19 @@ void load(const std::filesystem::path& path) { mw_themes = toml::find_or(gui, "theme", 0); m_window_size_W = toml::find_or(gui, "mw_width", 0); m_window_size_H = toml::find_or(gui, "mw_height", 0); - settings_install_dir = toml::find_fs_path_or(gui, "installDir", {}); + + auto old_game_install_dir = toml::find_fs_path_or(gui, "installDir", {}); + if (!old_game_install_dir.empty()) { + settings_install_dirs.push_back(old_game_install_dir); + data.as_table().erase("installDir"); + } + + const auto install_dir_array = + toml::find_or>(gui, "installDirs", {}); + for (const auto& dir : install_dir_array) { + settings_install_dirs.emplace_back(std::filesystem::path{dir}); + } + settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {}); main_window_geometry_x = toml::find_or(gui, "geometry_x", 0); main_window_geometry_y = toml::find_or(gui, "geometry_y", 0); @@ -601,7 +614,13 @@ void save(const std::filesystem::path& path) { data["GUI"]["gameTableMode"] = m_table_mode; data["GUI"]["mw_width"] = m_window_size_W; data["GUI"]["mw_height"] = m_window_size_H; - data["GUI"]["installDir"] = std::string{fmt::UTF(settings_install_dir.u8string()).data}; + + std::vector install_dirs; + for (const auto& dirString : settings_install_dirs) { + install_dirs.emplace_back(std::string{fmt::UTF(dirString.u8string()).data}); + } + data["GUI"]["installDirs"] = install_dirs; + data["GUI"]["addonInstallDir"] = std::string{fmt::UTF(settings_addon_install_dir.u8string()).data}; data["GUI"]["geometry_x"] = main_window_geometry_x; diff --git a/src/common/config.h b/src/common/config.h index 90ebdb58a..e76f389c2 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -85,7 +85,7 @@ bool vkCrashDiagnosticEnabled(); // Gui void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h); -void setGameInstallDir(const std::filesystem::path& dir); +void setGameInstallDirs(const std::vector& dir); void setAddonInstallDir(const std::filesystem::path& dir); void setMainWindowTheme(u32 theme); void setIconSize(u32 size); @@ -104,7 +104,7 @@ u32 getMainWindowGeometryX(); u32 getMainWindowGeometryY(); u32 getMainWindowGeometryW(); u32 getMainWindowGeometryH(); -std::filesystem::path getGameInstallDir(); +std::vector getGameInstallDirs(); std::filesystem::path getAddonInstallDir(); u32 getMainWindowTheme(); u32 getIconSize(); diff --git a/src/qt_gui/game_info.cpp b/src/qt_gui/game_info.cpp index 6e8d89713..d82f43f20 100644 --- a/src/qt_gui/game_info.cpp +++ b/src/qt_gui/game_info.cpp @@ -10,14 +10,16 @@ GameInfoClass::GameInfoClass() = default; GameInfoClass::~GameInfoClass() = default; void GameInfoClass::GetGameInfo(QWidget* parent) { - QString installDir; - Common::FS::PathToQString(installDir, Config::getGameInstallDir()); QStringList filePaths; - QDir parentFolder(installDir); - QFileInfoList fileList = parentFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); - for (const auto& fileInfo : fileList) { - if (fileInfo.isDir()) { - filePaths.append(fileInfo.absoluteFilePath()); + for (const auto& installLoc : Config::getGameInstallDirs()) { + QString installDir; + Common::FS::PathToQString(installDir, installLoc); + QDir parentFolder(installDir); + QFileInfoList fileList = parentFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + for (const auto& fileInfo : fileList) { + if (fileInfo.isDir()) { + filePaths.append(fileInfo.absoluteFilePath()); + } } } m_games = QtConcurrent::mapped(filePaths, [&](const QString& path) { diff --git a/src/qt_gui/game_install_dialog.cpp b/src/qt_gui/game_install_dialog.cpp index 11daf2de0..4418d9a48 100644 --- a/src/qt_gui/game_install_dialog.cpp +++ b/src/qt_gui/game_install_dialog.cpp @@ -51,7 +51,9 @@ QWidget* GameInstallDialog::SetupGamesDirectory() { // Input. m_gamesDirectory = new QLineEdit(); QString install_dir; - Common::FS::PathToQString(install_dir, Config::getGameInstallDir()); + std::filesystem::path install_path = + Config::getGameInstallDirs().empty() ? "" : Config::getGameInstallDirs().front(); + Common::FS::PathToQString(install_dir, install_path); m_gamesDirectory->setText(install_dir); m_gamesDirectory->setMinimumWidth(400); @@ -125,7 +127,9 @@ void GameInstallDialog::Save() { } } - Config::setGameInstallDir(Common::FS::PathFromQString(gamesDirectory)); + std::vector install_dirs; + install_dirs.emplace_back(Common::FS::PathFromQString(gamesDirectory)); + Config::setGameInstallDirs(install_dirs); Config::setAddonInstallDir(Common::FS::PathFromQString(addonsDirectory)); const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); Config::save(config_dir / "config.toml"); diff --git a/src/qt_gui/install_dir_select.cpp b/src/qt_gui/install_dir_select.cpp new file mode 100644 index 000000000..e0951b123 --- /dev/null +++ b/src/qt_gui/install_dir_select.cpp @@ -0,0 +1,76 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "install_dir_select.h" + +InstallDirSelect::InstallDirSelect() : selected_dir() { + selected_dir = Config::getGameInstallDirs().empty() ? "" : Config::getGameInstallDirs().front(); + + if (!Config::getGameInstallDirs().empty() && Config::getGameInstallDirs().size() == 1) { + reject(); + } + + auto layout = new QVBoxLayout(this); + + layout->addWidget(SetupInstallDirList()); + layout->addStretch(); + layout->addWidget(SetupDialogActions()); + + setWindowTitle(tr("shadPS4 - Choose directory")); + setWindowIcon(QIcon(":images/shadps4.ico")); +} + +InstallDirSelect::~InstallDirSelect() {} + +QWidget* InstallDirSelect::SetupInstallDirList() { + auto group = new QGroupBox(tr("Select which directory you want to install to.")); + auto vlayout = new QVBoxLayout(); + + auto m_path_list = new QListWidget(); + QList qt_list; + for (const auto& str : Config::getGameInstallDirs()) { + QString installDirPath; + Common::FS::PathToQString(installDirPath, str); + qt_list.append(installDirPath); + } + m_path_list->insertItems(0, qt_list); + m_path_list->setSpacing(1); + + connect(m_path_list, &QListWidget::itemClicked, this, &InstallDirSelect::setSelectedDirectory); + connect(m_path_list, &QListWidget::itemActivated, this, + &InstallDirSelect::setSelectedDirectory); + + vlayout->addWidget(m_path_list); + + group->setLayout(vlayout); + return group; +} + +void InstallDirSelect::setSelectedDirectory(QListWidgetItem* item) { + if (item) { + const auto highlighted_path = Common::FS::PathFromQString(item->text()); + if (!highlighted_path.empty()) { + selected_dir = highlighted_path; + } + } +} + +QWidget* InstallDirSelect::SetupDialogActions() { + auto actions = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + connect(actions, &QDialogButtonBox::accepted, this, &InstallDirSelect::accept); + connect(actions, &QDialogButtonBox::rejected, this, &InstallDirSelect::reject); + + return actions; +} diff --git a/src/qt_gui/install_dir_select.h b/src/qt_gui/install_dir_select.h new file mode 100644 index 000000000..fdadf2fe0 --- /dev/null +++ b/src/qt_gui/install_dir_select.h @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "common/config.h" +#include "common/path_util.h" + +class QLineEdit; + +class InstallDirSelect final : public QDialog { +public: + InstallDirSelect(); + ~InstallDirSelect(); + + std::filesystem::path getSelectedDirectory() { + return selected_dir; + } + +private slots: + void BrowseGamesDirectory(); + +private: + QWidget* SetupInstallDirList(); + QWidget* SetupDialogActions(); + void setSelectedDirectory(QListWidgetItem* item); + std::filesystem::path selected_dir; +}; diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index 8c565a19b..da8804f69 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -30,7 +30,7 @@ int main(int argc, char* argv[]) { bool has_command_line_argument = argc > 1; // Check if the game install directory is set - if (Config::getGameInstallDir().empty() && !has_command_line_argument) { + if (Config::getGameInstallDirs().empty() && !has_command_line_argument) { GameInstallDialog dlg; dlg.exec(); } diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 2323214e6..025749dd4 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -16,6 +16,7 @@ #include "core/file_format/pkg.h" #include "core/loader.h" #include "game_install_dialog.h" +#include "install_dir_select.h" #include "main_window.h" #include "settings_dialog.h" #include "video_core/renderer_vulkan/vk_instance.h" @@ -672,7 +673,10 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int QMessageBox::critical(this, tr("PKG ERROR"), QString::fromStdString(failreason)); return; } - auto extract_path = Config::getGameInstallDir() / pkg.GetTitleID(); + InstallDirSelect ids; + ids.exec(); + auto game_install_dir = ids.getSelectedDirectory(); + auto extract_path = game_install_dir / pkg.GetTitleID(); QString pkgType = QString::fromStdString(pkg.GetPkgFlags()); QString gameDirPath; Common::FS::PathToQString(gameDirPath, extract_path); @@ -821,7 +825,7 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int connect(&futureWatcher, &QFutureWatcher::finished, this, [=, this]() { if (pkgNum == nPkg) { QString path; - Common::FS::PathToQString(path, Config::getGameInstallDir()); + Common::FS::PathToQString(path, game_install_dir); QMessageBox extractMsgBox(this); extractMsgBox.setWindowTitle(tr("Extraction Finished")); extractMsgBox.setText( diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index efc438455..a6fe6a265 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -220,6 +220,55 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge [](int val) { Config::setNullGpu(val); }); } + // PATH TAB + { + for (const auto& dir : Config::getGameInstallDirs()) { + QString path_string; + Common::FS::PathToQString(path_string, dir); + QListWidgetItem* item = new QListWidgetItem(path_string); + ui->gameFoldersListWidget->addItem(item); + } + + ui->removeFolderButton->setEnabled(false); + + connect(ui->addFolderButton, &QPushButton::clicked, this, [this]() { + QString file_path_string = + QFileDialog::getExistingDirectory(this, tr("Directory to install games")); + auto file_path = Common::FS::PathFromQString(file_path_string); + if (!file_path.empty()) { + std::vector install_dirs = Config::getGameInstallDirs(); + install_dirs.push_back(file_path); + Config::setGameInstallDirs(install_dirs); + QListWidgetItem* item = new QListWidgetItem(file_path_string); + ui->gameFoldersListWidget->addItem(item); + } + }); + + connect(ui->gameFoldersListWidget, &QListWidget::itemSelectionChanged, this, [this]() { + ui->removeFolderButton->setEnabled( + !ui->gameFoldersListWidget->selectedItems().isEmpty()); + }); + + connect(ui->removeFolderButton, &QPushButton::clicked, this, [this]() { + QListWidgetItem* selected_item = ui->gameFoldersListWidget->currentItem(); + QString item_path_string = selected_item ? selected_item->text() : QString(); + if (!item_path_string.isEmpty()) { + auto file_path = Common::FS::PathFromQString(item_path_string); + std::vector install_dirs = Config::getGameInstallDirs(); + + auto iterator = std::remove_if( + install_dirs.begin(), install_dirs.end(), + [&file_path](const std::filesystem::path& dir) { return file_path == dir; }); + + if (iterator != install_dirs.end()) { + install_dirs.erase(iterator, install_dirs.end()); + delete selected_item; + } + Config::setGameInstallDirs(install_dirs); + } + }); + } + // DEBUG TAB { connect(ui->debugDump, &QCheckBox::stateChanged, this, diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 9637c5fec..e1c064d5a 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -918,6 +918,76 @@ + + + Paths + + + + + + + + Game Folders + + + + + 0 + 20 + 401 + 331 + + + + + + + 100 + 360 + 80 + 24 + + + + Add... + + + + + + 210 + 360 + 80 + 24 + + + + Remove + + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::Preferred + + + + 40 + 20 + + + + + + + + Debug From 100036aecfaf49cc00566035740f450f381d7994 Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Thu, 10 Oct 2024 17:47:39 +0300 Subject: [PATCH 26/93] spirv: Flush denormals if possible (#1302) --- .../backend/spirv/emit_spirv.cpp | 50 ++++++++++------ .../backend/spirv/spirv_emit_context.cpp | 7 +-- .../frontend/copy_shader.cpp | 2 +- src/shader_recompiler/frontend/copy_shader.h | 2 +- src/shader_recompiler/info.h | 4 +- .../ir/passes/ring_access_elimination.cpp | 15 +++-- src/shader_recompiler/ir/reg.h | 21 ------- src/shader_recompiler/profile.h | 5 -- src/shader_recompiler/runtime_info.h | 34 ++++++----- src/video_core/amdgpu/liverpool.h | 6 ++ src/video_core/amdgpu/types.h | 16 +++++- .../renderer_vulkan/vk_instance.cpp | 3 +- src/video_core/renderer_vulkan/vk_instance.h | 6 ++ .../renderer_vulkan/vk_pipeline_cache.cpp | 57 ++++++++++--------- 14 files changed, 130 insertions(+), 98 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index f90e9db77..e84908a57 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -206,10 +206,7 @@ Id DefineMain(EmitContext& ctx, const IR::Program& program) { return main; } -void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { - const auto& info = program.info; - const std::span interfaces(ctx.interfaces.data(), ctx.interfaces.size()); - spv::ExecutionModel execution_model{}; +void SetupCapabilities(const Info& info, EmitContext& ctx) { ctx.AddCapability(spv::Capability::Image1D); ctx.AddCapability(spv::Capability::Sampled1D); ctx.AddCapability(spv::Capability::ImageQuery); @@ -247,6 +244,19 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { if (info.uses_group_ballot) { ctx.AddCapability(spv::Capability::GroupNonUniformBallot); } + if (info.stage == Stage::Export || info.stage == Stage::Vertex) { + ctx.AddExtension("SPV_KHR_shader_draw_parameters"); + ctx.AddCapability(spv::Capability::DrawParameters); + } + if (info.stage == Stage::Geometry) { + ctx.AddCapability(spv::Capability::Geometry); + } +} + +void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { + const auto& info = program.info; + const std::span interfaces(ctx.interfaces.data(), ctx.interfaces.size()); + spv::ExecutionModel execution_model{}; switch (program.info.stage) { case Stage::Compute: { const std::array workgroup_size{ctx.runtime_info.cs_info.workgroup_size}; @@ -290,6 +300,24 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { ctx.AddEntryPoint(execution_model, main, "main", interfaces); } +void SetupFloatMode(EmitContext& ctx, const Profile& profile, const RuntimeInfo& runtime_info, + Id main_func) { + ctx.AddExtension("SPV_KHR_float_controls"); + const auto fp_denorm_mode = runtime_info.fp_denorm_mode32; + if (fp_denorm_mode == AmdGpu::FpDenormMode::InOutFlush) { + if (profile.support_fp32_denorm_flush) { + ctx.AddCapability(spv::Capability::DenormFlushToZero); + ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormFlushToZero, 32U); + } + } else { + LOG_WARNING(Render_Vulkan, "Unknown FP denorm mode {}", u32(fp_denorm_mode)); + } + const auto fp_round_mode = runtime_info.fp_round_mode32; + if (fp_round_mode != AmdGpu::FpRoundMode::NearestEven) { + LOG_WARNING(Render_Vulkan, "Unknown FP rounding mode {}", u32(fp_round_mode)); + } +} + void PatchPhiNodes(const IR::Program& program, EmitContext& ctx) { auto inst{program.blocks.front()->begin()}; size_t block_index{0}; @@ -314,18 +342,8 @@ std::vector EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_in EmitContext ctx{profile, runtime_info, program.info, binding}; const Id main{DefineMain(ctx, program)}; DefineEntryPoint(program, ctx, main); - switch (program.info.stage) { - case Stage::Export: - case Stage::Vertex: - ctx.AddExtension("SPV_KHR_shader_draw_parameters"); - ctx.AddCapability(spv::Capability::DrawParameters); - break; - case Stage::Geometry: - ctx.AddCapability(spv::Capability::Geometry); - break; - default: - break; - } + SetupCapabilities(program.info, ctx); + SetupFloatMode(ctx, profile, runtime_info, main); PatchPhiNodes(program, ctx); binding.user_data += program.info.ud_mask.NumRegs(); return ctx.Assemble(); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 5eee656dd..6581a7a56 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -284,7 +284,8 @@ void EmitContext::DefineInputs() { frag_coord = DefineVariable(F32[4], spv::BuiltIn::FragCoord, spv::StorageClass::Input); frag_depth = DefineVariable(F32[1], spv::BuiltIn::FragDepth, spv::StorageClass::Output); front_facing = DefineVariable(U1[1], spv::BuiltIn::FrontFacing, spv::StorageClass::Input); - for (const auto& input : runtime_info.fs_info.inputs) { + for (s32 i = 0; i < runtime_info.fs_info.num_inputs; i++) { + const auto& input = runtime_info.fs_info.inputs[i]; const u32 semantic = input.param_index; ASSERT(semantic < IR::NumParams); if (input.is_default && !input.is_flat) { @@ -333,7 +334,6 @@ void EmitContext::DefineInputs() { const auto num_params = runtime_info.gs_info.in_vertex_data_size / 4 - 1u; for (int param_id = 0; param_id < num_params; ++param_id) { - const IR::Attribute param{IR::Attribute::Param0 + param_id}; const Id type{TypeArray(F32[4], ConstU32(num_verts_in))}; const Id id{DefineInput(type, param_id)}; Name(id, fmt::format("in_attr{}", param_id)); @@ -394,8 +394,7 @@ void EmitContext::DefineOutputs() { case Stage::Geometry: { output_position = DefineVariable(F32[4], spv::BuiltIn::Position, spv::StorageClass::Output); - for (u32 attr_id = 0; attr_id < runtime_info.gs_info.copy_data.num_attrs; attr_id++) { - const IR::Attribute param{IR::Attribute::Param0 + attr_id}; + for (u32 attr_id = 0; attr_id < info.gs_copy_data.num_attrs; attr_id++) { const Id id{DefineOutput(F32[4], attr_id)}; Name(id, fmt::format("out_attr{}", attr_id)); output_params[attr_id] = {id, output_f32, F32[1], 4u}; diff --git a/src/shader_recompiler/frontend/copy_shader.cpp b/src/shader_recompiler/frontend/copy_shader.cpp index 363c1c821..b2c795667 100644 --- a/src/shader_recompiler/frontend/copy_shader.cpp +++ b/src/shader_recompiler/frontend/copy_shader.cpp @@ -7,7 +7,7 @@ namespace Shader { -CopyShaderData ParseCopyShader(const std::span& code) { +CopyShaderData ParseCopyShader(std::span code) { Gcn::GcnCodeSlice code_slice{code.data(), code.data() + code.size()}; Gcn::GcnDecodeContext decoder; diff --git a/src/shader_recompiler/frontend/copy_shader.h b/src/shader_recompiler/frontend/copy_shader.h index ca3e1ac3e..55cc31ebd 100644 --- a/src/shader_recompiler/frontend/copy_shader.h +++ b/src/shader_recompiler/frontend/copy_shader.h @@ -16,6 +16,6 @@ struct CopyShaderData { u32 num_attrs{0}; }; -CopyShaderData ParseCopyShader(const std::span& code); +CopyShaderData ParseCopyShader(std::span code); } // namespace Shader diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index 78a6805fd..e727c8a08 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -3,12 +3,12 @@ #pragma once #include -#include #include #include #include "common/assert.h" #include "common/types.h" #include "shader_recompiler/backend/bindings.h" +#include "shader_recompiler/frontend/copy_shader.h" #include "shader_recompiler/ir/attribute.h" #include "shader_recompiler/ir/reg.h" #include "shader_recompiler/ir/type.h" @@ -170,6 +170,8 @@ struct Info { }; UserDataMask ud_mask{}; + CopyShaderData gs_copy_data; + s8 vertex_offset_sgpr = -1; s8 instance_offset_sgpr = -1; diff --git a/src/shader_recompiler/ir/passes/ring_access_elimination.cpp b/src/shader_recompiler/ir/passes/ring_access_elimination.cpp index 857921b1f..eb1be2967 100644 --- a/src/shader_recompiler/ir/passes/ring_access_elimination.cpp +++ b/src/shader_recompiler/ir/passes/ring_access_elimination.cpp @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "shader_recompiler/frontend/translate/translate.h" +#include "shader_recompiler/ir/ir_emitter.h" #include "shader_recompiler/ir/opcodes.h" #include "shader_recompiler/ir/program.h" #include "shader_recompiler/ir/reg.h" @@ -11,6 +11,8 @@ namespace Shader::Optimization { void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtime_info, Stage stage) { + auto& info = program.info; + const auto& ForEachInstruction = [&](auto func) { for (IR::Block* block : program.blocks) { for (IR::Inst& inst : block->Instructions()) { @@ -52,6 +54,9 @@ void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtim break; } case Stage::Geometry: { + const auto& gs_info = runtime_info.gs_info; + info.gs_copy_data = Shader::ParseCopyShader(gs_info.vs_copy); + ForEachInstruction([&](IR::IREmitter& ir, IR::Inst& inst) { const auto opcode = inst.GetOpcode(); switch (opcode) { @@ -81,12 +86,12 @@ void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtim const auto offset = inst.Flags().inst_offset.Value(); const auto data = ir.BitCast(IR::U32{inst.Arg(2)}); - const auto comp_ofs = runtime_info.gs_info.output_vertices * 4u; - const auto output_size = comp_ofs * runtime_info.gs_info.out_vertex_data_size; + const auto comp_ofs = gs_info.output_vertices * 4u; + const auto output_size = comp_ofs * gs_info.out_vertex_data_size; const auto vc_read_ofs = (((offset / comp_ofs) * comp_ofs) % output_size) * 16u; - const auto& it = runtime_info.gs_info.copy_data.attr_map.find(vc_read_ofs); - ASSERT(it != runtime_info.gs_info.copy_data.attr_map.cend()); + const auto& it = info.gs_copy_data.attr_map.find(vc_read_ofs); + ASSERT(it != info.gs_copy_data.attr_map.cend()); const auto& [attr, comp] = it->second; inst.ReplaceOpcode(IR::Opcode::SetAttribute); diff --git a/src/shader_recompiler/ir/reg.h b/src/shader_recompiler/ir/reg.h index 9ec77e5f0..5facaf5c7 100644 --- a/src/shader_recompiler/ir/reg.h +++ b/src/shader_recompiler/ir/reg.h @@ -10,20 +10,6 @@ namespace Shader::IR { -enum class FpRoundMode : u32 { - NearestEven = 0, - PlusInf = 1, - MinInf = 2, - ToZero = 3, -}; - -enum class FpDenormMode : u32 { - InOutFlush = 0, - InAllowOutFlush = 1, - InFlushOutAllow = 2, - InOutAllow = 3, -}; - enum class FloatClassFunc : u32 { SignalingNan = 1 << 0, QuietNan = 1 << 1, @@ -41,13 +27,6 @@ enum class FloatClassFunc : u32 { }; DECLARE_ENUM_FLAG_OPERATORS(FloatClassFunc) -union Mode { - BitField<0, 4, FpRoundMode> fp_round; - BitField<4, 2, FpDenormMode> fp_denorm_single; - BitField<6, 2, FpDenormMode> fp_denorm_double; - BitField<8, 1, u32> dx10_clamp; -}; - union TextureInstInfo { u32 raw; BitField<0, 1, u32> is_depth; diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index badd54554..bbda731e0 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -19,13 +19,8 @@ struct Profile { bool support_float_controls{}; bool support_separate_denorm_behavior{}; bool support_separate_rounding_mode{}; - bool support_fp16_denorm_preserve{}; bool support_fp32_denorm_preserve{}; - bool support_fp16_denorm_flush{}; bool support_fp32_denorm_flush{}; - bool support_fp16_signed_zero_nan_preserve{}; - bool support_fp32_signed_zero_nan_preserve{}; - bool support_fp64_signed_zero_nan_preserve{}; bool support_explicit_workgroup_layout{}; bool has_broken_spirv_clamp{}; bool lower_left_origin_mode{}; diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 8c0838c96..4d15c2072 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -4,11 +4,9 @@ #pragma once #include +#include #include - -#include "common/assert.h" #include "common/types.h" -#include "frontend/copy_shader.h" #include "video_core/amdgpu/types.h" namespace Shader { @@ -62,7 +60,8 @@ enum class VsOutput : u8 { using VsOutputMap = std::array; struct VertexRuntimeInfo { - boost::container::static_vector outputs; + u32 num_outputs; + std::array outputs; bool emulate_depth_negative_one_to_one{}; bool operator==(const VertexRuntimeInfo& other) const noexcept { @@ -79,13 +78,13 @@ struct GeometryRuntimeInfo { u32 out_vertex_data_size{}; AmdGpu::PrimitiveType in_primitive; GsOutputPrimTypes out_primitive; - CopyShaderData copy_data; + std::span vs_copy; + u64 vs_copy_hash; bool operator==(const GeometryRuntimeInfo& other) const noexcept { return num_invocations && other.num_invocations && output_vertices == other.output_vertices && in_primitive == other.in_primitive && - std::ranges::equal(out_primitive, other.out_primitive) && - std::ranges::equal(copy_data.attr_map, other.copy_data.attr_map); + std::ranges::equal(out_primitive, other.out_primitive); } }; @@ -106,7 +105,8 @@ struct FragmentRuntimeInfo { auto operator<=>(const PsInput&) const noexcept = default; }; - boost::container::static_vector inputs; + u32 num_inputs; + std::array inputs; struct PsColorBuffer { AmdGpu::NumberFormat num_format; MrtSwizzle mrt_swizzle; @@ -117,7 +117,9 @@ struct FragmentRuntimeInfo { bool operator==(const FragmentRuntimeInfo& other) const noexcept { return std::ranges::equal(color_buffers, other.color_buffers) && - std::ranges::equal(inputs, other.inputs); + num_inputs == other.num_inputs && + std::ranges::equal(inputs.begin(), inputs.begin() + num_inputs, other.inputs.begin(), + other.inputs.begin() + num_inputs); } }; @@ -141,11 +143,15 @@ struct RuntimeInfo { u32 num_user_data; u32 num_input_vgprs; u32 num_allocated_vgprs; - ExportRuntimeInfo es_info; - VertexRuntimeInfo vs_info; - GeometryRuntimeInfo gs_info; - FragmentRuntimeInfo fs_info; - ComputeRuntimeInfo cs_info; + AmdGpu::FpDenormMode fp_denorm_mode32; + AmdGpu::FpRoundMode fp_round_mode32; + union { + ExportRuntimeInfo es_info; + VertexRuntimeInfo vs_info; + GeometryRuntimeInfo gs_info; + FragmentRuntimeInfo fs_info; + ComputeRuntimeInfo cs_info; + }; RuntimeInfo(Stage stage_) : stage{stage_} {} diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 508420bca..1c994d0a0 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -92,6 +92,12 @@ struct Liverpool { union { BitField<0, 6, u64> num_vgprs; BitField<6, 4, u64> num_sgprs; + BitField<10, 2, u64> priority; + BitField<12, 2, FpRoundMode> fp_round_mode32; + BitField<14, 2, FpRoundMode> fp_round_mode64; + BitField<16, 2, FpDenormMode> fp_denorm_mode32; + BitField<18, 2, FpDenormMode> fp_denorm_mode64; + BitField<12, 8, u64> float_mode; BitField<24, 2, u64> vgpr_comp_cnt; // SPI provided per-thread inputs BitField<33, 5, u64> num_user_regs; } settings; diff --git a/src/video_core/amdgpu/types.h b/src/video_core/amdgpu/types.h index 8cc023a79..6b95ed910 100644 --- a/src/video_core/amdgpu/types.h +++ b/src/video_core/amdgpu/types.h @@ -7,6 +7,20 @@ namespace AmdGpu { +enum class FpRoundMode : u32 { + NearestEven = 0, + PlusInf = 1, + MinInf = 2, + ToZero = 3, +}; + +enum class FpDenormMode : u32 { + InOutFlush = 0, + InAllowOutFlush = 1, + InFlushOutAllow = 2, + InOutAllow = 3, +}; + // See `VGT_PRIMITIVE_TYPE` description in [Radeon Sea Islands 3D/Compute Register Reference Guide] enum class PrimitiveType : u32 { None = 0, @@ -103,4 +117,4 @@ enum class NumberFormat : u32 { Ubscaled = 13, }; -} // namespace AmdGpu \ No newline at end of file +} // namespace AmdGpu diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 21941b8e9..dda4e0d9f 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -217,9 +217,10 @@ bool Instance::CreateDevice() { const vk::StructureChain properties_chain = physical_device.getProperties2< vk::PhysicalDeviceProperties2, vk::PhysicalDevicePortabilitySubsetPropertiesKHR, vk::PhysicalDeviceExternalMemoryHostPropertiesEXT, vk::PhysicalDeviceVulkan11Properties, - vk::PhysicalDevicePushDescriptorPropertiesKHR>(); + vk::PhysicalDevicePushDescriptorPropertiesKHR, vk::PhysicalDeviceVulkan12Properties>(); subgroup_size = properties_chain.get().subgroupSize; push_descriptor_props = properties_chain.get(); + vk12_props = properties_chain.get(); LOG_INFO(Render_Vulkan, "Physical device subgroup size {}", subgroup_size); features = feature_chain.get().features; diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index d77d0c20f..474b86e9a 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -242,6 +242,11 @@ public: return push_descriptor_props.maxPushDescriptors; } + /// Returns the vulkan 1.2 physical device properties. + const vk::PhysicalDeviceVulkan12Properties& GetVk12Properties() const noexcept { + return vk12_props; + } + /// Returns true if shaders can declare the ClipDistance attribute bool IsShaderClipDistanceSupported() const { return features.shaderClipDistance; @@ -279,6 +284,7 @@ private: vk::UniqueDevice device; vk::PhysicalDeviceProperties properties; vk::PhysicalDevicePushDescriptorPropertiesKHR push_descriptor_props; + vk::PhysicalDeviceVulkan12Properties vk12_props; vk::PhysicalDeviceFeatures features; vk::DriverIdKHR driver_id; vk::UniqueDebugUtilsMessengerEXT debug_callback{}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 929fa9cc1..a06d82eb3 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -7,7 +7,6 @@ #include "common/io_file.h" #include "common/path_util.h" #include "shader_recompiler/backend/spirv/emit_spirv.h" -#include "shader_recompiler/frontend/copy_shader.h" #include "shader_recompiler/info.h" #include "shader_recompiler/recompiler.h" #include "shader_recompiler/runtime_info.h" @@ -41,7 +40,7 @@ void GatherVertexOutputs(Shader::VertexRuntimeInfo& info, const auto add_output = [&](VsOutput x, VsOutput y, VsOutput z, VsOutput w) { if (x != VsOutput::None || y != VsOutput::None || z != VsOutput::None || w != VsOutput::None) { - info.outputs.emplace_back(Shader::VsOutputMap{x, y, z, w}); + info.outputs[info.num_outputs++] = Shader::VsOutputMap{x, y, z, w}; } }; // VS_OUT_MISC_VEC @@ -84,18 +83,21 @@ void GatherVertexOutputs(Shader::VertexRuntimeInfo& info, Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Shader::Stage stage) { auto info = Shader::RuntimeInfo{stage}; const auto& regs = liverpool->regs; + const auto BuildCommon = [&](const auto& program) { + info.num_user_data = program.settings.num_user_regs; + info.num_input_vgprs = program.settings.vgpr_comp_cnt; + info.num_allocated_vgprs = program.settings.num_vgprs * 4; + info.fp_denorm_mode32 = program.settings.fp_denorm_mode32; + info.fp_round_mode32 = program.settings.fp_round_mode32; + }; switch (stage) { case Shader::Stage::Export: { - info.num_user_data = regs.es_program.settings.num_user_regs; - info.num_input_vgprs = regs.es_program.settings.vgpr_comp_cnt; - info.num_allocated_vgprs = regs.es_program.settings.num_vgprs * 4; + BuildCommon(regs.es_program); info.es_info.vertex_data_size = regs.vgt_esgs_ring_itemsize; break; } case Shader::Stage::Vertex: { - info.num_user_data = regs.vs_program.settings.num_user_regs; - info.num_input_vgprs = regs.vs_program.settings.vgpr_comp_cnt; - info.num_allocated_vgprs = regs.vs_program.settings.num_vgprs * 4; + BuildCommon(regs.vs_program); GatherVertexOutputs(info.vs_info, regs.vs_output_control); info.vs_info.emulate_depth_negative_one_to_one = !instance.IsDepthClipControlSupported() && @@ -103,39 +105,35 @@ Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Shader::Stage stage) { break; } case Shader::Stage::Geometry: { - info.num_user_data = regs.gs_program.settings.num_user_regs; - info.num_input_vgprs = regs.gs_program.settings.vgpr_comp_cnt; - info.num_allocated_vgprs = regs.gs_program.settings.num_vgprs * 4; - info.gs_info.output_vertices = regs.vgt_gs_max_vert_out; - info.gs_info.num_invocations = + BuildCommon(regs.gs_program); + auto& gs_info = info.gs_info; + gs_info.output_vertices = regs.vgt_gs_max_vert_out; + gs_info.num_invocations = regs.vgt_gs_instance_cnt.IsEnabled() ? regs.vgt_gs_instance_cnt.count : 1; - info.gs_info.in_primitive = regs.primitive_type; + gs_info.in_primitive = regs.primitive_type; for (u32 stream_id = 0; stream_id < Shader::GsMaxOutputStreams; ++stream_id) { - info.gs_info.out_primitive[stream_id] = + gs_info.out_primitive[stream_id] = regs.vgt_gs_out_prim_type.GetPrimitiveType(stream_id); } - info.gs_info.in_vertex_data_size = regs.vgt_esgs_ring_itemsize; - info.gs_info.out_vertex_data_size = regs.vgt_gs_vert_itemsize[0]; - - // Extract semantics offsets from a copy shader - const auto vc_stage = Shader::Stage::Vertex; - const auto* pgm_vc = regs.ProgramForStage(static_cast(vc_stage)); - const auto params_vc = Liverpool::GetParams(*pgm_vc); - DumpShader(params_vc.code, params_vc.hash, Shader::Stage::Vertex, 0, "copy.bin"); - info.gs_info.copy_data = Shader::ParseCopyShader(params_vc.code); + gs_info.in_vertex_data_size = regs.vgt_esgs_ring_itemsize; + gs_info.out_vertex_data_size = regs.vgt_gs_vert_itemsize[0]; + const auto params_vc = Liverpool::GetParams(regs.vs_program); + gs_info.vs_copy = params_vc.code; + gs_info.vs_copy_hash = params_vc.hash; + DumpShader(gs_info.vs_copy, gs_info.vs_copy_hash, Shader::Stage::Vertex, 0, "copy.bin"); break; } case Shader::Stage::Fragment: { - info.num_user_data = regs.ps_program.settings.num_user_regs; - info.num_allocated_vgprs = regs.ps_program.settings.num_vgprs * 4; + BuildCommon(regs.ps_program); const auto& ps_inputs = regs.ps_inputs; + info.fs_info.num_inputs = regs.num_interp; for (u32 i = 0; i < regs.num_interp; i++) { - info.fs_info.inputs.push_back({ + info.fs_info.inputs[i] = { .param_index = u8(ps_inputs[i].input_offset.Value()), .is_default = bool(ps_inputs[i].use_default), .is_flat = bool(ps_inputs[i].flat_shade), .default_value = u8(ps_inputs[i].default_value), - }); + }; } for (u32 i = 0; i < Shader::MaxColorBuffers; i++) { info.fs_info.color_buffers[i] = { @@ -166,9 +164,12 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, AmdGpu::Liverpool* liverpool_) : instance{instance_}, scheduler{scheduler_}, liverpool{liverpool_}, desc_heap{instance, scheduler.GetMasterSemaphore(), DescriptorHeapSizes} { + const auto& vk12_props = instance.GetVk12Properties(); profile = Shader::Profile{ .supported_spirv = instance.ApiVersion() >= VK_API_VERSION_1_3 ? 0x00010600U : 0x00010500U, .subgroup_size = instance.SubgroupSize(), + .support_fp32_denorm_preserve = bool(vk12_props.shaderDenormPreserveFloat32), + .support_fp32_denorm_flush = bool(vk12_props.shaderDenormFlushToZeroFloat32), .support_explicit_workgroup_layout = true, }; auto [cache_result, cache] = instance.GetDevice().createPipelineCacheUnique({}); From 0a12ba4120bdbb49940a898d866798386eb9e4bc Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Thu, 10 Oct 2024 16:51:23 +0200 Subject: [PATCH 27/93] core/libraries: Initial fiber implementation (#1255) --- CMakeLists.txt | 5 + src/common/logging/filter.cpp | 1 + src/common/logging/types.h | 1 + src/core/libraries/error_codes.h | 10 +- src/core/libraries/fiber/fiber.cpp | 284 +++++++++++++++++++++++++++++ src/core/libraries/fiber/fiber.h | 83 +++++++++ src/core/libraries/libs.cpp | 2 + src/emulator.cpp | 3 +- 8 files changed, 387 insertions(+), 2 deletions(-) create mode 100644 src/core/libraries/fiber/fiber.cpp create mode 100644 src/core/libraries/fiber/fiber.h diff --git a/CMakeLists.txt b/CMakeLists.txt index eeca274ef..8a41e1ac9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -324,6 +324,10 @@ set(USBD_LIB src/core/libraries/usbd/usbd.cpp src/core/libraries/usbd/usbd.h ) +set(FIBER_LIB src/core/libraries/fiber/fiber.cpp + src/core/libraries/fiber/fiber.h +) + set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp src/core/libraries/np_manager/np_manager.h src/core/libraries/np_score/np_score.cpp @@ -464,6 +468,7 @@ set(CORE src/core/aerolib/stubs.cpp ${USBD_LIB} ${MISC_LIBS} ${DIALOGS_LIB} + ${FIBER_LIB} ${DEV_TOOLS} src/core/debug_state.cpp src/core/debug_state.h diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index c3088f926..2ff2a9003 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -114,6 +114,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, AvPlayer) \ SUB(Lib, Ngs2) \ SUB(Lib, Audio3d) \ + SUB(Lib, Fiber) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 749568da1..54388e590 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -81,6 +81,7 @@ enum class Class : u8 { Lib_AvPlayer, ///< The LibSceAvPlayer implementation. Lib_Ngs2, ///< The LibSceNgs2 implementation. Lib_Audio3d, ///< The LibSceAudio3d implementation. + Lib_Fiber, ///< The LibSceFiber implementation. Frontend, ///< Emulator UI Render, ///< Video Core Render_Vulkan, ///< Vulkan backend diff --git a/src/core/libraries/error_codes.h b/src/core/libraries/error_codes.h index b9896b6c3..041870ed7 100644 --- a/src/core/libraries/error_codes.h +++ b/src/core/libraries/error_codes.h @@ -498,4 +498,12 @@ constexpr int ORBIS_AVPLAYER_ERROR_INFO_OTHER_ENCRY = 0x806A00BF; // AppContent library constexpr int ORBIS_APP_CONTENT_ERROR_PARAMETER = 0x80D90002; constexpr int ORBIS_APP_CONTENT_ERROR_DRM_NO_ENTITLEMENT = 0x80D90007; -constexpr int ORBIS_APP_CONTENT_ERROR_NOT_FOUND = 0x80D90005; \ No newline at end of file +constexpr int ORBIS_APP_CONTENT_ERROR_NOT_FOUND = 0x80D90005; + +// Fiber library +constexpr int ORBIS_FIBER_ERROR_NULL = 0x80590001; +constexpr int ORBIS_FIBER_ERROR_ALIGNMENT = 0x80590002; +constexpr int ORBIS_FIBER_ERROR_RANGE = 0x80590003; +constexpr int ORBIS_FIBER_ERROR_INVALID = 0x80590004; +constexpr int ORBIS_FIBER_ERROR_PERMISSION = 0x80590005; +constexpr int ORBIS_FIBER_ERROR_STATE = 0x80590006; \ No newline at end of file diff --git a/src/core/libraries/fiber/fiber.cpp b/src/core/libraries/fiber/fiber.cpp new file mode 100644 index 000000000..bd1575dda --- /dev/null +++ b/src/core/libraries/fiber/fiber.cpp @@ -0,0 +1,284 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "fiber.h" + +#include "common/logging/log.h" +#include "common/singleton.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "core/linker.h" + +#ifdef _WIN64 +#include +#endif + +namespace Libraries::Fiber { + +constexpr static u64 kFiberSignature = 0x054ad954; + +thread_local SceFiber* gCurrentFiber = nullptr; +thread_local void* gFiberThread = nullptr; + +void FiberEntry(void* param) { + SceFiber* fiber = static_cast(param); + u64 argRun = 0; + u64 argRet = 0; + + gCurrentFiber = fiber; + + if (fiber->pArgRun != nullptr) { + argRun = *fiber->pArgRun; + } + + const auto* linker = Common::Singleton::Instance(); + linker->ExecuteGuest(fiber->entry, fiber->argOnInitialize, argRun); + + UNREACHABLE(); +} + +s32 PS4_SYSV_ABI sceFiberInitialize(SceFiber* fiber, const char* name, SceFiberEntry entry, + u64 argOnInitialize, void* addrContext, u64 sizeContext, + const SceFiberOptParam* optParam) { + LOG_INFO(Lib_Fiber, "called: name = {}", name); + + if (!fiber || !name || !entry) { + return ORBIS_FIBER_ERROR_NULL; + } + + fiber->signature = kFiberSignature; + + fiber->entry = entry; + fiber->argOnInitialize = argOnInitialize; + + fiber->argRun = 0; + fiber->pArgRun = &fiber->argRun; + fiber->argReturn = 0; + fiber->pArgReturn = &fiber->argReturn; + + fiber->sizeContext = sizeContext; + + fiber->state = FiberState::Init; +#ifdef _WIN64 + fiber->handle = CreateFiber(sizeContext, FiberEntry, fiber); +#else + UNREACHABLE_MSG("Missing implementation"); +#endif + strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH); + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberOptParamInitialize(SceFiberOptParam* optParam) { + LOG_ERROR(Lib_Fiber, "called"); + + if (!optParam) { + return ORBIS_FIBER_ERROR_NULL; + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberFinalize(SceFiber* fiber) { + LOG_TRACE(Lib_Fiber, "called"); + + if (!fiber) { + return ORBIS_FIBER_ERROR_NULL; + } + if ((u64)fiber % 8 != 0) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + if (fiber->signature != kFiberSignature) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (fiber->state != FiberState::Run) { + return ORBIS_FIBER_ERROR_STATE; + } + + fiber->signature = 0; + fiber->state = FiberState::None; + +#ifdef _WIN64 + DeleteFiber(fiber->handle); +#else + UNREACHABLE_MSG("Missing implementation"); +#endif + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberRun(SceFiber* fiber, u64 argOnRunTo, u64* argOnReturn) { + LOG_TRACE(Lib_Fiber, "called"); + + if (!fiber) { + return ORBIS_FIBER_ERROR_NULL; + } + if ((u64)fiber % 8 != 0) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + if (fiber->signature != kFiberSignature) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (fiber->state == FiberState::Run) { + return ORBIS_FIBER_ERROR_STATE; + } + + if (gFiberThread == nullptr) { +#ifdef _WIN64 + gFiberThread = ConvertThreadToFiber(nullptr); +#else + UNREACHABLE_MSG("Missing implementation"); +#endif + } + + gCurrentFiber = fiber; + + if (fiber->pArgRun != nullptr) { + *fiber->pArgRun = argOnRunTo; + } + + fiber->pArgReturn = argOnReturn; + fiber->state = FiberState::Run; +#ifdef _WIN64 + SwitchToFiber(fiber->handle); +#else + UNREACHABLE_MSG("Missing implementation"); +#endif + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberSwitch(SceFiber* fiber, u64 argOnRunTo, u64* argOnRun) { + LOG_TRACE(Lib_Fiber, "called"); + + if (!fiber) { + return ORBIS_FIBER_ERROR_NULL; + } + if ((u64)fiber % 8 != 0) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + if (fiber->signature != kFiberSignature) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (gCurrentFiber == nullptr) { + return ORBIS_FIBER_ERROR_PERMISSION; + } + if (fiber->state == FiberState::Run) { + return ORBIS_FIBER_ERROR_STATE; + } + + gCurrentFiber->state = FiberState::Suspend; + + // TODO: argOnRun + + *fiber->pArgRun = argOnRunTo; + fiber->state = FiberState::Run; + + gCurrentFiber = fiber; +#ifdef _WIN64 + SwitchToFiber(fiber->handle); +#else + UNREACHABLE_MSG("Missing implementation"); +#endif + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberGetSelf(SceFiber** fiber) { + LOG_TRACE(Lib_Fiber, "called"); + + if (!fiber || !gCurrentFiber) { + return ORBIS_FIBER_ERROR_NULL; + } + if (gCurrentFiber->signature != kFiberSignature) { + return ORBIS_FIBER_ERROR_PERMISSION; + } + + *fiber = gCurrentFiber; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 argOnReturn, u64* argOnRun) { + LOG_TRACE(Lib_Fiber, "called"); + + if (gCurrentFiber->signature != kFiberSignature) { + return ORBIS_FIBER_ERROR_PERMISSION; + } + + if (gCurrentFiber->pArgReturn != nullptr) { + *gCurrentFiber->pArgReturn = argOnReturn; + } + + // TODO: argOnRun + gCurrentFiber->state = FiberState::Suspend; + gCurrentFiber = nullptr; +#ifdef _WIN64 + SwitchToFiber(gFiberThread); +#else + UNREACHABLE_MSG("Missing implementation"); +#endif + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberGetInfo(SceFiber* fiber, SceFiberInfo* fiberInfo) { + LOG_INFO(Lib_Fiber, "called"); + + if (!fiber || !fiberInfo) { + return ORBIS_FIBER_ERROR_NULL; + } + + fiberInfo->entry = fiber->entry; + fiberInfo->argOnInitialize = fiber->argOnInitialize; + fiberInfo->addrContext = nullptr; + fiberInfo->sizeContext = fiber->sizeContext; + fiberInfo->sizeContextMargin = 0; + + strncpy(fiberInfo->name, fiber->name, ORBIS_FIBER_MAX_NAME_LENGTH); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags) { + LOG_ERROR(Lib_Fiber, "called"); + + if (flags != 0) { + return ORBIS_FIBER_ERROR_INVALID; + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck() { + LOG_ERROR(Lib_Fiber, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberRename(SceFiber* fiber, const char* name) { + LOG_INFO(Lib_Fiber, "called, name = {}", name); + + if (!fiber || !name) { + return ORBIS_FIBER_ERROR_NULL; + } + if ((u64)fiber % 8 != 0) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + + strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH); + return ORBIS_OK; +} + +void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("hVYD7Ou2pCQ", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize); + LIB_FUNCTION("asjUJJ+aa8s", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberOptParamInitialize); + LIB_FUNCTION("JeNX5F-NzQU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberFinalize); + + LIB_FUNCTION("a0LLrZWac0M", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRun); + LIB_FUNCTION("PFT2S-tJ7Uk", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberSwitch); + LIB_FUNCTION("p+zLIOg27zU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetSelf); + LIB_FUNCTION("B0ZX2hx9DMw", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberReturnToThread); + + LIB_FUNCTION("uq2Y5BFz0PE", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetInfo); + LIB_FUNCTION("Lcqty+QNWFc", "libSceFiber", 1, "libSceFiber", 1, 1, + sceFiberStartContextSizeCheck); + LIB_FUNCTION("Kj4nXMpnM8Y", "libSceFiber", 1, "libSceFiber", 1, 1, + sceFiberStopContextSizeCheck); + LIB_FUNCTION("JzyT91ucGDc", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRename); +} + +} // namespace Libraries::Fiber \ No newline at end of file diff --git a/src/core/libraries/fiber/fiber.h b/src/core/libraries/fiber/fiber.h new file mode 100644 index 000000000..930409caa --- /dev/null +++ b/src/core/libraries/fiber/fiber.h @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/assert.h" +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} +namespace Libraries::Fiber { + +#define ORBIS_FIBER_MAX_NAME_LENGTH (31) + +typedef void PS4_SYSV_ABI (*SceFiberEntry)(u64 argOnInitialize, u64 argOnRun); + +enum FiberState : u32 { + None = 0u, + Init = 1u, + Run = 2u, + Suspend = 3u, +}; + +struct SceFiber { + u64 signature; + FiberState state; + SceFiberEntry entry; + + u64 argOnInitialize; + + u64 argRun; + u64* pArgRun; + + u64 argReturn; + u64* pArgReturn; + + u64 sizeContext; + + char name[ORBIS_FIBER_MAX_NAME_LENGTH]; + void* handle; +}; +static_assert(sizeof(SceFiber) <= 256); + +struct SceFiberInfo { + u64 size; + SceFiberEntry entry; + u64 argOnInitialize; + void* addrContext; + u64 sizeContext; + char name[ORBIS_FIBER_MAX_NAME_LENGTH + 1]; + u64 sizeContextMargin; +}; +static_assert(sizeof(SceFiberInfo) <= 128); + +typedef void* SceFiberOptParam; + +s32 PS4_SYSV_ABI sceFiberInitialize(SceFiber* fiber, const char* name, SceFiberEntry entry, + u64 argOnInitialize, void* addrContext, u64 sizeContext, + const SceFiberOptParam* optParam); + +s32 PS4_SYSV_ABI sceFiberOptParamInitialize(SceFiberOptParam* optParam); + +s32 PS4_SYSV_ABI sceFiberFinalize(SceFiber* fiber); + +s32 PS4_SYSV_ABI sceFiberRun(SceFiber* fiber, u64 argOnRunTo, u64* argOnReturn); + +s32 PS4_SYSV_ABI sceFiberSwitch(SceFiber* fiber, u64 argOnRunTo, u64* argOnRun); + +s32 PS4_SYSV_ABI sceFiberGetSelf(SceFiber** fiber); + +s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 argOnReturn, u64* argOnRun); + +s32 PS4_SYSV_ABI sceFiberGetInfo(SceFiber* fiber, SceFiberInfo* fiberInfo); + +s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags); + +s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck(void); + +s32 PS4_SYSV_ABI sceFiberRename(SceFiber* fiber, const char* name); + +void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Fiber \ No newline at end of file diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 5b6c17b10..86f9fcf9d 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -11,6 +11,7 @@ #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/fiber/fiber.h" #include "core/libraries/gnmdriver/gnmdriver.h" #include "core/libraries/kernel/libkernel.h" #include "core/libraries/libc_internal/libc_internal.h" @@ -77,6 +78,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::ImeDialog::RegisterlibSceImeDialog(sym); Libraries::AvPlayer::RegisterlibSceAvPlayer(sym); Libraries::Audio3d::RegisterlibSceAudio3d(sym); + Libraries::Fiber::RegisterlibSceFiber(sym); } } // namespace Libraries diff --git a/src/emulator.cpp b/src/emulator.cpp index 9f801fb83..67aaa0492 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -26,6 +26,7 @@ #include "core/file_format/trp.h" #include "core/file_sys/fs.h" #include "core/libraries/disc_map/disc_map.h" +#include "core/libraries/fiber/fiber.h" #include "core/libraries/kernel/thread_management.h" #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libs.h" @@ -258,7 +259,7 @@ void Emulator::Run(const std::filesystem::path& file) { void Emulator::LoadSystemModules(const std::filesystem::path& file) { constexpr std::array ModulesToLoad{ {{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2}, - {"libSceFiber.sprx", nullptr}, + {"libSceFiber.sprx", &Libraries::Fiber::RegisterlibSceFiber}, {"libSceUlt.sprx", nullptr}, {"libSceJson.sprx", nullptr}, {"libSceJson2.sprx", nullptr}, From 299a29e243bb49378e2264889cc168ce8ea2fef3 Mon Sep 17 00:00:00 2001 From: ElBread3 <92335081+ElBread3@users.noreply.github.com> Date: Thu, 10 Oct 2024 09:52:20 -0500 Subject: [PATCH 28/93] Fix Multiple Install Folders (#1328) * attempt to fix pr * clang format --- src/common/config.cpp | 7 ++++++- src/qt_gui/settings_dialog.cpp | 36 +++++++++++++++++++++++++--------- src/qt_gui/translations/en.ts | 15 ++++++++++++++ 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 55cf29428..eedb69105 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -534,7 +534,12 @@ void load(const std::filesystem::path& path) { const auto install_dir_array = toml::find_or>(gui, "installDirs", {}); for (const auto& dir : install_dir_array) { - settings_install_dirs.emplace_back(std::filesystem::path{dir}); + bool not_already_included = + std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir) == + settings_install_dirs.end(); + if (not_already_included) { + settings_install_dirs.emplace_back(std::filesystem::path{dir}); + } } settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {}); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index a6fe6a265..cf64660f8 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -222,21 +222,17 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge // PATH TAB { - for (const auto& dir : Config::getGameInstallDirs()) { - QString path_string; - Common::FS::PathToQString(path_string, dir); - QListWidgetItem* item = new QListWidgetItem(path_string); - ui->gameFoldersListWidget->addItem(item); - } - ui->removeFolderButton->setEnabled(false); connect(ui->addFolderButton, &QPushButton::clicked, this, [this]() { + const auto config_dir = Config::getGameInstallDirs(); QString file_path_string = QFileDialog::getExistingDirectory(this, tr("Directory to install games")); auto file_path = Common::FS::PathFromQString(file_path_string); - if (!file_path.empty()) { - std::vector install_dirs = Config::getGameInstallDirs(); + bool not_already_included = + std::find(config_dir.begin(), config_dir.end(), file_path) == config_dir.end(); + if (!file_path.empty() && not_already_included) { + std::vector install_dirs = config_dir; install_dirs.push_back(file_path); Config::setGameInstallDirs(install_dirs); QListWidgetItem* item = new QListWidgetItem(file_path_string); @@ -307,6 +303,12 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge ui->dumpShadersCheckBox->installEventFilter(this); ui->nullGpuCheckBox->installEventFilter(this); + // Paths + ui->gameFoldersGroupBox->installEventFilter(this); + ui->gameFoldersListWidget->installEventFilter(this); + ui->addFolderButton->installEventFilter(this); + ui->removeFolderButton->installEventFilter(this); + // Debug ui->debugDump->installEventFilter(this); ui->vkValidationCheckBox->installEventFilter(this); @@ -357,6 +359,13 @@ void SettingsDialog::LoadValuesFromConfig() { } ui->updateComboBox->setCurrentText(QString::fromStdString(updateChannel)); + for (const auto& dir : Config::getGameInstallDirs()) { + QString path_string; + Common::FS::PathToQString(path_string, dir); + QListWidgetItem* item = new QListWidgetItem(path_string); + ui->gameFoldersListWidget->addItem(item); + } + QString backButtonBehavior = QString::fromStdString(Config::getBackButtonBehavior()); int index = ui->backButtonBehaviorComboBox->findData(backButtonBehavior); ui->backButtonBehaviorComboBox->setCurrentIndex(index != -1 ? index : 0); @@ -452,6 +461,15 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("nullGpuCheckBox"); } + // Path + if (elementName == "gameFoldersGroupBox" || elementName == "gameFoldersListWidget") { + text = tr("gameFoldersBox"); + } else if (elementName == "addFolderButton") { + text = tr("addFolderButton"); + } else if (elementName == "removeFolderButton") { + text = tr("removeFolderButton"); + } + // Debug if (elementName == "debugDump") { text = tr("debugDump"); diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 8ac683804..efbc48fff 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -1083,6 +1083,21 @@ nullGpuCheckBox Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card. + + + gameFoldersBox + Game Folders: The list of folders to check for installed games. + + + + addFolderButton + Add: Add a folder to the list. + + + + removeFolderButton + Remove: Remove a folder from the list. + debugDump From 87f8f3a59e7fa53f76d2bbb42b6e15e5b4c4b192 Mon Sep 17 00:00:00 2001 From: Exhigh Date: Thu, 10 Oct 2024 18:52:39 +0400 Subject: [PATCH 29/93] qt_gui: Organize settings page (#1316) * Wire up translations and descriptions for the cursor settings. * Move controller settings to input tab and rename it to controller (to inline it with how other settings are shown). * Fixed unnecessary double initialization of the back button setting. * Organize statements and functions w/ respect to their tabs and some minor QOL changes for the settings UI in general. --- src/common/config.cpp | 8 +- src/common/config.h | 13 +- src/qt_gui/settings_dialog.cpp | 50 +-- src/qt_gui/settings_dialog.ui | 590 ++++++++++++++++++++++----------- src/qt_gui/translations/en.ts | 80 ++++- 5 files changed, 499 insertions(+), 242 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index eedb69105..37e51c655 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -473,7 +473,6 @@ void load(const std::filesystem::path& path) { } isShowSplash = toml::find_or(general, "showSplash", true); isAutoUpdate = toml::find_or(general, "autoUpdate", false); - backButtonBehavior = toml::find_or(general, "backButtonBehavior", "left"); } if (data.contains("Input")) { @@ -481,6 +480,7 @@ void load(const std::filesystem::path& path) { cursorState = toml::find_or(input, "cursorState", HideCursorState::Idle); cursorHideTimeout = toml::find_or(input, "cursorHideTimeout", 5); + backButtonBehavior = toml::find_or(input, "backButtonBehavior", "left"); useSpecialPad = toml::find_or(input, "useSpecialPad", false); specialPadClass = toml::find_or(input, "specialPadClass", 1); } @@ -594,7 +594,7 @@ void save(const std::filesystem::path& path) { data["General"]["autoUpdate"] = isAutoUpdate; data["Input"]["cursorState"] = cursorState; data["Input"]["cursorHideTimeout"] = cursorHideTimeout; - data["General"]["backButtonBehavior"] = backButtonBehavior; + data["Input"]["backButtonBehavior"] = backButtonBehavior; data["Input"]["useSpecialPad"] = useSpecialPad; data["Input"]["specialPadClass"] = specialPadClass; data["GPU"]["screenWidth"] = screenWidth; @@ -650,8 +650,6 @@ void setDefaultValues() { playBGM = false; BGMvolume = 50; enableDiscordRPC = true; - cursorState = HideCursorState::Idle; - cursorHideTimeout = 5; screenWidth = 1280; screenHeight = 720; logFilter = ""; @@ -662,6 +660,8 @@ void setDefaultValues() { } else { updateChannel = "Nightly"; } + cursorState = HideCursorState::Idle; + cursorHideTimeout = 5; backButtonBehavior = "left"; useSpecialPad = false; specialPadClass = 1; diff --git a/src/common/config.h b/src/common/config.h index e76f389c2..8e799b55d 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -20,15 +20,14 @@ bool getPlayBGM(); int getBGMvolume(); bool getEnableDiscordRPC(); -s16 getCursorState(); -int getCursorHideTimeout(); - std::string getLogFilter(); std::string getLogType(); std::string getUserName(); std::string getUpdateChannel(); -std::string getBackButtonBehavior(); +s16 getCursorState(); +int getCursorHideTimeout(); +std::string getBackButtonBehavior(); bool getUseSpecialPad(); int getSpecialPadClass(); @@ -59,14 +58,14 @@ void setFullscreenMode(bool enable); void setPlayBGM(bool enable); void setBGMvolume(int volume); void setEnableDiscordRPC(bool enable); -void setCursorState(s16 cursorState); -void setCursorHideTimeout(int newcursorHideTimeout); void setLanguage(u32 language); void setNeoMode(bool enable); void setUserName(const std::string& type); void setUpdateChannel(const std::string& type); -void setBackButtonBehavior(const std::string& type); +void setCursorState(s16 cursorState); +void setCursorHideTimeout(int newcursorHideTimeout); +void setBackButtonBehavior(const std::string& type); void setUseSpecialPad(bool use); void setSpecialPadClass(int type); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index cf64660f8..1cc5a85e4 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -47,8 +47,6 @@ QStringList languageNames = {"Arabic", const QVector languageIndexes = {21, 23, 14, 6, 18, 1, 12, 22, 2, 4, 25, 24, 29, 5, 0, 9, 15, 16, 17, 7, 26, 8, 11, 20, 3, 13, 27, 10, 19, 28}; -QStringList hideCursorStates = {"Never", "Idle", "Always"}; - SettingsDialog::SettingsDialog(std::span physical_devices, QWidget* parent) : QDialog(parent), ui(new Ui::SettingsDialog) { ui->setupUi(this); @@ -69,7 +67,14 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge completer->setCaseSensitivity(Qt::CaseInsensitive); ui->consoleLanguageComboBox->setCompleter(completer); - ui->hideCursorComboBox->addItems(hideCursorStates); + ui->hideCursorComboBox->addItem(tr("Never")); + ui->hideCursorComboBox->addItem(tr("Idle")); + ui->hideCursorComboBox->addItem(tr("Always")); + + ui->backButtonBehaviorComboBox->addItem(tr("Touchpad Left"), "left"); + ui->backButtonBehaviorComboBox->addItem(tr("Touchpad Center"), "center"); + ui->backButtonBehaviorComboBox->addItem(tr("Touchpad Right"), "right"); + ui->backButtonBehaviorComboBox->addItem(tr("None"), "none"); InitializeEmulatorLanguages(); LoadValuesFromConfig(); @@ -102,15 +107,6 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setText(tr("Restore Defaults")); ui->buttonBox->button(QDialogButtonBox::Close)->setText(tr("Close")); - ui->backButtonBehaviorComboBox->addItem(tr("Touchpad Left"), "left"); - ui->backButtonBehaviorComboBox->addItem(tr("Touchpad Center"), "center"); - ui->backButtonBehaviorComboBox->addItem(tr("Touchpad Right"), "right"); - ui->backButtonBehaviorComboBox->addItem(tr("None"), "none"); - - QString currentBackButtonBehavior = QString::fromStdString(Config::getBackButtonBehavior()); - int index = ui->backButtonBehaviorComboBox->findData(currentBackButtonBehavior); - ui->backButtonBehaviorComboBox->setCurrentIndex(index != -1 ? index : 0); - connect(ui->tabWidgetSettings, &QTabWidget::currentChanged, this, [this]() { ui->buttonBox->button(QDialogButtonBox::Close)->setFocus(); }); @@ -175,14 +171,6 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge rpc->shutdown(); } }); - - connect(ui->backButtonBehaviorComboBox, QOverload::of(&QComboBox::currentIndexChanged), - this, [this](int index) { - if (index >= 0 && index < ui->backButtonBehaviorComboBox->count()) { - QString data = ui->backButtonBehaviorComboBox->itemData(index).toString(); - Config::setBackButtonBehavior(data.toStdString()); - } - }); } // Input TAB @@ -195,6 +183,14 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge connect(ui->idleTimeoutSpinBox, &QSpinBox::valueChanged, this, [](int index) { Config::setCursorHideTimeout(index); }); + + connect(ui->backButtonBehaviorComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, [this](int index) { + if (index >= 0 && index < ui->backButtonBehaviorComboBox->count()) { + QString data = ui->backButtonBehaviorComboBox->itemData(index).toString(); + Config::setBackButtonBehavior(data.toStdString()); + } + }); } // GPU TAB @@ -293,6 +289,11 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge ui->logFilter->installEventFilter(this); ui->updaterGroupBox->installEventFilter(this); ui->GUIgroupBox->installEventFilter(this); + + // Input + ui->cursorGroupBox->installEventFilter(this); + ui->hideCursorGroupBox->installEventFilter(this); + ui->idleTimeoutGroupBox->installEventFilter(this); ui->backButtonBehaviorGroupBox->installEventFilter(this); // Graphics @@ -442,6 +443,15 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("updaterGroupBox"); } else if (elementName == "GUIgroupBox") { text = tr("GUIgroupBox"); + } + + // Input + if (elementName == "cursorGroupBox") { + text = tr("cursorGroupBox"); + } else if (elementName == "hideCursorGroupBox") { + text = tr("hideCursorGroupBox"); + } else if (elementName == "idleTimeoutGroupBox") { + text = tr("idleTimeoutGroupBox"); } else if (elementName == "backButtonBehaviorGroupBox") { text = tr("backButtonBehaviorGroupBox"); } diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index e1c064d5a..9743e51bd 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -274,6 +274,9 @@ + + QLayout::SizeConstraint::SetDefaultConstraint + 0 @@ -286,8 +289,157 @@ 0 - + + + + 0 + 0 + + + + + 275 + 0 + + + + + 16777215 + 16777215 + + + + Update + + + + 5 + + + 1 + + + 11 + + + 11 + + + + + + 0 + 0 + + + + + 0 + 75 + + + + + 16777215 + 16777215 + + + + Update Channel + + + + 7 + + + 11 + + + 11 + + + 11 + + + 11 + + + + + + 0 + 0 + + + + + Release + + + + + Nightly + + + + + + + + + + + + 0 + 0 + + + + + 197 + 28 + + + + + 16777215 + 16777215 + + + + Check for Updates + + + + + + + + 0 + 0 + + + + + 11 + false + + + + Check for Updates at Startup + + + + + + + + + + + + 0 @@ -296,204 +448,141 @@ - 265 + 0 0 - - Update - - - - - 10 - 130 - 261 - 22 - - - - Check for Updates at Startup - - - - - - 12 - 30 - 241 - 65 - - - - Update Channel - - - - - 12 - 30 - 217 - 28 - - - - - Release - - - - - Nightly - - - - - - - - 25 - 100 - 215 - 24 - - - - Check for Updates - - - - - - - - - - - - - 0 - 0 - - GUI Settings - - - - 10 - 30 - 241 - 92 - + + + 1 - - - - - - 0 - 0 - - - - Play title music - - - - - - - - - - - Volume - - - - - - - Set the volume of the background music. - - - 100 - - - 10 - - - 20 - - - 50 - - - Qt::Orientation::Horizontal - - - false - - - false - - - QSlider::TickPosition::NoTicks - - - 10 - - - - - - - - - + + 11 + + + + + 1 + + + 0 + + + + + + 0 + 0 + + + + Play title music + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Fixed + + + + 20 + 2 + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Volume + + + + + + + Set the volume of the background music. + + + 100 + + + 10 + + + 20 + + + 50 + + + Qt::Orientation::Horizontal + + + false + + + false + + + QSlider::TickPosition::NoTicks + + + 10 + + + + + + + + + + 0 + 61 + + + + + - + - - - - 0 - 0 - + + + Qt::Orientation::Horizontal - - Controller Settings + + + 40 + 20 + - - - - 12 - 30 - 241 - 65 - - - - Back Button Behavior - - - - - 12 - 30 - 217 - 28 - - - - - + @@ -510,18 +599,48 @@ - - + + 7 + + + 0 + + + Cursor + + 0 + + + 11 + + + 11 + + + true + + + + 0 + 0 + + Hide Cursor + + 7 + + + 11 + @@ -533,10 +652,16 @@ true + + + 0 + 0 + + 0 - 85 + 0 @@ -549,19 +674,28 @@ false + + 6 + 70 - 11 + 5 - + + 5 + + + 5 + + true - + 0 0 @@ -620,26 +754,80 @@ - + - - - Qt::Orientation::Horizontal + + + + 0 + 0 + - - - 40 - 20 - + + Controller - + + + 0 + + + 11 + + + 11 + + + + + true + + + + 0 + 0 + + + + + 237 + 0 + + + + Back Button Behavior + + + + 11 + + + + + + + + + + + true + + + + 0 + 0 + + + + + + - + Qt::Orientation::Horizontal diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index efbc48fff..974045de1 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -434,6 +434,41 @@ Log Filter Log Filter + + + Input + Input + + + + Cursor + Cursor + + + + Hide Cursor + Hide Cursor + + + + Hide Cursor Idle Timeout + Hide Cursor Idle Timeout + + + + Input + Input + + + + Controller + Controller + + + + Back Button Behavior + Back Button Behavior + Graphics @@ -534,16 +569,6 @@ Volume Volume - - - Controller Settings - Controller Settings - - - - Back Button Behavior - Back Button Behavior - MainWindow @@ -1033,6 +1058,41 @@ GUIgroupBox Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. + + + cursorGroupBox + Cursor:\nChange settings related to the cursor. + + + + hideCursorGroupBox + Hide Cursor:\nSet cursor hiding behavior. + + + + idleTimeoutGroupBox + Hide Idle Cursor Timeout:\nThe duration (seconds) after which the cursor that has been idle hides itself. + + + + Never + Never + + + + Idle + Idle + + + + Always + Always + + + + backButtonBehaviorGroupBox + Back Button Behavior:\nAllows setting which part of the touchpad the back button will emulate a touch on. + backButtonBehaviorGroupBox From ab6901ae6a93c80ee864756f3a4f811dd73308c9 Mon Sep 17 00:00:00 2001 From: robyn-dressler Date: Thu, 10 Oct 2024 09:53:18 -0500 Subject: [PATCH 30/93] Using a more standard data directory for linux (#1227) * Using a more standard data directory for linux * Fixing format * Using XDG_DATA_HOME by default --- src/common/path_util.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index 27098e2d1..7551d3b05 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -95,6 +95,18 @@ static auto UserPaths = [] { user_dir = std::filesystem::path(getenv("HOME")) / "Library" / "Application Support" / "shadPS4"; } +#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"); + if (xdg_data_home != nullptr && strlen(xdg_data_home) > 0) { + user_dir = std::filesystem::path(xdg_data_home) / "shadPS4"; + } else { + user_dir = std::filesystem::path(getenv("HOME")) / ".local" / "share" / "shadPS4"; + } + } #else const auto user_dir = std::filesystem::current_path() / PORTABLE_DIR; #endif From 0e0de5a2a01b25a00217a7b0a175242713b36e22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Thu, 10 Oct 2024 15:54:07 +0100 Subject: [PATCH 31/93] Stub return value of sceNpCreateRequest (#1209) --- src/core/libraries/np_manager/np_manager.cpp | 9 +++++---- src/core/libraries/np_manager/np_manager.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp index 28d28cc93..e1aaee814 100644 --- a/src/core/libraries/np_manager/np_manager.cpp +++ b/src/core/libraries/np_manager/np_manager.cpp @@ -902,12 +902,13 @@ int PS4_SYSV_ABI sceNpCreateAsyncRequest() { } int PS4_SYSV_ABI sceNpCreateRequest() { - LOG_ERROR(Lib_NpManager, "(STUBBED) called"); - return ORBIS_OK; + LOG_ERROR(Lib_NpManager, "(DUMMY) called"); + static int id = 0; + return ++id; } -int PS4_SYSV_ABI sceNpDeleteRequest() { - LOG_ERROR(Lib_NpManager, "(STUBBED) called"); +int PS4_SYSV_ABI sceNpDeleteRequest(int reqId) { + LOG_ERROR(Lib_NpManager, "(DUMMY) called reqId = {}", reqId); return ORBIS_OK; } diff --git a/src/core/libraries/np_manager/np_manager.h b/src/core/libraries/np_manager/np_manager.h index 43ea49ce4..861d91e39 100644 --- a/src/core/libraries/np_manager/np_manager.h +++ b/src/core/libraries/np_manager/np_manager.h @@ -218,7 +218,7 @@ int PS4_SYSV_ABI sceNpCheckNpReachability(); int PS4_SYSV_ABI sceNpCheckPlus(); int PS4_SYSV_ABI sceNpCreateAsyncRequest(); int PS4_SYSV_ABI sceNpCreateRequest(); -int PS4_SYSV_ABI sceNpDeleteRequest(); +int PS4_SYSV_ABI sceNpDeleteRequest(int reqId); int PS4_SYSV_ABI sceNpGetAccountAge(); int PS4_SYSV_ABI sceNpGetAccountCountry(); int PS4_SYSV_ABI sceNpGetAccountCountryA(); From 3982ef7188a7fcfa16f5d884ac1a08c703940b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Thu, 10 Oct 2024 21:54:32 +0700 Subject: [PATCH 32/93] ci: add missing libs to enable Wayland backend for SDL (#1184) --- .github/linux-appimage-qt.sh | 2 ++ .github/workflows/build.yml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/linux-appimage-qt.sh b/.github/linux-appimage-qt.sh index fe77c678c..06d5cbc11 100755 --- a/.github/linux-appimage-qt.sh +++ b/.github/linux-appimage-qt.sh @@ -9,6 +9,8 @@ fi export Qt6_DIR="/usr/lib/qt6" export PATH="$Qt6_DIR/bin:$PATH" +export EXTRA_QT_PLUGINS="waylandcompositor" +export EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so" # Prepare Tools for building the AppImage wget -q https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7950084cd..ee09163fd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -287,7 +287,7 @@ jobs: submodules: recursive - name: Install dependencies - run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-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 - name: Cache CMake Configuration uses: actions/cache@v4 @@ -343,7 +343,7 @@ jobs: submodules: recursive - name: Install dependencies - run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-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 - name: Cache CMake Configuration uses: actions/cache@v4 From 6e986f81335129db728cc9270caeef9e781f4fd7 Mon Sep 17 00:00:00 2001 From: korenkonder Date: Thu, 10 Oct 2024 18:03:12 +0300 Subject: [PATCH 33/93] video_core: Implement sceGnmInsertPushColorMarker (#989) --- src/core/libraries/gnmdriver/gnmdriver.cpp | 24 ++++++++++++++++--- src/core/libraries/gnmdriver/gnmdriver.h | 2 +- src/video_core/amdgpu/liverpool.cpp | 11 +++++++++ .../renderer_vulkan/vk_rasterizer.cpp | 13 ++++++++++ .../renderer_vulkan/vk_rasterizer.h | 1 + 5 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 7e2153efa..ce30895ca 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -1076,9 +1076,27 @@ s32 PS4_SYSV_ABI sceGnmInsertPopMarker(u32* cmdbuf, u32 size) { return -1; } -int PS4_SYSV_ABI sceGnmInsertPushColorMarker() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceGnmInsertPushColorMarker(u32* cmdbuf, u32 size, const char* marker, u32 color) { + LOG_TRACE(Lib_GnmDriver, "called"); + + if (cmdbuf && marker) { + const auto len = std::strlen(marker); + const u32 packet_size = ((len + 0xc) >> 2) + ((len + 0x10) >> 3) * 2; + if (packet_size + 2 == size) { + auto* nop = reinterpret_cast(cmdbuf); + nop->header = + PM4Type3Header{PM4ItOpcode::Nop, packet_size, PM4ShaderType::ShaderGraphics}; + nop->data_block[0] = PM4CmdNop::PayloadType::DebugColorMarkerPush; + const auto marker_len = len + 1; + std::memcpy(&nop->data_block[1], marker, marker_len); + *reinterpret_cast(reinterpret_cast(&nop->data_block[1]) + marker_len + 8) = + color; + std::memset(reinterpret_cast(&nop->data_block[1]) + marker_len + 8 + sizeof(u32), + 0, packet_size * 4 - marker_len - 8 - sizeof(u32)); + return ORBIS_OK; + } + } + return -1; } s32 PS4_SYSV_ABI sceGnmInsertPushMarker(u32* cmdbuf, u32 size, const char* marker) { diff --git a/src/core/libraries/gnmdriver/gnmdriver.h b/src/core/libraries/gnmdriver/gnmdriver.h index 55a70cbf3..33bccf427 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.h +++ b/src/core/libraries/gnmdriver/gnmdriver.h @@ -105,7 +105,7 @@ int PS4_SYSV_ABI sceGnmGpuPaDebugEnter(); int PS4_SYSV_ABI sceGnmGpuPaDebugLeave(); int PS4_SYSV_ABI sceGnmInsertDingDongMarker(); s32 PS4_SYSV_ABI sceGnmInsertPopMarker(u32* cmdbuf, u32 size); -int PS4_SYSV_ABI sceGnmInsertPushColorMarker(); +s32 PS4_SYSV_ABI sceGnmInsertPushColorMarker(u32* cmdbuf, u32 size, const char* marker, u32 color); s32 PS4_SYSV_ABI sceGnmInsertPushMarker(u32* cmdbuf, u32 size, const char* marker); int PS4_SYSV_ABI sceGnmInsertSetColorMarker(); int PS4_SYSV_ABI sceGnmInsertSetMarker(); diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 3dce871fe..b3b718836 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -226,6 +226,17 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spanheader.count.Value() * 2; + const std::string_view label{reinterpret_cast(&nop->data_block[1]), + marker_sz}; + const u32 color = *reinterpret_cast( + reinterpret_cast(&nop->data_block[1]) + marker_sz); + if (rasterizer) { + rasterizer->ScopedMarkerInsertColor(label, color); + } + break; + } case PM4CmdNop::PayloadType::DebugMarkerPop: { if (rasterizer) { rasterizer->ScopeMarkerEnd(); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 6088d99cf..293dfbe6a 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -459,4 +459,17 @@ void Rasterizer::ScopedMarkerInsert(const std::string_view& str) { }); } +void Rasterizer::ScopedMarkerInsertColor(const std::string_view& str, const u32 color) { + if (Config::nullGpu() || !Config::vkMarkersEnabled()) { + return; + } + + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.insertDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{ + .pLabelName = str.data(), + .color = std::array( + {(f32)((color >> 16) & 0xff) / 255.0f, (f32)((color >> 8) & 0xff) / 255.0f, + (f32)(color & 0xff) / 255.0f, (f32)((color >> 24) & 0xff) / 255.0f})}); +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 82e8fc0c0..bc14f39a4 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -40,6 +40,7 @@ public: void ScopeMarkerBegin(const std::string_view& str); void ScopeMarkerEnd(); void ScopedMarkerInsert(const std::string_view& str); + void ScopedMarkerInsertColor(const std::string_view& str, const u32 color); void InlineDataToGds(u32 gds_offset, u32 value); u32 ReadDataFromGds(u32 gsd_offset); From fd4893f6ef0e2f774c9d2b7085cd9207bb903e64 Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Thu, 10 Oct 2024 19:26:56 +0300 Subject: [PATCH 34/93] hotfix: Don't unconditionally register fiber lib --- src/core/libraries/libs.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 86f9fcf9d..fb771bc22 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -78,7 +78,6 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::ImeDialog::RegisterlibSceImeDialog(sym); Libraries::AvPlayer::RegisterlibSceAvPlayer(sym); Libraries::Audio3d::RegisterlibSceAudio3d(sym); - Libraries::Fiber::RegisterlibSceFiber(sym); } } // namespace Libraries From d91ad6174ebc48d15e28f2bc94c15bf8f2740f17 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 10 Oct 2024 09:27:34 -0700 Subject: [PATCH 35/93] shader_recompiler: Move sampling parameter resolution to tracking pass and support more derivative types. (#1290) * shader_recompiler: Move sampling parameter resolution to tracking pass and support more derivative types. * shader_recompiler: Only track sampler sharp on sample instructions. * shader_recompiler: Fix Inst args size. --- .../backend/spirv/emit_spirv_image.cpp | 27 +- .../backend/spirv/emit_spirv_instructions.h | 6 +- .../frontend/translate/vector_memory.cpp | 194 +++++------- src/shader_recompiler/ir/ir_emitter.cpp | 43 ++- src/shader_recompiler/ir/ir_emitter.h | 26 +- src/shader_recompiler/ir/opcodes.h | 2 +- src/shader_recompiler/ir/opcodes.inc | 11 +- .../ir/passes/resource_tracking_pass.cpp | 296 +++++++++++------- src/shader_recompiler/ir/reg.h | 3 +- src/shader_recompiler/ir/value.h | 2 +- 10 files changed, 338 insertions(+), 272 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 8f062d6e7..fc99b8925 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -59,19 +59,22 @@ struct ImageOperands { } } - void AddDerivatives(EmitContext& ctx, Id derivatives) { - if (!Sirit::ValidId(derivatives)) { + void AddDerivatives(EmitContext& ctx, Id derivatives_dx, Id derivatives_dy) { + if (!Sirit::ValidId(derivatives_dx) || !Sirit::ValidId(derivatives_dy)) { return; } - const Id dx{ctx.OpVectorShuffle(ctx.F32[2], derivatives, derivatives, 0, 1)}; - const Id dy{ctx.OpVectorShuffle(ctx.F32[2], derivatives, derivatives, 2, 3)}; - Add(spv::ImageOperandsMask::Grad, dx, dy); + Add(spv::ImageOperandsMask::Grad, derivatives_dx, derivatives_dy); } spv::ImageOperandsMask mask{}; boost::container::static_vector operands; }; +Id EmitImageSampleRaw(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address1, Id address2, + Id address3, Id address4) { + UNREACHABLE_MSG("Unreachable instruction"); +} + Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id bias, const IR::Value& offset) { const auto& texture = ctx.images[handle & 0xFFFF]; @@ -114,7 +117,9 @@ Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, operands.AddOffset(ctx, offset); const Id sample = ctx.OpImageSampleDrefImplicitLod(result_type, sampled_image, coords, dref, operands.mask, operands.operands); - return texture.is_integer ? ctx.OpBitcast(ctx.F32[1], sample) : sample; + const Id sample_typed = texture.is_integer ? ctx.OpBitcast(ctx.F32[1], sample) : sample; + return ctx.OpCompositeConstruct(ctx.F32[4], sample_typed, ctx.f32_zero_value, + ctx.f32_zero_value, ctx.f32_zero_value); } Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id dref, @@ -129,7 +134,9 @@ Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, operands.Add(spv::ImageOperandsMask::Lod, lod); const Id sample = ctx.OpImageSampleDrefExplicitLod(result_type, sampled_image, coords, dref, operands.mask, operands.operands); - return texture.is_integer ? ctx.OpBitcast(ctx.F32[1], sample) : sample; + const Id sample_typed = texture.is_integer ? ctx.OpBitcast(ctx.F32[1], sample) : sample; + return ctx.OpCompositeConstruct(ctx.F32[4], sample_typed, ctx.f32_zero_value, + ctx.f32_zero_value, ctx.f32_zero_value); } Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, @@ -212,15 +219,15 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords) { return ctx.OpImageQueryLod(ctx.F32[2], sampled_image, coords); } -Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id derivatives, - const IR::Value& offset, Id lod_clamp) { +Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id derivatives_dx, + Id derivatives_dy, const IR::Value& offset, const IR::Value& lod_clamp) { const auto& texture = ctx.images[handle & 0xFFFF]; const Id image = ctx.OpLoad(texture.image_type, texture.id); const Id result_type = texture.data_types->Get(4); const Id sampler = ctx.OpLoad(ctx.sampler_type, ctx.samplers[handle >> 16]); const Id sampled_image = ctx.OpSampledImage(texture.sampled_type, image, sampler); ImageOperands operands; - operands.AddDerivatives(ctx, derivatives); + operands.AddDerivatives(ctx, derivatives_dx, derivatives_dy); operands.AddOffset(ctx, offset); const Id sample = ctx.OpImageSampleExplicitLod(result_type, sampled_image, coords, operands.mask, operands.operands); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 6ae1ef24a..02b98b343 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -368,6 +368,8 @@ Id EmitConvertF64U64(EmitContext& ctx, Id value); Id EmitConvertU16U32(EmitContext& ctx, Id value); Id EmitConvertU32U16(EmitContext& ctx, Id value); +Id EmitImageSampleRaw(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address1, Id address2, + Id address3, Id address4); Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id bias, const IR::Value& offset); Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, @@ -384,8 +386,8 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, const Id lod, Id ms); Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, u32 handle, Id lod, bool skip_mips); Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords); -Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id derivatives, - const IR::Value& offset, Id lod_clamp); +Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id derivatives_dx, + Id derivatives_dy, const IR::Value& offset, const IR::Value& lod_clamp); Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id color); diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index e76ba6d8a..b7ad3b36b 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -411,7 +411,7 @@ void Translator::IMAGE_LOAD(bool has_mip, const GcnInst& inst) { ir.GetVectorReg(addr_reg + 2), ir.GetVectorReg(addr_reg + 3)); IR::TextureInstInfo info{}; - info.explicit_lod.Assign(has_mip); + info.has_lod.Assign(has_mip); const IR::Value texel = ir.ImageFetch(handle, body, {}, {}, {}, info); for (u32 i = 0; i < 4; i++) { @@ -513,6 +513,76 @@ void Translator::IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst) { } } +IR::Value EmitImageSample(IR::IREmitter& ir, const GcnInst& inst, const IR::ScalarReg tsharp_reg, + const IR::ScalarReg sampler_reg, const IR::VectorReg addr_reg, + bool gather) { + const auto& mimg = inst.control.mimg; + const auto flags = MimgModifierFlags(mimg.mod); + + IR::TextureInstInfo info{}; + info.is_depth.Assign(flags.test(MimgModifier::Pcf)); + info.has_bias.Assign(flags.test(MimgModifier::LodBias)); + info.has_lod_clamp.Assign(flags.test(MimgModifier::LodClamp)); + info.force_level0.Assign(flags.test(MimgModifier::Level0)); + info.has_offset.Assign(flags.test(MimgModifier::Offset)); + info.has_lod.Assign(flags.any(MimgModifier::Lod)); + info.is_array.Assign(mimg.da); + + if (gather) { + info.gather_comp.Assign(std::bit_width(mimg.dmask) - 1); + info.is_gather.Assign(true); + } else { + info.has_derivatives.Assign(flags.test(MimgModifier::Derivative)); + } + + // Load first dword of T# and S#. We will use them as the handle that will guide resource + // tracking pass where to read the sharps. This will later also get patched to the SPIRV texture + // binding index. + const IR::Value handle = + ir.CompositeConstruct(ir.GetScalarReg(tsharp_reg), ir.GetScalarReg(sampler_reg)); + + // Determine how many address registers need to be passed. + // The image type is unknown, so add all 4 possible base registers and resolve later. + int num_addr_regs = 4; + if (info.has_offset) { + ++num_addr_regs; + } + if (info.has_bias) { + ++num_addr_regs; + } + if (info.is_depth) { + ++num_addr_regs; + } + if (info.has_derivatives) { + // The image type is unknown, so add all 6 possible derivative registers and resolve later. + num_addr_regs += 6; + } + + // Fetch all the address registers to pass in the IR instruction. There can be up to 13 + // registers. + const auto get_addr_reg = [&](int index) -> IR::F32 { + if (index >= num_addr_regs) { + return ir.Imm32(0.f); + } + return ir.GetVectorReg(addr_reg + index); + }; + const IR::Value address1 = + ir.CompositeConstruct(get_addr_reg(0), get_addr_reg(1), get_addr_reg(2), get_addr_reg(3)); + const IR::Value address2 = + ir.CompositeConstruct(get_addr_reg(4), get_addr_reg(5), get_addr_reg(6), get_addr_reg(7)); + const IR::Value address3 = + ir.CompositeConstruct(get_addr_reg(8), get_addr_reg(9), get_addr_reg(10), get_addr_reg(11)); + const IR::Value address4 = get_addr_reg(12); + + // Issue the placeholder IR instruction. + IR::Value texel = ir.ImageSampleRaw(handle, address1, address2, address3, address4, info); + if (info.is_depth && !gather) { + // For non-gather depth sampling, only return a single value. + texel = ir.CompositeExtract(texel, 0); + } + return texel; +} + void Translator::IMAGE_SAMPLE(const GcnInst& inst) { const auto& mimg = inst.control.mimg; IR::VectorReg addr_reg{inst.src[0].code}; @@ -521,72 +591,7 @@ void Translator::IMAGE_SAMPLE(const GcnInst& inst) { const IR::ScalarReg sampler_reg{inst.src[3].code * 4}; const auto flags = MimgModifierFlags(mimg.mod); - // Load first dword of T# and S#. We will use them as the handle that will guide resource - // tracking pass where to read the sharps. This will later also get patched to the SPIRV texture - // binding index. - const IR::Value handle = - ir.CompositeConstruct(ir.GetScalarReg(tsharp_reg), ir.GetScalarReg(sampler_reg)); - - // Load first address components as denoted in 8.2.4 VGPR Usage Sea Islands Series Instruction - // Set Architecture - const IR::U32 offset = - flags.test(MimgModifier::Offset) ? ir.GetVectorReg(addr_reg++) : IR::U32{}; - const IR::F32 bias = - flags.test(MimgModifier::LodBias) ? ir.GetVectorReg(addr_reg++) : IR::F32{}; - const IR::F32 dref = - flags.test(MimgModifier::Pcf) ? ir.GetVectorReg(addr_reg++) : IR::F32{}; - const IR::Value derivatives = [&] -> IR::Value { - if (!flags.test(MimgModifier::Derivative)) { - return {}; - } - addr_reg = addr_reg + 4; - return ir.CompositeConstruct( - ir.GetVectorReg(addr_reg - 4), ir.GetVectorReg(addr_reg - 3), - ir.GetVectorReg(addr_reg - 2), ir.GetVectorReg(addr_reg - 1)); - }(); - - // Now we can load body components as noted in Table 8.9 Image Opcodes with Sampler - // Since these are at most 4 dwords, we load them into a single uvec4 and place them - // in coords field of the instruction. Then the resource tracking pass will patch the - // IR instruction to fill in lod_clamp field. - const IR::Value body = ir.CompositeConstruct( - ir.GetVectorReg(addr_reg), ir.GetVectorReg(addr_reg + 1), - ir.GetVectorReg(addr_reg + 2), ir.GetVectorReg(addr_reg + 3)); - - // Derivatives are tricky because their number depends on the texture type which is located in - // T#. We don't have access to T# though until resource tracking pass. For now assume if - // derivatives are present, that a 2D image is bound. - const bool has_derivatives = flags.test(MimgModifier::Derivative); - const bool explicit_lod = flags.any(MimgModifier::Level0, MimgModifier::Lod); - - IR::TextureInstInfo info{}; - info.is_depth.Assign(flags.test(MimgModifier::Pcf)); - info.has_bias.Assign(flags.test(MimgModifier::LodBias)); - info.has_lod_clamp.Assign(flags.test(MimgModifier::LodClamp)); - info.force_level0.Assign(flags.test(MimgModifier::Level0)); - info.has_offset.Assign(flags.test(MimgModifier::Offset)); - info.explicit_lod.Assign(explicit_lod); - info.has_derivatives.Assign(has_derivatives); - info.is_array.Assign(mimg.da); - - // Issue IR instruction, leaving unknown fields blank to patch later. - const IR::Value texel = [&]() -> IR::Value { - if (has_derivatives) { - return ir.ImageGradient(handle, body, derivatives, offset, {}, info); - } - if (!flags.test(MimgModifier::Pcf)) { - if (explicit_lod) { - return ir.ImageSampleExplicitLod(handle, body, offset, info); - } else { - return ir.ImageSampleImplicitLod(handle, body, bias, offset, info); - } - } - if (explicit_lod) { - return ir.ImageSampleDrefExplicitLod(handle, body, dref, offset, info); - } - return ir.ImageSampleDrefImplicitLod(handle, body, dref, bias, offset, info); - }(); - + const IR::Value texel = EmitImageSample(ir, inst, tsharp_reg, sampler_reg, addr_reg, false); for (u32 i = 0; i < 4; i++) { if (((mimg.dmask >> i) & 1) == 0) { continue; @@ -609,60 +614,13 @@ void Translator::IMAGE_GATHER(const GcnInst& inst) { const IR::ScalarReg sampler_reg{inst.src[3].code * 4}; const auto flags = MimgModifierFlags(mimg.mod); - // Load first dword of T# and S#. We will use them as the handle that will guide resource - // tracking pass where to read the sharps. This will later also get patched to the SPIRV texture - // binding index. - const IR::Value handle = - ir.CompositeConstruct(ir.GetScalarReg(tsharp_reg), ir.GetScalarReg(sampler_reg)); - - // Load first address components as denoted in 8.2.4 VGPR Usage Sea Islands Series Instruction - // Set Architecture - const IR::Value offset = - flags.test(MimgModifier::Offset) ? ir.GetVectorReg(addr_reg++) : IR::Value{}; - const IR::F32 bias = - flags.test(MimgModifier::LodBias) ? ir.GetVectorReg(addr_reg++) : IR::F32{}; - const IR::F32 dref = - flags.test(MimgModifier::Pcf) ? ir.GetVectorReg(addr_reg++) : IR::F32{}; - - // Derivatives are tricky because their number depends on the texture type which is located in - // T#. We don't have access to T# though until resource tracking pass. For now assume no - // derivatives are present, otherwise we don't know where coordinates are placed in the address - // stream. - ASSERT_MSG(!flags.test(MimgModifier::Derivative), "Derivative image instruction"); - - // Now we can load body components as noted in Table 8.9 Image Opcodes with Sampler - // Since these are at most 4 dwords, we load them into a single uvec4 and place them - // in coords field of the instruction. Then the resource tracking pass will patch the - // IR instruction to fill in lod_clamp field. - const IR::Value body = ir.CompositeConstruct( - ir.GetVectorReg(addr_reg), ir.GetVectorReg(addr_reg + 1), - ir.GetVectorReg(addr_reg + 2), ir.GetVectorReg(addr_reg + 3)); - - const bool explicit_lod = flags.any(MimgModifier::Level0, MimgModifier::Lod); - - IR::TextureInstInfo info{}; - info.is_depth.Assign(flags.test(MimgModifier::Pcf)); - info.has_bias.Assign(flags.test(MimgModifier::LodBias)); - info.has_lod_clamp.Assign(flags.test(MimgModifier::LodClamp)); - info.force_level0.Assign(flags.test(MimgModifier::Level0)); - info.has_offset.Assign(flags.test(MimgModifier::Offset)); - // info.explicit_lod.Assign(explicit_lod); - info.gather_comp.Assign(std::bit_width(mimg.dmask) - 1); - info.is_array.Assign(mimg.da); - - // Issue IR instruction, leaving unknown fields blank to patch later. - const IR::Value texel = [&]() -> IR::Value { - const IR::F32 lod = flags.test(MimgModifier::Level0) ? ir.Imm32(0.f) : IR::F32{}; - if (!flags.test(MimgModifier::Pcf)) { - return ir.ImageGather(handle, body, offset, info); - } - ASSERT(mimg.dmask & 1); // should be always 1st (R) component - return ir.ImageGatherDref(handle, body, offset, dref, info); - }(); - // For gather4 instructions dmask selects which component to read and must have // only one bit set to 1 ASSERT_MSG(std::popcount(mimg.dmask) == 1, "Unexpected bits in gather dmask"); + // should be always 1st (R) component for depth + ASSERT(!flags.test(MimgModifier::Pcf) || mimg.dmask & 1); + + const IR::Value texel = EmitImageSample(ir, inst, tsharp_reg, sampler_reg, addr_reg, true); for (u32 i = 0; i < 4; i++) { const IR::F32 value = IR::F32{ir.CompositeExtract(texel, i)}; ir.SetVectorReg(dest_reg++, value); diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index 4f5eb5c33..e1b0eeed5 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -1492,27 +1492,34 @@ Value IREmitter::ImageAtomicExchange(const Value& handle, const Value& coords, c return Inst(Opcode::ImageAtomicExchange32, Flags{info}, handle, coords, value); } -Value IREmitter::ImageSampleImplicitLod(const Value& handle, const Value& body, const F32& bias, - const U32& offset, TextureInstInfo info) { - return Inst(Opcode::ImageSampleImplicitLod, Flags{info}, handle, body, bias, offset); +Value IREmitter::ImageSampleRaw(const Value& handle, const Value& address1, const Value& address2, + const Value& address3, const Value& address4, + TextureInstInfo info) { + return Inst(Opcode::ImageSampleRaw, Flags{info}, handle, address1, address2, address3, + address4); } -Value IREmitter::ImageSampleExplicitLod(const Value& handle, const Value& body, const U32& offset, - TextureInstInfo info) { - return Inst(Opcode::ImageSampleExplicitLod, Flags{info}, handle, body, IR::F32{}, offset); +Value IREmitter::ImageSampleImplicitLod(const Value& handle, const Value& coords, const F32& bias, + const Value& offset, TextureInstInfo info) { + return Inst(Opcode::ImageSampleImplicitLod, Flags{info}, handle, coords, bias, offset); } -F32 IREmitter::ImageSampleDrefImplicitLod(const Value& handle, const Value& body, const F32& dref, - const F32& bias, const U32& offset, - TextureInstInfo info) { - return Inst(Opcode::ImageSampleDrefImplicitLod, Flags{info}, handle, body, dref, bias, - offset); +Value IREmitter::ImageSampleExplicitLod(const Value& handle, const Value& coords, const F32& lod, + const Value& offset, TextureInstInfo info) { + return Inst(Opcode::ImageSampleExplicitLod, Flags{info}, handle, coords, lod, offset); } -F32 IREmitter::ImageSampleDrefExplicitLod(const Value& handle, const Value& body, const F32& dref, - const U32& offset, TextureInstInfo info) { - return Inst(Opcode::ImageSampleDrefExplicitLod, Flags{info}, handle, body, dref, IR::F32{}, - offset); +Value IREmitter::ImageSampleDrefImplicitLod(const Value& handle, const Value& coords, + const F32& dref, const F32& bias, const Value& offset, + TextureInstInfo info) { + return Inst(Opcode::ImageSampleDrefImplicitLod, Flags{info}, handle, coords, dref, bias, + offset); +} + +Value IREmitter::ImageSampleDrefExplicitLod(const Value& handle, const Value& coords, + const F32& dref, const F32& lod, const Value& offset, + TextureInstInfo info) { + return Inst(Opcode::ImageSampleDrefExplicitLod, Flags{info}, handle, coords, dref, lod, offset); } Value IREmitter::ImageGather(const Value& handle, const Value& coords, const Value& offset, @@ -1544,9 +1551,11 @@ Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, Texture return Inst(Opcode::ImageQueryLod, Flags{info}, handle, coords); } -Value IREmitter::ImageGradient(const Value& handle, const Value& coords, const Value& derivatives, +Value IREmitter::ImageGradient(const Value& handle, const Value& coords, + const Value& derivatives_dx, const Value& derivatives_dy, const Value& offset, const F32& lod_clamp, TextureInstInfo info) { - return Inst(Opcode::ImageGradient, Flags{info}, handle, coords, derivatives, offset, lod_clamp); + return Inst(Opcode::ImageGradient, Flags{info}, handle, coords, derivatives_dx, derivatives_dy, + offset, lod_clamp); } Value IREmitter::ImageRead(const Value& handle, const Value& coords, TextureInstInfo info) { diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index 2ebac037e..b3f513085 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -277,20 +277,25 @@ public: [[nodiscard]] Value ImageAtomicExchange(const Value& handle, const Value& coords, const Value& value, TextureInstInfo info); + [[nodiscard]] Value ImageSampleRaw(const Value& handle, const Value& address1, + const Value& address2, const Value& address3, + const Value& address4, TextureInstInfo info); + [[nodiscard]] Value ImageSampleImplicitLod(const Value& handle, const Value& body, - const F32& bias, const U32& offset, + const F32& bias, const Value& offset, TextureInstInfo info); [[nodiscard]] Value ImageSampleExplicitLod(const Value& handle, const Value& body, - const U32& offset, TextureInstInfo info); + const F32& lod, const Value& offset, + TextureInstInfo info); - [[nodiscard]] F32 ImageSampleDrefImplicitLod(const Value& handle, const Value& body, - const F32& dref, const F32& bias, - const U32& offset, TextureInstInfo info); + [[nodiscard]] Value ImageSampleDrefImplicitLod(const Value& handle, const Value& body, + const F32& dref, const F32& bias, + const Value& offset, TextureInstInfo info); - [[nodiscard]] F32 ImageSampleDrefExplicitLod(const Value& handle, const Value& body, - const F32& dref, const U32& offset, - TextureInstInfo info); + [[nodiscard]] Value ImageSampleDrefExplicitLod(const Value& handle, const Value& body, + const F32& dref, const F32& lod, + const Value& offset, TextureInstInfo info); [[nodiscard]] Value ImageQueryDimension(const Value& handle, const U32& lod, const U1& skip_mips); @@ -306,8 +311,9 @@ public: [[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const Value& offset, const U32& lod, const U32& multisampling, TextureInstInfo info); [[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords, - const Value& derivatives, const Value& offset, - const F32& lod_clamp, TextureInstInfo info); + const Value& derivatives_dx, const Value& derivatives_dy, + const Value& offset, const F32& lod_clamp, + TextureInstInfo info); [[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, TextureInstInfo info); void ImageWrite(const Value& handle, const Value& coords, const Value& color, TextureInstInfo info); diff --git a/src/shader_recompiler/ir/opcodes.h b/src/shader_recompiler/ir/opcodes.h index 2cea70090..200d7f421 100644 --- a/src/shader_recompiler/ir/opcodes.h +++ b/src/shader_recompiler/ir/opcodes.h @@ -21,7 +21,7 @@ namespace Detail { struct OpcodeMeta { std::string_view name; Type type; - std::array arg_types; + std::array arg_types; }; // using enum Type; diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index 41e94ab13..51e10fb38 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -317,16 +317,17 @@ OPCODE(ConvertU16U32, U16, U32, OPCODE(ConvertU32U16, U32, U16, ) // Image operations -OPCODE(ImageSampleImplicitLod, F32x4, Opaque, Opaque, F32, Opaque, ) -OPCODE(ImageSampleExplicitLod, F32x4, Opaque, Opaque, U32, Opaque, ) -OPCODE(ImageSampleDrefImplicitLod, F32, Opaque, Opaque, Opaque, F32, Opaque, ) -OPCODE(ImageSampleDrefExplicitLod, F32, Opaque, Opaque, Opaque, U32, Opaque, ) +OPCODE(ImageSampleRaw, F32x4, Opaque, F32x4, F32x4, F32x4, F32, ) +OPCODE(ImageSampleImplicitLod, F32x4, Opaque, F32x4, F32, Opaque, ) +OPCODE(ImageSampleExplicitLod, F32x4, Opaque, Opaque, F32, Opaque, ) +OPCODE(ImageSampleDrefImplicitLod, F32x4, Opaque, Opaque, F32, F32, Opaque, ) +OPCODE(ImageSampleDrefExplicitLod, F32x4, Opaque, Opaque, F32, F32, Opaque, ) OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, ) OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, F32, ) OPCODE(ImageFetch, F32x4, Opaque, Opaque, Opaque, U32, Opaque, ) OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, U1, ) OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, ) -OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, ) +OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, F32, ) OPCODE(ImageRead, U32x4, Opaque, Opaque, ) OPCODE(ImageWrite, Void, Opaque, Opaque, U32x4, ) diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index db0d75f0c..76ffec81f 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -132,38 +132,16 @@ bool IsImageStorageInstruction(const IR::Inst& inst) { bool IsImageInstruction(const IR::Inst& inst) { switch (inst.GetOpcode()) { - case IR::Opcode::ImageSampleExplicitLod: - case IR::Opcode::ImageSampleImplicitLod: - case IR::Opcode::ImageSampleDrefExplicitLod: - case IR::Opcode::ImageSampleDrefImplicitLod: case IR::Opcode::ImageFetch: - case IR::Opcode::ImageGather: - case IR::Opcode::ImageGatherDref: case IR::Opcode::ImageQueryDimensions: case IR::Opcode::ImageQueryLod: - case IR::Opcode::ImageGradient: + case IR::Opcode::ImageSampleRaw: return true; default: return IsImageStorageInstruction(inst); } } -u32 ImageOffsetArgumentPosition(const IR::Inst& inst) { - switch (inst.GetOpcode()) { - case IR::Opcode::ImageGather: - case IR::Opcode::ImageGatherDref: - return 2; - case IR::Opcode::ImageSampleExplicitLod: - case IR::Opcode::ImageSampleImplicitLod: - return 3; - case IR::Opcode::ImageSampleDrefExplicitLod: - case IR::Opcode::ImageSampleDrefImplicitLod: - return 4; - default: - UNREACHABLE(); - } -} - class Descriptors { public: explicit Descriptors(Info& info_) @@ -467,6 +445,185 @@ IR::Value PatchCubeCoord(IR::IREmitter& ir, const IR::Value& s, const IR::Value& } } +void PatchImageSampleInstruction(IR::Block& block, IR::Inst& inst, Info& info, + Descriptors& descriptors, const IR::Inst* producer, + const u32 image_binding, const AmdGpu::Image& image) { + // Read sampler sharp. This doesn't exist for IMAGE_LOAD/IMAGE_STORE instructions + const u32 sampler_binding = [&] { + ASSERT(producer->GetOpcode() == IR::Opcode::CompositeConstructU32x2); + const IR::Value& handle = producer->Arg(1); + // Inline sampler resource. + if (handle.IsImmediate()) { + LOG_WARNING(Render_Vulkan, "Inline sampler detected"); + return descriptors.Add(SamplerResource{ + .sgpr_base = std::numeric_limits::max(), + .dword_offset = 0, + .inline_sampler = AmdGpu::Sampler{.raw0 = handle.U32()}, + }); + } + // Normal sampler resource. + const auto ssharp_handle = handle.InstRecursive(); + const auto& [ssharp_ud, disable_aniso] = TryDisableAnisoLod0(ssharp_handle); + const auto ssharp = TrackSharp(ssharp_ud); + return descriptors.Add(SamplerResource{ + .sgpr_base = ssharp.sgpr_base, + .dword_offset = ssharp.dword_offset, + .associated_image = image_binding, + .disable_aniso = disable_aniso, + }); + }(); + + IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; + + const auto inst_info = inst.Flags(); + const IR::U32 handle = ir.Imm32(image_binding | sampler_binding << 16); + + IR::Inst* body1 = inst.Arg(1).InstRecursive(); + IR::Inst* body2 = inst.Arg(2).InstRecursive(); + IR::Inst* body3 = inst.Arg(3).InstRecursive(); + IR::Inst* body4 = inst.Arg(4).InstRecursive(); + const auto get_addr_reg = [&](u32 index) -> IR::F32 { + if (index <= 3) { + return IR::F32{body1->Arg(index)}; + } + if (index >= 4 && index <= 7) { + return IR::F32{body2->Arg(index - 4)}; + } + if (index >= 8 && index <= 11) { + return IR::F32{body3->Arg(index - 8)}; + } + if (index == 12) { + return IR::F32{body4}; + } + UNREACHABLE(); + }; + u32 addr_reg = 0; + + // Load first address components as denoted in 8.2.4 VGPR Usage Sea Islands Series Instruction + // Set Architecture + const IR::Value offset = [&] -> IR::Value { + if (!inst_info.has_offset) { + return IR::U32{}; + } + + // The offsets are six-bit signed integers: X=[5:0], Y=[13:8], and Z=[21:16]. + const IR::Value arg = get_addr_reg(addr_reg++); + + const auto read = [&](u32 off) -> IR::U32 { + if (arg.IsImmediate()) { + const u16 comp = (arg.U32() >> off) & 0x3F; + return ir.Imm32(s32(comp << 26) >> 26); + } + return ir.BitFieldExtract(IR::U32{arg}, ir.Imm32(off), ir.Imm32(6), true); + }; + + switch (image.GetType()) { + case AmdGpu::ImageType::Color1D: + case AmdGpu::ImageType::Color1DArray: + return read(0); + case AmdGpu::ImageType::Color2D: + case AmdGpu::ImageType::Color2DArray: + case AmdGpu::ImageType::Color2DMsaa: + return ir.CompositeConstruct(read(0), read(8)); + case AmdGpu::ImageType::Color3D: + case AmdGpu::ImageType::Cube: + return ir.CompositeConstruct(read(0), read(8), read(16)); + default: + UNREACHABLE(); + } + }(); + const IR::F32 bias = inst_info.has_bias ? get_addr_reg(addr_reg++) : IR::F32{}; + const IR::F32 dref = inst_info.is_depth ? get_addr_reg(addr_reg++) : IR::F32{}; + const auto [derivatives_dx, derivatives_dy] = [&] -> std::pair { + if (!inst_info.has_derivatives) { + return {}; + } + switch (image.GetType()) { + case AmdGpu::ImageType::Color1D: + case AmdGpu::ImageType::Color1DArray: + // du/dx, du/dy + addr_reg = addr_reg + 2; + return {get_addr_reg(addr_reg - 2), get_addr_reg(addr_reg - 1)}; + case AmdGpu::ImageType::Color2D: + case AmdGpu::ImageType::Color2DArray: + case AmdGpu::ImageType::Color2DMsaa: + // (du/dx, dv/dx), (du/dy, dv/dy) + addr_reg = addr_reg + 4; + return {ir.CompositeConstruct(get_addr_reg(addr_reg - 4), get_addr_reg(addr_reg - 3)), + ir.CompositeConstruct(get_addr_reg(addr_reg - 2), get_addr_reg(addr_reg - 1))}; + case AmdGpu::ImageType::Color3D: + case AmdGpu::ImageType::Cube: + // (du/dx, dv/dx, dw/dx), (du/dy, dv/dy, dw/dy) + addr_reg = addr_reg + 6; + return {ir.CompositeConstruct(get_addr_reg(addr_reg - 6), get_addr_reg(addr_reg - 5), + get_addr_reg(addr_reg - 4)), + ir.CompositeConstruct(get_addr_reg(addr_reg - 3), get_addr_reg(addr_reg - 2), + get_addr_reg(addr_reg - 1))}; + default: + UNREACHABLE(); + } + }(); + + // Now we can load body components as noted in Table 8.9 Image Opcodes with Sampler + const IR::Value coords = [&] -> IR::Value { + switch (image.GetType()) { + case AmdGpu::ImageType::Color1D: // x + addr_reg = addr_reg + 1; + return get_addr_reg(addr_reg - 1); + case AmdGpu::ImageType::Color1DArray: // x, slice + [[fallthrough]]; + case AmdGpu::ImageType::Color2D: // x, y + addr_reg = addr_reg + 2; + return ir.CompositeConstruct(get_addr_reg(addr_reg - 2), get_addr_reg(addr_reg - 1)); + case AmdGpu::ImageType::Color2DArray: // x, y, slice + [[fallthrough]]; + case AmdGpu::ImageType::Color2DMsaa: // x, y, frag + [[fallthrough]]; + case AmdGpu::ImageType::Color3D: // x, y, z + addr_reg = addr_reg + 3; + return ir.CompositeConstruct(get_addr_reg(addr_reg - 3), get_addr_reg(addr_reg - 2), + get_addr_reg(addr_reg - 1)); + case AmdGpu::ImageType::Cube: // x, y, face + addr_reg = addr_reg + 3; + return PatchCubeCoord(ir, get_addr_reg(addr_reg - 3), get_addr_reg(addr_reg - 2), + get_addr_reg(addr_reg - 1), false, inst_info.is_array); + default: + UNREACHABLE(); + } + }(); + + ASSERT(!inst_info.has_lod || !inst_info.has_lod_clamp); + const bool explicit_lod = inst_info.has_lod || inst_info.force_level0; + const IR::F32 lod = inst_info.has_lod ? get_addr_reg(addr_reg++) + : inst_info.force_level0 ? ir.Imm32(0.0f) + : IR::F32{}; + const IR::F32 lod_clamp = inst_info.has_lod_clamp ? get_addr_reg(addr_reg++) : IR::F32{}; + + const auto new_inst = [&] -> IR::Value { + if (inst_info.is_gather) { + if (inst_info.is_depth) { + return ir.ImageGatherDref(handle, coords, offset, dref, inst_info); + } + return ir.ImageGather(handle, coords, offset, inst_info); + } + if (inst_info.has_derivatives) { + return ir.ImageGradient(handle, coords, derivatives_dx, derivatives_dy, offset, + lod_clamp, inst_info); + } + if (inst_info.is_depth) { + if (explicit_lod) { + return ir.ImageSampleDrefExplicitLod(handle, coords, dref, lod, offset, inst_info); + } + return ir.ImageSampleDrefImplicitLod(handle, coords, dref, bias, offset, inst_info); + } + if (explicit_lod) { + return ir.ImageSampleExplicitLod(handle, coords, lod, offset, inst_info); + } + return ir.ImageSampleImplicitLod(handle, coords, bias, offset, inst_info); + }(); + inst.ReplaceUsesWith(new_inst); +} + void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& descriptors) { const auto pred = [](const IR::Inst* inst) -> std::optional { const auto opcode = inst->GetOpcode(); @@ -498,40 +655,18 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip .sgpr_base = tsharp.sgpr_base, .dword_offset = tsharp.dword_offset, .type = type, - .nfmt = static_cast(image.GetNumberFmt()), + .nfmt = image.GetNumberFmt(), .is_storage = is_storage, .is_depth = bool(inst_info.is_depth), .is_atomic = IsImageAtomicInstruction(inst), .is_array = bool(inst_info.is_array), }); - // Read sampler sharp. This doesn't exist for IMAGE_LOAD/IMAGE_STORE instructions - const u32 sampler_binding = [&] { - if (!has_sampler) { - return 0U; - } - const IR::Value& handle = producer->Arg(1); - // Inline sampler resource. - if (handle.IsImmediate()) { - LOG_WARNING(Render_Vulkan, "Inline sampler detected"); - return descriptors.Add(SamplerResource{ - .sgpr_base = std::numeric_limits::max(), - .dword_offset = 0, - .inline_sampler = AmdGpu::Sampler{.raw0 = handle.U32()}, - }); - } - // Normal sampler resource. - const auto ssharp_handle = handle.InstRecursive(); - const auto& [ssharp_ud, disable_aniso] = TryDisableAnisoLod0(ssharp_handle); - const auto ssharp = TrackSharp(ssharp_ud); - return descriptors.Add(SamplerResource{ - .sgpr_base = ssharp.sgpr_base, - .dword_offset = ssharp.dword_offset, - .associated_image = image_binding, - .disable_aniso = disable_aniso, - }); - }(); - image_binding |= (sampler_binding << 16); + // Sample instructions must be resolved into a new instruction using address register data. + if (inst.GetOpcode() == IR::Opcode::ImageSampleRaw) { + PatchImageSampleInstruction(block, inst, info, descriptors, producer, image_binding, image); + return; + } // Patch image handle IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; @@ -568,62 +703,9 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip }(); inst.SetArg(1, coords); - if (inst_info.has_offset) { - // The offsets are six-bit signed integers: X=[5:0], Y=[13:8], and Z=[21:16]. - const u32 arg_pos = ImageOffsetArgumentPosition(inst); - const IR::Value arg = inst.Arg(arg_pos); - ASSERT_MSG(arg.Type() == IR::Type::U32, "Unexpected offset type"); - - const auto read = [&](u32 offset) -> IR::U32 { - if (arg.IsImmediate()) { - const u16 comp = (arg.U32() >> offset) & 0x3F; - return ir.Imm32(s32(comp << 26) >> 26); - } - return ir.BitFieldExtract(IR::U32{arg}, ir.Imm32(offset), ir.Imm32(6), true); - }; - - switch (image.GetType()) { - case AmdGpu::ImageType::Color1D: - case AmdGpu::ImageType::Color1DArray: - inst.SetArg(arg_pos, read(0)); - break; - case AmdGpu::ImageType::Color2D: - case AmdGpu::ImageType::Color2DArray: - inst.SetArg(arg_pos, ir.CompositeConstruct(read(0), read(8))); - break; - case AmdGpu::ImageType::Color3D: - inst.SetArg(arg_pos, ir.CompositeConstruct(read(0), read(8), read(16))); - break; - default: - UNREACHABLE(); - } - } - if (inst_info.has_derivatives) { - ASSERT_MSG(image.GetType() == AmdGpu::ImageType::Color2D || - image.GetType() == AmdGpu::ImageType::Color2DArray, - "User derivatives only supported for 2D images"); - } - if (inst_info.has_lod_clamp) { - const u32 arg_pos = [&]() -> u32 { - switch (inst.GetOpcode()) { - case IR::Opcode::ImageSampleImplicitLod: - return 2; - case IR::Opcode::ImageSampleDrefImplicitLod: - return 3; - default: - break; - } - return inst_info.is_depth ? 5 : 4; - }(); - inst.SetArg(arg_pos, arg); - } - if (inst_info.explicit_lod) { - ASSERT(inst.GetOpcode() == IR::Opcode::ImageFetch || - inst.GetOpcode() == IR::Opcode::ImageSampleExplicitLod || - inst.GetOpcode() == IR::Opcode::ImageSampleDrefExplicitLod); - const u32 pos = inst.GetOpcode() == IR::Opcode::ImageSampleExplicitLod ? 2 : 3; - const IR::Value value = inst_info.force_level0 ? ir.Imm32(0.f) : arg; - inst.SetArg(pos, value); + if (inst_info.has_lod) { + ASSERT(inst.GetOpcode() == IR::Opcode::ImageFetch); + inst.SetArg(3, arg); } } diff --git a/src/shader_recompiler/ir/reg.h b/src/shader_recompiler/ir/reg.h index 5facaf5c7..d7c0b1db5 100644 --- a/src/shader_recompiler/ir/reg.h +++ b/src/shader_recompiler/ir/reg.h @@ -33,11 +33,12 @@ union TextureInstInfo { BitField<1, 1, u32> has_bias; BitField<2, 1, u32> has_lod_clamp; BitField<3, 1, u32> force_level0; - BitField<4, 1, u32> explicit_lod; + BitField<4, 1, u32> has_lod; BitField<5, 1, u32> has_offset; BitField<6, 2, u32> gather_comp; BitField<8, 1, u32> has_derivatives; BitField<9, 1, u32> is_array; + BitField<10, 1, u32> is_gather; }; union BufferInstInfo { diff --git a/src/shader_recompiler/ir/value.h b/src/shader_recompiler/ir/value.h index 060b9d2bb..a282b9168 100644 --- a/src/shader_recompiler/ir/value.h +++ b/src/shader_recompiler/ir/value.h @@ -209,7 +209,7 @@ private: union { NonTriviallyDummy dummy{}; boost::container::small_vector, 2> phi_args; - std::array args; + std::array args; }; }; static_assert(sizeof(Inst) <= 128, "Inst size unintentionally increased"); From 09cbccb40be311246079e2c3d3a0c1630f51b3bc Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 10 Oct 2024 09:40:19 -0700 Subject: [PATCH 36/93] shader_recompiler: Implement V_SUBB_U32 and V_SUBBREV_U32. (#1331) --- .../frontend/translate/translate.h | 6 +- .../frontend/translate/vector_alu.cpp | 111 +++++++++++++----- 2 files changed, 88 insertions(+), 29 deletions(-) diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index c77588280..b70d4b829 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -155,6 +155,8 @@ public: void V_SUB_I32(const GcnInst& inst); void V_SUBREV_I32(const GcnInst& inst); void V_ADDC_U32(const GcnInst& inst); + void V_SUBB_U32(const GcnInst& inst); + void V_SUBBREV_U32(const GcnInst& inst); void V_LDEXP_F32(const GcnInst& inst); void V_CVT_PKNORM_U16_F32(const GcnInst& inst); void V_CVT_PKRTZ_F16_F32(const GcnInst& inst); @@ -273,7 +275,9 @@ private: void SetDst(const InstOperand& operand, const IR::U32F32& value); void SetDst64(const InstOperand& operand, const IR::U64F64& value_raw); - // Vector ALU Helprers + // Vector ALU Helpers + IR::U32 GetCarryIn(const GcnInst& inst); + void SetCarryOut(const GcnInst& inst, const IR::U1& carry); IR::U32 VMovRelSHelper(u32 src_vgprno, const IR::U32 m0); void VMovRelDHelper(u32 dst_vgprno, const IR::U32 src_val, const IR::U32 m0); diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index b061d3b78..279695461 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -87,6 +87,10 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_SUBREV_I32(inst); case Opcode::V_ADDC_U32: return V_ADDC_U32(inst); + case Opcode::V_SUBB_U32: + return V_SUBB_U32(inst); + case Opcode::V_SUBBREV_U32: + return V_SUBBREV_U32(inst); case Opcode::V_LDEXP_F32: return V_LDEXP_F32(inst); case Opcode::V_CVT_PKNORM_U16_F32: @@ -546,51 +550,71 @@ void Translator::V_MBCNT_U32_B32(bool is_low, const GcnInst& inst) { } void Translator::V_ADD_I32(const GcnInst& inst) { + // Signed or unsigned components const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{ir.GetVectorReg(IR::VectorReg(inst.src[1].code))}; - SetDst(inst.dst[0], ir.IAdd(src0, src1)); - // TODO: Carry + const IR::U32 result{ir.IAdd(src0, src1)}; + SetDst(inst.dst[0], result); + + // TODO: Carry-out with signed or unsigned components } void Translator::V_SUB_I32(const GcnInst& inst) { + // Unsigned components const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; - SetDst(inst.dst[0], ir.ISub(src0, src1)); + const IR::U32 result{ir.ISub(src0, src1)}; + SetDst(inst.dst[0], result); + + const IR::U1 did_underflow{ir.IGreaterThan(src1, src0, false)}; + SetCarryOut(inst, did_underflow); } void Translator::V_SUBREV_I32(const GcnInst& inst) { + // Unsigned components const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; - SetDst(inst.dst[0], ir.ISub(src1, src0)); - // TODO: Carry-out + const IR::U32 result{ir.ISub(src1, src0)}; + SetDst(inst.dst[0], result); + + const IR::U1 did_underflow{ir.IGreaterThan(src0, src1, false)}; + SetCarryOut(inst, did_underflow); } void Translator::V_ADDC_U32(const GcnInst& inst) { - const auto src0 = GetSrc(inst.src[0]); - const auto src1 = GetSrc(inst.src[1]); - - IR::U1 carry; - if (inst.src_count == 3) { // VOP3 - if (inst.src[2].field == OperandField::VccLo) { - carry = ir.GetVcc(); - } else if (inst.src[2].field == OperandField::ScalarGPR) { - carry = ir.GetThreadBitScalarReg(IR::ScalarReg(inst.src[2].code)); - } else { - UNREACHABLE(); - } - } else { // VOP2 - carry = ir.GetVcc(); - } - - const IR::U32 scarry = IR::U32{ir.Select(carry, ir.Imm32(1), ir.Imm32(0))}; - const IR::U32 result = ir.IAdd(ir.IAdd(src0, src1), scarry); - + // Unsigned components + const IR::U32 src0{GetSrc(inst.src[0])}; + const IR::U32 src1{GetSrc(inst.src[1])}; + const IR::U32 carry{GetCarryIn(inst)}; + const IR::U32 result{ir.IAdd(ir.IAdd(src0, src1), carry)}; SetDst(inst.dst[0], result); - const IR::U1 less_src0 = ir.ILessThan(result, src0, false); - const IR::U1 less_src1 = ir.ILessThan(result, src1, false); - const IR::U1 did_overflow = ir.LogicalOr(less_src0, less_src1); - ir.SetVcc(did_overflow); + const IR::U1 less_src0{ir.ILessThan(result, src0, false)}; + const IR::U1 less_src1{ir.ILessThan(result, src1, false)}; + const IR::U1 did_overflow{ir.LogicalOr(less_src0, less_src1)}; + SetCarryOut(inst, did_overflow); +} + +void Translator::V_SUBB_U32(const GcnInst& inst) { + // Signed or unsigned components + const IR::U32 src0{GetSrc(inst.src[0])}; + const IR::U32 src1{GetSrc(inst.src[1])}; + const IR::U32 carry{GetCarryIn(inst)}; + const IR::U32 result{ir.ISub(ir.ISub(src0, src1), carry)}; + SetDst(inst.dst[0], result); + + // TODO: Carry-out with signed or unsigned components +} + +void Translator::V_SUBBREV_U32(const GcnInst& inst) { + // Signed or unsigned components + const IR::U32 src0{GetSrc(inst.src[0])}; + const IR::U32 src1{GetSrc(inst.src[1])}; + const IR::U32 carry{GetCarryIn(inst)}; + const IR::U32 result{ir.ISub(ir.ISub(src1, src0), carry)}; + SetDst(inst.dst[0], result); + + // TODO: Carry-out with signed or unsigned components } void Translator::V_LDEXP_F32(const GcnInst& inst) { @@ -1152,6 +1176,37 @@ void Translator::V_MAD_U64_U32(const GcnInst& inst) { ir.SetVcc(did_overflow); } +IR::U32 Translator::GetCarryIn(const GcnInst& inst) { + IR::U1 carry; + if (inst.src_count == 3) { // VOP3 + if (inst.src[2].field == OperandField::VccLo) { + carry = ir.GetVcc(); + } else if (inst.src[2].field == OperandField::ScalarGPR) { + carry = ir.GetThreadBitScalarReg(IR::ScalarReg(inst.src[2].code)); + } else { + UNREACHABLE(); + } + } else { // VOP2 + carry = ir.GetVcc(); + } + + return IR::U32{ir.Select(carry, ir.Imm32(1), ir.Imm32(0))}; +} + +void Translator::SetCarryOut(const GcnInst& inst, const IR::U1& carry) { + if (inst.dst_count == 2) { // VOP3 + if (inst.dst[1].field == OperandField::VccLo) { + ir.SetVcc(carry); + } else if (inst.dst[1].field == OperandField::ScalarGPR) { + ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[1].code), carry); + } else { + UNREACHABLE(); + } + } else { // VOP2 + ir.SetVcc(carry); + } +} + // TODO: add range analysis pass to hopefully put an upper bound on m0, and only select one of // [src_vgprno, src_vgprno + max_m0]. Same for dst regs we may write back to From 0a5f46942e7639e6840979da73309e001cd73684 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:32:13 -0700 Subject: [PATCH 37/93] shader_recompiler: Make sure RuntimeInfo is zero initialized. (#1332) --- src/shader_recompiler/runtime_info.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 4d15c2072..03936f3a8 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -153,7 +153,10 @@ struct RuntimeInfo { ComputeRuntimeInfo cs_info; }; - RuntimeInfo(Stage stage_) : stage{stage_} {} + RuntimeInfo(Stage stage_) { + memset(this, 0, sizeof(*this)); + stage = stage_; + } bool operator==(const RuntimeInfo& other) const noexcept { switch (stage) { From 0df0d0cb66632006f3300709d94b76b297f76c80 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 10 Oct 2024 12:51:11 -0700 Subject: [PATCH 38/93] shader_recompiler: Fix last image sample address parameter. (#1334) --- src/shader_recompiler/ir/passes/resource_tracking_pass.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 76ffec81f..0cb6bff29 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -481,7 +481,7 @@ void PatchImageSampleInstruction(IR::Block& block, IR::Inst& inst, Info& info, IR::Inst* body1 = inst.Arg(1).InstRecursive(); IR::Inst* body2 = inst.Arg(2).InstRecursive(); IR::Inst* body3 = inst.Arg(3).InstRecursive(); - IR::Inst* body4 = inst.Arg(4).InstRecursive(); + IR::F32 body4 = IR::F32{inst.Arg(4)}; const auto get_addr_reg = [&](u32 index) -> IR::F32 { if (index <= 3) { return IR::F32{body1->Arg(index)}; @@ -493,7 +493,7 @@ void PatchImageSampleInstruction(IR::Block& block, IR::Inst& inst, Info& info, return IR::F32{body3->Arg(index - 8)}; } if (index == 12) { - return IR::F32{body4}; + return body4; } UNREACHABLE(); }; From dcc4057dd87d090b55affee6fb2f3859a326cb0f Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 10 Oct 2024 13:04:51 -0700 Subject: [PATCH 39/93] shader_recompiler: Set correct operand field for VOP3b sdst. (#1335) --- src/shader_recompiler/frontend/decode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shader_recompiler/frontend/decode.cpp b/src/shader_recompiler/frontend/decode.cpp index 98f97dd12..796bed127 100644 --- a/src/shader_recompiler/frontend/decode.cpp +++ b/src/shader_recompiler/frontend/decode.cpp @@ -654,7 +654,7 @@ void GcnDecodeContext::decodeInstructionVOP3(uint64_t hexInstruction) { OpcodeVOP3 vop3Op = static_cast(op); if (IsVop3BEncoding(m_instruction.opcode)) { - m_instruction.dst[1].field = OperandField::ScalarGPR; + m_instruction.dst[1].field = getOperandField(sdst); m_instruction.dst[1].type = ScalarType::Uint64; m_instruction.dst[1].code = sdst; } else { From 21eb175aa10a5939a4eb306143af5713e3d80cee Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 10 Oct 2024 13:14:50 -0700 Subject: [PATCH 40/93] shader_recompiler: Add asserts for get/set register bounds. (#1336) --- src/shader_recompiler/ir/ir_emitter.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index e1b0eeed5..cfd044f9e 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -130,19 +130,23 @@ void IREmitter::DeviceMemoryBarrier() { } U32 IREmitter::GetUserData(IR::ScalarReg reg) { + ASSERT(static_cast(reg) < IR::NumScalarRegs); return Inst(Opcode::GetUserData, reg); } U1 IREmitter::GetThreadBitScalarReg(IR::ScalarReg reg) { + ASSERT(static_cast(reg) < IR::NumScalarRegs); return Inst(Opcode::GetThreadBitScalarReg, reg); } void IREmitter::SetThreadBitScalarReg(IR::ScalarReg reg, const U1& value) { + ASSERT(static_cast(reg) < IR::NumScalarRegs); Inst(Opcode::SetThreadBitScalarReg, reg, value); } template <> U32 IREmitter::GetScalarReg(IR::ScalarReg reg) { + ASSERT(static_cast(reg) < IR::NumScalarRegs); return Inst(Opcode::GetScalarRegister, reg); } @@ -153,6 +157,7 @@ F32 IREmitter::GetScalarReg(IR::ScalarReg reg) { template <> U32 IREmitter::GetVectorReg(IR::VectorReg reg) { + ASSERT(static_cast(reg) < IR::NumVectorRegs); return Inst(Opcode::GetVectorRegister, reg); } @@ -162,11 +167,13 @@ F32 IREmitter::GetVectorReg(IR::VectorReg reg) { } void IREmitter::SetScalarReg(IR::ScalarReg reg, const U32F32& value) { + ASSERT(static_cast(reg) < IR::NumScalarRegs); const U32 value_typed = value.Type() == Type::F32 ? BitCast(F32{value}) : U32{value}; Inst(Opcode::SetScalarRegister, reg, value_typed); } void IREmitter::SetVectorReg(IR::VectorReg reg, const U32F32& value) { + ASSERT(static_cast(reg) < IR::NumVectorRegs); const U32 value_typed = value.Type() == Type::F32 ? BitCast(F32{value}) : U32{value}; Inst(Opcode::SetVectorRegister, reg, value_typed); } From 2f80d7565de3c93b0813b360817d5f152e5c0e24 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 10 Oct 2024 13:30:09 -0700 Subject: [PATCH 41/93] resource_tracking_pass: Fix type handling of sample offsets. (#1337) --- src/shader_recompiler/ir/passes/resource_tracking_pass.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 0cb6bff29..21f2115d5 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -507,7 +507,11 @@ void PatchImageSampleInstruction(IR::Block& block, IR::Inst& inst, Info& info, } // The offsets are six-bit signed integers: X=[5:0], Y=[13:8], and Z=[21:16]. - const IR::Value arg = get_addr_reg(addr_reg++); + IR::Value arg = get_addr_reg(addr_reg++); + if (const IR::Inst* offset_inst = arg.TryInstRecursive()) { + ASSERT(offset_inst->GetOpcode() == IR::Opcode::BitCastF32U32); + arg = offset_inst->Arg(0); + } const auto read = [&](u32 off) -> IR::U32 { if (arg.IsImmediate()) { From 0f9166166074be844bce88bf25f6cf6cef1a77a0 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 10 Oct 2024 13:58:01 -0700 Subject: [PATCH 42/93] resource_tracking_pass: Make sure immediate offset is accessed as correct type. (#1339) --- src/shader_recompiler/ir/passes/resource_tracking_pass.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 21f2115d5..0d91badda 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -515,7 +515,9 @@ void PatchImageSampleInstruction(IR::Block& block, IR::Inst& inst, Info& info, const auto read = [&](u32 off) -> IR::U32 { if (arg.IsImmediate()) { - const u16 comp = (arg.U32() >> off) & 0x3F; + const u32 imm = + arg.Type() == IR::Type::F32 ? std::bit_cast(arg.F32()) : arg.U32(); + const u16 comp = (imm >> off) & 0x3F; return ir.Imm32(s32(comp << 26) >> 26); } return ir.BitFieldExtract(IR::U32{arg}, ir.Imm32(off), ir.Imm32(6), true); From 66f1bb937f056a899d7e7e6be8090fe98b194d0f Mon Sep 17 00:00:00 2001 From: Lander Gallastegi Date: Fri, 11 Oct 2024 03:53:32 +0200 Subject: [PATCH 43/93] ime_dialog: Initial implementation (#1267) * Add C string types * Prepare existing enums * Added missing enums * Types update * State base * Compile Ime Dialog UI * UI implementation * Scoped lock * Functional implementation * Link against iconv on macOS * Fix building on windows * Better UI * clang-format * Some fixes and cleanup * Enable reserved checks * clang-format * Fix default text encoding max size * clang-format (again) * Some review changes * Use std::vector for dynamic strings * Use CString * Accept dialog on enter press * clang-format * Use ImGUI for encoding/decoding --- CMakeLists.txt | 2 + src/common/cstring.h | 22 +- src/core/libraries/dialogs/ime_dialog.cpp | 207 ++++++++-- src/core/libraries/dialogs/ime_dialog.h | 280 +++++++------ src/core/libraries/dialogs/ime_dialog_ui.cpp | 390 +++++++++++++++++++ src/core/libraries/dialogs/ime_dialog_ui.h | 84 ++++ src/imgui/imgui_config.h | 5 +- 7 files changed, 847 insertions(+), 143 deletions(-) mode change 100644 => 100755 CMakeLists.txt create mode 100644 src/core/libraries/dialogs/ime_dialog_ui.cpp create mode 100644 src/core/libraries/dialogs/ime_dialog_ui.h diff --git a/CMakeLists.txt b/CMakeLists.txt old mode 100644 new mode 100755 index 8a41e1ac9..7c58af6bb --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -297,6 +297,8 @@ set(LIBC_SOURCES src/core/libraries/libc_internal/libc_internal.cpp set(DIALOGS_LIB src/core/libraries/dialogs/error_dialog.cpp src/core/libraries/dialogs/error_dialog.h + src/core/libraries/dialogs/ime_dialog_ui.cpp + src/core/libraries/dialogs/ime_dialog_ui.h src/core/libraries/dialogs/ime_dialog.cpp src/core/libraries/dialogs/ime_dialog.h src/core/libraries/dialogs/error_codes.h diff --git a/src/common/cstring.h b/src/common/cstring.h index fb29443ee..45c291c14 100644 --- a/src/common/cstring.h +++ b/src/common/cstring.h @@ -81,34 +81,42 @@ public: return std::basic_string_view{data}; } - char* begin() { + T* begin() { if (this == nullptr) { return nullptr; } return data; } - const char* begin() const { + const T* begin() const { if (this == nullptr) { return nullptr; } return data; } - char* end() { + T* end() { if (this == nullptr) { return nullptr; } return data + N; } - const char* end() const { + const T* end() const { if (this == nullptr) { return nullptr; } return data + N; } + constexpr std::size_t capacity() const { + return N; + } + + std::size_t size() const { + return std::char_traits::length(data); + } + T& operator[](size_t idx) { return data[idx]; } @@ -152,6 +160,12 @@ public: static_assert(sizeof(CString<13>) == sizeof(char[13])); // Ensure size still matches a simple array static_assert(std::weakly_incrementable::Iterator>); +template +using CWString = CString; + +template +using CU16String = CString; + #pragma clang diagnostic pop } // namespace Common \ No newline at end of file diff --git a/src/core/libraries/dialogs/ime_dialog.cpp b/src/core/libraries/dialogs/ime_dialog.cpp index ddb1a89f8..3d168bb79 100644 --- a/src/core/libraries/dialogs/ime_dialog.cpp +++ b/src/core/libraries/dialogs/ime_dialog.cpp @@ -1,28 +1,75 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "ime_dialog.h" +#include "ime_dialog_ui.h" + +static constexpr std::array MAX_X_POSITIONS = {3840.0f, 1920.0f}; +static constexpr std::array MAX_Y_POSITIONS = {2160.0f, 1080.0f}; namespace Libraries::ImeDialog { -static OrbisImeDialogStatus g_ime_dlg_status = OrbisImeDialogStatus::ORBIS_IME_DIALOG_STATUS_NONE; +static OrbisImeDialogStatus g_ime_dlg_status = OrbisImeDialogStatus::NONE; +static OrbisImeDialogResult g_ime_dlg_result{}; +static ImeDialogState g_ime_dlg_state{}; +static ImeDialogUi g_ime_dlg_ui; -int PS4_SYSV_ABI sceImeDialogAbort() { - LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); - return ORBIS_OK; +static bool IsValidOption(OrbisImeDialogOption option, OrbisImeType type) { + if (False(~option & + (OrbisImeDialogOption::MULTILINE | OrbisImeDialogOption::NO_AUTO_COMPLETION))) { + return false; + } + + if (True(option & OrbisImeDialogOption::MULTILINE) && type != OrbisImeType::DEFAULT && + type != OrbisImeType::BASIC_LATIN) { + return false; + } + + if (True(option & OrbisImeDialogOption::NO_AUTO_COMPLETION) && type != OrbisImeType::NUMBER && + type != OrbisImeType::BASIC_LATIN) { + return false; + } + + return true; } -int PS4_SYSV_ABI sceImeDialogForceClose() { - LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); - return ORBIS_OK; +Error PS4_SYSV_ABI sceImeDialogAbort() { + if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + LOG_INFO(Lib_ImeDialog, "IME dialog not in use"); + return Error::DIALOG_NOT_IN_USE; + } + + if (g_ime_dlg_status != OrbisImeDialogStatus::RUNNING) { + LOG_INFO(Lib_ImeDialog, "IME dialog not running"); + return Error::DIALOG_NOT_RUNNING; + } + + g_ime_dlg_status = OrbisImeDialogStatus::FINISHED; + g_ime_dlg_result.endstatus = OrbisImeDialogEndStatus::ABORTED; + + return Error::OK; } -int PS4_SYSV_ABI sceImeDialogForTestFunction() { - LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); - return ORBIS_OK; +Error PS4_SYSV_ABI sceImeDialogForceClose() { + if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + LOG_INFO(Lib_ImeDialog, "IME dialog not in use"); + return Error::DIALOG_NOT_IN_USE; + } + + g_ime_dlg_status = OrbisImeDialogStatus::NONE; + g_ime_dlg_ui = ImeDialogUi(); + g_ime_dlg_state = ImeDialogState(); + + return Error::OK; +} + +Error PS4_SYSV_ABI sceImeDialogForTestFunction() { + return Error::INTERNAL; } int PS4_SYSV_ABI sceImeDialogGetCurrentStarState() { @@ -45,26 +92,118 @@ int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended() { return ORBIS_OK; } -int PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result) { - result->endstatus = OrbisImeDialogEndStatus::ORBIS_IME_DIALOG_END_STATUS_OK; - LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); - return ORBIS_OK; +Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result) { + if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + LOG_INFO(Lib_ImeDialog, "IME dialog is not running"); + return Error::DIALOG_NOT_IN_USE; + } + + if (result == nullptr) { + LOG_INFO(Lib_ImeDialog, "called with result (NULL)"); + return Error::INVALID_ADDRESS; + } + + result->endstatus = g_ime_dlg_result.endstatus; + + if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) { + return Error::DIALOG_NOT_FINISHED; + } + + g_ime_dlg_state.CopyTextToOrbisBuffer(); + return Error::OK; } -int PS4_SYSV_ABI sceImeDialogGetStatus() { - if (g_ime_dlg_status == OrbisImeDialogStatus::ORBIS_IME_DIALOG_STATUS_RUNNING) { - return OrbisImeDialogStatus::ORBIS_IME_DIALOG_STATUS_FINISHED; +OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus() { + if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) { + g_ime_dlg_state.CallTextFilter(); } + return g_ime_dlg_status; } -int PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended) { - LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); - const std::wstring_view text = L"shadPS4"; - param->maxTextLength = text.size(); - std::memcpy(param->inputTextBuffer, text.data(), text.size() * sizeof(wchar_t)); - g_ime_dlg_status = OrbisImeDialogStatus::ORBIS_IME_DIALOG_STATUS_RUNNING; - return ORBIS_OK; +Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended) { + if (g_ime_dlg_status != OrbisImeDialogStatus::NONE) { + LOG_INFO(Lib_ImeDialog, "IME dialog is already running"); + return Error::BUSY; + } + + if (param == nullptr) { + LOG_INFO(Lib_ImeDialog, "called with param (NULL)"); + return Error::INVALID_ADDRESS; + } + + if (!magic_enum::enum_contains(param->type)) { + LOG_INFO(Lib_ImeDialog, "Invalid param->type"); + return Error::INVALID_ADDRESS; + } + + // TODO: do correct param->option validation + // TODO: do correct param->supportedLanguages validation + + if (param->posx < 0.0f || + param->posx >= + MAX_X_POSITIONS[False(param->option & OrbisImeDialogOption::LARGE_RESOLUTION)]) { + LOG_INFO(Lib_ImeDialog, "Invalid param->posx"); + return Error::INVALID_POSX; + } + + if (param->posy < 0.0f || + param->posy >= + MAX_Y_POSITIONS[False(param->option & OrbisImeDialogOption::LARGE_RESOLUTION)]) { + LOG_INFO(Lib_ImeDialog, "Invalid param->posy"); + return Error::INVALID_POSY; + } + + if (!magic_enum::enum_contains(param->horizontalAlignment)) { + LOG_INFO(Lib_ImeDialog, "Invalid param->horizontalAlignment"); + return Error::INVALID_HORIZONTALIGNMENT; + } + + if (!magic_enum::enum_contains(param->verticalAlignment)) { + LOG_INFO(Lib_ImeDialog, "Invalid param->verticalAlignment"); + return Error::INVALID_VERTICALALIGNMENT; + } + + if (!IsValidOption(param->option, param->type)) { + LOG_INFO(Lib_ImeDialog, "Invalid param->option"); + return Error::INVALID_PARAM; + } + + if (param->inputTextBuffer == nullptr) { + LOG_INFO(Lib_ImeDialog, "Invalid param->inputTextBuffer"); + return Error::INVALID_INPUT_TEXT_BUFFER; + } + + if (extended) { + if (magic_enum::enum_contains(extended->priority)) { + LOG_INFO(Lib_ImeDialog, "Invalid extended->priority"); + return Error::INVALID_EXTENDED; + } + + // TODO: do correct extended->option validation + + if ((extended->extKeyboardMode & 0xe3fffffc) != 0) { + LOG_INFO(Lib_ImeDialog, "Invalid extended->extKeyboardMode"); + return Error::INVALID_EXTENDED; + } + + if (extended->disableDevice > 7) { + LOG_INFO(Lib_ImeDialog, "Invalid extended->disableDevice"); + return Error::INVALID_EXTENDED; + } + } + + if (param->maxTextLength > ORBIS_IME_DIALOG_MAX_TEXT_LENGTH) { + LOG_INFO(Lib_ImeDialog, "Invalid param->maxTextLength"); + return Error::INVALID_MAX_TEXT_LENGTH; + } + + g_ime_dlg_result = {}; + g_ime_dlg_state = ImeDialogState(param, extended); + g_ime_dlg_status = OrbisImeDialogStatus::RUNNING; + g_ime_dlg_ui = ImeDialogUi(&g_ime_dlg_state, &g_ime_dlg_status, &g_ime_dlg_result); + + return Error::OK; } int PS4_SYSV_ABI sceImeDialogInitInternal() { @@ -87,10 +226,22 @@ int PS4_SYSV_ABI sceImeDialogSetPanelPosition() { return ORBIS_OK; } -int PS4_SYSV_ABI sceImeDialogTerm() { - LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); - g_ime_dlg_status = OrbisImeDialogStatus::ORBIS_IME_DIALOG_STATUS_NONE; - return ORBIS_OK; +Error PS4_SYSV_ABI sceImeDialogTerm() { + if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + LOG_INFO(Lib_ImeDialog, "IME dialog not in use"); + return Error::DIALOG_NOT_IN_USE; + } + + if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) { + LOG_INFO(Lib_ImeDialog, "IME dialog is still running"); + return Error::DIALOG_NOT_FINISHED; + } + + g_ime_dlg_status = OrbisImeDialogStatus::NONE; + g_ime_dlg_ui = ImeDialogUi(); + g_ime_dlg_state = ImeDialogState(); + + return Error::OK; } void RegisterlibSceImeDialog(Core::Loader::SymbolsResolver* sym) { diff --git a/src/core/libraries/dialogs/ime_dialog.h b/src/core/libraries/dialogs/ime_dialog.h index ffe42b31a..66cf9fb93 100644 --- a/src/core/libraries/dialogs/ime_dialog.h +++ b/src/core/libraries/dialogs/ime_dialog.h @@ -3,6 +3,7 @@ #pragma once +#include "common/enum.h" #include "common/types.h" namespace Core::Loader { @@ -11,71 +12,150 @@ class SymbolsResolver; namespace Libraries::ImeDialog { -enum OrbisImeDialogStatus { - ORBIS_IME_DIALOG_STATUS_NONE = 0, - ORBIS_IME_DIALOG_STATUS_RUNNING = 1, - ORBIS_IME_DIALOG_STATUS_FINISHED = 2 +constexpr u32 ORBIS_IME_DIALOG_MAX_TEXT_LENGTH = 0x78; + +enum class Error : u32 { + OK = 0x0, + BUSY = 0x80bc0001, + NOT_OPENED = 0x80bc0002, + NO_MEMORY = 0x80bc0003, + CONNECTION_FAILED = 0x80bc0004, + TOO_MANY_REQUESTS = 0x80bc0005, + INVALID_TEXT = 0x80bc0006, + EVENT_OVERFLOW = 0x80bc0007, + NOT_ACTIVE = 0x80bc0008, + IME_SUSPENDING = 0x80bc0009, + DEVICE_IN_USE = 0x80bc000a, + INVALID_USER_ID = 0x80bc0010, + INVALID_TYPE = 0x80bc0011, + INVALID_SUPPORTED_LANGUAGES = 0x80bc0012, + INVALID_ENTER_LABEL = 0x80bc0013, + INVALID_INPUT_METHOD = 0x80bc0014, + INVALID_OPTION = 0x80bc0015, + INVALID_MAX_TEXT_LENGTH = 0x80bc0016, + INVALID_INPUT_TEXT_BUFFER = 0x80bc0017, + INVALID_POSX = 0x80bc0018, + INVALID_POSY = 0x80bc0019, + INVALID_HORIZONTALIGNMENT = 0x80bc001a, + INVALID_VERTICALALIGNMENT = 0x80bc001b, + INVALID_EXTENDED = 0x80bc001c, + INVALID_KEYBOARD_TYPE = 0x80bc001d, + INVALID_WORK = 0x80bc0020, + INVALID_ARG = 0x80bc0021, + INVALID_HANDLER = 0x80bc0022, + NO_RESOURCE_ID = 0x80bc0023, + INVALID_MODE = 0x80bc0024, + INVALID_PARAM = 0x80bc0030, + INVALID_ADDRESS = 0x80bc0031, + INVALID_RESERVED = 0x80bc0032, + INVALID_TIMING = 0x80bc0033, + INTERNAL = 0x80bc00ff, + DIALOG_INVALID_TITLE = 0x80bc0101, + DIALOG_NOT_RUNNING = 0x80bc0105, + DIALOG_NOT_FINISHED = 0x80bc0106, + DIALOG_NOT_IN_USE = 0x80bc0107, }; -enum OrbisImeDialogEndStatus { - ORBIS_IME_DIALOG_END_STATUS_OK = 0, - ORBIS_IME_DIALOG_END_STATUS_USER_CANCELED = 1, - ORBIS_IME_DIALOG_END_STATUS_ABORTED = 2 +enum class OrbisImeDialogStatus : u32 { + NONE = 0, + RUNNING = 1, + FINISHED = 2, }; -struct OrbisImeDialogResult { - OrbisImeDialogEndStatus endstatus; - s32 reserved[12]; +enum class OrbisImeDialogEndStatus : u32 { + OK = 0, + USER_CANCELED = 1, + ABORTED = 2, }; -enum OrbisImeType { - ORBIS_IME_TYPE_DEFAULT = 0, - ORBIS_IME_TYPE_BASIC_LATIN = 1, - ORBIS_IME_TYPE_URL = 2, - ORBIS_IME_TYPE_MAIL = 3, - ORBIS_IME_TYPE_NUMBER = 4 +enum class OrbisImeType : u32 { + DEFAULT = 0, + BASIC_LATIN = 1, + URL = 2, + MAIL = 3, + NUMBER = 4, }; -enum OrbisImeEnterLabel { - ORBIS_IME_ENTER_LABEL_DEFAULT = 0, - ORBIS_IME_ENTER_LABEL_SEND = 1, - ORBIS_IME_ENTER_LABEL_SEARCH = 2, - ORBIS_IME_ENTER_LABEL_GO = 3 -}; -enum OrbiImeInputMethod { ORBIS_IME_INPUT_METHOD_DEFAULT = 0 }; - -typedef int (*OrbisImeTextFilter)(wchar_t* outText, u32* outTextLength, const wchar_t* srcText, - u32 srcTextLength); - -enum OrbisImeHorizontalAlignment { - ORBIS_IME_HALIGN_LEFT = 0, - ORBIS_IME_HALIGN_CENTER = 1, - ORBIS_IME_HALIGN_RIGHT = 2 +enum class OrbisImeEnterLabel : u32 { + DEFAULT = 0, + SEND = 1, + SEARCH = 2, + GO = 3, }; -enum OrbisImeVerticalAlignment { - ORBIS_IME_VALIGN_TOP = 0, - ORBIS_IME_VALIGN_CENTER = 1, - ORBIS_IME_VALIGN_BOTTOM = 2 +enum class OrbisImeDialogOption : u32 { + DEFAULT = 0, + MULTILINE = 1, + NO_AUTO_CORRECTION = 2, + NO_AUTO_COMPLETION = 4, + // TODO: Document missing options + LARGE_RESOLUTION = 1024, }; -struct OrbisImeDialogParam { - s32 userId; - OrbisImeType type; - u64 supportedLanguages; - OrbisImeEnterLabel enterLabel; - OrbiImeInputMethod inputMethod; - OrbisImeTextFilter filter; - u32 option; - u32 maxTextLength; - wchar_t* inputTextBuffer; - float posx; - float posy; - OrbisImeHorizontalAlignment horizontalAlignment; - OrbisImeVerticalAlignment verticalAlignment; - const wchar_t* placeholder; - const wchar_t* title; - s8 reserved[16]; +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 { + DEFAULT = 0, + ALPHABET = 1, + SYMBOL = 2, + 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 { @@ -85,57 +165,14 @@ struct OrbisImeColor { u8 a; }; -enum OrbisImePanelPriority { - ORBIS_IME_PANEL_PRIORITY_DEFAULT = 0, - ORBIS_IME_PANEL_PRIORITY_ALPHABET = 1, - ORBIS_IME_PANEL_PRIORITY_SYMBOL = 2, - ORBIS_IME_PANEL_PRIORITY_ACCENT = 3 -}; - -enum OrbisImeKeyboardType { - ORBIS_IME_KEYBOARD_TYPE_NONE = 0, - ORBIS_IME_KEYBOARD_TYPE_DANISH = 1, - ORBIS_IME_KEYBOARD_TYPE_GERMAN = 2, - ORBIS_IME_KEYBOARD_TYPE_GERMAN_SW = 3, - ORBIS_IME_KEYBOARD_TYPE_ENGLISH_US = 4, - ORBIS_IME_KEYBOARD_TYPE_ENGLISH_GB = 5, - ORBIS_IME_KEYBOARD_TYPE_SPANISH = 6, - ORBIS_IME_KEYBOARD_TYPE_SPANISH_LA = 7, - ORBIS_IME_KEYBOARD_TYPE_FINNISH = 8, - ORBIS_IME_KEYBOARD_TYPE_FRENCH = 9, - ORBIS_IME_KEYBOARD_TYPE_FRENCH_BR = 10, - ORBIS_IME_KEYBOARD_TYPE_FRENCH_CA = 11, - ORBIS_IME_KEYBOARD_TYPE_FRENCH_SW = 12, - ORBIS_IME_KEYBOARD_TYPE_ITALIAN = 13, - ORBIS_IME_KEYBOARD_TYPE_DUTCH = 14, - ORBIS_IME_KEYBOARD_TYPE_NORWEGIAN = 15, - ORBIS_IME_KEYBOARD_TYPE_POLISH = 16, - ORBIS_IME_KEYBOARD_TYPE_PORTUGUESE_BR = 17, - ORBIS_IME_KEYBOARD_TYPE_PORTUGUESE_PT = 18, - ORBIS_IME_KEYBOARD_TYPE_RUSSIAN = 19, - ORBIS_IME_KEYBOARD_TYPE_SWEDISH = 20, - ORBIS_IME_KEYBOARD_TYPE_TURKISH = 21, - ORBIS_IME_KEYBOARD_TYPE_JAPANESE_ROMAN = 22, - ORBIS_IME_KEYBOARD_TYPE_JAPANESE_KANA = 23, - ORBIS_IME_KEYBOARD_TYPE_KOREAN = 24, - ORBIS_IME_KEYBOARD_TYPE_SM_CHINESE = 25, - ORBIS_IME_KEYBOARD_TYPE_TR_CHINESE_ZY = 26, - ORBIS_IME_KEYBOARD_TYPE_TR_CHINESE_PY_HK = 27, - ORBIS_IME_KEYBOARD_TYPE_TR_CHINESE_PY_TW = 28, - ORBIS_IME_KEYBOARD_TYPE_TR_CHINESE_CG = 29, - ORBIS_IME_KEYBOARD_TYPE_ARABIC_AR = 30, - ORBIS_IME_KEYBOARD_TYPE_THAI = 31, - ORBIS_IME_KEYBOARD_TYPE_CZECH = 32, - ORBIS_IME_KEYBOARD_TYPE_GREEK = 33, - ORBIS_IME_KEYBOARD_TYPE_INDONESIAN = 34, - ORBIS_IME_KEYBOARD_TYPE_VIETNAMESE = 35, - ORBIS_IME_KEYBOARD_TYPE_ROMANIAN = 36, - ORBIS_IME_KEYBOARD_TYPE_HUNGARIAN = 37 +struct OrbisImeDialogResult { + OrbisImeDialogEndStatus endstatus; + s32 reserved[12]; }; struct OrbisImeKeycode { u16 keycode; - wchar_t character; + char16_t character; u32 status; OrbisImeKeyboardType type; s32 userId; @@ -143,11 +180,34 @@ struct OrbisImeKeycode { u64 timestamp; }; -typedef int (*OrbisImeExtKeyboardFilter)(const OrbisImeKeycode* srcKeycode, u16* outKeycode, - u32* outStatus, void* reserved); +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, + u16* outKeycode, u32* outStatus, + void* reserved); + +struct OrbisImeDialogParam { + s32 userId; + OrbisImeType type; + u64 supportedLanguages; + OrbisImeEnterLabel enterLabel; + OrbisImeInputMethod inputMethod; + OrbisImeTextFilter filter; + OrbisImeDialogOption option; + u32 maxTextLength; + char16_t* inputTextBuffer; + float posx; + float posy; + OrbisImeHorizontalAlignment horizontalAlignment; + OrbisImeVerticalAlignment verticalAlignment; + const char16_t* placeholder; + const char16_t* title; + s8 reserved[16]; +}; struct OrbisImeParamExtended { - u32 option; + u32 option; // OrbisImeDialogOptionExtended OrbisImeColor colorBase; OrbisImeColor colorLine; OrbisImeColor colorTextField; @@ -165,21 +225,21 @@ struct OrbisImeParamExtended { int8_t reserved[60]; }; -int PS4_SYSV_ABI sceImeDialogAbort(); -int PS4_SYSV_ABI sceImeDialogForceClose(); -int PS4_SYSV_ABI sceImeDialogForTestFunction(); +Error PS4_SYSV_ABI sceImeDialogAbort(); +Error PS4_SYSV_ABI sceImeDialogForceClose(); +Error PS4_SYSV_ABI sceImeDialogForTestFunction(); int PS4_SYSV_ABI sceImeDialogGetCurrentStarState(); int PS4_SYSV_ABI sceImeDialogGetPanelPositionAndForm(); int PS4_SYSV_ABI sceImeDialogGetPanelSize(); int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended(); -int PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result); -/*OrbisImeDialogStatus*/ int PS4_SYSV_ABI sceImeDialogGetStatus(); -int PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended); +Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result); +OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus(); +Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended); int PS4_SYSV_ABI sceImeDialogInitInternal(); int PS4_SYSV_ABI sceImeDialogInitInternal2(); int PS4_SYSV_ABI sceImeDialogInitInternal3(); int PS4_SYSV_ABI sceImeDialogSetPanelPosition(); -int PS4_SYSV_ABI sceImeDialogTerm(); +Error PS4_SYSV_ABI sceImeDialogTerm(); void RegisterlibSceImeDialog(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::ImeDialog \ No newline at end of file diff --git a/src/core/libraries/dialogs/ime_dialog_ui.cpp b/src/core/libraries/dialogs/ime_dialog_ui.cpp new file mode 100644 index 000000000..48f5d75dc --- /dev/null +++ b/src/core/libraries/dialogs/ime_dialog_ui.cpp @@ -0,0 +1,390 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include + +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/singleton.h" +#include "core/libraries/dialogs/ime_dialog.h" +#include "core/libraries/dialogs/ime_dialog_ui.h" +#include "core/linker.h" +#include "imgui/imgui_std.h" + +using namespace ImGui; + +static constexpr ImVec2 BUTTON_SIZE{100.0f, 30.0f}; + +namespace Libraries::ImeDialog { + +ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param, + const OrbisImeParamExtended* extended) { + if (!param) + return; + + userId = param->userId; + is_multiLine = True(param->option & OrbisImeDialogOption::MULTILINE); + is_numeric = param->type == OrbisImeType::NUMBER; + type = param->type; + enter_label = param->enterLabel; + text_filter = param->filter; + keyboard_filter = extended ? extended->extKeyboardFilter : nullptr; + max_text_length = param->maxTextLength; + text_buffer = param->inputTextBuffer; + + if (param->title) { + std::size_t title_len = std::char_traits::length(param->title); + title.resize(title_len * 4 + 1); + title[title_len * 4] = '\0'; + + if (!ConvertOrbisToUTF8(param->title, title_len, &title[0], title_len * 4)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert title to utf8 encoding"); + } + } + + if (param->placeholder) { + std::size_t placeholder_len = std::char_traits::length(param->placeholder); + placeholder.resize(placeholder_len * 4 + 1); + placeholder[placeholder_len * 4] = '\0'; + + if (!ConvertOrbisToUTF8(param->placeholder, placeholder_len, &placeholder[0], + placeholder_len * 4)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert placeholder to utf8 encoding"); + } + } + + std::size_t text_len = std::char_traits::length(text_buffer); + if (!ConvertOrbisToUTF8(text_buffer, text_len, current_text.begin(), + ORBIS_IME_DIALOG_MAX_TEXT_LENGTH * 4)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert text to utf8 encoding"); + } +} + +ImeDialogState::ImeDialogState(ImeDialogState&& other) noexcept + : input_changed(other.input_changed), userId(other.userId), is_multiLine(other.is_multiLine), + is_numeric(other.is_numeric), type(other.type), enter_label(other.enter_label), + text_filter(other.text_filter), keyboard_filter(other.keyboard_filter), + max_text_length(other.max_text_length), text_buffer(other.text_buffer), + title(std::move(other.title)), placeholder(std::move(other.placeholder)), + current_text(other.current_text) { + + other.text_buffer = nullptr; +} + +ImeDialogState& ImeDialogState::operator=(ImeDialogState&& other) { + if (this != &other) { + input_changed = other.input_changed; + userId = other.userId; + is_multiLine = other.is_multiLine; + is_numeric = other.is_numeric; + type = other.type; + enter_label = other.enter_label; + text_filter = other.text_filter; + keyboard_filter = other.keyboard_filter; + max_text_length = other.max_text_length; + text_buffer = other.text_buffer; + title = std::move(other.title); + placeholder = std::move(other.placeholder); + current_text = other.current_text; + + other.text_buffer = nullptr; + } + + return *this; +} + +bool ImeDialogState::CopyTextToOrbisBuffer() { + if (!text_buffer) { + return false; + } + + return ConvertUTF8ToOrbis(current_text.begin(), current_text.capacity(), text_buffer, + max_text_length); +} + +bool ImeDialogState::CallTextFilter() { + if (!text_filter || !input_changed) { + return true; + } + + input_changed = false; + + char16_t src_text[ORBIS_IME_DIALOG_MAX_TEXT_LENGTH + 1] = {0}; + u32 src_text_length = current_text.size(); + char16_t out_text[ORBIS_IME_DIALOG_MAX_TEXT_LENGTH + 1] = {0}; + u32 out_text_length = ORBIS_IME_DIALOG_MAX_TEXT_LENGTH; + + if (!ConvertUTF8ToOrbis(current_text.begin(), src_text_length, src_text, + ORBIS_IME_DIALOG_MAX_TEXT_LENGTH)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert text to orbis encoding"); + return false; + } + + auto* linker = Common::Singleton::Instance(); + int ret = + linker->ExecuteGuest(text_filter, out_text, &out_text_length, src_text, src_text_length); + + if (ret != 0) { + return false; + } + + if (!ConvertOrbisToUTF8(out_text, out_text_length, current_text.begin(), + ORBIS_IME_DIALOG_MAX_TEXT_LENGTH * 4)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert text to utf8 encoding"); + return false; + } + + return true; +} + +bool ImeDialogState::CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16* out_keycode, + u32* out_status) { + if (!keyboard_filter) { + return true; + } + + auto* linker = Common::Singleton::Instance(); + int ret = linker->ExecuteGuest(keyboard_filter, src_keycode, out_keycode, out_status, nullptr); + + return ret == 0; +} + +bool ImeDialogState::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(orbis_text); + ImTextStrToUtf8(utf8_text, utf8_text_len, orbis_text_ptr, orbis_text_ptr + orbis_text_len); + + return true; +} + +bool ImeDialogState::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(orbis_text), orbis_text_len, utf8_text, nullptr); + + return true; +} + +ImeDialogUi::ImeDialogUi(ImeDialogState* state, OrbisImeDialogStatus* status, + OrbisImeDialogResult* result) + : state(state), status(status), result(result) { + + if (state && *status == OrbisImeDialogStatus::RUNNING) { + AddLayer(this); + } +} + +ImeDialogUi::~ImeDialogUi() { + std::scoped_lock lock(draw_mutex); + + Free(); +} + +ImeDialogUi::ImeDialogUi(ImeDialogUi&& other) noexcept + : state(other.state), status(other.status), result(other.result), + first_render(other.first_render) { + + std::scoped_lock lock(draw_mutex, other.draw_mutex); + other.state = nullptr; + other.status = nullptr; + other.result = nullptr; + + if (state && *status == OrbisImeDialogStatus::RUNNING) { + AddLayer(this); + } +} + +ImeDialogUi& ImeDialogUi::operator=(ImeDialogUi&& other) { + std::scoped_lock lock(draw_mutex, other.draw_mutex); + Free(); + + state = other.state; + status = other.status; + result = other.result; + first_render = other.first_render; + other.state = nullptr; + other.status = nullptr; + other.result = nullptr; + + if (state && *status == OrbisImeDialogStatus::RUNNING) { + AddLayer(this); + } + + return *this; +} + +void ImeDialogUi::Free() { + RemoveLayer(this); +} + +void ImeDialogUi::Draw() { + std::unique_lock lock{draw_mutex}; + + if (!state) { + return; + } + + if (!status || *status != OrbisImeDialogStatus::RUNNING) { + return; + } + + const auto& ctx = *GetCurrentContext(); + const auto& io = ctx.IO; + + ImVec2 window_size; + + if (state->is_multiLine) { + window_size = {500.0f, 300.0f}; + } else { + window_size = {500.0f, 150.0f}; + } + + CentralizeWindow(); + SetNextWindowSize(window_size); + SetNextWindowCollapsed(false); + + if (first_render || !io.NavActive) { + SetNextWindowFocus(); + } + + if (Begin("IME Dialog##ImeDialog", nullptr, + ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings)) { + DrawPrettyBackground(); + + if (!state->title.empty()) { + SetWindowFontScale(1.7f); + TextUnformatted(state->title.data()); + SetWindowFontScale(1.0f); + } + + if (state->is_multiLine) { + DrawMultiLineInputText(); + } else { + DrawInputText(); + } + + SetCursorPosY(GetCursorPosY() + 10.0f); + + const char* button_text; + + switch (state->enter_label) { + case OrbisImeEnterLabel::GO: + button_text = "Go##ImeDialogOK"; + break; + case OrbisImeEnterLabel::SEARCH: + button_text = "Search##ImeDialogOK"; + break; + case OrbisImeEnterLabel::SEND: + button_text = "Send##ImeDialogOK"; + break; + case OrbisImeEnterLabel::DEFAULT: + default: + button_text = "OK##ImeDialogOK"; + break; + } + + 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) || + (!state->is_multiLine && IsKeyPressed(ImGuiKey_Enter))) { + *status = OrbisImeDialogStatus::FINISHED; + result->endstatus = OrbisImeDialogEndStatus::OK; + } + + SameLine(0.0f, button_spacing); + + if (Button("Cancel##ImeDialogCancel", BUTTON_SIZE)) { + *status = OrbisImeDialogStatus::FINISHED; + result->endstatus = OrbisImeDialogEndStatus::USER_CANCELED; + } + } + End(); + + first_render = false; +} + +void ImeDialogUi::DrawInputText() { + ImVec2 input_size = {GetWindowWidth() - 40.0f, 0.0f}; + SetCursorPosX(20.0f); + if (first_render) { + SetKeyboardFocusHere(); + } + const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data(); + if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(), + state->max_text_length, input_size, ImGuiInputTextFlags_CallbackCharFilter, + InputTextCallback, this)) { + state->input_changed = true; + } +} + +void ImeDialogUi::DrawMultiLineInputText() { + ImVec2 input_size = {GetWindowWidth() - 40.0f, 200.0f}; + SetCursorPosX(20.0f); + ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackCharFilter | + static_cast(ImGuiInputTextFlags_Multiline); + if (first_render) { + SetKeyboardFocusHere(); + } + const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data(); + if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(), + state->max_text_length, input_size, flags, InputTextCallback, this)) { + state->input_changed = true; + } +} + +int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) { + ImeDialogUi* ui = static_cast(data->UserData); + + ASSERT(ui); + + // Should we filter punctuation? + if (ui->state->is_numeric && (data->EventChar < '0' || data->EventChar > '9') && + data->EventChar != '\b' && data->EventChar != ',' && data->EventChar != '.') { + return 1; + } + + if (!ui->state->keyboard_filter) { + return 0; + } + + // ImGui encodes ImWchar32 as multi-byte UTF-8 characters + char* event_char = reinterpret_cast(&data->EventChar); + + // Call the keyboard filter + OrbisImeKeycode src_keycode = { + .keycode = 0, + .character = 0, + .status = 1, // ??? 1 = key pressed, 0 = key released + .type = OrbisImeKeyboardType::ENGLISH_US, // TODO set this to the correct value (maybe use + // the current language?) + .userId = ui->state->userId, + .resourceId = 0, + .timestamp = 0}; + + if (!ui->state->ConvertUTF8ToOrbis(event_char, 4, &src_keycode.character, 1)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert orbis char to utf8"); + return 0; + } + src_keycode.keycode = src_keycode.character; // TODO set this to the correct value + + u16 out_keycode; + u32 out_status; + + ui->state->CallKeyboardFilter(&src_keycode, &out_keycode, &out_status); + + // TODO. set the keycode + + return 0; +} + +} // namespace Libraries::ImeDialog \ No newline at end of file diff --git a/src/core/libraries/dialogs/ime_dialog_ui.h b/src/core/libraries/dialogs/ime_dialog_ui.h new file mode 100644 index 000000000..96c83954a --- /dev/null +++ b/src/core/libraries/dialogs/ime_dialog_ui.h @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include "common/cstring.h" +#include "common/types.h" +#include "core/libraries/dialogs/ime_dialog.h" +#include "imgui/imgui_layer.h" + +namespace Libraries::ImeDialog { + +class ImeDialogUi; + +class ImeDialogState final { + friend ImeDialogUi; + + bool input_changed = false; + + s32 userId{}; + bool is_multiLine{}; + bool is_numeric{}; + OrbisImeType type{}; + OrbisImeEnterLabel enter_label{}; + OrbisImeTextFilter text_filter{}; + OrbisImeExtKeyboardFilter keyboard_filter{}; + u32 max_text_length{}; + char16_t* text_buffer{}; + std::vector title; + std::vector placeholder; + + // A character can hold up to 4 bytes in UTF-8 + Common::CString current_text; + +public: + ImeDialogState(const OrbisImeDialogParam* param = nullptr, + const OrbisImeParamExtended* extended = nullptr); + ImeDialogState(const ImeDialogState& other) = delete; + ImeDialogState(ImeDialogState&& other) noexcept; + ImeDialogState& operator=(ImeDialogState&& other); + + bool CopyTextToOrbisBuffer(); + bool CallTextFilter(); + +private: + bool CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16* out_keycode, u32* out_status); + + 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 ImeDialogUi final : public ImGui::Layer { + ImeDialogState* state{}; + OrbisImeDialogStatus* status{}; + OrbisImeDialogResult* result{}; + + bool first_render = true; + std::mutex draw_mutex; + +public: + explicit ImeDialogUi(ImeDialogState* state = nullptr, OrbisImeDialogStatus* status = nullptr, + OrbisImeDialogResult* result = nullptr); + ~ImeDialogUi() override; + ImeDialogUi(const ImeDialogUi& other) = delete; + ImeDialogUi(ImeDialogUi&& other) noexcept; + ImeDialogUi& operator=(ImeDialogUi&& other); + + void Draw() override; + +private: + void Free(); + + void DrawInputText(); + void DrawMultiLineInputText(); + + static int InputTextCallback(ImGuiInputTextCallbackData* data); +}; + +} // namespace Libraries::ImeDialog diff --git a/src/imgui/imgui_config.h b/src/imgui/imgui_config.h index 66d2b25c1..ccb084d94 100644 --- a/src/imgui/imgui_config.h +++ b/src/imgui/imgui_config.h @@ -21,7 +21,6 @@ extern void assert_fail_debug_msg(const char* msg); } \ }()) -#define IMGUI_USE_WCHAR32 #define IMGUI_ENABLE_STB_TRUETYPE #define IMGUI_DEFINE_MATH_OPERATORS @@ -30,3 +29,7 @@ extern void assert_fail_debug_msg(const char* msg); #define IM_VEC4_CLASS_EXTRA \ constexpr ImVec4(float _v) : x(_v), y(_v), z(_v), w(_v) {} + +#ifdef IMGUI_USE_WCHAR32 +#error "This project uses 16 bits wchar standard like Orbis" +#endif \ No newline at end of file From 04ad430115f3ccdf22d013d0c3e2efd38b444df1 Mon Sep 17 00:00:00 2001 From: Alexandre Bouvier Date: Fri, 11 Oct 2024 06:26:30 +0000 Subject: [PATCH 44/93] cmake: add install rules (#1341) --- CMakeLists.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c58af6bb..3f3d7fcff 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -872,3 +872,11 @@ endif() # Discord RPC target_link_libraries(shadps4 PRIVATE discord-rpc) + +# Install rules +install(TARGETS shadps4 BUNDLE DESTINATION .) + +if (ENABLE_QT_GUI AND CMAKE_SYSTEM_NAME STREQUAL "Linux") + install(FILES ".github/shadps4.desktop" DESTINATION "share/applications") + install(FILES ".github/shadps4.png" DESTINATION "share/icons/hicolor/512x512/apps") +endif() From dc99d3ebfc5ce69ed08a1b83129e99305851c1aa Mon Sep 17 00:00:00 2001 From: KrisCris <38860226+KrisCris@users.noreply.github.com> Date: Fri, 11 Oct 2024 14:27:28 +0800 Subject: [PATCH 45/93] Slightly refactor the game install dirs code (#1329) * fix: game dir dupes on each launch * fix copy around settings_addon_install_dir vector * do not show the path if it is not added * remove installDir from config file * format * moved migration code to save, and added notice * move ui->removeFolderButton->setEnabled to LoadValuesFromConfig * avoid checking duplicates for gameDirs loaded from config * use else condition to switch to the installDirs --- src/common/config.cpp | 38 ++++++++++++++++++------------ src/common/config.h | 5 ++-- src/qt_gui/game_install_dialog.cpp | 5 +--- src/qt_gui/settings_dialog.cpp | 24 ++++--------------- 4 files changed, 32 insertions(+), 40 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 37e51c655..78da372e7 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -325,9 +325,19 @@ void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) { main_window_geometry_w = w; main_window_geometry_h = h; } -void setGameInstallDirs(const std::vector& dir) { - settings_install_dirs.resize(dir.size()); - settings_install_dirs = dir; +bool addGameInstallDir(const std::filesystem::path& dir) { + if (std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir) == + settings_install_dirs.end()) { + settings_install_dirs.push_back(dir); + return true; + } + return false; +} +void removeGameInstallDir(const std::filesystem::path& dir) { + auto iterator = std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir); + if (iterator != settings_install_dirs.end()) { + settings_install_dirs.erase(iterator); + } } void setAddonInstallDir(const std::filesystem::path& dir) { settings_addon_install_dir = dir; @@ -385,7 +395,7 @@ u32 getMainWindowGeometryW() { u32 getMainWindowGeometryH() { return main_window_geometry_h; } -std::vector getGameInstallDirs() { +const std::vector& getGameInstallDirs() { return settings_install_dirs; } std::filesystem::path getAddonInstallDir() { @@ -525,19 +535,14 @@ void load(const std::filesystem::path& path) { m_window_size_W = toml::find_or(gui, "mw_width", 0); m_window_size_H = toml::find_or(gui, "mw_height", 0); + // TODO Migration code, after a major release this should be removed. auto old_game_install_dir = toml::find_fs_path_or(gui, "installDir", {}); if (!old_game_install_dir.empty()) { - settings_install_dirs.push_back(old_game_install_dir); - data.as_table().erase("installDir"); - } - - const auto install_dir_array = - toml::find_or>(gui, "installDirs", {}); - for (const auto& dir : install_dir_array) { - bool not_already_included = - std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir) == - settings_install_dirs.end(); - if (not_already_included) { + settings_install_dirs.emplace_back(std::filesystem::path{old_game_install_dir}); + } else { + const auto install_dir_array = + toml::find_or>(gui, "installDirs", {}); + for (const auto& dir : install_dir_array) { settings_install_dirs.emplace_back(std::filesystem::path{dir}); } } @@ -639,6 +644,9 @@ void save(const std::filesystem::path& path) { data["Settings"]["consoleLanguage"] = m_language; + // TODO Migration code, after a major release this should be removed. + data.at("GUI").as_table().erase("installDir"); + std::ofstream file(path, std::ios::out); file << data; file.close(); diff --git a/src/common/config.h b/src/common/config.h index 8e799b55d..591d6dced 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -84,7 +84,8 @@ bool vkCrashDiagnosticEnabled(); // Gui void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h); -void setGameInstallDirs(const std::vector& dir); +bool addGameInstallDir(const std::filesystem::path& dir); +void removeGameInstallDir(const std::filesystem::path& dir); void setAddonInstallDir(const std::filesystem::path& dir); void setMainWindowTheme(u32 theme); void setIconSize(u32 size); @@ -103,7 +104,7 @@ u32 getMainWindowGeometryX(); u32 getMainWindowGeometryY(); u32 getMainWindowGeometryW(); u32 getMainWindowGeometryH(); -std::vector getGameInstallDirs(); +const std::vector& getGameInstallDirs(); std::filesystem::path getAddonInstallDir(); u32 getMainWindowTheme(); u32 getIconSize(); diff --git a/src/qt_gui/game_install_dialog.cpp b/src/qt_gui/game_install_dialog.cpp index 4418d9a48..e53c58315 100644 --- a/src/qt_gui/game_install_dialog.cpp +++ b/src/qt_gui/game_install_dialog.cpp @@ -126,10 +126,7 @@ void GameInstallDialog::Save() { return; } } - - std::vector install_dirs; - install_dirs.emplace_back(Common::FS::PathFromQString(gamesDirectory)); - Config::setGameInstallDirs(install_dirs); + Config::addGameInstallDir(Common::FS::PathFromQString(gamesDirectory)); Config::setAddonInstallDir(Common::FS::PathFromQString(addonsDirectory)); const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); Config::save(config_dir / "config.toml"); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 1cc5a85e4..efc2cd3eb 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -218,19 +218,12 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge // PATH TAB { - ui->removeFolderButton->setEnabled(false); - connect(ui->addFolderButton, &QPushButton::clicked, this, [this]() { const auto config_dir = Config::getGameInstallDirs(); QString file_path_string = QFileDialog::getExistingDirectory(this, tr("Directory to install games")); auto file_path = Common::FS::PathFromQString(file_path_string); - bool not_already_included = - std::find(config_dir.begin(), config_dir.end(), file_path) == config_dir.end(); - if (!file_path.empty() && not_already_included) { - std::vector install_dirs = config_dir; - install_dirs.push_back(file_path); - Config::setGameInstallDirs(install_dirs); + if (!file_path.empty() && Config::addGameInstallDir(file_path)) { QListWidgetItem* item = new QListWidgetItem(file_path_string); ui->gameFoldersListWidget->addItem(item); } @@ -246,17 +239,8 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge QString item_path_string = selected_item ? selected_item->text() : QString(); if (!item_path_string.isEmpty()) { auto file_path = Common::FS::PathFromQString(item_path_string); - std::vector install_dirs = Config::getGameInstallDirs(); - - auto iterator = std::remove_if( - install_dirs.begin(), install_dirs.end(), - [&file_path](const std::filesystem::path& dir) { return file_path == dir; }); - - if (iterator != install_dirs.end()) { - install_dirs.erase(iterator, install_dirs.end()); - delete selected_item; - } - Config::setGameInstallDirs(install_dirs); + Config::removeGameInstallDir(file_path); + delete selected_item; } }); } @@ -370,6 +354,8 @@ void SettingsDialog::LoadValuesFromConfig() { QString backButtonBehavior = QString::fromStdString(Config::getBackButtonBehavior()); int index = ui->backButtonBehaviorComboBox->findData(backButtonBehavior); ui->backButtonBehaviorComboBox->setCurrentIndex(index != -1 ? index : 0); + + ui->removeFolderButton->setEnabled(!ui->gameFoldersListWidget->selectedItems().isEmpty()); } void SettingsDialog::InitializeEmulatorLanguages() { From dbdbb40c0bbe68215b9239efabc0a7b10893ede8 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Fri, 11 Oct 2024 10:37:36 +0300 Subject: [PATCH 46/93] Network libs fixes , stubs and more (#1324) * some rework on netctl calls * added libs stubs using from RE6 * added ORBIS_NET_CTL_INFO_IP_ADDRESS case in sceNetCtlGetInfo * added sceNetInetPton * some dummy functions and change some functions to debug level * clang format fix * fix for sceNetInetPton * posix OS fix? * linux + macOS fixes? * fix? * sceSharePlayGetCurrentConnectionInfo --- CMakeLists.txt | 8 + src/common/logging/filter.cpp | 4 + src/common/logging/types.h | 128 ++++--- .../game_live_streaming/gamelivestreaming.cpp | 354 ++++++++++++++++++ .../game_live_streaming/gamelivestreaming.h | 81 ++++ src/core/libraries/ime/ime.cpp | 340 +++++++++++++++++ src/core/libraries/ime/ime.h | 70 ++++ src/core/libraries/libs.cpp | 9 +- src/core/libraries/network/net.cpp | 13 +- src/core/libraries/network/net.h | 2 +- src/core/libraries/network/net_ctl_codes.h | 8 + src/core/libraries/network/net_obj.cpp | 0 src/core/libraries/network/net_obj.h | 0 src/core/libraries/network/netctl.cpp | 39 +- src/core/libraries/network/netctl.h | 1 + src/core/libraries/pad/pad.cpp | 2 +- src/core/libraries/remote_play/remoteplay.cpp | 309 +++++++++++++++ src/core/libraries/remote_play/remoteplay.h | 62 +++ src/core/libraries/share_play/shareplay.cpp | 186 +++++++++ src/core/libraries/share_play/shareplay.h | 54 +++ src/core/libraries/system/userservice.cpp | 2 +- 21 files changed, 1599 insertions(+), 73 deletions(-) create mode 100644 src/core/libraries/game_live_streaming/gamelivestreaming.cpp create mode 100644 src/core/libraries/game_live_streaming/gamelivestreaming.h create mode 100644 src/core/libraries/ime/ime.cpp create mode 100644 src/core/libraries/ime/ime.h create mode 100644 src/core/libraries/network/net_obj.cpp create mode 100644 src/core/libraries/network/net_obj.h create mode 100644 src/core/libraries/remote_play/remoteplay.cpp create mode 100644 src/core/libraries/remote_play/remoteplay.h create mode 100644 src/core/libraries/share_play/shareplay.cpp create mode 100644 src/core/libraries/share_play/shareplay.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f3d7fcff..37021746d 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -282,6 +282,14 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/audio3d/audio3d_error.h src/core/libraries/audio3d/audio3d_impl.cpp 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.h + src/core/libraries/remote_play/remoteplay.cpp + src/core/libraries/remote_play/remoteplay.h + src/core/libraries/share_play/shareplay.cpp + src/core/libraries/share_play/shareplay.h ) set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 2ff2a9003..051bbd79e 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -114,6 +114,10 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, AvPlayer) \ SUB(Lib, Ngs2) \ SUB(Lib, Audio3d) \ + SUB(Lib, Ime) \ + SUB(Lib, GameLiveStreaming) \ + SUB(Lib, Remoteplay) \ + SUB(Lib, SharePlay) \ SUB(Lib, Fiber) \ CLS(Frontend) \ CLS(Render) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 54388e590..39319d0dc 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -29,68 +29,72 @@ enum class Level : u8 { * filter.cpp. */ enum class Class : u8 { - Log, ///< Messages about the log system itself - Common, ///< Library routines - Common_Filesystem, ///< Filesystem interface library - Common_Memory, ///< Memory mapping and management functions - Core, ///< LLE emulation core - Core_Linker, ///< The module linker - Config, ///< Emulator configuration (including commandline) - Debug, ///< Debugging tools - Kernel, ///< The HLE implementation of the PS4 kernel. - Kernel_Pthread, ///< The pthread implementation of the kernel. - Kernel_Fs, ///< The filesystem implementation of the kernel. - Kernel_Vmm, ///< The virtual memory implementation of the kernel. - Kernel_Event, ///< The event management implementation of the kernel. - Kernel_Sce, ///< The sony specific interfaces provided by the kernel. - Lib, ///< HLE implementation of system library. Each major library - ///< should have its own subclass. - Lib_LibC, ///< The LibC implementation. - Lib_Kernel, ///< The LibKernel implementation. - Lib_Pad, ///< The LibScePad implementation. - Lib_GnmDriver, ///< The LibSceGnmDriver implementation. - Lib_SystemService, ///< The LibSceSystemService implementation. - Lib_UserService, ///< The LibSceUserService implementation. - Lib_VideoOut, ///< The LibSceVideoOut implementation. - Lib_CommonDlg, ///< The LibSceCommonDialog implementation. - Lib_MsgDlg, ///< The LibSceMsgDialog implementation. - Lib_AudioOut, ///< The LibSceAudioOut implementation. - Lib_AudioIn, ///< The LibSceAudioIn implementation. - Lib_Net, ///< The LibSceNet implementation. - Lib_NetCtl, ///< The LibSecNetCtl implementation. - Lib_SaveData, ///< The LibSceSaveData implementation. - Lib_SaveDataDialog, ///< The LibSceSaveDataDialog implementation. - Lib_Ssl, ///< The LibSceSsl implementation. - Lib_Http, ///< The LibSceHttp implementation. - Lib_SysModule, ///< The LibSceSysModule implementation - Lib_NpManager, ///< The LibSceNpManager implementation - Lib_NpScore, ///< The LibSceNpScore implementation - Lib_NpTrophy, ///< The LibSceNpTrophy implementation - Lib_Screenshot, ///< The LibSceScreenshot implementation - Lib_LibCInternal, ///< The LibCInternal implementation. - Lib_AppContent, ///< The LibSceAppContent implementation. - Lib_Rtc, ///< The LibSceRtc implementation. - Lib_DiscMap, ///< The LibSceDiscMap implementation. - Lib_Png, ///< The LibScePng implementation. - Lib_PlayGo, ///< The LibScePlayGo implementation. - Lib_Random, ///< The libSceRandom implementation. - Lib_Usbd, ///< The LibSceUsbd implementation. - Lib_Ajm, ///< The LibSceAjm implementation. - Lib_ErrorDialog, ///< The LibSceErrorDialog implementation. - Lib_ImeDialog, ///< The LibSceImeDialog implementation. - Lib_AvPlayer, ///< The LibSceAvPlayer implementation. - Lib_Ngs2, ///< The LibSceNgs2 implementation. - Lib_Audio3d, ///< The LibSceAudio3d implementation. - Lib_Fiber, ///< The LibSceFiber implementation. - Frontend, ///< Emulator UI - Render, ///< Video Core - Render_Vulkan, ///< Vulkan backend - Render_Recompiler, ///< Shader recompiler - ImGui, ///< ImGui - Loader, ///< ROM loader - Input, ///< Input emulation - Tty, ///< Debug output from emu - Count ///< Total number of logging classes + Log, ///< Messages about the log system itself + Common, ///< Library routines + Common_Filesystem, ///< Filesystem interface library + Common_Memory, ///< Memory mapping and management functions + Core, ///< LLE emulation core + Core_Linker, ///< The module linker + Config, ///< Emulator configuration (including commandline) + Debug, ///< Debugging tools + Kernel, ///< The HLE implementation of the PS4 kernel. + Kernel_Pthread, ///< The pthread implementation of the kernel. + Kernel_Fs, ///< The filesystem implementation of the kernel. + Kernel_Vmm, ///< The virtual memory implementation of the kernel. + Kernel_Event, ///< The event management implementation of the kernel. + Kernel_Sce, ///< The sony specific interfaces provided by the kernel. + Lib, ///< HLE implementation of system library. Each major library + ///< should have its own subclass. + Lib_LibC, ///< The LibC implementation. + Lib_Kernel, ///< The LibKernel implementation. + Lib_Pad, ///< The LibScePad implementation. + Lib_GnmDriver, ///< The LibSceGnmDriver implementation. + Lib_SystemService, ///< The LibSceSystemService implementation. + Lib_UserService, ///< The LibSceUserService implementation. + Lib_VideoOut, ///< The LibSceVideoOut implementation. + Lib_CommonDlg, ///< The LibSceCommonDialog implementation. + Lib_MsgDlg, ///< The LibSceMsgDialog implementation. + Lib_AudioOut, ///< The LibSceAudioOut implementation. + Lib_AudioIn, ///< The LibSceAudioIn implementation. + Lib_Net, ///< The LibSceNet implementation. + Lib_NetCtl, ///< The LibSecNetCtl implementation. + Lib_SaveData, ///< The LibSceSaveData implementation. + Lib_SaveDataDialog, ///< The LibSceSaveDataDialog implementation. + Lib_Ssl, ///< The LibSceSsl implementation. + Lib_Http, ///< The LibSceHttp implementation. + Lib_SysModule, ///< The LibSceSysModule implementation + Lib_NpManager, ///< The LibSceNpManager implementation + Lib_NpScore, ///< The LibSceNpScore implementation + Lib_NpTrophy, ///< The LibSceNpTrophy implementation + Lib_Screenshot, ///< The LibSceScreenshot implementation + Lib_LibCInternal, ///< The LibCInternal implementation. + Lib_AppContent, ///< The LibSceAppContent implementation. + Lib_Rtc, ///< The LibSceRtc implementation. + Lib_DiscMap, ///< The LibSceDiscMap implementation. + Lib_Png, ///< The LibScePng implementation. + Lib_PlayGo, ///< The LibScePlayGo implementation. + Lib_Random, ///< The libSceRandom implementation. + Lib_Usbd, ///< The LibSceUsbd implementation. + Lib_Ajm, ///< The LibSceAjm implementation. + Lib_ErrorDialog, ///< The LibSceErrorDialog implementation. + Lib_ImeDialog, ///< The LibSceImeDialog implementation. + Lib_AvPlayer, ///< The LibSceAvPlayer implementation. + Lib_Ngs2, ///< The LibSceNgs2 implementation. + Lib_Audio3d, ///< The LibSceAudio3d implementation. + Lib_Ime, ///< The LibSceIme implementation + Lib_GameLiveStreaming, ///< The LibSceGameLiveStreaming implementation + Lib_Remoteplay, ///< The LibSceRemotePlay implementation + Lib_SharePlay, ///< The LibSceSharePlay implemenation + Lib_Fiber, ///< The LibSceFiber implementation. + Frontend, ///< Emulator UI + Render, ///< Video Core + Render_Vulkan, ///< Vulkan backend + Render_Recompiler, ///< Shader recompiler + ImGui, ///< ImGui + Loader, ///< ROM loader + Input, ///< Input emulation + Tty, ///< Debug output from emu + Count ///< Total number of logging classes }; } // namespace Common::Log diff --git a/src/core/libraries/game_live_streaming/gamelivestreaming.cpp b/src/core/libraries/game_live_streaming/gamelivestreaming.cpp new file mode 100644 index 000000000..a1ebddfbf --- /dev/null +++ b/src/core/libraries/game_live_streaming/gamelivestreaming.cpp @@ -0,0 +1,354 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "gamelivestreaming.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" + +namespace Libraries::GameLiveStreaming { + +int PS4_SYSV_ABI sceGameLiveStreamingStartDebugBroadcast() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingStopDebugBroadcast() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingApplySocialFeedbackMessageFilter() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingCheckCallback() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingClearPresetSocialFeedbackCommands() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingClearSocialFeedbackMessages() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingClearSpoilerTag() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingEnableLiveStreaming() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingEnableSocialFeedback() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingGetCurrentBroadcastScreenLayout() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingGetCurrentStatus(OrbisGameLiveStreamingStatus* status) { + memset(status, 0, sizeof(*status)); + status->isOnAir = false; + LOG_DEBUG(Lib_GameLiveStreaming, "(STUBBED) called userid = {}", status->userId); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingGetCurrentStatus2() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingGetProgramInfo() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingGetSocialFeedbackMessages() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingGetSocialFeedbackMessagesCount() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingInitialize() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingLaunchLiveViewer() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingLaunchLiveViewerA() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingPermitLiveStreaming() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingPermitServerSideRecording() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingPostSocialMessage() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingRegisterCallback() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingScreenCloseSeparateMode() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingScreenConfigureSeparateMode() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingScreenInitialize() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingScreenInitializeSeparateModeParameter() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingScreenOpenSeparateMode() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingScreenSetMode() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingScreenTerminate() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetCameraFrameSetting() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetDefaultServiceProviderPermission() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetGuardAreas() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetInvitationSessionId() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetLinkCommentPreset() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetMaxBitrate() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetMetadata() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetPresetSocialFeedbackCommands() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetPresetSocialFeedbackCommandsDescription() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetServiceProviderPermission() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetSpoilerTag() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetStandbyScreenResource() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingStartGenerateStandbyScreenResource() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingStartSocialFeedbackMessageFiltering() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingStopGenerateStandbyScreenResource() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingStopSocialFeedbackMessageFiltering() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingTerminate() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingUnregisterCallback() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceGameLiveStreaming(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("caqgDl+V9qA", "libSceGameLiveStreaming_debug", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingStartDebugBroadcast); + LIB_FUNCTION("0i8Lrllxwow", "libSceGameLiveStreaming_debug", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingStopDebugBroadcast); + LIB_FUNCTION("NqkTzemliC0", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingApplySocialFeedbackMessageFilter); + LIB_FUNCTION("PC4jq87+YQI", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingCheckCallback); + LIB_FUNCTION("FcHBfHjFXkA", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingClearPresetSocialFeedbackCommands); + LIB_FUNCTION("lZ2Sd0uEvpo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingClearSocialFeedbackMessages); + LIB_FUNCTION("6c2zGtThFww", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingClearSpoilerTag); + LIB_FUNCTION("dWM80AX39o4", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingEnableLiveStreaming); + LIB_FUNCTION("wBOQWjbWMfU", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingEnableSocialFeedback); + LIB_FUNCTION("aRSQNqbats4", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingGetCurrentBroadcastScreenLayout); + LIB_FUNCTION("CoPMx369EqM", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingGetCurrentStatus); + LIB_FUNCTION("lK8dLBNp9OE", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingGetCurrentStatus2); + LIB_FUNCTION("OIIm19xu+NM", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingGetProgramInfo); + LIB_FUNCTION("PMx7N4WqNdo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingGetSocialFeedbackMessages); + LIB_FUNCTION("yeQKjHETi40", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingGetSocialFeedbackMessagesCount); + LIB_FUNCTION("kvYEw2lBndk", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingInitialize); + LIB_FUNCTION("ysWfX5PPbfc", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingLaunchLiveViewer); + LIB_FUNCTION("cvRCb7DTAig", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingLaunchLiveViewerA); + LIB_FUNCTION("K0QxEbD7q+c", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingPermitLiveStreaming); + LIB_FUNCTION("-EHnU68gExU", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingPermitServerSideRecording); + LIB_FUNCTION("hggKhPySVgI", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingPostSocialMessage); + LIB_FUNCTION("nFP8qT9YXbo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingRegisterCallback); + LIB_FUNCTION("b5RaMD2J0So", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingScreenCloseSeparateMode); + LIB_FUNCTION("hBdd8n6kuvE", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingScreenConfigureSeparateMode); + LIB_FUNCTION("uhCmn81s-mU", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingScreenInitialize); + LIB_FUNCTION("fo5B8RUaBxQ", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingScreenInitializeSeparateModeParameter); + LIB_FUNCTION("iorzW0pKOiA", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingScreenOpenSeparateMode); + LIB_FUNCTION("gDSvt78H3Oo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingScreenSetMode); + LIB_FUNCTION("HE93dr-5rx4", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingScreenTerminate); + LIB_FUNCTION("3PSiwAzFISE", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetCameraFrameSetting); + LIB_FUNCTION("TwuUzTKKeek", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetDefaultServiceProviderPermission); + LIB_FUNCTION("Gw6S4oqlY7E", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetGuardAreas); + LIB_FUNCTION("QmQYwQ7OTJI", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetInvitationSessionId); + LIB_FUNCTION("Sb5bAXyUt5c", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetLinkCommentPreset); + LIB_FUNCTION("q-kxuaF7URU", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetMaxBitrate); + LIB_FUNCTION("hUY-mSOyGL0", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetMetadata); + LIB_FUNCTION("ycodiP2I0xo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetPresetSocialFeedbackCommands); + LIB_FUNCTION("x6deXUpQbBo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetPresetSocialFeedbackCommandsDescription); + LIB_FUNCTION("mCoz3k3zPmA", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetServiceProviderPermission); + LIB_FUNCTION("ZuX+zzz2DkA", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetSpoilerTag); + LIB_FUNCTION("MLvYI86FFAo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetStandbyScreenResource); + LIB_FUNCTION("y0KkAydy9xE", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingStartGenerateStandbyScreenResource); + LIB_FUNCTION("Y1WxX7dPMCw", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingStartSocialFeedbackMessageFiltering); + LIB_FUNCTION("D7dg5QJ4FlE", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingStopGenerateStandbyScreenResource); + LIB_FUNCTION("bYuGUBuIsaY", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingStopSocialFeedbackMessageFiltering); + LIB_FUNCTION("9yK6Fk8mKOQ", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingTerminate); + LIB_FUNCTION("5XHaH3kL+bA", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingUnregisterCallback); + LIB_FUNCTION("caqgDl+V9qA", "libSceGameLiveStreaming_direct_streaming", 1, + "libSceGameLiveStreaming", 1, 1, sceGameLiveStreamingStartDebugBroadcast); + LIB_FUNCTION("0i8Lrllxwow", "libSceGameLiveStreaming_direct_streaming", 1, + "libSceGameLiveStreaming", 1, 1, sceGameLiveStreamingStopDebugBroadcast); + LIB_FUNCTION("CoPMx369EqM", "libSceGameLiveStreamingCompat", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingGetCurrentStatus); + LIB_FUNCTION("ysWfX5PPbfc", "libSceGameLiveStreamingCompat", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingLaunchLiveViewer); +}; + +} // namespace Libraries::GameLiveStreaming \ No newline at end of file diff --git a/src/core/libraries/game_live_streaming/gamelivestreaming.h b/src/core/libraries/game_live_streaming/gamelivestreaming.h new file mode 100644 index 000000000..468750fd1 --- /dev/null +++ b/src/core/libraries/game_live_streaming/gamelivestreaming.h @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::GameLiveStreaming { + +struct OrbisGameLiveStreamingStatus { + bool isOnAir; + u8 align[3]; + u32 spectatorCounts; + s32 userId; + u8 reserved[60]; +}; +struct OrbisGameLiveStreamingStatus2 { + s32 userId; + bool isOnAir; + u8 align[3]; + u32 spectatorCounts; + u32 textMessageCounts; + u32 commandMessageCounts; + u32 broadcastVideoResolution; + u8 reserved[48]; +}; + +int PS4_SYSV_ABI sceGameLiveStreamingStartDebugBroadcast(); +int PS4_SYSV_ABI sceGameLiveStreamingStopDebugBroadcast(); +int PS4_SYSV_ABI sceGameLiveStreamingApplySocialFeedbackMessageFilter(); +int PS4_SYSV_ABI sceGameLiveStreamingCheckCallback(); +int PS4_SYSV_ABI sceGameLiveStreamingClearPresetSocialFeedbackCommands(); +int PS4_SYSV_ABI sceGameLiveStreamingClearSocialFeedbackMessages(); +int PS4_SYSV_ABI sceGameLiveStreamingClearSpoilerTag(); +int PS4_SYSV_ABI sceGameLiveStreamingEnableLiveStreaming(); +int PS4_SYSV_ABI sceGameLiveStreamingEnableSocialFeedback(); +int PS4_SYSV_ABI sceGameLiveStreamingGetCurrentBroadcastScreenLayout(); +int PS4_SYSV_ABI sceGameLiveStreamingGetCurrentStatus(OrbisGameLiveStreamingStatus* status); +int PS4_SYSV_ABI sceGameLiveStreamingGetCurrentStatus2(); +int PS4_SYSV_ABI sceGameLiveStreamingGetProgramInfo(); +int PS4_SYSV_ABI sceGameLiveStreamingGetSocialFeedbackMessages(); +int PS4_SYSV_ABI sceGameLiveStreamingGetSocialFeedbackMessagesCount(); +int PS4_SYSV_ABI sceGameLiveStreamingInitialize(); +int PS4_SYSV_ABI sceGameLiveStreamingLaunchLiveViewer(); +int PS4_SYSV_ABI sceGameLiveStreamingLaunchLiveViewerA(); +int PS4_SYSV_ABI sceGameLiveStreamingPermitLiveStreaming(); +int PS4_SYSV_ABI sceGameLiveStreamingPermitServerSideRecording(); +int PS4_SYSV_ABI sceGameLiveStreamingPostSocialMessage(); +int PS4_SYSV_ABI sceGameLiveStreamingRegisterCallback(); +int PS4_SYSV_ABI sceGameLiveStreamingScreenCloseSeparateMode(); +int PS4_SYSV_ABI sceGameLiveStreamingScreenConfigureSeparateMode(); +int PS4_SYSV_ABI sceGameLiveStreamingScreenInitialize(); +int PS4_SYSV_ABI sceGameLiveStreamingScreenInitializeSeparateModeParameter(); +int PS4_SYSV_ABI sceGameLiveStreamingScreenOpenSeparateMode(); +int PS4_SYSV_ABI sceGameLiveStreamingScreenSetMode(); +int PS4_SYSV_ABI sceGameLiveStreamingScreenTerminate(); +int PS4_SYSV_ABI sceGameLiveStreamingSetCameraFrameSetting(); +int PS4_SYSV_ABI sceGameLiveStreamingSetDefaultServiceProviderPermission(); +int PS4_SYSV_ABI sceGameLiveStreamingSetGuardAreas(); +int PS4_SYSV_ABI sceGameLiveStreamingSetInvitationSessionId(); +int PS4_SYSV_ABI sceGameLiveStreamingSetLinkCommentPreset(); +int PS4_SYSV_ABI sceGameLiveStreamingSetMaxBitrate(); +int PS4_SYSV_ABI sceGameLiveStreamingSetMetadata(); +int PS4_SYSV_ABI sceGameLiveStreamingSetPresetSocialFeedbackCommands(); +int PS4_SYSV_ABI sceGameLiveStreamingSetPresetSocialFeedbackCommandsDescription(); +int PS4_SYSV_ABI sceGameLiveStreamingSetServiceProviderPermission(); +int PS4_SYSV_ABI sceGameLiveStreamingSetSpoilerTag(); +int PS4_SYSV_ABI sceGameLiveStreamingSetStandbyScreenResource(); +int PS4_SYSV_ABI sceGameLiveStreamingStartGenerateStandbyScreenResource(); +int PS4_SYSV_ABI sceGameLiveStreamingStartSocialFeedbackMessageFiltering(); +int PS4_SYSV_ABI sceGameLiveStreamingStopGenerateStandbyScreenResource(); +int PS4_SYSV_ABI sceGameLiveStreamingStopSocialFeedbackMessageFiltering(); +int PS4_SYSV_ABI sceGameLiveStreamingTerminate(); +int PS4_SYSV_ABI sceGameLiveStreamingUnregisterCallback(); + +void RegisterlibSceGameLiveStreaming(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::GameLiveStreaming \ No newline at end of file diff --git a/src/core/libraries/ime/ime.cpp b/src/core/libraries/ime/ime.cpp new file mode 100644 index 000000000..13a70acf7 --- /dev/null +++ b/src/core/libraries/ime/ime.cpp @@ -0,0 +1,340 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ime.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" + +namespace Libraries::Ime { + +int PS4_SYSV_ABI FinalizeImeModule() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI InitializeImeModule() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeCheckFilterText() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeCheckRemoteEventParam() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeCheckUpdateTextInfo() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeClose() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeConfigGet() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeConfigSet() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeConfirmCandidate() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeDicAddWord() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeDicDeleteLearnDics() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeDicDeleteUserDics() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeDicDeleteWord() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeDicGetWords() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeDicReplaceWord() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeDisableController() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeFilterText() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeForTestFunction() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeGetPanelPositionAndForm() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeGetPanelSize() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeKeyboardClose() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeKeyboardGetInfo() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeKeyboardGetResourceId() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeKeyboardOpen() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeKeyboardOpenInternal() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeKeyboardSetMode() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeKeyboardUpdate() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeOpen() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeOpenInternal() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeParamInit() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeSetCandidateIndex() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeSetCaret() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeSetText() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeSetTextGeometry() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeUpdate() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshClearPreedit() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshClose() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshConfirmPreedit() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshDisableController() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshGetPanelPositionAndForm() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshInformConfirmdString() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshInformConfirmdString2() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshOpen() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshSendTextInfo() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshSetCaretGeometry() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshSetCaretIndexInPreedit() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshSetPanelPosition() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshSetParam() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshSetPreeditGeometry() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshSetSelectGeometry() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshSetSelectionText() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshUpdate() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshUpdateContext() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshUpdateContext2() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceIme(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("mN+ZoSN-8hQ", "libSceIme", 1, "libSceIme", 1, 1, FinalizeImeModule); + LIB_FUNCTION("uTW+63goeJs", "libSceIme", 1, "libSceIme", 1, 1, InitializeImeModule); + LIB_FUNCTION("Lf3DeGWC6xg", "libSceIme", 1, "libSceIme", 1, 1, sceImeCheckFilterText); + LIB_FUNCTION("zHuMUGb-AQI", "libSceIme", 1, "libSceIme", 1, 1, sceImeCheckRemoteEventParam); + LIB_FUNCTION("OTb0Mg+1i1k", "libSceIme", 1, "libSceIme", 1, 1, sceImeCheckUpdateTextInfo); + LIB_FUNCTION("TmVP8LzcFcY", "libSceIme", 1, "libSceIme", 1, 1, sceImeClose); + LIB_FUNCTION("Ho5NVQzpKHo", "libSceIme", 1, "libSceIme", 1, 1, sceImeConfigGet); + LIB_FUNCTION("P5dPeiLwm-M", "libSceIme", 1, "libSceIme", 1, 1, sceImeConfigSet); + LIB_FUNCTION("tKLmVIUkpyM", "libSceIme", 1, "libSceIme", 1, 1, sceImeConfirmCandidate); + LIB_FUNCTION("NYDsL9a0oEo", "libSceIme", 1, "libSceIme", 1, 1, sceImeDicAddWord); + LIB_FUNCTION("l01GKoyiQrY", "libSceIme", 1, "libSceIme", 1, 1, sceImeDicDeleteLearnDics); + LIB_FUNCTION("E2OcGgi-FPY", "libSceIme", 1, "libSceIme", 1, 1, sceImeDicDeleteUserDics); + LIB_FUNCTION("JAiMBkOTYKI", "libSceIme", 1, "libSceIme", 1, 1, sceImeDicDeleteWord); + LIB_FUNCTION("JoPdCUXOzMU", "libSceIme", 1, "libSceIme", 1, 1, sceImeDicGetWords); + LIB_FUNCTION("FuEl46uHDyo", "libSceIme", 1, "libSceIme", 1, 1, sceImeDicReplaceWord); + LIB_FUNCTION("E+f1n8e8DAw", "libSceIme", 1, "libSceIme", 1, 1, sceImeDisableController); + LIB_FUNCTION("evjOsE18yuI", "libSceIme", 1, "libSceIme", 1, 1, sceImeFilterText); + LIB_FUNCTION("wVkehxutK-U", "libSceIme", 1, "libSceIme", 1, 1, sceImeForTestFunction); + LIB_FUNCTION("T6FYjZXG93o", "libSceIme", 1, "libSceIme", 1, 1, sceImeGetPanelPositionAndForm); + LIB_FUNCTION("ziPDcIjO0Vk", "libSceIme", 1, "libSceIme", 1, 1, sceImeGetPanelSize); + LIB_FUNCTION("PMVehSlfZ94", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardClose); + LIB_FUNCTION("VkqLPArfFdc", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardGetInfo); + LIB_FUNCTION("dKadqZFgKKQ", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardGetResourceId); + LIB_FUNCTION("eaFXjfJv3xs", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardOpen); + LIB_FUNCTION("oYkJlMK51SA", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardOpenInternal); + LIB_FUNCTION("ua+13Hk9kKs", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardSetMode); + LIB_FUNCTION("3Hx2Uw9xnv8", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardUpdate); + LIB_FUNCTION("RPydv-Jr1bc", "libSceIme", 1, "libSceIme", 1, 1, sceImeOpen); + LIB_FUNCTION("16UI54cWRQk", "libSceIme", 1, "libSceIme", 1, 1, sceImeOpenInternal); + LIB_FUNCTION("WmYDzdC4EHI", "libSceIme", 1, "libSceIme", 1, 1, sceImeParamInit); + LIB_FUNCTION("TQaogSaqkEk", "libSceIme", 1, "libSceIme", 1, 1, sceImeSetCandidateIndex); + LIB_FUNCTION("WLxUN2WMim8", "libSceIme", 1, "libSceIme", 1, 1, sceImeSetCaret); + LIB_FUNCTION("ieCNrVrzKd4", "libSceIme", 1, "libSceIme", 1, 1, sceImeSetText); + LIB_FUNCTION("TXYHFRuL8UY", "libSceIme", 1, "libSceIme", 1, 1, sceImeSetTextGeometry); + LIB_FUNCTION("-4GCfYdNF1s", "libSceIme", 1, "libSceIme", 1, 1, sceImeUpdate); + LIB_FUNCTION("oOwl47ouxoM", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshClearPreedit); + LIB_FUNCTION("gtoTsGM9vEY", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshClose); + LIB_FUNCTION("wTKF4mUlSew", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshConfirmPreedit); + LIB_FUNCTION("rM-1hkuOhh0", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshDisableController); + LIB_FUNCTION("42xMaQ+GLeQ", "libSceIme", 1, "libSceIme", 1, 1, + sceImeVshGetPanelPositionAndForm); + LIB_FUNCTION("ZmmV6iukhyo", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshInformConfirmdString); + LIB_FUNCTION("EQBusz6Uhp8", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshInformConfirmdString2); + LIB_FUNCTION("LBicRa-hj3A", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshOpen); + LIB_FUNCTION("-IAOwd2nO7g", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSendTextInfo); + LIB_FUNCTION("qDagOjvJdNk", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetCaretGeometry); + LIB_FUNCTION("tNOlmxee-Nk", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetCaretIndexInPreedit); + LIB_FUNCTION("rASXozKkQ9g", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetPanelPosition); + LIB_FUNCTION("idvMaIu5H+k", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetParam); + LIB_FUNCTION("ga5GOgThbjo", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetPreeditGeometry); + LIB_FUNCTION("RuSca8rS6yA", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetSelectGeometry); + LIB_FUNCTION("J7COZrgSFRA", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetSelectionText); + LIB_FUNCTION("WqAayyok5p0", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshUpdate); + LIB_FUNCTION("O7Fdd+Oc-qQ", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshUpdateContext); + LIB_FUNCTION("fwcPR7+7Rks", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshUpdateContext2); +}; + +} // namespace Libraries::Ime \ No newline at end of file diff --git a/src/core/libraries/ime/ime.h b/src/core/libraries/ime/ime.h new file mode 100644 index 000000000..807616f14 --- /dev/null +++ b/src/core/libraries/ime/ime.h @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Ime { + +int PS4_SYSV_ABI FinalizeImeModule(); +int PS4_SYSV_ABI InitializeImeModule(); +int PS4_SYSV_ABI sceImeCheckFilterText(); +int PS4_SYSV_ABI sceImeCheckRemoteEventParam(); +int PS4_SYSV_ABI sceImeCheckUpdateTextInfo(); +int PS4_SYSV_ABI sceImeClose(); +int PS4_SYSV_ABI sceImeConfigGet(); +int PS4_SYSV_ABI sceImeConfigSet(); +int PS4_SYSV_ABI sceImeConfirmCandidate(); +int PS4_SYSV_ABI sceImeDicAddWord(); +int PS4_SYSV_ABI sceImeDicDeleteLearnDics(); +int PS4_SYSV_ABI sceImeDicDeleteUserDics(); +int PS4_SYSV_ABI sceImeDicDeleteWord(); +int PS4_SYSV_ABI sceImeDicGetWords(); +int PS4_SYSV_ABI sceImeDicReplaceWord(); +int PS4_SYSV_ABI sceImeDisableController(); +int PS4_SYSV_ABI sceImeFilterText(); +int PS4_SYSV_ABI sceImeForTestFunction(); +int PS4_SYSV_ABI sceImeGetPanelPositionAndForm(); +int PS4_SYSV_ABI sceImeGetPanelSize(); +int PS4_SYSV_ABI sceImeKeyboardClose(); +int PS4_SYSV_ABI sceImeKeyboardGetInfo(); +int PS4_SYSV_ABI sceImeKeyboardGetResourceId(); +int PS4_SYSV_ABI sceImeKeyboardOpen(); +int PS4_SYSV_ABI sceImeKeyboardOpenInternal(); +int PS4_SYSV_ABI sceImeKeyboardSetMode(); +int PS4_SYSV_ABI sceImeKeyboardUpdate(); +int PS4_SYSV_ABI sceImeOpen(); +int PS4_SYSV_ABI sceImeOpenInternal(); +int PS4_SYSV_ABI sceImeParamInit(); +int PS4_SYSV_ABI sceImeSetCandidateIndex(); +int PS4_SYSV_ABI sceImeSetCaret(); +int PS4_SYSV_ABI sceImeSetText(); +int PS4_SYSV_ABI sceImeSetTextGeometry(); +int PS4_SYSV_ABI sceImeUpdate(); +int PS4_SYSV_ABI sceImeVshClearPreedit(); +int PS4_SYSV_ABI sceImeVshClose(); +int PS4_SYSV_ABI sceImeVshConfirmPreedit(); +int PS4_SYSV_ABI sceImeVshDisableController(); +int PS4_SYSV_ABI sceImeVshGetPanelPositionAndForm(); +int PS4_SYSV_ABI sceImeVshInformConfirmdString(); +int PS4_SYSV_ABI sceImeVshInformConfirmdString2(); +int PS4_SYSV_ABI sceImeVshOpen(); +int PS4_SYSV_ABI sceImeVshSendTextInfo(); +int PS4_SYSV_ABI sceImeVshSetCaretGeometry(); +int PS4_SYSV_ABI sceImeVshSetCaretIndexInPreedit(); +int PS4_SYSV_ABI sceImeVshSetPanelPosition(); +int PS4_SYSV_ABI sceImeVshSetParam(); +int PS4_SYSV_ABI sceImeVshSetPreeditGeometry(); +int PS4_SYSV_ABI sceImeVshSetSelectGeometry(); +int PS4_SYSV_ABI sceImeVshSetSelectionText(); +int PS4_SYSV_ABI sceImeVshUpdate(); +int PS4_SYSV_ABI sceImeVshUpdateContext(); +int PS4_SYSV_ABI sceImeVshUpdateContext2(); + +void RegisterlibSceIme(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Ime \ No newline at end of file diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index fb771bc22..caa254fd8 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -11,8 +11,9 @@ #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/fiber/fiber.h" +#include "core/libraries/game_live_streaming/gamelivestreaming.h" #include "core/libraries/gnmdriver/gnmdriver.h" +#include "core/libraries/ime/ime.h" #include "core/libraries/kernel/libkernel.h" #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libpng/pngdec.h" @@ -27,10 +28,12 @@ #include "core/libraries/pad/pad.h" #include "core/libraries/playgo/playgo.h" #include "core/libraries/random/random.h" +#include "core/libraries/remote_play/remoteplay.h" #include "core/libraries/rtc/rtc.h" #include "core/libraries/save_data/dialog/savedatadialog.h" #include "core/libraries/save_data/savedata.h" #include "core/libraries/screenshot/screenshot.h" +#include "core/libraries/share_play/shareplay.h" #include "core/libraries/system/commondialog.h" #include "core/libraries/system/msgdialog.h" #include "core/libraries/system/posix.h" @@ -78,6 +81,10 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::ImeDialog::RegisterlibSceImeDialog(sym); Libraries::AvPlayer::RegisterlibSceAvPlayer(sym); Libraries::Audio3d::RegisterlibSceAudio3d(sym); + Libraries::Ime::RegisterlibSceIme(sym); + Libraries::GameLiveStreaming::RegisterlibSceGameLiveStreaming(sym); + Libraries::SharePlay::RegisterlibSceSharePlay(sym); + Libraries::Remoteplay::RegisterlibSceRemoteplay(sym); } } // namespace Libraries diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp index 35d5851ea..161fc5214 100644 --- a/src/core/libraries/network/net.cpp +++ b/src/core/libraries/network/net.cpp @@ -734,9 +734,16 @@ int PS4_SYSV_ABI sceNetInetNtopWithScopeId() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetInetPton() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetInetPton(int af, const char* src, void* dst) { +#ifdef WIN32 + int res = InetPtonA(af, src, dst); +#else + int res = inet_pton(af, src, dst); +#endif + if (res < 0) { + UNREACHABLE(); + } + return res; } int PS4_SYSV_ABI sceNetInetPtonEx() { diff --git a/src/core/libraries/network/net.h b/src/core/libraries/network/net.h index 3f010f685..beef38b56 100644 --- a/src/core/libraries/network/net.h +++ b/src/core/libraries/network/net.h @@ -169,7 +169,7 @@ u64 PS4_SYSV_ABI sceNetHtonll(u64 host64); u16 PS4_SYSV_ABI sceNetHtons(u16 host16); const char* PS4_SYSV_ABI sceNetInetNtop(int af, const void* src, char* dst, u32 size); int PS4_SYSV_ABI sceNetInetNtopWithScopeId(); -int PS4_SYSV_ABI sceNetInetPton(); +int PS4_SYSV_ABI sceNetInetPton(int af, const char* src, void* dst); int PS4_SYSV_ABI sceNetInetPtonEx(); int PS4_SYSV_ABI sceNetInetPtonWithScopeId(); int PS4_SYSV_ABI sceNetInfoDumpStart(); diff --git a/src/core/libraries/network/net_ctl_codes.h b/src/core/libraries/network/net_ctl_codes.h index a38565c1e..0dac4201e 100644 --- a/src/core/libraries/network/net_ctl_codes.h +++ b/src/core/libraries/network/net_ctl_codes.h @@ -26,3 +26,11 @@ constexpr int ORBIS_NET_CTL_STATE_IPOBTAINED = 3; constexpr int ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED = 1; constexpr int ORBIS_SCE_NET_CTL_EVENT_TYPE_DISCONNECT_REQ_FINISHED = 2; constexpr int ORBIS_NET_CTL_EVENT_TYPE_IPOBTAINED = 3; + +// get info codes +// device +constexpr int ORBIS_NET_CTL_DEVICE_WIRED = 0; +constexpr int ORBIS_NET_CTL_DEVICE_WIRELESS = 1; +// link +constexpr int ORBIS_NET_CTL_LINK_DISCONNECTED = 0; +constexpr int ORBIS_NET_CTL_LINK_CONNECTED = 1; diff --git a/src/core/libraries/network/net_obj.cpp b/src/core/libraries/network/net_obj.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/core/libraries/network/net_obj.h b/src/core/libraries/network/net_obj.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/core/libraries/network/netctl.cpp b/src/core/libraries/network/netctl.cpp index 3ecdde773..e7a69611e 100644 --- a/src/core/libraries/network/netctl.cpp +++ b/src/core/libraries/network/netctl.cpp @@ -1,6 +1,17 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#ifdef WIN32 +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#include +#include +#include +#else +#include +#include +#include +#endif + #include "common/logging/log.h" #include "common/singleton.h" #include "core/libraries/error_codes.h" @@ -149,15 +160,32 @@ int PS4_SYSV_ABI sceNetCtlGetIfStat() { int PS4_SYSV_ABI sceNetCtlGetInfo(int code, OrbisNetCtlInfo* info) { switch (code) { case ORBIS_NET_CTL_INFO_DEVICE: - info->device = 0; + info->device = ORBIS_NET_CTL_DEVICE_WIRED; break; case ORBIS_NET_CTL_INFO_LINK: - info->link = 0; // disconnected + info->link = ORBIS_NET_CTL_LINK_DISCONNECTED; break; + case ORBIS_NET_CTL_INFO_IP_ADDRESS: { + strcpy(info->ip_address, + "127.0.0.1"); // placeholder in case gethostbyname can't find another ip + char devname[80]; + gethostname(devname, 80); + struct hostent* resolved = gethostbyname(devname); + for (int i = 0; resolved->h_addr_list[i] != nullptr; ++i) { + struct in_addr addrIn; + memcpy(&addrIn, resolved->h_addr_list[i], sizeof(u32)); + char* addr = inet_ntoa(addrIn); + if (strcmp(addr, "127.0.0.1") != 0) { + strcpy(info->ip_address, addr); + break; + } + } + break; + } default: LOG_ERROR(Lib_NetCtl, "{} unsupported code", code); } - LOG_ERROR(Lib_NetCtl, "(STUBBED) called"); + LOG_DEBUG(Lib_NetCtl, "(STUBBED) called"); return ORBIS_OK; } @@ -187,7 +215,10 @@ int PS4_SYSV_ABI sceNetCtlGetNetEvConfigInfoIpcInt() { } int PS4_SYSV_ABI sceNetCtlGetResult(int eventType, int* errorCode) { - LOG_ERROR(Lib_NetCtl, "(STUBBED) called eventType = {} ", eventType); + if (!errorCode) { + return ORBIS_NET_CTL_ERROR_INVALID_ADDR; + } + LOG_DEBUG(Lib_NetCtl, "(STUBBED) called eventType = {} ", eventType); *errorCode = 0; return ORBIS_OK; } diff --git a/src/core/libraries/network/netctl.h b/src/core/libraries/network/netctl.h index 850650f97..89ba34c31 100644 --- a/src/core/libraries/network/netctl.h +++ b/src/core/libraries/network/netctl.h @@ -50,6 +50,7 @@ typedef union OrbisNetCtlInfo { // GetInfo codes constexpr int ORBIS_NET_CTL_INFO_DEVICE = 1; constexpr int ORBIS_NET_CTL_INFO_LINK = 4; +constexpr int ORBIS_NET_CTL_INFO_IP_ADDRESS = 14; int PS4_SYSV_ABI sceNetBweCheckCallbackIpcInt(); int PS4_SYSV_ABI sceNetBweClearEventIpcInt(); diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index b671e0077..d786647c2 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -88,7 +88,7 @@ int PS4_SYSV_ABI scePadGetCapability() { } int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerInformation* pInfo) { - LOG_INFO(Lib_Pad, "called handle = {}", handle); + LOG_DEBUG(Lib_Pad, "called handle = {}", handle); if (handle < 0) { pInfo->touchPadInfo.pixelDensity = 1; pInfo->touchPadInfo.resolution.x = 1920; diff --git a/src/core/libraries/remote_play/remoteplay.cpp b/src/core/libraries/remote_play/remoteplay.cpp new file mode 100644 index 000000000..b7402173e --- /dev/null +++ b/src/core/libraries/remote_play/remoteplay.cpp @@ -0,0 +1,309 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "remoteplay.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" + +namespace Libraries::Remoteplay { + +int PS4_SYSV_ABI sceRemoteplayApprove() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayChangeEnterKey() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayClearAllRegistData() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayClearConnectHistory() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayConfirmDeviceRegist() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayDisconnect() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayGeneratePinCode() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayGetApMode() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayGetConnectHistory() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayGetConnectionStatus(s32 userId, int* pStatus) { + *pStatus = ORBIS_REMOTEPLAY_CONNECTION_STATUS_DISCONNECT; + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayGetConnectUserId() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayGetMbusDeviceInfo() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayGetOperationStatus() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayGetRemoteplayStatus() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayGetRpMode() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayImeClose() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayImeFilterResult() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayImeGetEvent() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayImeNotify() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayImeNotifyEventResult() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayImeOpen() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayImeSetCaret() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayImeSetText() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayInitialize() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayIsRemoteOskReady() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayIsRemotePlaying() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayNotifyMbusDeviceRegistComplete() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayNotifyNpPushWakeup() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayNotifyPinCodeError() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayNotifyUserDelete() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayPrintAllRegistData() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayProhibit() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayProhibitStreaming() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayServerLock() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayServerUnLock() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplaySetApMode() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplaySetLogLevel() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplaySetProhibition() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplaySetProhibitionForVsh() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplaySetRpMode() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayTerminate() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_1D5EE365ED5FADB3() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceRemoteplay(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("xQeIryTX7dY", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayApprove); + LIB_FUNCTION("IYZ+Mu+8tPo", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayChangeEnterKey); + LIB_FUNCTION("ZYUsJtcAnqA", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayClearAllRegistData); + LIB_FUNCTION("cCheyCbF7qw", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayClearConnectHistory); + LIB_FUNCTION("tPYT-kGbZh8", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayConfirmDeviceRegist); + LIB_FUNCTION("6Lg4BNleJWc", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayDisconnect); + LIB_FUNCTION("j98LdSGy4eY", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayGeneratePinCode); + LIB_FUNCTION("L+cL-M-DP3w", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayGetApMode); + LIB_FUNCTION("g4K51cY+PEw", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayGetConnectHistory); + LIB_FUNCTION("g3PNjYKWqnQ", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayGetConnectionStatus); + LIB_FUNCTION("3eBNV9A0BUM", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayGetConnectUserId); + LIB_FUNCTION("ufesWMVX6iU", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayGetMbusDeviceInfo); + LIB_FUNCTION("DxU4JGh4S2k", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayGetOperationStatus); + LIB_FUNCTION("n5OxFJEvPlc", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayGetRemoteplayStatus); + LIB_FUNCTION("Cekhs6LSHC0", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayGetRpMode); + LIB_FUNCTION("ig1ocbR7Ptw", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayImeClose); + LIB_FUNCTION("gV9-8cJPM3I", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayImeFilterResult); + LIB_FUNCTION("cMk57DZXe6c", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayImeGetEvent); + LIB_FUNCTION("-gwkQpOCl68", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayImeNotify); + LIB_FUNCTION("58v9tSlRxc8", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayImeNotifyEventResult); + LIB_FUNCTION("C3r2zT5ebMg", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayImeOpen); + LIB_FUNCTION("oB730zwoz0s", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayImeSetCaret); + LIB_FUNCTION("rOTg1Nljp8w", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayImeSetText); + LIB_FUNCTION("k1SwgkMSOM8", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayInitialize); + LIB_FUNCTION("R8RZC1ZIkzU", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayIsRemoteOskReady); + LIB_FUNCTION("uYhiELUtLgA", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayIsRemotePlaying); + LIB_FUNCTION("d-BBSEq1nfc", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayNotifyMbusDeviceRegistComplete); + LIB_FUNCTION("Yytq7NE38R8", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayNotifyNpPushWakeup); + LIB_FUNCTION("Wg-w8xjMZA4", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayNotifyPinCodeError); + LIB_FUNCTION("yheulqylKwI", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayNotifyUserDelete); + LIB_FUNCTION("t5ZvUiZ1hpE", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayPrintAllRegistData); + LIB_FUNCTION("mrNh78tBpmg", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayProhibit); + LIB_FUNCTION("7QLrixwVHcU", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayProhibitStreaming); + LIB_FUNCTION("-ThIlThsN80", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayServerLock); + LIB_FUNCTION("0Z-Pm5rZJOI", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayServerUnLock); + LIB_FUNCTION("xSrhtSLIjOc", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplaySetApMode); + LIB_FUNCTION("5-2agAeaE+c", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplaySetLogLevel); + LIB_FUNCTION("Rf0XMVR7xPw", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplaySetProhibition); + LIB_FUNCTION("n4l3FTZtNQM", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplaySetProhibitionForVsh); + LIB_FUNCTION("-BPcEQ1w8xc", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplaySetRpMode); + LIB_FUNCTION("BOwybKVa3Do", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayTerminate); + LIB_FUNCTION("HV7jZe1frbM", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + Func_1D5EE365ED5FADB3); +}; + +} // namespace Libraries::Remoteplay \ No newline at end of file diff --git a/src/core/libraries/remote_play/remoteplay.h b/src/core/libraries/remote_play/remoteplay.h new file mode 100644 index 000000000..979f73a9e --- /dev/null +++ b/src/core/libraries/remote_play/remoteplay.h @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +// returning codes in sceRemoteplayGetConnectionStatus pstatus +constexpr int ORBIS_REMOTEPLAY_CONNECTION_STATUS_DISCONNECT = 0; +constexpr int ORBIS_REMOTEPLAY_CONNECTION_STATUS_CONNECT = 1; + +namespace Libraries::Remoteplay { + +int PS4_SYSV_ABI sceRemoteplayApprove(); +int PS4_SYSV_ABI sceRemoteplayChangeEnterKey(); +int PS4_SYSV_ABI sceRemoteplayClearAllRegistData(); +int PS4_SYSV_ABI sceRemoteplayClearConnectHistory(); +int PS4_SYSV_ABI sceRemoteplayConfirmDeviceRegist(); +int PS4_SYSV_ABI sceRemoteplayDisconnect(); +int PS4_SYSV_ABI sceRemoteplayGeneratePinCode(); +int PS4_SYSV_ABI sceRemoteplayGetApMode(); +int PS4_SYSV_ABI sceRemoteplayGetConnectHistory(); +int PS4_SYSV_ABI sceRemoteplayGetConnectionStatus(s32 userId, int* pStatus); +int PS4_SYSV_ABI sceRemoteplayGetConnectUserId(); +int PS4_SYSV_ABI sceRemoteplayGetMbusDeviceInfo(); +int PS4_SYSV_ABI sceRemoteplayGetOperationStatus(); +int PS4_SYSV_ABI sceRemoteplayGetRemoteplayStatus(); +int PS4_SYSV_ABI sceRemoteplayGetRpMode(); +int PS4_SYSV_ABI sceRemoteplayImeClose(); +int PS4_SYSV_ABI sceRemoteplayImeFilterResult(); +int PS4_SYSV_ABI sceRemoteplayImeGetEvent(); +int PS4_SYSV_ABI sceRemoteplayImeNotify(); +int PS4_SYSV_ABI sceRemoteplayImeNotifyEventResult(); +int PS4_SYSV_ABI sceRemoteplayImeOpen(); +int PS4_SYSV_ABI sceRemoteplayImeSetCaret(); +int PS4_SYSV_ABI sceRemoteplayImeSetText(); +int PS4_SYSV_ABI sceRemoteplayInitialize(); +int PS4_SYSV_ABI sceRemoteplayIsRemoteOskReady(); +int PS4_SYSV_ABI sceRemoteplayIsRemotePlaying(); +int PS4_SYSV_ABI sceRemoteplayNotifyMbusDeviceRegistComplete(); +int PS4_SYSV_ABI sceRemoteplayNotifyNpPushWakeup(); +int PS4_SYSV_ABI sceRemoteplayNotifyPinCodeError(); +int PS4_SYSV_ABI sceRemoteplayNotifyUserDelete(); +int PS4_SYSV_ABI sceRemoteplayPrintAllRegistData(); +int PS4_SYSV_ABI sceRemoteplayProhibit(); +int PS4_SYSV_ABI sceRemoteplayProhibitStreaming(); +int PS4_SYSV_ABI sceRemoteplayServerLock(); +int PS4_SYSV_ABI sceRemoteplayServerUnLock(); +int PS4_SYSV_ABI sceRemoteplaySetApMode(); +int PS4_SYSV_ABI sceRemoteplaySetLogLevel(); +int PS4_SYSV_ABI sceRemoteplaySetProhibition(); +int PS4_SYSV_ABI sceRemoteplaySetProhibitionForVsh(); +int PS4_SYSV_ABI sceRemoteplaySetRpMode(); +int PS4_SYSV_ABI sceRemoteplayTerminate(); +int PS4_SYSV_ABI Func_1D5EE365ED5FADB3(); + +void RegisterlibSceRemoteplay(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Remoteplay \ No newline at end of file diff --git a/src/core/libraries/share_play/shareplay.cpp b/src/core/libraries/share_play/shareplay.cpp new file mode 100644 index 000000000..8370438b9 --- /dev/null +++ b/src/core/libraries/share_play/shareplay.cpp @@ -0,0 +1,186 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "shareplay.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" + +namespace Libraries::SharePlay { + +int PS4_SYSV_ABI sceSharePlayCrashDaemon() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayGetCurrentConnectionInfo(OrbisSharePlayConnectionInfo* pInfo) { + memset(pInfo, 0, sizeof(*pInfo)); + pInfo->status = ORBIS_SHARE_PLAY_CONNECTION_STATUS_DORMANT; + LOG_DEBUG(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayGetCurrentConnectionInfoA() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayGetCurrentInfo() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayGetEvent() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayInitialize() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayNotifyDialogOpen() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayNotifyForceCloseForCdlg() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayNotifyOpenQuickMenu() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayResumeScreenForCdlg() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayServerLock() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayServerUnLock() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlaySetMode() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlaySetProhibition() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlaySetProhibitionModeWithAppId() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayStartStandby() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayStartStreaming() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayStopStandby() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayStopStreaming() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayTerminate() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_2E93C0EA6A6B67C4() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C1C236728D88E177() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E9E80C474781F115() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F3DD6199DA15ED44() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceSharePlay(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("ggnCfalLU-8", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayCrashDaemon); + LIB_FUNCTION("OOrLKB0bSDs", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayGetCurrentConnectionInfo); + LIB_FUNCTION("+MCXJlWdi+s", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayGetCurrentConnectionInfoA); + LIB_FUNCTION("vUMkWXQff3w", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayGetCurrentInfo); + LIB_FUNCTION("Md7Mdkr8LBc", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayGetEvent); + LIB_FUNCTION("isruqthpYcw", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayInitialize); + LIB_FUNCTION("9zwJpai7jGc", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayNotifyDialogOpen); + LIB_FUNCTION("VUW2V9cUTP4", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayNotifyForceCloseForCdlg); + LIB_FUNCTION("XL0WwUJoQPg", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayNotifyOpenQuickMenu); + LIB_FUNCTION("6-1fKaa5HlY", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayResumeScreenForCdlg); + LIB_FUNCTION("U28jAuLHj6c", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayServerLock); + LIB_FUNCTION("3Oaux9ITEtY", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayServerUnLock); + LIB_FUNCTION("QZy+KmyqKPU", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, sceSharePlaySetMode); + LIB_FUNCTION("co2NCj--pnc", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlaySetProhibition); + LIB_FUNCTION("KADsbjNCgPo", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlaySetProhibitionModeWithAppId); + LIB_FUNCTION("-F6NddfUsa4", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayStartStandby); + LIB_FUNCTION("rWVNHNnEx6g", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayStartStreaming); + LIB_FUNCTION("zEDkUWLVwFI", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayStopStandby); + LIB_FUNCTION("aGlema+JxUU", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayStopStreaming); + LIB_FUNCTION("UaLjloJinow", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayTerminate); + LIB_FUNCTION("LpPA6mprZ8Q", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + Func_2E93C0EA6A6B67C4); + LIB_FUNCTION("wcI2co2I4Xc", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + Func_C1C236728D88E177); + LIB_FUNCTION("6egMR0eB8RU", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + Func_E9E80C474781F115); + LIB_FUNCTION("891hmdoV7UQ", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + Func_F3DD6199DA15ED44); + LIB_FUNCTION("OOrLKB0bSDs", "libSceSharePlayCompat", 1, "libSceSharePlay", 0, 0, + sceSharePlayGetCurrentConnectionInfo); +}; + +} // namespace Libraries::SharePlay \ No newline at end of file diff --git a/src/core/libraries/share_play/shareplay.h b/src/core/libraries/share_play/shareplay.h new file mode 100644 index 000000000..8b1ad5f47 --- /dev/null +++ b/src/core/libraries/share_play/shareplay.h @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::SharePlay { + +constexpr int ORBIS_SHARE_PLAY_CONNECTION_STATUS_DORMANT = 0x00; +constexpr int ORBIS_SHARE_PLAY_CONNECTION_STATUS_READY = 0x01; +constexpr int ORBIS_SHARE_PLAY_CONNECTION_STATUS_CONNECTED = 0x02; + +struct OrbisSharePlayConnectionInfo { + int status; + int mode; + Libraries::NpManager::OrbisNpOnlineId hostOnlineId; + Libraries::NpManager::OrbisNpOnlineId visitorOnlineId; + s32 hostUserId; + s32 visitorUserId; +}; + +int PS4_SYSV_ABI sceSharePlayCrashDaemon(); +int PS4_SYSV_ABI sceSharePlayGetCurrentConnectionInfo(OrbisSharePlayConnectionInfo* pInfo); +int PS4_SYSV_ABI sceSharePlayGetCurrentConnectionInfoA(); +int PS4_SYSV_ABI sceSharePlayGetCurrentInfo(); +int PS4_SYSV_ABI sceSharePlayGetEvent(); +int PS4_SYSV_ABI sceSharePlayInitialize(); +int PS4_SYSV_ABI sceSharePlayNotifyDialogOpen(); +int PS4_SYSV_ABI sceSharePlayNotifyForceCloseForCdlg(); +int PS4_SYSV_ABI sceSharePlayNotifyOpenQuickMenu(); +int PS4_SYSV_ABI sceSharePlayResumeScreenForCdlg(); +int PS4_SYSV_ABI sceSharePlayServerLock(); +int PS4_SYSV_ABI sceSharePlayServerUnLock(); +int PS4_SYSV_ABI sceSharePlaySetMode(); +int PS4_SYSV_ABI sceSharePlaySetProhibition(); +int PS4_SYSV_ABI sceSharePlaySetProhibitionModeWithAppId(); +int PS4_SYSV_ABI sceSharePlayStartStandby(); +int PS4_SYSV_ABI sceSharePlayStartStreaming(); +int PS4_SYSV_ABI sceSharePlayStopStandby(); +int PS4_SYSV_ABI sceSharePlayStopStreaming(); +int PS4_SYSV_ABI sceSharePlayTerminate(); +int PS4_SYSV_ABI Func_2E93C0EA6A6B67C4(); +int PS4_SYSV_ABI Func_C1C236728D88E177(); +int PS4_SYSV_ABI Func_E9E80C474781F115(); +int PS4_SYSV_ABI Func_F3DD6199DA15ED44(); + +void RegisterlibSceSharePlay(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::SharePlay \ No newline at end of file diff --git a/src/core/libraries/system/userservice.cpp b/src/core/libraries/system/userservice.cpp index 560855474..140ca8921 100644 --- a/src/core/libraries/system/userservice.cpp +++ b/src/core/libraries/system/userservice.cpp @@ -491,7 +491,7 @@ int PS4_SYSV_ABI sceUserServiceGetImeRunCount() { } s32 PS4_SYSV_ABI sceUserServiceGetInitialUser(int* user_id) { - LOG_INFO(Lib_UserService, "called"); + LOG_DEBUG(Lib_UserService, "called"); if (user_id == nullptr) { LOG_ERROR(Lib_UserService, "user_id is null"); return ORBIS_USER_SERVICE_ERROR_INVALID_ARGUMENT; From 169b641bd1e0147a02b453d0a027e62d94a68441 Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Fri, 11 Oct 2024 10:48:32 +0200 Subject: [PATCH 47/93] hotfix: map rasterizer memory on pool commit --- src/core/memory.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index dd63522fc..644f9389f 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -245,6 +245,7 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot) new_vma.is_exec = false; new_vma.phys_base = 0; + rasterizer->MapMemory(mapped_addr, size); return ORBIS_OK; } From cd085ea2de5e63fecef848b7acee33c3fedcac2d Mon Sep 17 00:00:00 2001 From: adjonesey Date: Fri, 11 Oct 2024 15:52:48 +0100 Subject: [PATCH 48/93] Use GetSystemTimePreciseAsFileTime to fix fps timing issues (#1344) -ws --- src/core/libraries/kernel/time_management.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/kernel/time_management.cpp b/src/core/libraries/kernel/time_management.cpp index 5fa26b789..853f8d54c 100644 --- a/src/core/libraries/kernel/time_management.cpp +++ b/src/core/libraries/kernel/time_management.cpp @@ -148,7 +148,7 @@ int PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) { #ifdef _WIN64 FILETIME filetime; - GetSystemTimeAsFileTime(&filetime); + GetSystemTimePreciseAsFileTime(&filetime); constexpr u64 UNIX_TIME_START = 0x295E9648864000; constexpr u64 TICKS_PER_SECOND = 1000000; From 87a76af86fb0c3ba989f87f88ff81fcfe22e5b50 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Fri, 11 Oct 2024 21:44:29 +0300 Subject: [PATCH 49/93] AvPlayer: Patch all guest calls (#1346) --- src/core/libraries/avplayer/avplayer_impl.cpp | 26 +++++++++++++------ .../libraries/avplayer/avplayer_state.cpp | 14 +++++++--- src/core/libraries/avplayer/avplayer_state.h | 3 +++ 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/core/libraries/avplayer/avplayer_impl.cpp b/src/core/libraries/avplayer/avplayer_impl.cpp index 6de7b4c20..d193e765f 100644 --- a/src/core/libraries/avplayer/avplayer_impl.cpp +++ b/src/core/libraries/avplayer/avplayer_impl.cpp @@ -6,8 +6,10 @@ #include "avplayer_impl.h" #include "common/logging/log.h" +#include "common/singleton.h" #include "core/libraries/error_codes.h" #include "core/libraries/kernel/libkernel.h" +#include "core/linker.h" using namespace Libraries::Kernel; @@ -17,28 +19,32 @@ void* PS4_SYSV_ABI AvPlayer::Allocate(void* handle, u32 alignment, u32 size) { const auto* const self = reinterpret_cast(handle); const auto allocate = self->m_init_data_original.memory_replacement.allocate; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - return allocate(ptr, alignment, size); + const auto* linker = Common::Singleton::Instance(); + return linker->ExecuteGuest(allocate, ptr, alignment, size); } void PS4_SYSV_ABI AvPlayer::Deallocate(void* handle, void* memory) { const auto* const self = reinterpret_cast(handle); const auto deallocate = self->m_init_data_original.memory_replacement.deallocate; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - return deallocate(ptr, memory); + const auto* linker = Common::Singleton::Instance(); + return linker->ExecuteGuest(deallocate, ptr, memory); } void* PS4_SYSV_ABI AvPlayer::AllocateTexture(void* handle, u32 alignment, u32 size) { const auto* const self = reinterpret_cast(handle); const auto allocate = self->m_init_data_original.memory_replacement.allocate_texture; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - return allocate(ptr, alignment, size); + const auto* linker = Common::Singleton::Instance(); + return linker->ExecuteGuest(allocate, ptr, alignment, size); } void PS4_SYSV_ABI AvPlayer::DeallocateTexture(void* handle, void* memory) { const auto* const self = reinterpret_cast(handle); const auto deallocate = self->m_init_data_original.memory_replacement.deallocate_texture; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - return deallocate(ptr, memory); + const auto* linker = Common::Singleton::Instance(); + return linker->ExecuteGuest(deallocate, ptr, memory); } int PS4_SYSV_ABI AvPlayer::OpenFile(void* handle, const char* filename) { @@ -47,7 +53,8 @@ int PS4_SYSV_ABI AvPlayer::OpenFile(void* handle, const char* filename) { const auto open = self->m_init_data_original.file_replacement.open; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - return open(ptr, filename); + const auto* linker = Common::Singleton::Instance(); + return linker->ExecuteGuest(open, ptr, filename); } int PS4_SYSV_ABI AvPlayer::CloseFile(void* handle) { @@ -56,7 +63,8 @@ int PS4_SYSV_ABI AvPlayer::CloseFile(void* handle) { const auto close = self->m_init_data_original.file_replacement.close; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - return close(ptr); + const auto* linker = Common::Singleton::Instance(); + return linker->ExecuteGuest(close, ptr); } int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position, u32 length) { @@ -65,7 +73,8 @@ int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position const auto read_offset = self->m_init_data_original.file_replacement.readOffset; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - return read_offset(ptr, buffer, position, length); + const auto* linker = Common::Singleton::Instance(); + return linker->ExecuteGuest(read_offset, ptr, buffer, position, length); } u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) { @@ -74,7 +83,8 @@ u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) { const auto size = self->m_init_data_original.file_replacement.size; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - return size(ptr); + const auto* linker = Common::Singleton::Instance(); + return linker->ExecuteGuest(size, ptr); } SceAvPlayerInitData AvPlayer::StubInitData(const SceAvPlayerInitData& data) { diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index b0e498479..5cabdba2c 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -87,7 +87,12 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayer return; } - // Pass other events to the game + DefaultEventCallback(opaque, event_id, 0, event_data); +} + +void AvPlayerState::DefaultEventCallback(void* opaque, SceAvPlayerEvents event_id, s32 source_id, + void* event_data) { + auto const self = reinterpret_cast(opaque); const auto callback = self->m_event_replacement.event_callback; const auto ptr = self->m_event_replacement.object_ptr; if (callback != nullptr) { @@ -102,8 +107,10 @@ AvPlayerState::AvPlayerState(const SceAvPlayerInitData& init_data) if (m_event_replacement.event_callback == nullptr || init_data.auto_start) { m_auto_start = true; m_init_data.event_replacement.event_callback = &AvPlayerState::AutoPlayEventCallback; - m_init_data.event_replacement.object_ptr = this; + } else { + m_init_data.event_replacement.event_callback = &AvPlayerState::DefaultEventCallback; } + m_init_data.event_replacement.object_ptr = this; if (init_data.default_language != nullptr) { std::memcpy(m_default_language, init_data.default_language, sizeof(m_default_language)); } @@ -367,8 +374,7 @@ void AvPlayerState::EmitEvent(SceAvPlayerEvents event_id, void* event_data) { const auto callback = m_init_data.event_replacement.event_callback; if (callback) { const auto ptr = m_init_data.event_replacement.object_ptr; - const auto* linker = Common::Singleton::Instance(); - linker->ExecuteGuest(callback, ptr, event_id, 0, event_data); + callback(ptr, event_id, 0, event_data); } } diff --git a/src/core/libraries/avplayer/avplayer_state.h b/src/core/libraries/avplayer/avplayer_state.h index f50d1bc1f..151eea52c 100644 --- a/src/core/libraries/avplayer/avplayer_state.h +++ b/src/core/libraries/avplayer/avplayer_state.h @@ -42,6 +42,9 @@ private: static void PS4_SYSV_ABI AutoPlayEventCallback(void* handle, SceAvPlayerEvents event_id, s32 source_id, void* event_data); + static void PS4_SYSV_ABI DefaultEventCallback(void* handle, SceAvPlayerEvents event_id, + s32 source_id, void* event_data); + void OnWarning(u32 id) override; void OnError() override; void OnEOF() override; From b412cb4cca4fe130244a88c7debf939a81eb23b8 Mon Sep 17 00:00:00 2001 From: Lander Gallastegi Date: Fri, 11 Oct 2024 20:45:26 +0200 Subject: [PATCH 50/93] Linux unlink implementation (#1347) --- src/common/io_file.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/common/io_file.cpp b/src/common/io_file.cpp index 1b28d2bba..dd3a40cae 100644 --- a/src/common/io_file.cpp +++ b/src/common/io_file.cpp @@ -231,7 +231,7 @@ void IOFile::Unlink() { // Mark the file for deletion // TODO: Also remove the file path? -#if _WIN64 +#ifdef _WIN64 FILE_DISPOSITION_INFORMATION disposition; IO_STATUS_BLOCK iosb; @@ -242,7 +242,11 @@ void IOFile::Unlink() { NtSetInformationFile(hfile, &iosb, &disposition, sizeof(disposition), FileDispositionInformation); #else - UNREACHABLE_MSG("Missing Linux implementation"); + if (unlink(file_path.c_str()) != 0) { + const auto ec = std::error_code{errno, std::generic_category()}; + LOG_ERROR(Common_Filesystem, "Failed to unlink the file at path={}, ec_message={}", + PathToUTF8String(file_path), ec.message()); + } #endif } From 7c00ac637afc7a33e095a9eccb161c0d671d84ef Mon Sep 17 00:00:00 2001 From: "Daniel R." <47796739+polybiusproxy@users.noreply.github.com> Date: Sat, 12 Oct 2024 16:35:12 +0200 Subject: [PATCH 51/93] core: Fix mmap being unable to map GPU memory --- src/core/address_space.cpp | 26 +++++++++++------------ src/core/libraries/kernel/file_system.cpp | 18 ++++++++-------- src/core/libraries/kernel/libkernel.cpp | 1 + src/core/memory.h | 2 +- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/core/address_space.cpp b/src/core/address_space.cpp index b2cb95a54..8ba99e32d 100644 --- a/src/core/address_space.cpp +++ b/src/core/address_space.cpp @@ -30,14 +30,13 @@ static constexpr size_t BackingSize = SCE_KERNEL_MAIN_DMEM_SIZE_PRO; #ifdef _WIN32 [[nodiscard]] constexpr u64 ToWindowsProt(Core::MemoryProt prot) { - switch (prot) { - case Core::MemoryProt::NoAccess: - default: - return PAGE_NOACCESS; - case Core::MemoryProt::CpuRead: - return PAGE_READONLY; - case Core::MemoryProt::CpuReadWrite: + if (True(prot & Core::MemoryProt::CpuReadWrite) || + True(prot & Core::MemoryProt::GpuReadWrite)) { return PAGE_READWRITE; + } else if (True(prot & Core::MemoryProt::CpuRead) || True(prot & Core::MemoryProt::GpuRead)) { + return PAGE_READONLY; + } else { + return PAGE_NOACCESS; } } @@ -290,14 +289,13 @@ enum PosixPageProtection { }; [[nodiscard]] constexpr PosixPageProtection ToPosixProt(Core::MemoryProt prot) { - switch (prot) { - case Core::MemoryProt::NoAccess: - default: - return PAGE_NOACCESS; - case Core::MemoryProt::CpuRead: - return PAGE_READONLY; - case Core::MemoryProt::CpuReadWrite: + if (True(prot & Core::MemoryProt::CpuReadWrite) || + True(prot & Core::MemoryProt::GpuReadWrite)) { return PAGE_READWRITE; + } else if (True(prot & Core::MemoryProt::CpuRead) || True(prot & Core::MemoryProt::GpuRead)) { + return PAGE_READONLY; + } else { + return PAGE_NOACCESS; } } diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index e2093ce21..7f86ee540 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -321,26 +321,26 @@ int PS4_SYSV_ABI sceKernelRmdir(const char* path) { const std::filesystem::path dir_name = mnt->GetHostPath(path, &ro); if (dir_name.empty()) { - LOG_INFO(Kernel_Fs, "Failed to remove directory: {}, permission denied", - fmt::UTF(dir_name.u8string())); + LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, permission denied", + fmt::UTF(dir_name.u8string())); return SCE_KERNEL_ERROR_EACCES; } if (ro) { - LOG_INFO(Kernel_Fs, "Failed to remove directory: {}, directory is read only", - fmt::UTF(dir_name.u8string())); + LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, directory is read only", + fmt::UTF(dir_name.u8string())); return SCE_KERNEL_ERROR_EROFS; } if (!std::filesystem::is_directory(dir_name)) { - LOG_INFO(Kernel_Fs, "Failed to remove directory: {}, path is not a directory", - fmt::UTF(dir_name.u8string())); + LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, path is not a directory", + fmt::UTF(dir_name.u8string())); return ORBIS_KERNEL_ERROR_ENOTDIR; } if (!std::filesystem::exists(dir_name)) { - LOG_INFO(Kernel_Fs, "Failed to remove directory: {}, no such file or directory", - fmt::UTF(dir_name.u8string())); + LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, no such file or directory", + fmt::UTF(dir_name.u8string())); return ORBIS_KERNEL_ERROR_ENOENT; } @@ -348,7 +348,7 @@ int PS4_SYSV_ABI sceKernelRmdir(const char* path) { int result = std::filesystem::remove_all(dir_name, ec); if (!ec) { - LOG_DEBUG(Kernel_Fs, "Removed directory: {}", fmt::UTF(dir_name.u8string())); + LOG_INFO(Kernel_Fs, "Removed directory: {}", fmt::UTF(dir_name.u8string())); return ORBIS_OK; } LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, error_code={}", diff --git a/src/core/libraries/kernel/libkernel.cpp b/src/core/libraries/kernel/libkernel.cpp index 72f8f1d1d..b013f29bf 100644 --- a/src/core/libraries/kernel/libkernel.cpp +++ b/src/core/libraries/kernel/libkernel.cpp @@ -157,6 +157,7 @@ void SetPosixErrno(int e) { g_posix_errno = e; } } + int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, size_t offset, void** res) { LOG_INFO(Kernel_Vmm, "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, offset = {}", diff --git a/src/core/memory.h b/src/core/memory.h index 752209cfc..320aa367a 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -28,7 +28,7 @@ enum class MemoryProt : u32 { CpuReadWrite = 2, GpuRead = 16, GpuWrite = 32, - GpuReadWrite = 38, + GpuReadWrite = 48, }; DECLARE_ENUM_FLAG_OPERATORS(MemoryProt) From 97b5289f15cc1e654d42ec9703426e7afdadbb04 Mon Sep 17 00:00:00 2001 From: ElBread3 <92335081+ElBread3@users.noreply.github.com> Date: Sat, 12 Oct 2024 09:53:47 -0500 Subject: [PATCH 52/93] use addGameInstallDir here (#1354) --- src/common/config.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 78da372e7..17862e6aa 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -538,12 +538,12 @@ void load(const std::filesystem::path& path) { // TODO Migration code, after a major release this should be removed. auto old_game_install_dir = toml::find_fs_path_or(gui, "installDir", {}); if (!old_game_install_dir.empty()) { - settings_install_dirs.emplace_back(std::filesystem::path{old_game_install_dir}); + addGameInstallDir(std::filesystem::path{old_game_install_dir}); } else { const auto install_dir_array = toml::find_or>(gui, "installDirs", {}); for (const auto& dir : install_dir_array) { - settings_install_dirs.emplace_back(std::filesystem::path{dir}); + addGameInstallDir(std::filesystem::path{dir}); } } From 1e16fe534948aa95a343aa17c15abf7971ac721c Mon Sep 17 00:00:00 2001 From: Pavel <68122101+red-prig@users.noreply.github.com> Date: Sat, 12 Oct 2024 21:33:45 +0300 Subject: [PATCH 53/93] clarification of PC Requirements (#1357) --- documents/Quickstart/Quickstart.md | 1 + 1 file changed, 1 insertion(+) diff --git a/documents/Quickstart/Quickstart.md b/documents/Quickstart/Quickstart.md index 7e496bcce..58549e067 100644 --- a/documents/Quickstart/Quickstart.md +++ b/documents/Quickstart/Quickstart.md @@ -22,6 +22,7 @@ SPDX-License-Identifier: GPL-2.0-or-later - A processor with at least 4 cores and 6 threads - Above 2.5 GHz frequency +- required support AVX2 extension or Rosetta 2 on ARM ### GPU From d3491bfced823e9d28b054ea286e2421957c1151 Mon Sep 17 00:00:00 2001 From: Lander Gallastegi Date: Sun, 13 Oct 2024 10:35:05 +0200 Subject: [PATCH 54/93] hotfix: correctly check for valid priority (#1359) --- src/core/libraries/dialogs/ime_dialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/dialogs/ime_dialog.cpp b/src/core/libraries/dialogs/ime_dialog.cpp index 3d168bb79..63b52706a 100644 --- a/src/core/libraries/dialogs/ime_dialog.cpp +++ b/src/core/libraries/dialogs/ime_dialog.cpp @@ -175,7 +175,7 @@ Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExt } if (extended) { - if (magic_enum::enum_contains(extended->priority)) { + if (!magic_enum::enum_contains(extended->priority)) { LOG_INFO(Lib_ImeDialog, "Invalid extended->priority"); return Error::INVALID_EXTENDED; } From 1cc4ab7e889a622fb97e6b68ed4c7d18e7d8f40d Mon Sep 17 00:00:00 2001 From: RDN000 <109141852+RDN000@users.noreply.github.com> Date: Sun, 13 Oct 2024 10:36:52 +0200 Subject: [PATCH 55/93] Update sq translation (#1351) * Update sq translation * Update sq translation --- src/qt_gui/translations/sq.ts | 112 +++++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 062226369..dae5525e8 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -434,6 +434,41 @@ Log Filter Filtri i Ditarit + + + Input + Hyrja + + + + Cursor + Kursori + + + + Hide Cursor + Fshih kursorin + + + + Hide Cursor Idle Timeout + Koha për fshehjen e kursorit joaktiv + + + + Input + Hyrja + + + + Controller + Dorezë + + + + Back Button Behavior + Sjellja e butonit mbrapa + Graphics @@ -1023,6 +1058,66 @@ GUIgroupBox Luaj muzikën e titullit:\nNëse një lojë e mbështet, aktivizohet luajtja e muzikës të veçantë kur të zgjidhësh lojën në GUI. + + + cursorGroupBox + Kursori:\nNdrysho cilësimet në lidhje me kursorin. + + + + hideCursorGroupBox + Fshih kursorin:\nCakto sjelljen e fshehjes së kursorit. + + + + idleTimeoutGroupBox + Koha për fshehjen e kursorit joaktiv:\Kohëzgjatja (në sekonda) pas së cilës kursori që ka nuk ka qënë në veprim fshihet. + + + + Never + Kurrë + + + + Idle + Pa vepruar + + + + Always + Gjithmonë + + + + backButtonBehaviorGroupBox + Back Button Behavior:\nAllows setting which part of the touchpad the back button will emulate a touch on. + + + + backButtonBehaviorGroupBox + Sjellja e butonit mbrapa:\nLejon të përcaktohet se në cilën pjesë të tastierës prekëse do të imitojë një prekje butoni prapa. + + + + Touchpad Left + Tastiera prekëse majtas + + + + Touchpad Right + Tastiera prekëse djathtas + + + + Touchpad Center + Tastiera prekëse në qendër + + + + None + Asnjë + graphicsAdapterGroupBox @@ -1048,6 +1143,21 @@ nullGpuCheckBox Aktivizo GPU-në Null:\nPër qëllime të korrigjimit teknik, çaktivizon pasqyrimin e lojës sikur nuk ka një kartë grafike. + + + gameFoldersBox + Dosjet e lojërave: Lista e dosjeve për t'u kontrolluar për lojëra të instaluara. + + + + addFolderButton + Shto: Shto një dosje në listë. + + + + removeFolderButton + Hiq: Hiq një dosje nga lista. + debugDump @@ -1114,7 +1224,7 @@ Play Time - Kohë Lojë + Koha e luajtjes From 8776eba8c89ae65aac3c0a7d0787a8843d216edd Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Sun, 13 Oct 2024 12:22:14 +0300 Subject: [PATCH 56/93] Added adaptive mutex initializer handling (#1353) --- .../libraries/kernel/thread_management.cpp | 19 ++++++++++++------- src/core/libraries/kernel/thread_management.h | 9 +++++++++ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/core/libraries/kernel/thread_management.cpp b/src/core/libraries/kernel/thread_management.cpp index aa53d7570..39c0eaf80 100644 --- a/src/core/libraries/kernel/thread_management.cpp +++ b/src/core/libraries/kernel/thread_management.cpp @@ -36,6 +36,10 @@ void init_pthreads() { ScePthreadMutexattr default_mutexattr = nullptr; scePthreadMutexattrInit(&default_mutexattr); g_pthread_cxt->setDefaultMutexattr(default_mutexattr); + ScePthreadMutexattr adaptive_mutexattr = nullptr; + scePthreadMutexattrInit(&adaptive_mutexattr); + scePthreadMutexattrSettype(&adaptive_mutexattr, ORBIS_PTHREAD_MUTEX_ADAPTIVE); + g_pthread_cxt->setAdaptiveMutexattr(adaptive_mutexattr); // default cond init ScePthreadCondattr default_condattr = nullptr; scePthreadCondattrInit(&default_condattr); @@ -412,7 +416,8 @@ int PS4_SYSV_ABI scePthreadGetaffinity(ScePthread thread, /*SceKernelCpumask*/ u } ScePthreadMutex* createMutex(ScePthreadMutex* addr) { - if (addr == nullptr || *addr != nullptr) { + if (addr == nullptr || + (*addr != nullptr && *addr != ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER)) { return addr; } @@ -429,14 +434,14 @@ int PS4_SYSV_ABI scePthreadMutexInit(ScePthreadMutex* mutex, const ScePthreadMut if (mutex == nullptr) { return SCE_KERNEL_ERROR_EINVAL; } - if (mutex_attr == nullptr) { - attr = g_pthread_cxt->getDefaultMutexattr(); - } else { - if (*mutex_attr == nullptr) { - attr = g_pthread_cxt->getDefaultMutexattr(); + if (mutex_attr == nullptr || *mutex_attr == nullptr) { + if (*mutex == ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER) { + attr = g_pthread_cxt->getAdaptiveMutexattr(); } else { - attr = mutex_attr; + attr = g_pthread_cxt->getDefaultMutexattr(); } + } else { + attr = mutex_attr; } *mutex = new PthreadMutexInternal{}; diff --git a/src/core/libraries/kernel/thread_management.h b/src/core/libraries/kernel/thread_management.h index 7385b55ce..3cdb300f7 100644 --- a/src/core/libraries/kernel/thread_management.h +++ b/src/core/libraries/kernel/thread_management.h @@ -13,6 +13,8 @@ #include "common/types.h" +#define ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER (reinterpret_cast(1)) + namespace Core::Loader { class SymbolsResolver; } @@ -134,6 +136,12 @@ public: void setDefaultMutexattr(ScePthreadMutexattr attr) { m_default_mutexattr = attr; } + ScePthreadMutexattr* getAdaptiveMutexattr() { + return &m_adaptive_mutexattr; + } + void setAdaptiveMutexattr(ScePthreadMutexattr attr) { + m_adaptive_mutexattr = attr; + } ScePthreadCondattr* getDefaultCondattr() { return &m_default_condattr; } @@ -161,6 +169,7 @@ public: private: ScePthreadMutexattr m_default_mutexattr = nullptr; + ScePthreadMutexattr m_adaptive_mutexattr = nullptr; ScePthreadCondattr m_default_condattr = nullptr; ScePthreadAttr m_default_attr = nullptr; PThreadPool* m_pthread_pool = nullptr; From cf2e617f08df118494dde8845e00b0fe4efc5134 Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Sun, 13 Oct 2024 09:02:22 -0300 Subject: [PATCH 57/93] Devtools - Inspect regs/User data/Shader disassembly (#1358) * devtools: pm4 - show markers * SaveDataDialogLib: fix compile with mingw * devtools: pm4 - show program state * devtools: pm4 - show program disassembly * devtools: pm4 - show frame regs * devtools: pm4 - show color buffer info as popup add ux improvements for open new windows with shift+click better window titles * imgui: skip all textures to avoid hanging with crash diagnostic enabled not sure why this happens :c * devtools: pm4 - show reg depth buffer --- CMakeLists.txt | 10 + src/core/debug_state.cpp | 65 +- src/core/debug_state.h | 46 +- src/core/devtools/layer.cpp | 111 +- src/core/devtools/options.cpp | 24 + src/core/devtools/options.h | 21 + src/core/devtools/widget/cmd_list.cpp | 558 ++-- src/core/devtools/widget/cmd_list.h | 69 +- src/core/devtools/widget/common.h | 100 + src/core/devtools/widget/frame_dump.cpp | 87 +- src/core/devtools/widget/frame_dump.h | 6 +- src/core/devtools/widget/reg_popup.cpp | 182 ++ src/core/devtools/widget/reg_popup.h | 38 + src/core/devtools/widget/reg_view.cpp | 305 +++ src/core/devtools/widget/reg_view.h | 50 + src/core/devtools/widget/text_editor.cpp | 2334 +++++++++++++++++ src/core/devtools/widget/text_editor.h | 408 +++ src/core/libraries/dialogs/error_dialog.cpp | 2 +- src/core/libraries/gnmdriver/gnmdriver.cpp | 31 +- src/core/libraries/gnmdriver/gnmdriver.h | 2 +- .../save_data/dialog/savedatadialog_ui.cpp | 4 +- src/core/libraries/system/msgdialog_ui.cpp | 2 +- src/imgui/imgui_std.h | 7 +- src/imgui/renderer/texture_manager.cpp | 6 + src/video_core/amdgpu/liverpool.cpp | 27 + src/video_core/amdgpu/liverpool.h | 6 +- src/video_core/amdgpu/pm4_cmds.h | 1 + 27 files changed, 4215 insertions(+), 287 deletions(-) create mode 100644 src/core/devtools/options.cpp create mode 100644 src/core/devtools/options.h create mode 100644 src/core/devtools/widget/common.h create mode 100644 src/core/devtools/widget/reg_popup.cpp create mode 100644 src/core/devtools/widget/reg_popup.h create mode 100644 src/core/devtools/widget/reg_view.cpp create mode 100644 src/core/devtools/widget/reg_view.h create mode 100644 src/core/devtools/widget/text_editor.cpp create mode 100644 src/core/devtools/widget/text_editor.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 37021746d..781e93e10 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -354,15 +354,25 @@ set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp set(DEV_TOOLS src/core/devtools/layer.cpp src/core/devtools/layer.h + src/core/devtools/options.cpp + src/core/devtools/options.h src/core/devtools/gcn/gcn_context_regs.cpp src/core/devtools/gcn/gcn_op_names.cpp src/core/devtools/gcn/gcn_shader_regs.cpp src/core/devtools/widget/cmd_list.cpp src/core/devtools/widget/cmd_list.h + src/core/devtools/widget/common.h src/core/devtools/widget/frame_dump.cpp src/core/devtools/widget/frame_dump.h src/core/devtools/widget/frame_graph.cpp src/core/devtools/widget/frame_graph.h + src/core/devtools/widget/imgui_memory_editor.h + src/core/devtools/widget/reg_popup.cpp + src/core/devtools/widget/reg_popup.h + src/core/devtools/widget/reg_view.cpp + src/core/devtools/widget/reg_view.h + src/core/devtools/widget/text_editor.cpp + src/core/devtools/widget/text_editor.h ) set(COMMON src/common/logging/backend.cpp diff --git a/src/core/debug_state.cpp b/src/core/debug_state.cpp index 050143e6e..93b00285d 100644 --- a/src/core/debug_state.cpp +++ b/src/core/debug_state.cpp @@ -1,13 +1,16 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/assert.h" #include "common/native_clock.h" #include "common/singleton.h" #include "debug_state.h" -#include "libraries/kernel/event_queues.h" +#include "devtools/widget/common.h" #include "libraries/kernel/time_management.h" #include "libraries/system/msgdialog.h" +#include "video_core/amdgpu/pm4_cmds.h" using namespace DebugStateType; @@ -95,8 +98,68 @@ void DebugStateImpl::ResumeGuestThreads() { } void DebugStateImpl::RequestFrameDump(s32 count) { + ASSERT(!DumpingCurrentFrame()); gnm_frame_dump_request_count = count; frame_dump_list.clear(); frame_dump_list.resize(count); waiting_submit_pause = true; } + +void DebugStateImpl::PushQueueDump(QueueDump dump) { + ASSERT(DumpingCurrentFrame()); + std::unique_lock lock{frame_dump_list_mutex}; + auto& frame = GetFrameDump(); + { // Find draw calls + auto data = std::span{dump.data}; + auto initial_data = data.data(); + while (!data.empty()) { + const auto* header = reinterpret_cast(data.data()); + const auto type = header->type; + if (type == 2) { + data = data.subspan(1); + } else if (type != 3) { + UNREACHABLE(); + } + const AmdGpu::PM4ItOpcode opcode = header->opcode; + if (Core::Devtools::Widget::IsDrawCall(opcode)) { + const auto offset = + reinterpret_cast(header) - reinterpret_cast(initial_data); + const auto addr = dump.base_addr + offset; + waiting_reg_dumps.emplace(addr, &frame); + waiting_reg_dumps_dbg.emplace( + addr, + fmt::format("#{} h({}) queue {} {} {}", + frame_dump_list.size() - gnm_frame_dump_request_count, addr, + magic_enum::enum_name(dump.type), dump.submit_num, dump.num2)); + } + data = data.subspan(header->NumWords() + 1); + } + } + frame.queues.push_back(std::move(dump)); +} + +void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, + const AmdGpu::Liverpool::Regs& regs) { + std::scoped_lock lock{frame_dump_list_mutex}; + const auto it = waiting_reg_dumps.find(header_addr); + if (it == waiting_reg_dumps.end()) { + return; + } + auto& frame = *it->second; + waiting_reg_dumps.erase(it); + waiting_reg_dumps_dbg.erase(waiting_reg_dumps_dbg.find(header_addr)); + auto& dump = frame.regs[header_addr - base_addr]; + dump.regs = regs; + for (int i = 0; i < RegDump::MaxShaderStages; i++) { + if (regs.stage_enable.IsStageEnabled(i)) { + auto stage = regs.ProgramForStage(i); + if (stage->address_lo != 0) { + auto code = stage->Code(); + dump.stages[i] = ShaderDump{ + .user_data = *stage, + .code = std::vector{code.begin(), code.end()}, + }; + } + } + } +} diff --git a/src/core/debug_state.h b/src/core/debug_state.h index 00c687fa5..26dfa202e 100644 --- a/src/core/debug_state.h +++ b/src/core/debug_state.h @@ -5,10 +5,14 @@ #include #include +#include +#include #include #include #include "common/types.h" +#include "video_core/amdgpu/liverpool.h" +#include "video_core/renderer_vulkan/vk_pipeline_cache.h" #ifdef _WIN32 #ifndef WIN32_LEAN_AND_MEAN @@ -42,10 +46,23 @@ struct QueueDump { u32 submit_num; u32 num2; // acb: queue_num; else: buffer_in_submit std::vector data; + uintptr_t base_addr; +}; + +struct ShaderDump { + Vulkan::Liverpool::ShaderProgram user_data{}; + std::vector code{}; +}; + +struct RegDump { + static constexpr size_t MaxShaderStages = 5; + Vulkan::Liverpool::Regs regs{}; + std::array stages{}; }; struct FrameDump { std::vector queues; + std::unordered_map regs; // address -> reg dump }; class DebugStateImpl { @@ -61,15 +78,24 @@ class DebugStateImpl { std::atomic_int32_t gnm_frame_count = 0; s32 gnm_frame_dump_request_count = -1; + std::unordered_map waiting_reg_dumps; + std::unordered_map waiting_reg_dumps_dbg; bool waiting_submit_pause = false; bool should_show_frame_dump = false; - std::mutex frame_dump_list_mutex; + std::shared_mutex frame_dump_list_mutex; std::vector frame_dump_list{}; std::queue debug_message_popup; public: + void ShowDebugMessage(std::string message) { + if (message.empty()) { + return; + } + debug_message_popup.push(std::move(message)); + } + void AddCurrentThreadToGuestList(); void RemoveCurrentThreadFromGuestList(); @@ -99,6 +125,11 @@ public: return gnm_frame_dump_request_count > 0; } + bool DumpingCurrentReg() { + std::shared_lock lock{frame_dump_list_mutex}; + return !waiting_reg_dumps.empty(); + } + bool ShouldPauseInSubmit() const { return waiting_submit_pause && gnm_frame_dump_request_count == 0; } @@ -109,17 +140,10 @@ public: return frame_dump_list[frame_dump_list.size() - gnm_frame_dump_request_count]; } - void PushQueueDump(QueueDump dump) { - std::unique_lock lock{frame_dump_list_mutex}; - GetFrameDump().queues.push_back(std::move(dump)); - } + void PushQueueDump(QueueDump dump); - void ShowDebugMessage(std::string message) { - if (message.empty()) { - return; - } - debug_message_popup.push(std::move(message)); - } + void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, + const AmdGpu::Liverpool::Regs& regs); }; } // namespace DebugStateType diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index 0c7e85e4c..17ef43bbc 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -10,6 +10,7 @@ #include "imgui/imgui_std.h" #include "imgui_internal.h" #include "layer.h" +#include "options.h" #include "widget/frame_dump.h" #include "widget/frame_graph.h" @@ -18,10 +19,9 @@ using namespace Core::Devtools; using L = Core::Devtools::Layer; static bool show_simple_fps = false; + static float fps_scale = 1.0f; - static bool show_advanced_debug = false; - static int dump_frame_count = 1; static Widget::FrameGraph frame_graph; @@ -29,12 +29,16 @@ static std::vector frame_viewers; static float debug_popup_timing = 3.0f; +static bool just_opened_options = false; + void L::DrawMenuBar() { const auto& ctx = *GImGui; const auto& io = ctx.IO; auto isSystemPaused = DebugState.IsGuestThreadsPaused(); + bool open_popup_options = false; + if (BeginMainMenuBar()) { if (BeginMenu("Options")) { if (MenuItemEx("Emulator Paused", nullptr, nullptr, isSystemPaused)) { @@ -55,6 +59,7 @@ void L::DrawMenuBar() { } ImGui::EndMenu(); } + open_popup_options = MenuItem("Options"); ImGui::EndMenu(); } EndMainMenuBar(); @@ -74,6 +79,11 @@ void L::DrawMenuBar() { } } } + + if (open_popup_options) { + OpenPopup("GPU Tools Options"); + just_opened_options = true; + } } void L::DrawAdvanced() { @@ -91,13 +101,19 @@ void L::DrawAdvanced() { ->AddText({10.0f, io.DisplaySize.y - 40.0f}, IM_COL32_WHITE, "Emulator paused"); } - if (DebugState.should_show_frame_dump) { + if (DebugState.should_show_frame_dump && DebugState.waiting_reg_dumps.empty()) { DebugState.should_show_frame_dump = false; std::unique_lock lock{DebugState.frame_dump_list_mutex}; while (!DebugState.frame_dump_list.empty()) { - auto frame_dump = std::move(DebugState.frame_dump_list.back()); - DebugState.frame_dump_list.pop_back(); + const auto& frame_dump = DebugState.frame_dump_list.back(); frame_viewers.emplace_back(frame_dump); + DebugState.frame_dump_list.pop_back(); + } + static bool first_time = true; + if (first_time) { + first_time = false; + DebugState.ShowDebugMessage("Tip: You can shift+click any\n" + "popup to open a new window"); } } @@ -133,6 +149,30 @@ void L::DrawAdvanced() { debug_popup_timing = 3.0f; } } + + bool close_popup_options = true; + if (BeginPopupModal("GPU Tools Options", &close_popup_options, + ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) { + static char disassembly_cli[512]; + + if (just_opened_options) { + just_opened_options = false; + auto s = Options.disassembly_cli.copy(disassembly_cli, sizeof(disassembly_cli) - 1); + disassembly_cli[s] = '\0'; + } + + InputText("Shader disassembler: ", disassembly_cli, sizeof(disassembly_cli)); + if (IsItemHovered()) { + SetTooltip(R"(Command to disassemble shaders. Example "dis.exe" --raw "{src}")"); + } + + if (Button("Save")) { + Options.disassembly_cli = disassembly_cli; + SaveIniSettingsToDisk(io.IniFilename); + CloseCurrentPopup(); + } + EndPopup(); + } } void L::DrawSimple() { @@ -140,26 +180,54 @@ void L::DrawSimple() { Text("Frame time: %.3f ms (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); } +static void LoadSettings(const char* line) { + int i; + float f; + if (sscanf(line, "fps_scale=%f", &f) == 1) { + fps_scale = f; + return; + } + if (sscanf(line, "show_advanced_debug=%d", &i) == 1) { + show_advanced_debug = i != 0; + return; + } + if (sscanf(line, "show_frame_graph=%d", &i) == 1) { + frame_graph.is_open = i != 0; + return; + } + if (sscanf(line, "dump_frame_count=%d", &i) == 1) { + dump_frame_count = i; + return; + } +} + void L::SetupSettings() { frame_graph.is_open = true; + using SettingLoader = void (*)(const char*); + ImGuiSettingsHandler handler{}; handler.TypeName = "DevtoolsLayer"; handler.TypeHash = ImHashStr(handler.TypeName); handler.ReadOpenFn = [](ImGuiContext*, ImGuiSettingsHandler*, const char* name) { - return std::string_view("Data") == name ? (void*)1 : nullptr; + if (std::string_view("Data") == name) { + static_assert(std::is_same_v); + return (void*)&LoadSettings; + } + if (std::string_view("CmdList") == name) { + static_assert( + std::is_same_v); + return (void*)&Widget::CmdListViewer::LoadConfig; + } + if (std::string_view("Options") == name) { + static_assert(std::is_same_v); + return (void*)&LoadOptionsConfig; + } + return (void*)nullptr; }; - handler.ReadLineFn = [](ImGuiContext*, ImGuiSettingsHandler*, void*, const char* line) { - int v; - float f; - if (sscanf(line, "fps_scale=%f", &f) == 1) { - fps_scale = f; - } else if (sscanf(line, "show_advanced_debug=%d", &v) == 1) { - show_advanced_debug = v != 0; - } else if (sscanf(line, "show_frame_graph=%d", &v) == 1) { - frame_graph.is_open = v != 0; - } else if (sscanf(line, "dump_frame_count=%d", &v) == 1) { - dump_frame_count = v; + handler.ReadLineFn = [](ImGuiContext*, ImGuiSettingsHandler*, void* handle, const char* line) { + if (handle != nullptr) { + reinterpret_cast(handle)(line); } }; handler.WriteAllFn = [](ImGuiContext*, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) { @@ -169,12 +237,19 @@ void L::SetupSettings() { buf->appendf("show_frame_graph=%d\n", frame_graph.is_open); buf->appendf("dump_frame_count=%d\n", dump_frame_count); buf->append("\n"); + buf->appendf("[%s][CmdList]\n", handler->TypeName); + Widget::CmdListViewer::SerializeConfig(buf); + buf->append("\n"); + buf->appendf("[%s][Options]\n", handler->TypeName); + SerializeOptionsConfig(buf); + buf->append("\n"); }; AddSettingsHandler(&handler); const ImGuiID dock_id = ImHashStr("FrameDumpDock"); DockBuilderAddNode(dock_id, 0); - DockBuilderSetNodePos(dock_id, ImVec2{50.0, 50.0}); + DockBuilderSetNodePos(dock_id, ImVec2{450.0, 150.0}); + DockBuilderSetNodeSize(dock_id, ImVec2{400.0, 500.0}); DockBuilderFinish(dock_id); } diff --git a/src/core/devtools/options.cpp b/src/core/devtools/options.cpp new file mode 100644 index 000000000..82fa6e87e --- /dev/null +++ b/src/core/devtools/options.cpp @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "options.h" + +namespace Core::Devtools { + +TOptions Options; + +void LoadOptionsConfig(const char* line) { + char str[512]; + if (sscanf(line, "disassembly_cli=%511[^\n]", str) == 1) { + Options.disassembly_cli = str; + return; + } +} + +void SerializeOptionsConfig(ImGuiTextBuffer* buf) { + buf->appendf("disassembly_cli=%s\n", Options.disassembly_cli.c_str()); +} + +} // namespace Core::Devtools diff --git a/src/core/devtools/options.h b/src/core/devtools/options.h new file mode 100644 index 000000000..9d291d768 --- /dev/null +++ b/src/core/devtools/options.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +struct ImGuiTextBuffer; + +namespace Core::Devtools { + +struct TOptions { + std::string disassembly_cli; +}; + +extern TOptions Options; + +void LoadOptionsConfig(const char* line); +void SerializeOptionsConfig(ImGuiTextBuffer* buf); + +} // namespace Core::Devtools diff --git a/src/core/devtools/widget/cmd_list.cpp b/src/core/devtools/widget/cmd_list.cpp index 012891c37..f5d31efef 100644 --- a/src/core/devtools/widget/cmd_list.cpp +++ b/src/core/devtools/widget/cmd_list.cpp @@ -32,6 +32,26 @@ const char* GetOpCodeName(u32 op); namespace Core::Devtools::Widget { +static bool group_batches = true; +static bool show_markers = false; + +void CmdListViewer::LoadConfig(const char* line) { + int i; + if (sscanf(line, "group_batches=%d", &i) == 1) { + group_batches = i != 0; + return; + } + if (sscanf(line, "show_markers=%d", &i) == 1) { + show_markers = i != 0; + return; + } +} + +void CmdListViewer::SerializeConfig(ImGuiTextBuffer* buf) { + buf->appendf("group_batches=%d\n", group_batches); + buf->appendf("show_markers=%d\n", show_markers); +} + template static HdrType GetNext(HdrType this_pm4, uint32_t n) { HdrType curr_pm4 = this_pm4; @@ -43,10 +63,11 @@ static HdrType GetNext(HdrType this_pm4, uint32_t n) { return curr_pm4; } -static void ParsePolygonControl(u32 value) { +void ParsePolygonControl(u32 value, bool begin_table) { auto const reg = reinterpret_cast(value); - if (BeginTable("PA_SU_SC_MODE_CNTL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (!begin_table || + BeginTable("PA_SU_SC_MODE_CNTL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("CULL_FRONT"); @@ -126,14 +147,17 @@ static void ParsePolygonControl(u32 value) { TableSetColumnIndex(1); Text("%X", reg.multi_prim_ib_ena.Value()); - EndTable(); + if (begin_table) { + EndTable(); + } } } -static void ParseAaConfig(u32 value) { +void ParseAaConfig(u32 value, bool begin_table) { auto const reg = reinterpret_cast(value); - if (BeginTable("PA_SC_AA_CONFIG", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (!begin_table || + BeginTable("PA_SC_AA_CONFIG", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("MSAA_NUM_SAMPLES"); @@ -164,14 +188,17 @@ static void ParseAaConfig(u32 value) { TableSetColumnIndex(1); Text("%X", reg.detail_to_exposed_mode.Value()); - EndTable(); + if (begin_table) { + EndTable(); + } } } -static void ParseViewportControl(u32 value) { +void ParseViewportControl(u32 value, bool begin_table) { auto const reg = reinterpret_cast(value); - if (BeginTable("PA_CL_VTE_CNTL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (!begin_table || + BeginTable("PA_CL_VTE_CNTL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("VPORT_X_SCALE_ENA"); @@ -232,14 +259,17 @@ static void ParseViewportControl(u32 value) { TableSetColumnIndex(1); Text("%X", reg.perfcounter_ref.Value()); - EndTable(); + if (begin_table) { + EndTable(); + } } } -static void ParseColorControl(u32 value) { +void ParseColorControl(u32 value, bool begin_table) { auto const reg = reinterpret_cast(value); - if (BeginTable("CB_COLOR_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (!begin_table || + BeginTable("CB_COLOR_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("DISABLE_DUAL_QUAD__VI"); @@ -264,14 +294,17 @@ static void ParseColorControl(u32 value) { TableSetColumnIndex(1); Text("%X", reg.rop3.Value()); - EndTable(); + if (begin_table) { + EndTable(); + } } } -static void ParseColor0Info(u32 value) { +void ParseColor0Info(u32 value, bool begin_table) { auto const reg = reinterpret_cast(value); - if (BeginTable("CB_COLOR_INFO", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (!begin_table || + BeginTable("CB_COLOR_INFO", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("ENDIAN"); @@ -380,14 +413,17 @@ static void ParseColor0Info(u32 value) { TableSetColumnIndex(1); Text("%X", reg.cmask_addr_type.Value()); - EndTable(); + if (begin_table) { + EndTable(); + } } } -static void ParseColor0Attrib(u32 value) { +void ParseColor0Attrib(u32 value, bool begin_table) { auto const reg = reinterpret_cast(value); - if (BeginTable("CB_COLOR_ATTRIB", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (!begin_table || + BeginTable("CB_COLOR_ATTRIB", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("TILE_MODE_INDEX"); @@ -424,14 +460,17 @@ static void ParseColor0Attrib(u32 value) { TableSetColumnIndex(1); Text("%X", reg.force_dst_alpha_1.Value()); - EndTable(); + if (begin_table) { + EndTable(); + } } } -static void ParseBlendControl(u32 value) { +void ParseBlendControl(u32 value, bool begin_table) { auto const reg = reinterpret_cast(value); - if (BeginTable("CB_BLEND_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (!begin_table || + BeginTable("CB_BLEND_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("COLOR_SRCBLEND"); @@ -490,14 +529,17 @@ static void ParseBlendControl(u32 value) { TableSetColumnIndex(1); Text("%X", reg.disable_rop3.Value()); - EndTable(); + if (begin_table) { + EndTable(); + } } } -static void ParseDepthRenderControl(u32 value) { +void ParseDepthRenderControl(u32 value, bool begin_table) { auto const reg = reinterpret_cast(value); - if (BeginTable("DB_RENDER_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (!begin_table || + BeginTable("DB_RENDER_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("DEPTH_CLEAR_ENABLE"); @@ -558,14 +600,17 @@ static void ParseDepthRenderControl(u32 value) { TableSetColumnIndex(1); Text("%X", reg.decompress_enable.Value()); - EndTable(); + if (begin_table) { + EndTable(); + } } } -static void ParseDepthControl(u32 value) { +void ParseDepthControl(u32 value, bool begin_table) { auto const reg = reinterpret_cast(value); - if (BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (!begin_table || + BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("STENCIL_ENABLE"); @@ -628,14 +673,17 @@ static void ParseDepthControl(u32 value) { TableSetColumnIndex(1); Text("%X", reg.disable_color_writes_on_depth_pass.Value()); - EndTable(); + if (begin_table) { + EndTable(); + } } } -static void ParseEqaa(u32 value) { +void ParseEqaa(u32 value, bool begin_table) { auto const reg = reinterpret_cast(value); - if (BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (!begin_table || + BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("MAX_ANCHOR_SAMPLES"); @@ -708,14 +756,17 @@ static void ParseEqaa(u32 value) { TableSetColumnIndex(1); Text("%X", reg.enable_postz_overrasterization.Value()); - EndTable(); + if (begin_table) { + EndTable(); + } } } -static void ParseZInfo(u32 value) { +void ParseZInfo(u32 value, bool begin_table) { auto const reg = reinterpret_cast(value); - if (BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + if (!begin_table || + BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { TableNextRow(); TableSetColumnIndex(0); Text("FORMAT"); @@ -776,40 +827,41 @@ static void ParseZInfo(u32 value) { TableSetColumnIndex(1); Text("%X", reg.zrange_precision.Value()); - EndTable(); + if (begin_table) { + EndTable(); + } } } void CmdListViewer::OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body) { using namespace std::string_view_literals; - enum class NOP_PAYLOAD : u32 { - ACB_SUBMIT_MRK = 0x68750013, - ALLOC_ALIGN8 = 0x68753000, - PUSH_MARKER = 0x68750001, - SET_VSHARP = 0x68750004, - SET_TSHARP = 0x68750005, - SET_SSHARP = 0x68750006, - SET_USER_DATA = 0x6875000d, - }; - auto get_noppayload_text = [](NOP_PAYLOAD const nop_payload) { +#define NOP_PAYLOAD \ + P(PUSH_MARKER, 0x68750001) \ + P(POP_MARKER, 0x68750002) \ + P(SET_MARKER, 0x68750003) \ + P(SET_VSHARP, 0x68750004) \ + P(SET_TSHARP, 0x68750005) \ + P(SET_SSHARP, 0x68750006) \ + P(ACB_SUBMIT_MRK, 0x68750013) \ + P(SET_USER_DATA, 0x6875000D) \ + P(PATCHED_FLIP, 0x68750776) \ + P(PREPARE_FLIP, 0x68750777) \ + P(PREPARE_FLIP_LABEL, 0x68750778) \ + P(PREPARE_FLIP_INTERRUPT, 0x68750780) \ + P(PREPARE_FLIP_INTERRUPT_LABEL, 0x68750781) \ + P(ALLOC_ALIGN8, 0x68753000) + + auto get_nop_payload_text = [](u32 const nop_payload) { switch (nop_payload) { - case NOP_PAYLOAD::ACB_SUBMIT_MRK: - return "ACB_SUBMIT_MRK"sv; - case NOP_PAYLOAD::ALLOC_ALIGN8: - return "ALLOC_ALIGN8"sv; - case NOP_PAYLOAD::PUSH_MARKER: - return "PUSH_MARKER"sv; - case NOP_PAYLOAD::SET_VSHARP: - return "SET_VSHARP"sv; - case NOP_PAYLOAD::SET_TSHARP: - return "SET_TSHARP"sv; - case NOP_PAYLOAD::SET_SSHARP: - return "SET_SSHARP"sv; - case NOP_PAYLOAD::SET_USER_DATA: - return "SET_USER_DATA"sv; +#define P(name, value) \ + case value: \ + return #name##sv; + NOP_PAYLOAD +#undef P + default: + return ""sv; } - return ""sv; }; Separator(); @@ -822,7 +874,7 @@ void CmdListViewer::OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body) for (unsigned i = 0; i < pkt->header.count + 1; ++i) { Text("%02X: %08X", i, payload[i]); if ((payload[i] & 0xffff0000) == 0x68750000) { - const auto& e = get_noppayload_text((NOP_PAYLOAD)payload[i]); + const auto& e = get_nop_payload_text(payload[i]); if (!e.empty()) { SameLine(); Text("(%s)", e.data()); @@ -836,7 +888,7 @@ void CmdListViewer::OnSetBase(AmdGpu::PM4Type3Header const* header, u32 const* b Separator(); BeginGroup(); - auto const* pkt = reinterpret_cast(header); + // auto const* pkt = reinterpret_cast(header); Text("BASE_INDEX: %08X", body[0]); Text("ADDRESS0 : %08X", body[1]); Text("ADDRESS1 : %08X", body[2]); @@ -1025,20 +1077,31 @@ void CmdListViewer::OnDispatch(AmdGpu::PM4Type3Header const* header, u32 const* EndGroup(); } -CmdListViewer::CmdListViewer(FrameDumpViewer* parent, const std::vector& cmd_list) - : parent(parent) { +CmdListViewer::CmdListViewer(DebugStateType::FrameDump* _frame_dump, + const std::vector& cmd_list, uintptr_t _base_addr, + std::string _name) + : frame_dump(_frame_dump), base_addr(_base_addr), name(std::move(_name)) { using namespace AmdGpu; cmdb_addr = (uintptr_t)cmd_list.data(); cmdb_size = cmd_list.size() * sizeof(u32); + cmdb_view_name = fmt::format("[GFX] Command buffer {}###cmdview_hex_{}", this->name, cmdb_addr); + cmdb_view.Open = false; + cmdb_view.ReadOnly = true; + auto const* pm4_hdr = reinterpret_cast(cmdb_addr); size_t processed_size = 0; size_t prev_offset = 0; + u32 batch_id = 0; std::string marker{}; + if (cmdb_size > 0) { + events.emplace_back(BatchBegin{.id = 0}); + } + while (processed_size < cmdb_size) { auto* next_pm4_hdr = GetNext(pm4_hdr, 1); auto processed_len = @@ -1048,22 +1111,28 @@ CmdListViewer::CmdListViewer(FrameDumpViewer* parent, const std::vector& cm if (pm4_hdr->type == PM4Type3Header::TYPE) { auto const* pm4_t3 = reinterpret_cast(pm4_hdr); + auto opcode = pm4_t3->opcode; - if (pm4_t3->opcode == PM4ItOpcode::Nop) { + if (opcode == PM4ItOpcode::Nop) { auto const* it_body = reinterpret_cast(pm4_hdr + 1); - if (it_body[0] == 0x68750001) { + switch (it_body[0]) { + case PM4CmdNop::PayloadType::DebugSetMarker: marker = std::string{(char*)&it_body[1]}; + break; + case PM4CmdNop::PayloadType::DebugMarkerPush: + events.emplace_back(PushMarker{ + .name = std::string{(char*)&it_body[1]}, + }); + break; + case PM4CmdNop::PayloadType::DebugMarkerPop: + events.emplace_back(PopMarker{}); + break; + default: + break; } } - if (pm4_t3->opcode == PM4ItOpcode::DispatchDirect || - pm4_t3->opcode == PM4ItOpcode::DispatchIndirect || - pm4_t3->opcode == PM4ItOpcode::DrawIndex2 || - pm4_t3->opcode == PM4ItOpcode::DrawIndexAuto || - pm4_t3->opcode == PM4ItOpcode::DrawIndexOffset2 || - pm4_t3->opcode == PM4ItOpcode::DrawIndexIndirect - // ... - ) { + if (IsDrawCall(opcode)) { // All these commands are terminated by NOP at the end, so // it is safe to skip it to be even with CP // next_pm4_hdr = get_next(next_pm4_hdr, 1); @@ -1071,15 +1140,17 @@ CmdListViewer::CmdListViewer(FrameDumpViewer* parent, const std::vector& cm // processed_len += nop_len; // processed_size += nop_len; - batches.emplace_back(BatchInfo{ - marker, - prev_offset, - processed_size, - processed_size - processed_len, - pm4_t3->opcode, + events.emplace_back(BatchInfo{ + .id = batch_id++, + .marker = marker, + .start_addr = prev_offset, + .end_addr = processed_size, + .command_addr = processed_size - processed_len, + .type = opcode, }); prev_offset = processed_size; marker.clear(); + events.emplace_back(BatchBegin{.id = batch_id}); } } @@ -1088,18 +1159,58 @@ CmdListViewer::CmdListViewer(FrameDumpViewer* parent, const std::vector& cm // state batch (last) if (processed_size - prev_offset > 0) { - batches.emplace_back(BatchInfo{ - marker, - prev_offset, - processed_size, - 0, - static_cast(0xFF), + events.emplace_back(BatchInfo{ + .id = batch_id++, + .marker = marker, + .start_addr = prev_offset, + .end_addr = processed_size, + .command_addr = 0, + .type = static_cast(0xFF), }); } + if (!events.empty() && std::holds_alternative(events.back())) { + events.pop_back(); + } } void CmdListViewer::Draw() { + const auto& ctx = *GetCurrentContext(); + + if (batch_view.open) { + batch_view.Draw(); + } + for (auto it = extra_batch_view.begin(); it != extra_batch_view.end();) { + if (!it->open) { + it = extra_batch_view.erase(it); + continue; + } + it->Draw(); + ++it; + } + + if (cmdb_view.Open) { + MemoryEditor::Sizes s; + cmdb_view.CalcSizes(s, cmdb_size, cmdb_addr); + SetNextWindowSize({s.WindowWidth, s.WindowWidth * 0.6f}, ImGuiCond_FirstUseEver); + SetNextWindowSizeConstraints({0.0f}, {s.WindowWidth, FLT_MAX}); + if (Begin(cmdb_view_name.c_str(), &cmdb_view.Open, + ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings)) { + cmdb_view.DrawContents((void*)cmdb_addr, cmdb_size, base_addr); + if (cmdb_view.ContentsWidthChanged) { + cmdb_view.CalcSizes(s, cmdb_size, cmdb_addr); + SetWindowSize({s.WindowWidth, s.WindowWidth * 0.6f}); + } + } + End(); + } + + PushID(name.c_str()); if (BeginChild("cmd_queue", {})) { + + Checkbox("Group batches", &group_batches); + SameLine(); + Checkbox("Show markers", &show_markers); + char queue_name[32]{}; if (vqid < 254) { std::snprintf(queue_name, sizeof(queue_name), "%s %d", vqid > 254 ? "GFX" : "ASC", @@ -1111,113 +1222,240 @@ void CmdListViewer::Draw() { Text("queue : %s", queue_name); Text("base addr: %08llX", cmdb_addr); SameLine(); - if (SmallButton(">")) { - parent->cmdb_view.Open ^= true; + if (SmallButton("Memory >")) { + cmdb_view.Open ^= true; } Text("size : %04llX", cmdb_size); Separator(); - char batch_hdr[128]; - for (int batch_id = 0; batch_id < batches.size(); ++batch_id) { - auto processed_size = 0ull; - auto const* pm4_hdr = - reinterpret_cast(cmdb_addr + batches[batch_id].start_addr); + if (TreeNode("Batches")) { + int tree_depth = 0; + int tree_depth_show = 0; - sprintf(batch_hdr, "%08llX: batch-%03d | %s", cmdb_addr + batches[batch_id].start_addr, - batch_id, batches[batch_id].marker.c_str()); - - if (batch_id == batch_bp) { // highlight batch at breakpoint - PushStyleColor(ImGuiCol_Header, ImVec4{1.0f, 0.5f, 0.5f, 0.5f}); + u32 last_batch_id = ~0u; + if (!events.empty() && std::holds_alternative(events.back())) { + last_batch_id = std::get(events.back()).id; } - if (batches[batch_id].type == static_cast(0xFF) || - CollapsingHeader(batch_hdr)) { - auto const batch_sz = batches[batch_id].end_addr - batches[batch_id].start_addr; - while (processed_size < batch_sz) { - AmdGpu::PM4ItOpcode op{0xFFu}; + u32 batch_id = ~0u; + u32 current_highlight_batch = ~0u; - if (pm4_hdr->type == AmdGpu::PM4Type3Header::TYPE) { - auto const* pm4_t3 = - reinterpret_cast(pm4_hdr); - op = pm4_t3->opcode; + int id = 0; + PushID(0); + for (const auto& event : events) { + PopID(); + PushID(id++); - static char header_name[128]; - sprintf(header_name, "%08llX: %s", - cmdb_addr + batches[batch_id].start_addr + processed_size, - Gcn::GetOpCodeName((u32)op)); + if (std::holds_alternative(event)) { + batch_id = std::get(event).id; + } - if (TreeNode(header_name)) { - bool just_opened = IsItemToggledOpen(); - if (BeginTable("split", 1)) { - TableNextColumn(); - Text("size: %d", pm4_hdr->count + 1); + if (show_markers) { + if (std::holds_alternative(event)) { + if (tree_depth_show >= tree_depth) { + auto& marker = std::get(event); + bool show = TreeNode(&event, "%s", marker.name.c_str()); + if (show) { + tree_depth_show++; + } + } + tree_depth++; + continue; + } + if (std::holds_alternative(event)) { + if (tree_depth_show >= tree_depth) { + tree_depth_show--; + TreePop(); + } + tree_depth--; + continue; + } + if (tree_depth_show < tree_depth) { + continue; + } + } - if (just_opened) { + if (!std::holds_alternative(event)) { + continue; + } + + auto& batch = std::get(event); + auto const* pm4_hdr = + reinterpret_cast(cmdb_addr + batch.start_addr); + + char batch_hdr[128]; + if (batch.type == static_cast(0xFF)) { + snprintf(batch_hdr, sizeof(batch_hdr), "State batch"); + } else if (!batch.marker.empty()) { + snprintf(batch_hdr, sizeof(batch_hdr), "%08llX: batch-%03d %s | %s", + cmdb_addr + batch.start_addr, batch.id, + Gcn::GetOpCodeName(static_cast(batch.type)), + batch.marker.c_str()); + } else { + snprintf(batch_hdr, sizeof(batch_hdr), "%08llX: batch-%03d %s", + cmdb_addr + batch.start_addr, batch.id, + Gcn::GetOpCodeName(static_cast(batch.type))); + } + + if (batch.id == batch_bp) { // highlight batch at breakpoint + PushStyleColor(ImGuiCol_Header, ImVec4{1.0f, 0.5f, 0.5f, 0.5f}); + } + if (batch.id == highlight_batch) { + PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 0.7f, 0.7f, 1.0f}); + } + + const auto open_batch_view = [&, this] { + if (frame_dump->regs.contains(batch.command_addr)) { + auto data = frame_dump->regs.at(batch.command_addr); + if (GetIO().KeyShift) { + auto& pop = extra_batch_view.emplace_back(); + pop.SetData(data, batch_id); + pop.open = true; + } else { + batch_view.SetData(data, batch_id); + batch_view.open = true; + } + } + }; + + bool show_batch_content = true; + + if (group_batches) { + show_batch_content = + CollapsingHeader(batch_hdr, ImGuiTreeNodeFlags_AllowOverlap); + SameLine(GetContentRegionAvail().x - 40.0f); + if (Button("->", {40.0f, 0.0f})) { + open_batch_view(); + } + } + + if (show_batch_content) { + auto processed_size = 0ull; + auto bb = ctx.LastItemData.Rect; + if (group_batches) { + Indent(); + } + auto const batch_sz = batch.end_addr - batch.start_addr; + + while (processed_size < batch_sz) { + AmdGpu::PM4ItOpcode op{0xFFu}; + + if (pm4_hdr->type == AmdGpu::PM4Type3Header::TYPE) { + auto const* pm4_t3 = + reinterpret_cast(pm4_hdr); + op = pm4_t3->opcode; + + char header_name[128]; + sprintf(header_name, "%08llX: %s", + cmdb_addr + batch.start_addr + processed_size, + Gcn::GetOpCodeName((u32)op)); + + bool open_pm4 = TreeNode(header_name); + if (!group_batches) { + if (IsDrawCall(op)) { + SameLine(GetContentRegionAvail().x - 40.0f); + if (Button("->", {40.0f, 0.0f})) { + open_batch_view(); + } + } + if (IsItemHovered() && ctx.IO.KeyShift) { + if (BeginTooltip()) { + Text("Batch %d", batch_id); + EndTooltip(); + } + } + } + if (open_pm4) { + if (IsItemToggledOpen()) { // Editor - parent->cmdb_view.GotoAddrAndHighlight( + cmdb_view.GotoAddrAndHighlight( reinterpret_cast(pm4_hdr) - cmdb_addr, reinterpret_cast(pm4_hdr) - cmdb_addr + (pm4_hdr->count + 2) * 4); } - auto const* it_body = - reinterpret_cast(pm4_hdr + 1); + if (BeginTable("split", 1)) { + TableNextColumn(); + Text("size: %d", pm4_hdr->count + 1); - switch (op) { - case AmdGpu::PM4ItOpcode::Nop: { - OnNop(pm4_t3, it_body); - break; - } - case AmdGpu::PM4ItOpcode::SetBase: { - OnSetBase(pm4_t3, it_body); - break; - } - case AmdGpu::PM4ItOpcode::SetContextReg: { - OnSetContextReg(pm4_t3, it_body); - break; - } - case AmdGpu::PM4ItOpcode::SetShReg: { - OnSetShReg(pm4_t3, it_body); - break; - } - case AmdGpu::PM4ItOpcode::DispatchDirect: { - OnDispatch(pm4_t3, it_body); - break; - } - default: { - auto const* payload = &it_body[0]; - for (unsigned i = 0; i < pm4_hdr->count + 1; ++i) { - Text("%02X: %08X", i, payload[i]); + auto const* it_body = + reinterpret_cast(pm4_hdr + 1); + + switch (op) { + case AmdGpu::PM4ItOpcode::Nop: { + OnNop(pm4_t3, it_body); + break; + } + case AmdGpu::PM4ItOpcode::SetBase: { + OnSetBase(pm4_t3, it_body); + break; + } + case AmdGpu::PM4ItOpcode::SetContextReg: { + OnSetContextReg(pm4_t3, it_body); + break; + } + case AmdGpu::PM4ItOpcode::SetShReg: { + OnSetShReg(pm4_t3, it_body); + break; + } + case AmdGpu::PM4ItOpcode::DispatchDirect: { + OnDispatch(pm4_t3, it_body); + break; + } + default: { + auto const* payload = &it_body[0]; + for (unsigned i = 0; i < pm4_hdr->count + 1; ++i) { + Text("%02X: %08X", i, payload[i]); + } + } } - } - } - EndTable(); + EndTable(); + } + TreePop(); } - TreePop(); + + } else { + Text(""); } - } else { - Text(""); + + auto const* next_pm4_hdr = GetNext(pm4_hdr, 1); + auto const processed_len = reinterpret_cast(next_pm4_hdr) - + reinterpret_cast(pm4_hdr); + pm4_hdr = next_pm4_hdr; + processed_size += processed_len; } - auto const* next_pm4_hdr = GetNext(pm4_hdr, 1); - auto const processed_len = reinterpret_cast(next_pm4_hdr) - - reinterpret_cast(pm4_hdr); - pm4_hdr = next_pm4_hdr; - processed_size += processed_len; + if (group_batches) { + Unindent(); + }; + bb = {{0.0f, bb.Max.y}, ctx.LastItemData.Rect.Max}; + if (bb.Contains(ctx.IO.MousePos)) { + current_highlight_batch = batch.id; + } + } + + if (batch.id == highlight_batch) { + PopStyleColor(); + } + + if (batch.id == batch_bp) { + PopStyleColor(); + } + + if (batch.id == last_batch_id) { + Separator(); } } + PopID(); - if (batch_id == batch_bp) { - PopStyleColor(); - } + highlight_batch = current_highlight_batch; - if (batch_id == batches.size() - 2) { - Separator(); - } + TreePop(); } } EndChild(); + PopID(); } } // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/devtools/widget/cmd_list.h b/src/core/devtools/widget/cmd_list.h index a6ecd9323..971c8fffe 100644 --- a/src/core/devtools/widget/cmd_list.h +++ b/src/core/devtools/widget/cmd_list.h @@ -5,10 +5,14 @@ #pragma once +#include #include +#include +#include "common.h" #include "common/types.h" -#include "video_core/buffer_cache/buffer_cache.h" +#include "imgui_memory_editor.h" +#include "reg_view.h" namespace AmdGpu { union PM4Type3Header; @@ -19,43 +23,50 @@ namespace Core::Devtools::Widget { class FrameDumpViewer; -class CmdListViewer { - /* - * Generic PM4 header - */ - union PM4Header { - struct { - u32 reserved : 16; - u32 count : 14; - u32 type : 2; // PM4_TYPE - }; - u32 u32All; - }; - struct BatchInfo { - std::string marker{}; - size_t start_addr; - size_t end_addr; - size_t command_addr; - AmdGpu::PM4ItOpcode type; - bool bypass{false}; - }; +void ParsePolygonControl(u32 value, bool begin_table = true); +void ParseAaConfig(u32 value, bool begin_table = true); +void ParseViewportControl(u32 value, bool begin_table = true); +void ParseColorControl(u32 value, bool begin_table = true); +void ParseColor0Info(u32 value, bool begin_table = true); +void ParseColor0Attrib(u32 value, bool begin_table = true); +void ParseBlendControl(u32 value, bool begin_table = true); +void ParseDepthRenderControl(u32 value, bool begin_table = true); +void ParseDepthControl(u32 value, bool begin_table = true); +void ParseEqaa(u32 value, bool begin_table = true); +void ParseZInfo(u32 value, bool begin_table = true); - FrameDumpViewer* parent; - std::vector batches{}; +class CmdListViewer { + + DebugStateType::FrameDump* frame_dump; + + uintptr_t base_addr; + std::string name; + std::vector events{}; uintptr_t cmdb_addr; size_t cmdb_size; + std::string cmdb_view_name; + MemoryEditor cmdb_view; + int batch_bp{-1}; int vqid{255}; + u32 highlight_batch{~0u}; - void OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body); - void OnSetBase(AmdGpu::PM4Type3Header const* header, u32 const* body); - void OnSetContextReg(AmdGpu::PM4Type3Header const* header, u32 const* body); - void OnSetShReg(AmdGpu::PM4Type3Header const* header, u32 const* body); - void OnDispatch(AmdGpu::PM4Type3Header const* header, u32 const* body); + RegView batch_view; + std::vector extra_batch_view; + + static void OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body); + static void OnSetBase(AmdGpu::PM4Type3Header const* header, u32 const* body); + static void OnSetContextReg(AmdGpu::PM4Type3Header const* header, u32 const* body); + static void OnSetShReg(AmdGpu::PM4Type3Header const* header, u32 const* body); + static void OnDispatch(AmdGpu::PM4Type3Header const* header, u32 const* body); public: - explicit CmdListViewer(FrameDumpViewer* parent, const std::vector& cmd_list); + static void LoadConfig(const char* line); + static void SerializeConfig(ImGuiTextBuffer* buf); + + explicit CmdListViewer(DebugStateType::FrameDump* frame_dump, const std::vector& cmd_list, + uintptr_t base_addr = 0, std::string name = ""); void Draw(); }; diff --git a/src/core/devtools/widget/common.h b/src/core/devtools/widget/common.h new file mode 100644 index 000000000..701d16399 --- /dev/null +++ b/src/core/devtools/widget/common.h @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include + +#include "common/types.h" +#include "video_core/amdgpu/pm4_opcodes.h" + +namespace Core::Devtools::Widget { + +/* + * Generic PM4 header + */ +union PM4Header { + struct { + u32 reserved : 16; + u32 count : 14; + u32 type : 2; // PM4_TYPE + }; + u32 u32All; +}; + +struct PushMarker { + std::string name{}; +}; + +struct PopMarker {}; + +struct BatchBegin { + u32 id; +}; + +struct BatchInfo { + u32 id; + std::string marker{}; + size_t start_addr; + size_t end_addr; + size_t command_addr; + AmdGpu::PM4ItOpcode type; + bool bypass{false}; +}; + +using GPUEvent = std::variant; + +template +void DrawRow(const char* text, const char* fmt, Args... args) { + ImGui::TableNextColumn(); + ImGui::TextUnformatted(text); + ImGui::TableNextColumn(); + char buf[128]; + snprintf(buf, sizeof(buf), fmt, args...); + ImGui::TextUnformatted(buf); +} + +template +void DrawEnumRow(const char* text, T value) { + DrawRow(text, "%X (%s)", V(value), magic_enum::enum_name(value).data()); +} + +template +void DrawMultipleRow(const char* text, const char* fmt, V arg, Extra&&... extra_args) { + DrawRow(text, fmt, arg); + if constexpr (sizeof...(extra_args) > 0) { + DrawMultipleRow(std::forward(extra_args)...); + } +} + +template +static void DoTooltip(const char* str_id, Args&&... args) { + if (ImGui::BeginTooltip()) { + if (ImGui::BeginTable(str_id, 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { + DrawMultipleRow(std::forward(args)...); + ImGui::EndTable(); + } + ImGui::EndTooltip(); + } +} + +static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) { + using AmdGpu::PM4ItOpcode; + switch (opcode) { + case PM4ItOpcode::DrawIndex2: + case PM4ItOpcode::DrawIndexOffset2: + case PM4ItOpcode::DrawIndexAuto: + case PM4ItOpcode::DrawIndirect: + case PM4ItOpcode::DrawIndexIndirect: + case PM4ItOpcode::DispatchDirect: + case PM4ItOpcode::DispatchIndirect: + return true; + default: + return false; + } +} + +} // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/devtools/widget/frame_dump.cpp b/src/core/devtools/widget/frame_dump.cpp index d27bab90a..29b5cb8ee 100644 --- a/src/core/devtools/widget/frame_dump.cpp +++ b/src/core/devtools/widget/frame_dump.cpp @@ -36,7 +36,8 @@ static std::array small_int_to_str(const s32 i) { namespace Core::Devtools::Widget { -FrameDumpViewer::FrameDumpViewer(FrameDump _frame_dump) : frame_dump(std::move(_frame_dump)) { +FrameDumpViewer::FrameDumpViewer(const FrameDump& _frame_dump) + : frame_dump(std::make_shared(_frame_dump)) { static int unique_id = 0; id = unique_id++; @@ -44,20 +45,19 @@ FrameDumpViewer::FrameDumpViewer(FrameDump _frame_dump) : frame_dump(std::move(_ selected_submit_num = 0; selected_queue_num2 = 0; - cmd_list_viewer.reserve(frame_dump.queues.size()); - for (const auto& cmd : frame_dump.queues) { - cmd_list_viewer.emplace_back(this, cmd.data); - if (cmd.type == QueueType::dcb && cmd.submit_num == selected_submit_num && - cmd.num2 == selected_queue_num2) { - selected_cmd = cmd_list_viewer.size() - 1; + cmd_list_viewer.reserve(frame_dump->queues.size()); + for (const auto& cmd : frame_dump->queues) { + const auto fname = + fmt::format("{}_{}_{:02}_{:02}", id, magic_enum::enum_name(selected_queue_type), + selected_submit_num, selected_queue_num2); + cmd_list_viewer.emplace_back(frame_dump.get(), cmd.data, cmd.base_addr, fname); + if (cmd.type == QueueType::dcb && cmd.submit_num == 0 && cmd.num2 == 0) { + selected_cmd = static_cast(cmd_list_viewer.size() - 1); } } - - cmdb_view.Open = false; - cmdb_view.ReadOnly = true; } -FrameDumpViewer::~FrameDumpViewer() {} +FrameDumpViewer::~FrameDumpViewer() = default; void FrameDumpViewer::Draw() { if (!is_open) { @@ -66,11 +66,11 @@ void FrameDumpViewer::Draw() { char name[32]; snprintf(name, sizeof(name), "Frame #%d dump", id); - static ImGuiID dock_id = ImHashStr("FrameDumpDock"); - SetNextWindowDockID(dock_id, ImGuiCond_Appearing); if (Begin(name, &is_open, ImGuiWindowFlags_NoSavedSettings)) { if (IsWindowAppearing()) { auto window = GetCurrentWindow(); + static ImGuiID dock_id = ImHashStr("FrameDumpDock"); + SetWindowDock(window, dock_id, ImGuiCond_Once | ImGuiCond_FirstUseEver); SetWindowSize(window, ImVec2{470.0f, 600.0f}); } BeginGroup(); @@ -89,12 +89,30 @@ void FrameDumpViewer::Draw() { EndCombo(); } SameLine(); + BeginDisabled(selected_cmd == -1); + if (SmallButton("Dump cmd")) { + auto now_time = fmt::localtime(std::time(nullptr)); + const auto fname = fmt::format("{:%F %H-%M-%S} {}_{}_{}.bin", now_time, + magic_enum::enum_name(selected_queue_type), + selected_submit_num, selected_queue_num2); + Common::FS::IOFile file(fname, Common::FS::FileAccessMode::Write); + const auto& data = frame_dump->queues[selected_cmd].data; + if (file.IsOpen()) { + DebugState.ShowDebugMessage(fmt::format("Dumping cmd as {}", fname)); + file.Write(data); + } else { + DebugState.ShowDebugMessage(fmt::format("Failed to save {}", fname)); + LOG_ERROR(Core, "Failed to open file {}", fname); + } + } + EndDisabled(); + TextEx("Submit num"); SameLine(); if (BeginCombo("##select_submit_num", small_int_to_str(selected_submit_num).data(), ImGuiComboFlags_WidthFitPreview)) { std::array available_submits{}; - for (const auto& cmd : frame_dump.queues) { + for (const auto& cmd : frame_dump->queues) { if (cmd.type == selected_queue_type) { available_submits[cmd.submit_num] = true; } @@ -119,7 +137,7 @@ void FrameDumpViewer::Draw() { if (BeginCombo("##select_queue_num2", small_int_to_str(selected_queue_num2).data(), ImGuiComboFlags_WidthFitPreview)) { std::array available_queues{}; - for (const auto& cmd : frame_dump.queues) { + for (const auto& cmd : frame_dump->queues) { if (cmd.type == selected_queue_type && cmd.submit_num == selected_submit_num) { available_queues[cmd.num2] = true; } @@ -134,34 +152,16 @@ void FrameDumpViewer::Draw() { } } if (selected) { - const auto it = std::ranges::find_if(frame_dump.queues, [&](const auto& cmd) { + const auto it = std::ranges::find_if(frame_dump->queues, [&](const auto& cmd) { return cmd.type == selected_queue_type && cmd.submit_num == selected_submit_num && cmd.num2 == selected_queue_num2; }); - if (it != frame_dump.queues.end()) { - selected_cmd = std::distance(frame_dump.queues.begin(), it); + if (it != frame_dump->queues.end()) { + selected_cmd = static_cast(std::distance(frame_dump->queues.begin(), it)); } } EndCombo(); } - SameLine(); - BeginDisabled(selected_cmd == -1); - if (SmallButton("Dump cmd")) { - auto now_time = fmt::localtime(std::time(nullptr)); - const auto fname = fmt::format("{:%F %H-%M-%S} {}_{}_{}.bin", now_time, - magic_enum::enum_name(selected_queue_type), - selected_submit_num, selected_queue_num2); - Common::FS::IOFile file(fname, Common::FS::FileAccessMode::Write); - auto& data = frame_dump.queues[selected_cmd].data; - if (file.IsOpen()) { - DebugState.ShowDebugMessage(fmt::format("Dumping cmd as {}", fname)); - file.Write(data); - } else { - DebugState.ShowDebugMessage(fmt::format("Failed to save {}", fname)); - LOG_ERROR(Core, "Failed to open file {}", fname); - } - } - EndDisabled(); EndGroup(); if (selected_cmd != -1) { @@ -169,21 +169,6 @@ void FrameDumpViewer::Draw() { } } End(); - - if (cmdb_view.Open && selected_cmd != -1) { - auto& cmd = frame_dump.queues[selected_cmd].data; - auto cmd_size = cmd.size() * sizeof(u32); - MemoryEditor::Sizes s; - cmdb_view.CalcSizes(s, cmd_size, (size_t)cmd.data()); - SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(s.WindowWidth, FLT_MAX)); - - char name[64]; - snprintf(name, sizeof(name), "[GFX] Command buffer %d###cmdbuf_hex_%d", id, id); - if (Begin(name, &cmdb_view.Open, ImGuiWindowFlags_NoScrollbar)) { - cmdb_view.DrawContents(cmd.data(), cmd_size, (size_t)cmd.data()); - } - End(); - } } } // namespace Core::Devtools::Widget diff --git a/src/core/devtools/widget/frame_dump.h b/src/core/devtools/widget/frame_dump.h index d9d11f825..2b3ff2411 100644 --- a/src/core/devtools/widget/frame_dump.h +++ b/src/core/devtools/widget/frame_dump.h @@ -8,7 +8,6 @@ #include "cmd_list.h" #include "core/debug_state.h" -#include "imgui_memory_editor.h" namespace Core::Devtools::Widget { @@ -17,11 +16,10 @@ class CmdListViewer; class FrameDumpViewer { friend class CmdListViewer; - DebugStateType::FrameDump frame_dump; + std::shared_ptr frame_dump; int id; std::vector cmd_list_viewer; - MemoryEditor cmdb_view; DebugStateType::QueueType selected_queue_type; s32 selected_submit_num; @@ -31,7 +29,7 @@ class FrameDumpViewer { public: bool is_open = true; - explicit FrameDumpViewer(DebugStateType::FrameDump frame_dump); + explicit FrameDumpViewer(const DebugStateType::FrameDump& frame_dump); ~FrameDumpViewer(); diff --git a/src/core/devtools/widget/reg_popup.cpp b/src/core/devtools/widget/reg_popup.cpp new file mode 100644 index 000000000..d012437c3 --- /dev/null +++ b/src/core/devtools/widget/reg_popup.cpp @@ -0,0 +1,182 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "reg_popup.h" + +#include +#include +#include + +#include "cmd_list.h" +#include "common.h" + +using namespace ImGui; +using magic_enum::enum_name; + +namespace Core::Devtools::Widget { + +void RegPopup::DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer) { + if (BeginTable("COLOR_BUFFER", 2, ImGuiTableFlags_Borders)) { + TableNextRow(); + + // clang-format off + + DrawMultipleRow( + "BASE_ADDR", "%X", buffer.base_address, + "PITCH.TILE_MAX", "%X", buffer.pitch.tile_max, + "PITCH.FMASK_TILE_MAX", "%X", buffer.pitch.fmask_tile_max, + "SLICE.TILE_MAX", "%X", buffer.slice.tile_max, + "VIEW.SLICE_START", "%X", buffer.view.slice_start, + "VIEW.SLICE_MAX", "%X", buffer.view.slice_max + ); + + TableNextRow(); + TableNextColumn(); + if (TreeNode("Color0Info")) { + TableNextRow(); + TableNextColumn(); + ParseColor0Info(buffer.info.u32all, false); + TreePop(); + } + + TableNextRow(); + TableNextColumn(); + if (TreeNode("Color0Attrib")) { + TableNextRow(); + TableNextColumn(); + ParseColor0Attrib(buffer.attrib.u32all, false); + TreePop(); + } + + TableNextRow(); + DrawMultipleRow( + "CMASK_BASE_EXT", "%X", buffer.cmask_base_address, + "FMASK_BASE_EXT", "%X", buffer.fmask_base_address, + "FMASK_SLICE.TILE_MAX", "%X", buffer.fmask_slice.tile_max, + "CLEAR_WORD0", "%X", buffer.clear_word0, + "CLEAR_WORD1", "%X", buffer.clear_word1 + ); + + DrawMultipleRow( + "Pitch()", "%X", buffer.Pitch(), + "Height()", "%X", buffer.Height(), + "Address()", "%X", buffer.Address(), + "CmaskAddress", "%X", buffer.CmaskAddress(), + "FmaskAddress", "%X", buffer.FmaskAddress(), + "NumSamples()", "%X", buffer.NumSamples(), + "NumSlices()", "%X", buffer.NumSlices(), + "GetColorSliceSize()", "%X", buffer.GetColorSliceSize() + ); + + auto tiling_mode = buffer.GetTilingMode(); + auto num_format = buffer.NumFormat(); + DrawEnumRow("GetTilingMode()", tiling_mode); + DrawRow("IsTiled()", "%X", buffer.IsTiled()); + DrawEnumRow("NumFormat()", num_format); + + // clang-format on + + EndTable(); + } +} + +void RegPopup::DrawDepthBuffer(const DepthBuffer& depth_data) { + const auto& [depth_buffer, depth_control] = depth_data; + + SeparatorText("Depth buffer"); + + if (BeginTable("DEPTH_BUFFER", 2, ImGuiTableFlags_Borders)) { + TableNextRow(); + + // clang-format off + DrawEnumRow("Z_INFO.FORMAT", depth_buffer.z_info.format.Value()); + DrawMultipleRow( + "Z_INFO.NUM_SAMPLES", "%X", depth_buffer.z_info.num_samples, + "Z_INFO.TILE_SPLIT", "%X", depth_buffer.z_info.tile_split, + "Z_INFO.TILE_MODE_INDEX", "%X", depth_buffer.z_info.tile_mode_index, + "Z_INFO.DECOMPRESS_ON_N_ZPLANES", "%X", depth_buffer.z_info.decompress_on_n_zplanes, + "Z_INFO.ALLOW_EXPCLEAR", "%X", depth_buffer.z_info.allow_expclear, + "Z_INFO.READ_SIZE", "%X", depth_buffer.z_info.read_size, + "Z_INFO.TILE_SURFACE_EN", "%X", depth_buffer.z_info.tile_surface_en, + "Z_INFO.CLEAR_DISALLOWED", "%X", depth_buffer.z_info.clear_disallowed, + "Z_INFO.ZRANGE_PRECISION", "%X", depth_buffer.z_info.zrange_precision + ); + + DrawEnumRow("STENCIL_INFO.FORMAT", depth_buffer.stencil_info.format.Value()); + + DrawMultipleRow( + "Z_READ_BASE", "%X", depth_buffer.z_read_base, + "STENCIL_READ_BASE", "%X", depth_buffer.stencil_read_base, + "Z_WRITE_BASE", "%X", depth_buffer.z_write_base, + "STENCIL_WRITE_BASE", "%X", depth_buffer.stencil_write_base, + "DEPTH_SIZE.PITCH_TILE_MAX", "%X", depth_buffer.depth_size.pitch_tile_max, + "DEPTH_SIZE.HEIGHT_TILE_MAX", "%X", depth_buffer.depth_size.height_tile_max, + "DEPTH_SLICE.TILE_MAX", "%X", depth_buffer.depth_slice.tile_max, + "Pitch()", "%X", depth_buffer.Pitch(), + "Height()", "%X", depth_buffer.Height(), + "Address()", "%X", depth_buffer.Address(), + "NumSamples()", "%X", depth_buffer.NumSamples(), + "NumBits()", "%X", depth_buffer.NumBits(), + "GetDepthSliceSize()", "%X", depth_buffer.GetDepthSliceSize() + ); + // clang-format on + EndTable(); + } + SeparatorText("Depth control"); + if (BeginTable("DEPTH_CONTROL", 2, ImGuiTableFlags_Borders)) { + TableNextRow(); + + // clang-format off + DrawMultipleRow( + "STENCIL_ENABLE", "%X", depth_control.stencil_enable, + "DEPTH_ENABLE", "%X", depth_control.depth_enable, + "DEPTH_WRITE_ENABLE", "%X", depth_control.depth_write_enable, + "DEPTH_BOUNDS_ENABLE", "%X", depth_control.depth_bounds_enable + ); + DrawEnumRow("DEPTH_FUNC", depth_control.depth_func.Value()); + DrawRow("BACKFACE_ENABLE", "%X", depth_control.backface_enable); + DrawEnumRow("STENCIL_FUNC", depth_control.stencil_ref_func.Value()); + DrawEnumRow("STENCIL_FUNC_BF", depth_control.stencil_bf_func.Value()); + DrawMultipleRow( + "ENABLE_COLOR_WRITES_ON_DEPTH_FAIL", "%X", depth_control.enable_color_writes_on_depth_fail, + "DISABLE_COLOR_WRITES_ON_DEPTH_PASS", "%X", depth_control.disable_color_writes_on_depth_pass + ); + // clang-format on + + EndTable(); + } +} + +RegPopup::RegPopup() { + static int unique_id = 0; + id = unique_id++; +} + +void RegPopup::SetData(AmdGpu::Liverpool::ColorBuffer color_buffer, u32 batch_id, u32 cb_id) { + this->data = color_buffer; + this->title = fmt::format("Batch #{} CB #{}", batch_id, cb_id); +} + +void RegPopup::SetData(AmdGpu::Liverpool::DepthBuffer depth_buffer, + AmdGpu::Liverpool::DepthControl depth_control, u32 batch_id) { + this->data = std::make_tuple(depth_buffer, depth_control); + this->title = fmt::format("Batch #{} Depth", batch_id); +} + +void RegPopup::Draw() { + + char name[128]; + snprintf(name, sizeof(name), "%s###reg_popup_%d", title.c_str(), id); + + SetNextWindowSize({250.0f, 300.0f}, ImGuiCond_FirstUseEver); + if (Begin(name, &open, ImGuiWindowFlags_NoSavedSettings)) { + if (const auto* buffer = std::get_if(&data)) { + DrawColorBuffer(*buffer); + } else if (const auto* depth_data = std::get_if(&data)) { + DrawDepthBuffer(*depth_data); + } + } + End(); +} + +} // namespace Core::Devtools::Widget diff --git a/src/core/devtools/widget/reg_popup.h b/src/core/devtools/widget/reg_popup.h new file mode 100644 index 000000000..ba4224d73 --- /dev/null +++ b/src/core/devtools/widget/reg_popup.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/types.h" +#include "video_core/renderer_vulkan/liverpool_to_vk.h" + +namespace Core::Devtools::Widget { + +class RegPopup { + int id; + + using DepthBuffer = std::tuple; + + std::variant data; + std::string title{}; + + void DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer); + + void DrawDepthBuffer(const DepthBuffer& depth_data); + +public: + bool open = false; + + RegPopup(); + + void SetData(AmdGpu::Liverpool::ColorBuffer color_buffer, u32 batch_id, u32 cb_id); + + void SetData(AmdGpu::Liverpool::DepthBuffer depth_buffer, + AmdGpu::Liverpool::DepthControl depth_control, u32 batch_id); + + void Draw(); +}; + +} // namespace Core::Devtools::Widget diff --git a/src/core/devtools/widget/reg_view.cpp b/src/core/devtools/widget/reg_view.cpp new file mode 100644 index 000000000..2e8bb8f54 --- /dev/null +++ b/src/core/devtools/widget/reg_view.cpp @@ -0,0 +1,305 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "common/io_file.h" +#include "core/devtools/options.h" +#include "imgui_internal.h" +#include "reg_view.h" + +#if defined(_WIN32) +#define popen _popen +#define pclose _pclose +#endif + +using namespace ImGui; +using magic_enum::enum_name; + +static std::optional exec_cli(const char* cli) { + std::array buffer{}; + std::string output; + const auto f = popen(cli, "r"); + if (!f) { + pclose(f); + return {}; + } + while (fgets(buffer.data(), buffer.size(), f)) { + output += buffer.data(); + } + pclose(f); + return output; +} + +namespace Core::Devtools::Widget { + +void RegView::ProcessShader(int shader_id) { + auto shader = data.stages[shader_id]; + + std::string shader_dis; + + if (Options.disassembly_cli.empty()) { + shader_dis = "No disassembler set"; + } else { + auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin"; + + constexpr std::string_view src_arg = "{src}"; + std::string cli = Options.disassembly_cli; + const auto pos = cli.find(src_arg); + if (pos == std::string::npos) { + DebugState.ShowDebugMessage("Disassembler CLI does not contain {src} argument"); + } else { + cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\""); + Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write); + file.Write(shader.code); + file.Close(); + + auto result = exec_cli(cli.c_str()); + shader_dis = result.value_or("Could not disassemble shader"); + if (shader_dis.empty()) { + shader_dis = "Disassembly empty or failed"; + } + + std::filesystem::remove(bin_path); + } + } + + MemoryEditor hex_view; + hex_view.Open = true; + hex_view.ReadOnly = true; + hex_view.Cols = 16; + hex_view.OptShowAscii = false; + + TextEditor dis_view; + dis_view.SetPalette(TextEditor::GetDarkPalette()); + dis_view.SetReadOnly(true); + dis_view.SetText(shader_dis); + + ShaderCache cache{ + .hex_view = hex_view, + .dis_view = dis_view, + .user_data = shader.user_data.user_data, + }; + shader_decomp.emplace(shader_id, std::move(cache)); +} +void RegView::SelectShader(int id) { + selected_shader = id; + if (!shader_decomp.contains(id)) { + ProcessShader(id); + } +} + +void RegView::DrawRegs() { + const auto& regs = data.regs; + + if (BeginTable("REGS", 2, ImGuiTableFlags_Borders)) { + + auto& s = regs.screen_scissor; + DrawRow("Scissor", "(%d, %d, %d, %d)", s.top_left_x, s.top_left_y, s.bottom_right_x, + s.bottom_right_y); + + auto cc_mode = regs.color_control.mode.Value(); + DrawRow("Color control", "%X (%s)", cc_mode, enum_name(cc_mode).data()); + + const auto open_new_popup = [&](int cb, auto... args) { + if (GetIO().KeyShift) { + auto& pop = extra_reg_popup.emplace_back(); + pop.SetData(args...); + pop.open = true; + } else if (last_selected_cb == cb && default_reg_popup.open) { + default_reg_popup.open = false; + } else { + last_selected_cb = cb; + default_reg_popup.SetData(args...); + if (!default_reg_popup.open) { + default_reg_popup.open = true; + auto popup_pos = + GetCurrentContext()->LastItemData.Rect.Max + ImVec2(5.0f, 0.0f); + SetNextWindowPos(popup_pos, ImGuiCond_Always); + default_reg_popup.Draw(); + } + } + }; + + for (int cb = 0; cb < AmdGpu::Liverpool::NumColorBuffers; ++cb) { + PushID(cb); + + TableNextRow(); + TableNextColumn(); + + const auto& buffer = regs.color_buffers[cb]; + + Text("Color buffer %d", cb); + TableNextColumn(); + if (!buffer || !regs.color_target_mask.GetMask(cb)) { + TextUnformatted("N/A"); + } else { + const char* text = last_selected_cb == cb && default_reg_popup.open ? "x" : "->"; + if (SmallButton(text)) { + open_new_popup(cb, buffer, batch_id, cb); + } + } + + PopID(); + } + + TableNextRow(); + TableNextColumn(); + TextUnformatted("Depth buffer"); + TableNextColumn(); + if (regs.depth_buffer.Address() == 0 || !regs.depth_control.depth_enable) { + TextUnformatted("N/A"); + } else { + constexpr auto depth_id = 0xF3; + const char* text = last_selected_cb == depth_id && default_reg_popup.open ? "x" : "->"; + if (SmallButton(text)) { + open_new_popup(depth_id, regs.depth_buffer, regs.depth_control, batch_id); + } + } + + EndTable(); + } +} + +RegView::RegView() { + static int unique_id = 0; + id = unique_id++; + + char name[128]; + snprintf(name, sizeof(name), "BatchView###reg_dump_%d", id); + SetNextWindowPos({400.0f, 200.0f}); + SetNextWindowSize({450.0f, 500.0f}); + ImGuiID root_dock_id; + Begin(name); + { + char dock_name[64]; + snprintf(dock_name, sizeof(dock_name), "BatchView###reg_dump_%d/dock_space", id); + root_dock_id = ImHashStr(dock_name); + DockSpace(root_dock_id); + } + End(); + + ImGuiID up1, down1; + + DockBuilderRemoveNodeChildNodes(root_dock_id); + DockBuilderSplitNode(root_dock_id, ImGuiDir_Up, 0.2f, &up1, &down1); + + snprintf(name, sizeof(name), "User data###reg_dump_%d/user_data", id); + DockBuilderDockWindow(name, up1); + + snprintf(name, sizeof(name), "Regs###reg_dump_%d/regs", id); + DockBuilderDockWindow(name, down1); + + snprintf(name, sizeof(name), "Disassembly###reg_dump_%d/disassembly", id); + DockBuilderDockWindow(name, down1); + + DockBuilderFinish(root_dock_id); +} + +void RegView::SetData(DebugStateType::RegDump data, u32 batch_id) { + this->data = std::move(data); + this->batch_id = batch_id; + // clear cache + selected_shader = -1; + shader_decomp.clear(); + default_reg_popup.open = false; + extra_reg_popup.clear(); +} + +void RegView::Draw() { + + char name[128]; + snprintf(name, sizeof(name), "BatchView %u###reg_dump_%d", batch_id, id); + if (Begin(name, &open, ImGuiWindowFlags_MenuBar)) { + const char* names[] = {"vs", "ps", "gs", "es", "hs", "ls"}; + + if (BeginMenuBar()) { + if (BeginMenu("Stage")) { + for (int i = 0; i < DebugStateType::RegDump::MaxShaderStages; i++) { + if (data.regs.stage_enable.IsStageEnabled(i)) { + bool selected = selected_shader == i; + if (Selectable(names[i], &selected)) { + SelectShader(i); + } + } + } + ImGui::EndMenu(); + } + if (BeginMenu("Windows")) { + Checkbox("Registers", &show_registers); + Checkbox("User data", &show_user_data); + Checkbox("Disassembly", &show_disassembly); + ImGui::EndMenu(); + } + EndMenuBar(); + } + + char dock_name[64]; + snprintf(dock_name, sizeof(dock_name), "BatchView###reg_dump_%d/dock_space", id); + auto root_dock_id = ImHashStr(dock_name); + DockSpace(root_dock_id); + } + End(); + + auto get_shader = [&]() -> ShaderCache* { + auto shader_cache = shader_decomp.find(selected_shader); + if (shader_cache == shader_decomp.end()) { + return nullptr; + } + return &shader_cache->second; + }; + + if (show_user_data) { + snprintf(name, sizeof(name), "User data###reg_dump_%d/user_data", id); + if (Begin(name, &show_user_data)) { + auto shader = get_shader(); + if (!shader) { + Text("Select a stage"); + } else { + shader->hex_view.DrawContents(shader->user_data.data(), shader->user_data.size()); + } + } + End(); + } + + if (show_disassembly) { + snprintf(name, sizeof(name), "Disassembly###reg_dump_%d/disassembly", id); + if (Begin(name, &show_disassembly)) { + auto shader = get_shader(); + if (!shader) { + Text("Select a stage"); + } else { + shader->dis_view.Render("Disassembly", GetContentRegionAvail()); + } + } + End(); + } + + if (show_registers) { + snprintf(name, sizeof(name), "Regs###reg_dump_%d/regs", id); + if (Begin(name, &show_registers)) { + DrawRegs(); + } + End(); + } + + if (default_reg_popup.open) { + default_reg_popup.Draw(); + } + for (auto it = extra_reg_popup.begin(); it != extra_reg_popup.end();) { + if (!it->open) { + it = extra_reg_popup.erase(it); + continue; + } + it->Draw(); + ++it; + } +} + +} // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/devtools/widget/reg_view.h b/src/core/devtools/widget/reg_view.h new file mode 100644 index 000000000..67ab1e04f --- /dev/null +++ b/src/core/devtools/widget/reg_view.h @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include "core/debug_state.h" +#include "imgui_memory_editor.h" +#include "reg_popup.h" +#include "text_editor.h" + +namespace Core::Devtools::Widget { + +struct ShaderCache { + MemoryEditor hex_view; + TextEditor dis_view; + Vulkan::Liverpool::UserData user_data; +}; + +class RegView { + int id; + + DebugStateType::RegDump data; + u32 batch_id{~0u}; + + std::unordered_map shader_decomp; + int selected_shader{-1}; + RegPopup default_reg_popup; + int last_selected_cb{-1}; + std::vector extra_reg_popup; + + bool show_registers{true}; + bool show_user_data{true}; + bool show_disassembly{true}; + + void ProcessShader(int shader_id); + + void SelectShader(int shader_id); + + void DrawRegs(); + +public: + bool open = false; + + RegView(); + + void SetData(DebugStateType::RegDump data, u32 batch_id); + + void Draw(); +}; + +} // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/devtools/widget/text_editor.cpp b/src/core/devtools/widget/text_editor.cpp new file mode 100644 index 000000000..f447e45a2 --- /dev/null +++ b/src/core/devtools/widget/text_editor.cpp @@ -0,0 +1,2334 @@ +// SPDX-FileCopyrightText: Copyright (c) 2017 BalazsJako +// SPDX-License-Identifier: MIT + +// source: https://github.com/BalazsJako/ImGuiColorTextEdit + +#include +#include +#include +#include +#include + +#include "text_editor.h" + +#define IMGUI_DEFINE_MATH_OPERATORS +#include "imgui.h" // for imGui::GetCurrentWindow() + +// TODO +// - multiline comments vs single-line: latter is blocking start of a ML + +namespace Core::Devtools::Widget { + +template +bool equals(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, BinaryPredicate p) { + for (; first1 != last1 && first2 != last2; ++first1, ++first2) { + if (!p(*first1, *first2)) + return false; + } + return first1 == last1 && first2 == last2; +} + +TextEditor::TextEditor() + : mLineSpacing(1.0f), mUndoIndex(0), mTabSize(4), mOverwrite(false), mReadOnly(false), + mWithinRender(false), mScrollToCursor(false), mScrollToTop(false), mTextChanged(false), + mColorizerEnabled(true), mTextStart(20.0f), mLeftMargin(10), mCursorPositionChanged(false), + mColorRangeMin(0), mColorRangeMax(0), mSelectionMode(SelectionMode::Normal), + mCheckComments(true), mLastClick(-1.0f), mHandleKeyboardInputs(true), + mHandleMouseInputs(true), mIgnoreImGuiChild(false), mShowWhitespaces(true), + mStartTime(std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count()) { + SetPalette(GetDarkPalette()); + mLines.push_back(Line()); +} + +TextEditor::~TextEditor() {} + +void TextEditor::SetLanguageDefinition(const LanguageDefinition& aLanguageDef) { + mLanguageDefinition = aLanguageDef; + mRegexList.clear(); + + for (auto& r : mLanguageDefinition.mTokenRegexStrings) + mRegexList.push_back( + std::make_pair(std::regex(r.first, std::regex_constants::optimize), r.second)); + + Colorize(); +} + +void TextEditor::SetPalette(const Palette& aValue) { + mPaletteBase = aValue; +} + +std::string TextEditor::GetText(const Coordinates& aStart, const Coordinates& aEnd) const { + std::string result; + + auto lstart = aStart.mLine; + auto lend = aEnd.mLine; + auto istart = GetCharacterIndex(aStart); + auto iend = GetCharacterIndex(aEnd); + size_t s = 0; + + for (size_t i = lstart; i < lend; i++) + s += mLines[i].size(); + + result.reserve(s + s / 8); + + while (istart < iend || lstart < lend) { + if (lstart >= (int)mLines.size()) + break; + + auto& line = mLines[lstart]; + if (istart < (int)line.size()) { + result += line[istart].mChar; + istart++; + } else { + istart = 0; + ++lstart; + result += '\n'; + } + } + + return result; +} + +TextEditor::Coordinates TextEditor::GetActualCursorCoordinates() const { + return SanitizeCoordinates(mState.mCursorPosition); +} + +TextEditor::Coordinates TextEditor::SanitizeCoordinates(const Coordinates& aValue) const { + auto line = aValue.mLine; + auto column = aValue.mColumn; + if (line >= (int)mLines.size()) { + if (mLines.empty()) { + line = 0; + column = 0; + } else { + line = (int)mLines.size() - 1; + column = GetLineMaxColumn(line); + } + return Coordinates(line, column); + } else { + column = mLines.empty() ? 0 : std::min(column, GetLineMaxColumn(line)); + return Coordinates(line, column); + } +} + +// https://en.wikipedia.org/wiki/UTF-8 +// We assume that the char is a standalone character (<128) or a leading byte of an UTF-8 code +// sequence (non-10xxxxxx code) +static int UTF8CharLength(TextEditor::Char c) { + if ((c & 0xFE) == 0xFC) + return 6; + if ((c & 0xFC) == 0xF8) + return 5; + if ((c & 0xF8) == 0xF0) + return 4; + else if ((c & 0xF0) == 0xE0) + return 3; + else if ((c & 0xE0) == 0xC0) + return 2; + return 1; +} + +// "Borrowed" from ImGui source +static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) { + if (c < 0x80) { + buf[0] = (char)c; + return 1; + } + if (c < 0x800) { + if (buf_size < 2) + return 0; + buf[0] = (char)(0xc0 + (c >> 6)); + buf[1] = (char)(0x80 + (c & 0x3f)); + return 2; + } + if (c >= 0xdc00 && c < 0xe000) { + return 0; + } + if (c >= 0xd800 && c < 0xdc00) { + if (buf_size < 4) + return 0; + buf[0] = (char)(0xf0 + (c >> 18)); + buf[1] = (char)(0x80 + ((c >> 12) & 0x3f)); + buf[2] = (char)(0x80 + ((c >> 6) & 0x3f)); + buf[3] = (char)(0x80 + ((c) & 0x3f)); + return 4; + } + // else if (c < 0x10000) + { + if (buf_size < 3) + return 0; + buf[0] = (char)(0xe0 + (c >> 12)); + buf[1] = (char)(0x80 + ((c >> 6) & 0x3f)); + buf[2] = (char)(0x80 + ((c) & 0x3f)); + return 3; + } +} + +void TextEditor::Advance(Coordinates& aCoordinates) const { + if (aCoordinates.mLine < (int)mLines.size()) { + auto& line = mLines[aCoordinates.mLine]; + auto cindex = GetCharacterIndex(aCoordinates); + + if (cindex + 1 < (int)line.size()) { + auto delta = UTF8CharLength(line[cindex].mChar); + cindex = std::min(cindex + delta, (int)line.size() - 1); + } else { + ++aCoordinates.mLine; + cindex = 0; + } + aCoordinates.mColumn = GetCharacterColumn(aCoordinates.mLine, cindex); + } +} + +void TextEditor::DeleteRange(const Coordinates& aStart, const Coordinates& aEnd) { + ASSERT(aEnd >= aStart); + ASSERT(!mReadOnly); + + // printf("D(%d.%d)-(%d.%d)\n", aStart.mLine, aStart.mColumn, aEnd.mLine, aEnd.mColumn); + + if (aEnd == aStart) + return; + + auto start = GetCharacterIndex(aStart); + auto end = GetCharacterIndex(aEnd); + + if (aStart.mLine == aEnd.mLine) { + auto& line = mLines[aStart.mLine]; + auto n = GetLineMaxColumn(aStart.mLine); + if (aEnd.mColumn >= n) + line.erase(line.begin() + start, line.end()); + else + line.erase(line.begin() + start, line.begin() + end); + } else { + auto& firstLine = mLines[aStart.mLine]; + auto& lastLine = mLines[aEnd.mLine]; + + firstLine.erase(firstLine.begin() + start, firstLine.end()); + lastLine.erase(lastLine.begin(), lastLine.begin() + end); + + if (aStart.mLine < aEnd.mLine) + firstLine.insert(firstLine.end(), lastLine.begin(), lastLine.end()); + + if (aStart.mLine < aEnd.mLine) + RemoveLine(aStart.mLine + 1, aEnd.mLine + 1); + } + + mTextChanged = true; +} + +int TextEditor::InsertTextAt(Coordinates& /* inout */ aWhere, const char* aValue) { + ASSERT(!mReadOnly); + + int cindex = GetCharacterIndex(aWhere); + int totalLines = 0; + while (*aValue != '\0') { + ASSERT(!mLines.empty()); + + if (*aValue == '\r') { + // skip + ++aValue; + } else if (*aValue == '\n') { + if (cindex < (int)mLines[aWhere.mLine].size()) { + auto& newLine = InsertLine(aWhere.mLine + 1); + auto& line = mLines[aWhere.mLine]; + newLine.insert(newLine.begin(), line.begin() + cindex, line.end()); + line.erase(line.begin() + cindex, line.end()); + } else { + InsertLine(aWhere.mLine + 1); + } + ++aWhere.mLine; + aWhere.mColumn = 0; + cindex = 0; + ++totalLines; + ++aValue; + } else { + auto& line = mLines[aWhere.mLine]; + auto d = UTF8CharLength(*aValue); + while (d-- > 0 && *aValue != '\0') + line.insert(line.begin() + cindex++, Glyph(*aValue++, PaletteIndex::Default)); + ++aWhere.mColumn; + } + + mTextChanged = true; + } + + return totalLines; +} + +void TextEditor::AddUndo(UndoRecord& aValue) { + ASSERT(!mReadOnly); + // printf("AddUndo: (@%d.%d) +\'%s' [%d.%d .. %d.%d], -\'%s', [%d.%d .. %d.%d] (@%d.%d)\n", + // aValue.mBefore.mCursorPosition.mLine, aValue.mBefore.mCursorPosition.mColumn, + // aValue.mAdded.c_str(), aValue.mAddedStart.mLine, aValue.mAddedStart.mColumn, + // aValue.mAddedEnd.mLine, aValue.mAddedEnd.mColumn, aValue.mRemoved.c_str(), + // aValue.mRemovedStart.mLine, aValue.mRemovedStart.mColumn, aValue.mRemovedEnd.mLine, + // aValue.mRemovedEnd.mColumn, aValue.mAfter.mCursorPosition.mLine, + // aValue.mAfter.mCursorPosition.mColumn + // ); + + mUndoBuffer.resize((size_t)(mUndoIndex + 1)); + mUndoBuffer.back() = aValue; + ++mUndoIndex; +} + +TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPosition) const { + ImVec2 origin = ImGui::GetCursorScreenPos(); + ImVec2 local(aPosition.x - origin.x, aPosition.y - origin.y); + + int lineNo = std::max(0, (int)floor(local.y / mCharAdvance.y)); + + int columnCoord = 0; + + if (lineNo >= 0 && lineNo < (int)mLines.size()) { + auto& line = mLines.at(lineNo); + + int columnIndex = 0; + float columnX = 0.0f; + + while ((size_t)columnIndex < line.size()) { + float columnWidth = 0.0f; + + if (line[columnIndex].mChar == '\t') { + float spaceSize = + ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ").x; + float oldX = columnX; + float newColumnX = + (1.0f + std::floor((1.0f + columnX) / (float(mTabSize) * spaceSize))) * + (float(mTabSize) * spaceSize); + columnWidth = newColumnX - oldX; + if (mTextStart + columnX + columnWidth * 0.5f > local.x) + break; + columnX = newColumnX; + columnCoord = (columnCoord / mTabSize) * mTabSize + mTabSize; + columnIndex++; + } else { + char buf[7]; + auto d = UTF8CharLength(line[columnIndex].mChar); + int i = 0; + while (i < 6 && d-- > 0) + buf[i++] = line[columnIndex++].mChar; + buf[i] = '\0'; + columnWidth = + ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf).x; + if (mTextStart + columnX + columnWidth * 0.5f > local.x) + break; + columnX += columnWidth; + columnCoord++; + } + } + } + + return SanitizeCoordinates(Coordinates(lineNo, columnCoord)); +} + +TextEditor::Coordinates TextEditor::FindWordStart(const Coordinates& aFrom) const { + Coordinates at = aFrom; + if (at.mLine >= (int)mLines.size()) + return at; + + auto& line = mLines[at.mLine]; + auto cindex = GetCharacterIndex(at); + + if (cindex >= (int)line.size()) + return at; + + while (cindex > 0 && isspace(line[cindex].mChar)) + --cindex; + + auto cstart = (PaletteIndex)line[cindex].mColorIndex; + while (cindex > 0) { + auto c = line[cindex].mChar; + if ((c & 0xC0) != 0x80) // not UTF code sequence 10xxxxxx + { + if (c <= 32 && isspace(c)) { + cindex++; + break; + } + if (cstart != (PaletteIndex)line[size_t(cindex - 1)].mColorIndex) + break; + } + --cindex; + } + return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex)); +} + +TextEditor::Coordinates TextEditor::FindWordEnd(const Coordinates& aFrom) const { + Coordinates at = aFrom; + if (at.mLine >= (int)mLines.size()) + return at; + + auto& line = mLines[at.mLine]; + auto cindex = GetCharacterIndex(at); + + if (cindex >= (int)line.size()) + return at; + + bool prevspace = (bool)isspace(line[cindex].mChar); + auto cstart = (PaletteIndex)line[cindex].mColorIndex; + while (cindex < (int)line.size()) { + auto c = line[cindex].mChar; + auto d = UTF8CharLength(c); + if (cstart != (PaletteIndex)line[cindex].mColorIndex) + break; + + if (prevspace != !!isspace(c)) { + if (isspace(c)) + while (cindex < (int)line.size() && isspace(line[cindex].mChar)) + ++cindex; + break; + } + cindex += d; + } + return Coordinates(aFrom.mLine, GetCharacterColumn(aFrom.mLine, cindex)); +} + +TextEditor::Coordinates TextEditor::FindNextWord(const Coordinates& aFrom) const { + Coordinates at = aFrom; + if (at.mLine >= (int)mLines.size()) + return at; + + // skip to the next non-word character + auto cindex = GetCharacterIndex(aFrom); + bool isword = false; + bool skip = false; + if (cindex < (int)mLines[at.mLine].size()) { + auto& line = mLines[at.mLine]; + isword = isalnum(line[cindex].mChar); + skip = isword; + } + + while (!isword || skip) { + if (at.mLine >= mLines.size()) { + auto l = std::max(0, (int)mLines.size() - 1); + return Coordinates(l, GetLineMaxColumn(l)); + } + + auto& line = mLines[at.mLine]; + if (cindex < (int)line.size()) { + isword = isalnum(line[cindex].mChar); + + if (isword && !skip) + return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex)); + + if (!isword) + skip = false; + + cindex++; + } else { + cindex = 0; + ++at.mLine; + skip = false; + isword = false; + } + } + + return at; +} + +int TextEditor::GetCharacterIndex(const Coordinates& aCoordinates) const { + if (aCoordinates.mLine >= mLines.size()) + return -1; + auto& line = mLines[aCoordinates.mLine]; + int c = 0; + int i = 0; + for (; i < line.size() && c < aCoordinates.mColumn;) { + if (line[i].mChar == '\t') + c = (c / mTabSize) * mTabSize + mTabSize; + else + ++c; + i += UTF8CharLength(line[i].mChar); + } + return i; +} + +int TextEditor::GetCharacterColumn(int aLine, int aIndex) const { + if (aLine >= mLines.size()) + return 0; + auto& line = mLines[aLine]; + int col = 0; + int i = 0; + while (i < aIndex && i < (int)line.size()) { + auto c = line[i].mChar; + i += UTF8CharLength(c); + if (c == '\t') + col = (col / mTabSize) * mTabSize + mTabSize; + else + col++; + } + return col; +} + +int TextEditor::GetLineCharacterCount(int aLine) const { + if (aLine >= mLines.size()) + return 0; + auto& line = mLines[aLine]; + int c = 0; + for (unsigned i = 0; i < line.size(); c++) + i += UTF8CharLength(line[i].mChar); + return c; +} + +int TextEditor::GetLineMaxColumn(int aLine) const { + if (aLine >= mLines.size()) + return 0; + auto& line = mLines[aLine]; + int col = 0; + for (unsigned i = 0; i < line.size();) { + auto c = line[i].mChar; + if (c == '\t') + col = (col / mTabSize) * mTabSize + mTabSize; + else + col++; + i += UTF8CharLength(c); + } + return col; +} + +bool TextEditor::IsOnWordBoundary(const Coordinates& aAt) const { + if (aAt.mLine >= (int)mLines.size() || aAt.mColumn == 0) + return true; + + auto& line = mLines[aAt.mLine]; + auto cindex = GetCharacterIndex(aAt); + if (cindex >= (int)line.size()) + return true; + + if (mColorizerEnabled) + return line[cindex].mColorIndex != line[size_t(cindex - 1)].mColorIndex; + + return isspace(line[cindex].mChar) != isspace(line[cindex - 1].mChar); +} + +void TextEditor::RemoveLine(int aStart, int aEnd) { + ASSERT(!mReadOnly); + ASSERT(aEnd >= aStart); + ASSERT(mLines.size() > (size_t)(aEnd - aStart)); + + ErrorMarkers etmp; + for (auto& i : mErrorMarkers) { + ErrorMarkers::value_type e(i.first >= aStart ? i.first - 1 : i.first, i.second); + if (e.first >= aStart && e.first <= aEnd) + continue; + etmp.insert(e); + } + mErrorMarkers = std::move(etmp); + + Breakpoints btmp; + for (auto i : mBreakpoints) { + if (i >= aStart && i <= aEnd) + continue; + btmp.insert(i >= aStart ? i - 1 : i); + } + mBreakpoints = std::move(btmp); + + mLines.erase(mLines.begin() + aStart, mLines.begin() + aEnd); + ASSERT(!mLines.empty()); + + mTextChanged = true; +} + +void TextEditor::RemoveLine(int aIndex) { + ASSERT(!mReadOnly); + ASSERT(mLines.size() > 1); + + ErrorMarkers etmp; + for (auto& i : mErrorMarkers) { + ErrorMarkers::value_type e(i.first > aIndex ? i.first - 1 : i.first, i.second); + if (e.first - 1 == aIndex) + continue; + etmp.insert(e); + } + mErrorMarkers = std::move(etmp); + + Breakpoints btmp; + for (auto i : mBreakpoints) { + if (i == aIndex) + continue; + btmp.insert(i >= aIndex ? i - 1 : i); + } + mBreakpoints = std::move(btmp); + + mLines.erase(mLines.begin() + aIndex); + ASSERT(!mLines.empty()); + + mTextChanged = true; +} + +TextEditor::Line& TextEditor::InsertLine(int aIndex) { + ASSERT(!mReadOnly); + + auto& result = *mLines.insert(mLines.begin() + aIndex, Line()); + + ErrorMarkers etmp; + for (auto& i : mErrorMarkers) + etmp.insert(ErrorMarkers::value_type(i.first >= aIndex ? i.first + 1 : i.first, i.second)); + mErrorMarkers = std::move(etmp); + + Breakpoints btmp; + for (auto i : mBreakpoints) + btmp.insert(i >= aIndex ? i + 1 : i); + mBreakpoints = std::move(btmp); + + return result; +} + +std::string TextEditor::GetWordUnderCursor() const { + auto c = GetCursorPosition(); + return GetWordAt(c); +} + +std::string TextEditor::GetWordAt(const Coordinates& aCoords) const { + auto start = FindWordStart(aCoords); + auto end = FindWordEnd(aCoords); + + std::string r; + + auto istart = GetCharacterIndex(start); + auto iend = GetCharacterIndex(end); + + for (auto it = istart; it < iend; ++it) + r.push_back(mLines[aCoords.mLine][it].mChar); + + return r; +} + +ImU32 TextEditor::GetGlyphColor(const Glyph& aGlyph) const { + if (!mColorizerEnabled) + return mPalette[(int)PaletteIndex::Default]; + if (aGlyph.mComment) + return mPalette[(int)PaletteIndex::Comment]; + if (aGlyph.mMultiLineComment) + return mPalette[(int)PaletteIndex::MultiLineComment]; + auto const color = mPalette[(int)aGlyph.mColorIndex]; + if (aGlyph.mPreprocessor) { + const auto ppcolor = mPalette[(int)PaletteIndex::Preprocessor]; + const int c0 = ((ppcolor & 0xff) + (color & 0xff)) / 2; + const int c1 = (((ppcolor >> 8) & 0xff) + ((color >> 8) & 0xff)) / 2; + const int c2 = (((ppcolor >> 16) & 0xff) + ((color >> 16) & 0xff)) / 2; + const int c3 = (((ppcolor >> 24) & 0xff) + ((color >> 24) & 0xff)) / 2; + return ImU32(c0 | (c1 << 8) | (c2 << 16) | (c3 << 24)); + } + return color; +} + +void TextEditor::HandleKeyboardInputs() { + ImGuiIO& io = ImGui::GetIO(); + auto shift = io.KeyShift; + auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl; + auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt; + + if (ImGui::IsWindowFocused()) { + if (ImGui::IsWindowHovered()) + ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput); + // ImGui::CaptureKeyboardFromApp(true); + + io.WantCaptureKeyboard = true; + io.WantTextInput = true; + + if (!IsReadOnly() && ctrl && !shift && !alt && + ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Z))) + Undo(); + else if (!IsReadOnly() && !ctrl && !shift && alt && + ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace))) + Undo(); + else if (!IsReadOnly() && ctrl && !shift && !alt && + ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Y))) + Redo(); + else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow))) + MoveUp(1, shift); + else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow))) + MoveDown(1, shift); + else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow))) + MoveLeft(1, shift, ctrl); + else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow))) + MoveRight(1, shift, ctrl); + else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp))) + MoveUp(GetPageSize() - 4, shift); + else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown))) + MoveDown(GetPageSize() - 4, shift); + else if (!alt && ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home))) + MoveTop(shift); + else if (ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End))) + MoveBottom(shift); + else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home))) + MoveHome(shift); + else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End))) + MoveEnd(shift); + else if (!IsReadOnly() && !ctrl && !shift && !alt && + ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) + Delete(); + else if (!IsReadOnly() && !ctrl && !shift && !alt && + ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace))) + Backspace(); + else if (!ctrl && !shift && !alt && + ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) + mOverwrite ^= true; + else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) + Copy(); + else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C))) + Copy(); + else if (!IsReadOnly() && !ctrl && shift && !alt && + ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) + Paste(); + else if (!IsReadOnly() && ctrl && !shift && !alt && + ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_V))) + Paste(); + else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_X))) + Cut(); + else if (!ctrl && shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) + Cut(); + else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_A))) + SelectAll(); + else if (!IsReadOnly() && !ctrl && !shift && !alt && + ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Enter))) + EnterCharacter('\n', false); + else if (!IsReadOnly() && !ctrl && !alt && + ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Tab))) + EnterCharacter('\t', shift); + + if (!IsReadOnly() && !io.InputQueueCharacters.empty()) { + for (int i = 0; i < io.InputQueueCharacters.Size; i++) { + auto c = io.InputQueueCharacters[i]; + if (c != 0 && (c == '\n' || c >= 32)) + EnterCharacter(c, shift); + } + io.InputQueueCharacters.resize(0); + } + } +} + +void TextEditor::HandleMouseInputs() { + ImGuiIO& io = ImGui::GetIO(); + auto shift = io.KeyShift; + auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl; + auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt; + + if (ImGui::IsWindowHovered()) { + if (!shift && !alt) { + auto click = ImGui::IsMouseClicked(0); + auto doubleClick = ImGui::IsMouseDoubleClicked(0); + auto t = ImGui::GetTime(); + auto tripleClick = click && !doubleClick && + (mLastClick != -1.0f && (t - mLastClick) < io.MouseDoubleClickTime); + + /* + Left mouse button triple click + */ + + if (tripleClick) { + if (!ctrl) { + mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = + ScreenPosToCoordinates(ImGui::GetMousePos()); + mSelectionMode = SelectionMode::Line; + SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + } + + mLastClick = -1.0f; + } + + /* + Left mouse button double click + */ + + else if (doubleClick) { + if (!ctrl) { + mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = + ScreenPosToCoordinates(ImGui::GetMousePos()); + if (mSelectionMode == SelectionMode::Line) + mSelectionMode = SelectionMode::Normal; + else + mSelectionMode = SelectionMode::Word; + SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + } + + mLastClick = (float)ImGui::GetTime(); + } + + /* + Left mouse button click + */ + else if (click) { + mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = + ScreenPosToCoordinates(ImGui::GetMousePos()); + if (ctrl) + mSelectionMode = SelectionMode::Word; + else + mSelectionMode = SelectionMode::Normal; + SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + + mLastClick = (float)ImGui::GetTime(); + } + // Mouse left button dragging (=> update selection) + else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0)) { + io.WantCaptureMouse = true; + mState.mCursorPosition = mInteractiveEnd = + ScreenPosToCoordinates(ImGui::GetMousePos()); + SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + } + } + } +} + +void TextEditor::Render() { + /* Compute mCharAdvance regarding to scaled font size (Ctrl + mouse wheel)*/ + const float fontSize = + ImGui::GetFont() + ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr) + .x; + mCharAdvance = ImVec2(fontSize, ImGui::GetTextLineHeightWithSpacing() * mLineSpacing); + + /* Update palette with the current alpha from style */ + for (int i = 0; i < (int)PaletteIndex::Max; ++i) { + auto color = ImGui::ColorConvertU32ToFloat4(mPaletteBase[i]); + color.w *= ImGui::GetStyle().Alpha; + mPalette[i] = ImGui::ColorConvertFloat4ToU32(color); + } + + ASSERT(mLineBuffer.empty()); + + auto contentSize = ImGui::GetWindowContentRegionMax(); + auto drawList = ImGui::GetWindowDrawList(); + float longest(mTextStart); + + if (mScrollToTop) { + mScrollToTop = false; + ImGui::SetScrollY(0.f); + } + + ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos(); + auto scrollX = ImGui::GetScrollX(); + auto scrollY = ImGui::GetScrollY(); + + auto lineNo = (int)floor(scrollY / mCharAdvance.y); + auto globalLineMax = (int)mLines.size(); + auto lineMax = + std::max(0, std::min((int)mLines.size() - 1, + lineNo + (int)floor((scrollY + contentSize.y) / mCharAdvance.y))); + + // Deduce mTextStart by evaluating mLines size (global lineMax) plus two spaces as text width + char buf[16]; + snprintf(buf, 16, " %d ", globalLineMax); + mTextStart = ImGui::GetFont() + ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr) + .x + + mLeftMargin; + + if (!mLines.empty()) { + float spaceSize = + ImGui::GetFont() + ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr) + .x; + + while (lineNo <= lineMax) { + ImVec2 lineStartScreenPos = + ImVec2(cursorScreenPos.x, cursorScreenPos.y + lineNo * mCharAdvance.y); + ImVec2 textScreenPos = ImVec2(lineStartScreenPos.x + mTextStart, lineStartScreenPos.y); + + auto& line = mLines[lineNo]; + longest = std::max( + mTextStart + TextDistanceToLineStart(Coordinates(lineNo, GetLineMaxColumn(lineNo))), + longest); + auto columnNo = 0; + Coordinates lineStartCoord(lineNo, 0); + Coordinates lineEndCoord(lineNo, GetLineMaxColumn(lineNo)); + + // Draw selection for the current line + float sstart = -1.0f; + float ssend = -1.0f; + + ASSERT(mState.mSelectionStart <= mState.mSelectionEnd); + if (mState.mSelectionStart <= lineEndCoord) + sstart = mState.mSelectionStart > lineStartCoord + ? TextDistanceToLineStart(mState.mSelectionStart) + : 0.0f; + if (mState.mSelectionEnd > lineStartCoord) + ssend = TextDistanceToLineStart( + mState.mSelectionEnd < lineEndCoord ? mState.mSelectionEnd : lineEndCoord); + + if (mState.mSelectionEnd.mLine > lineNo) + ssend += mCharAdvance.x; + + if (sstart != -1 && ssend != -1 && sstart < ssend) { + ImVec2 vstart(lineStartScreenPos.x + mTextStart + sstart, lineStartScreenPos.y); + ImVec2 vend(lineStartScreenPos.x + mTextStart + ssend, + lineStartScreenPos.y + mCharAdvance.y); + drawList->AddRectFilled(vstart, vend, mPalette[(int)PaletteIndex::Selection]); + } + + // Draw breakpoints + auto start = ImVec2(lineStartScreenPos.x + scrollX, lineStartScreenPos.y); + + if (mBreakpoints.count(lineNo + 1) != 0) { + auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, + lineStartScreenPos.y + mCharAdvance.y); + drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::Breakpoint]); + } + + // Draw error markers + auto errorIt = mErrorMarkers.find(lineNo + 1); + if (errorIt != mErrorMarkers.end()) { + auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, + lineStartScreenPos.y + mCharAdvance.y); + drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::ErrorMarker]); + + if (ImGui::IsMouseHoveringRect(lineStartScreenPos, end)) { + ImGui::BeginTooltip(); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.2f, 0.2f, 1.0f)); + ImGui::Text("Error at line %d:", errorIt->first); + ImGui::PopStyleColor(); + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 0.2f, 1.0f)); + ImGui::Text("%s", errorIt->second.c_str()); + ImGui::PopStyleColor(); + ImGui::EndTooltip(); + } + } + + // Draw line number (right aligned) + snprintf(buf, 16, "%d ", lineNo + 1); + + auto lineNoWidth = + ImGui::GetFont() + ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr) + .x; + drawList->AddText( + ImVec2(lineStartScreenPos.x + mTextStart - lineNoWidth, lineStartScreenPos.y), + mPalette[(int)PaletteIndex::LineNumber], buf); + + if (mState.mCursorPosition.mLine == lineNo) { + auto focused = ImGui::IsWindowFocused(); + + // Highlight the current line (where the cursor is) + if (!HasSelection()) { + auto end = ImVec2(start.x + contentSize.x + scrollX, start.y + mCharAdvance.y); + drawList->AddRectFilled( + start, end, + mPalette[(int)(focused ? PaletteIndex::CurrentLineFill + : PaletteIndex::CurrentLineFillInactive)]); + drawList->AddRect(start, end, mPalette[(int)PaletteIndex::CurrentLineEdge], + 1.0f); + } + + // Render the cursor + if (focused) { + auto timeEnd = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + auto elapsed = timeEnd - mStartTime; + if (elapsed > 400) { + float width = 1.0f; + auto cindex = GetCharacterIndex(mState.mCursorPosition); + float cx = TextDistanceToLineStart(mState.mCursorPosition); + + if (mOverwrite && cindex < (int)line.size()) { + auto c = line[cindex].mChar; + if (c == '\t') { + auto x = (1.0f + + std::floor((1.0f + cx) / (float(mTabSize) * spaceSize))) * + (float(mTabSize) * spaceSize); + width = x - cx; + } else { + char buf2[2]; + buf2[0] = line[cindex].mChar; + buf2[1] = '\0'; + width = + ImGui::GetFont() + ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf2) + .x; + } + } + ImVec2 cstart(textScreenPos.x + cx, lineStartScreenPos.y); + ImVec2 cend(textScreenPos.x + cx + width, + lineStartScreenPos.y + mCharAdvance.y); + drawList->AddRectFilled(cstart, cend, mPalette[(int)PaletteIndex::Cursor]); + if (elapsed > 800) + mStartTime = timeEnd; + } + } + } + + // Render colorized text + auto prevColor = + line.empty() ? mPalette[(int)PaletteIndex::Default] : GetGlyphColor(line[0]); + ImVec2 bufferOffset; + + for (int i = 0; i < line.size();) { + auto& glyph = line[i]; + auto color = GetGlyphColor(glyph); + + if ((color != prevColor || glyph.mChar == '\t' || glyph.mChar == ' ') && + !mLineBuffer.empty()) { + const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, + textScreenPos.y + bufferOffset.y); + drawList->AddText(newOffset, prevColor, mLineBuffer.c_str()); + auto textSize = + ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, + mLineBuffer.c_str(), nullptr, nullptr); + bufferOffset.x += textSize.x; + mLineBuffer.clear(); + } + prevColor = color; + + if (glyph.mChar == '\t') { + auto oldX = bufferOffset.x; + bufferOffset.x = (1.0f + std::floor((1.0f + bufferOffset.x) / + (float(mTabSize) * spaceSize))) * + (float(mTabSize) * spaceSize); + ++i; + + if (mShowWhitespaces) { + const auto s = ImGui::GetFontSize(); + const auto x1 = textScreenPos.x + oldX + 1.0f; + const auto x2 = textScreenPos.x + bufferOffset.x - 1.0f; + const auto y = textScreenPos.y + bufferOffset.y + s * 0.5f; + const ImVec2 p1(x1, y); + const ImVec2 p2(x2, y); + const ImVec2 p3(x2 - s * 0.2f, y - s * 0.2f); + const ImVec2 p4(x2 - s * 0.2f, y + s * 0.2f); + drawList->AddLine(p1, p2, 0x90909090); + drawList->AddLine(p2, p3, 0x90909090); + drawList->AddLine(p2, p4, 0x90909090); + } + } else if (glyph.mChar == ' ') { + if (mShowWhitespaces) { + const auto s = ImGui::GetFontSize(); + const auto x = textScreenPos.x + bufferOffset.x + spaceSize * 0.5f; + const auto y = textScreenPos.y + bufferOffset.y + s * 0.5f; + drawList->AddCircleFilled(ImVec2(x, y), 1.5f, 0x80808080, 4); + } + bufferOffset.x += spaceSize; + i++; + } else { + auto l = UTF8CharLength(glyph.mChar); + while (l-- > 0) + mLineBuffer.push_back(line[i++].mChar); + } + ++columnNo; + } + + if (!mLineBuffer.empty()) { + const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, + textScreenPos.y + bufferOffset.y); + drawList->AddText(newOffset, prevColor, mLineBuffer.c_str()); + mLineBuffer.clear(); + } + + ++lineNo; + } + + // Draw a tooltip on known identifiers/preprocessor symbols + if (ImGui::IsMousePosValid()) { + auto id = GetWordAt(ScreenPosToCoordinates(ImGui::GetMousePos())); + if (!id.empty()) { + auto it = mLanguageDefinition.mIdentifiers.find(id); + if (it != mLanguageDefinition.mIdentifiers.end()) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(it->second.mDeclaration.c_str()); + ImGui::EndTooltip(); + } else { + auto pi = mLanguageDefinition.mPreprocIdentifiers.find(id); + if (pi != mLanguageDefinition.mPreprocIdentifiers.end()) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(pi->second.mDeclaration.c_str()); + ImGui::EndTooltip(); + } + } + } + } + } + + ImGui::Dummy(ImVec2((longest + 2), mLines.size() * mCharAdvance.y)); + + if (mScrollToCursor) { + EnsureCursorVisible(); + ImGui::SetWindowFocus(); + mScrollToCursor = false; + } +} + +void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) { + mWithinRender = true; + mTextChanged = false; + mCursorPositionChanged = false; + + ImGui::PushStyleColor(ImGuiCol_ChildBg, + ImGui::ColorConvertU32ToFloat4(mPalette[(int)PaletteIndex::Background])); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); + if (!mIgnoreImGuiChild) + ImGui::BeginChild(aTitle, aSize, aBorder, + ImGuiWindowFlags_HorizontalScrollbar | + ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NoMove); + + if (mHandleKeyboardInputs) { + HandleKeyboardInputs(); + ImGui::PushAllowKeyboardFocus(true); + } + + if (mHandleMouseInputs) + HandleMouseInputs(); + + ColorizeInternal(); + Render(); + + if (mHandleKeyboardInputs) + ImGui::PopAllowKeyboardFocus(); + + if (!mIgnoreImGuiChild) + ImGui::EndChild(); + + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + + mWithinRender = false; +} + +void TextEditor::SetText(const std::string& aText) { + mLines.clear(); + mLines.emplace_back(Line()); + for (auto chr : aText) { + if (chr == '\r') { + // ignore the carriage return character + } else if (chr == '\n') + mLines.emplace_back(Line()); + else { + mLines.back().emplace_back(Glyph(chr, PaletteIndex::Default)); + } + } + + mTextChanged = true; + mScrollToTop = true; + + mUndoBuffer.clear(); + mUndoIndex = 0; + + Colorize(); +} + +void TextEditor::SetTextLines(const std::vector& aLines) { + mLines.clear(); + + if (aLines.empty()) { + mLines.emplace_back(Line()); + } else { + mLines.resize(aLines.size()); + + for (size_t i = 0; i < aLines.size(); ++i) { + const std::string& aLine = aLines[i]; + + mLines[i].reserve(aLine.size()); + for (size_t j = 0; j < aLine.size(); ++j) + mLines[i].emplace_back(Glyph(aLine[j], PaletteIndex::Default)); + } + } + + mTextChanged = true; + mScrollToTop = true; + + mUndoBuffer.clear(); + mUndoIndex = 0; + + Colorize(); +} + +void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) { + ASSERT(!mReadOnly); + + UndoRecord u; + + u.mBefore = mState; + + if (HasSelection()) { + if (aChar == '\t' && mState.mSelectionStart.mLine != mState.mSelectionEnd.mLine) { + + auto start = mState.mSelectionStart; + auto end = mState.mSelectionEnd; + auto originalEnd = end; + + if (start > end) + std::swap(start, end); + start.mColumn = 0; + // end.mColumn = end.mLine < mLines.size() ? mLines[end.mLine].size() : 0; + if (end.mColumn == 0 && end.mLine > 0) + --end.mLine; + if (end.mLine >= (int)mLines.size()) + end.mLine = mLines.empty() ? 0 : (int)mLines.size() - 1; + end.mColumn = GetLineMaxColumn(end.mLine); + + // if (end.mColumn >= GetLineMaxColumn(end.mLine)) + // end.mColumn = GetLineMaxColumn(end.mLine) - 1; + + u.mRemovedStart = start; + u.mRemovedEnd = end; + u.mRemoved = GetText(start, end); + + bool modified = false; + + for (int i = start.mLine; i <= end.mLine; i++) { + auto& line = mLines[i]; + if (aShift) { + if (!line.empty()) { + if (line.front().mChar == '\t') { + line.erase(line.begin()); + modified = true; + } else { + for (int j = 0; + j < mTabSize && !line.empty() && line.front().mChar == ' '; j++) { + line.erase(line.begin()); + modified = true; + } + } + } + } else { + line.insert(line.begin(), Glyph('\t', TextEditor::PaletteIndex::Background)); + modified = true; + } + } + + if (modified) { + start = Coordinates(start.mLine, GetCharacterColumn(start.mLine, 0)); + Coordinates rangeEnd; + if (originalEnd.mColumn != 0) { + end = Coordinates(end.mLine, GetLineMaxColumn(end.mLine)); + rangeEnd = end; + u.mAdded = GetText(start, end); + } else { + end = Coordinates(originalEnd.mLine, 0); + rangeEnd = Coordinates(end.mLine - 1, GetLineMaxColumn(end.mLine - 1)); + u.mAdded = GetText(start, rangeEnd); + } + + u.mAddedStart = start; + u.mAddedEnd = rangeEnd; + u.mAfter = mState; + + mState.mSelectionStart = start; + mState.mSelectionEnd = end; + AddUndo(u); + + mTextChanged = true; + + EnsureCursorVisible(); + } + + return; + } // c == '\t' + else { + u.mRemoved = GetSelectedText(); + u.mRemovedStart = mState.mSelectionStart; + u.mRemovedEnd = mState.mSelectionEnd; + DeleteSelection(); + } + } // HasSelection + + auto coord = GetActualCursorCoordinates(); + u.mAddedStart = coord; + + ASSERT(!mLines.empty()); + + if (aChar == '\n') { + InsertLine(coord.mLine + 1); + auto& line = mLines[coord.mLine]; + auto& newLine = mLines[coord.mLine + 1]; + + if (mLanguageDefinition.mAutoIndentation) + for (size_t it = 0; + it < line.size() && isascii(line[it].mChar) && isblank(line[it].mChar); ++it) + newLine.push_back(line[it]); + + const size_t whitespaceSize = newLine.size(); + auto cindex = GetCharacterIndex(coord); + newLine.insert(newLine.end(), line.begin() + cindex, line.end()); + line.erase(line.begin() + cindex, line.begin() + line.size()); + SetCursorPosition( + Coordinates(coord.mLine + 1, GetCharacterColumn(coord.mLine + 1, (int)whitespaceSize))); + u.mAdded = (char)aChar; + } else { + char buf[7]; + int e = ImTextCharToUtf8(buf, 7, aChar); + if (e > 0) { + buf[e] = '\0'; + auto& line = mLines[coord.mLine]; + auto cindex = GetCharacterIndex(coord); + + if (mOverwrite && cindex < (int)line.size()) { + auto d = UTF8CharLength(line[cindex].mChar); + + u.mRemovedStart = mState.mCursorPosition; + u.mRemovedEnd = + Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex + d)); + + while (d-- > 0 && cindex < (int)line.size()) { + u.mRemoved += line[cindex].mChar; + line.erase(line.begin() + cindex); + } + } + + for (auto p = buf; *p != '\0'; p++, ++cindex) + line.insert(line.begin() + cindex, Glyph(*p, PaletteIndex::Default)); + u.mAdded = buf; + + SetCursorPosition(Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex))); + } else + return; + } + + mTextChanged = true; + + u.mAddedEnd = GetActualCursorCoordinates(); + u.mAfter = mState; + + AddUndo(u); + + Colorize(coord.mLine - 1, 3); + EnsureCursorVisible(); +} + +void TextEditor::SetReadOnly(bool aValue) { + mReadOnly = aValue; +} + +void TextEditor::SetColorizerEnable(bool aValue) { + mColorizerEnabled = aValue; +} + +void TextEditor::SetCursorPosition(const Coordinates& aPosition) { + if (mState.mCursorPosition != aPosition) { + mState.mCursorPosition = aPosition; + mCursorPositionChanged = true; + EnsureCursorVisible(); + } +} + +void TextEditor::SetSelectionStart(const Coordinates& aPosition) { + mState.mSelectionStart = SanitizeCoordinates(aPosition); + if (mState.mSelectionStart > mState.mSelectionEnd) + std::swap(mState.mSelectionStart, mState.mSelectionEnd); +} + +void TextEditor::SetSelectionEnd(const Coordinates& aPosition) { + mState.mSelectionEnd = SanitizeCoordinates(aPosition); + if (mState.mSelectionStart > mState.mSelectionEnd) + std::swap(mState.mSelectionStart, mState.mSelectionEnd); +} + +void TextEditor::SetSelection(const Coordinates& aStart, const Coordinates& aEnd, + SelectionMode aMode) { + auto oldSelStart = mState.mSelectionStart; + auto oldSelEnd = mState.mSelectionEnd; + + mState.mSelectionStart = SanitizeCoordinates(aStart); + mState.mSelectionEnd = SanitizeCoordinates(aEnd); + if (mState.mSelectionStart > mState.mSelectionEnd) + std::swap(mState.mSelectionStart, mState.mSelectionEnd); + + switch (aMode) { + case TextEditor::SelectionMode::Normal: + break; + case TextEditor::SelectionMode::Word: { + mState.mSelectionStart = FindWordStart(mState.mSelectionStart); + if (!IsOnWordBoundary(mState.mSelectionEnd)) + mState.mSelectionEnd = FindWordEnd(FindWordStart(mState.mSelectionEnd)); + break; + } + case TextEditor::SelectionMode::Line: { + const auto lineNo = mState.mSelectionEnd.mLine; + const auto lineSize = (size_t)lineNo < mLines.size() ? mLines[lineNo].size() : 0; + mState.mSelectionStart = Coordinates(mState.mSelectionStart.mLine, 0); + mState.mSelectionEnd = Coordinates(lineNo, GetLineMaxColumn(lineNo)); + break; + } + default: + break; + } + + if (mState.mSelectionStart != oldSelStart || mState.mSelectionEnd != oldSelEnd) + mCursorPositionChanged = true; +} + +void TextEditor::SetTabSize(int aValue) { + mTabSize = std::max(0, std::min(32, aValue)); +} + +void TextEditor::InsertText(const std::string& aValue) { + InsertText(aValue.c_str()); +} + +void TextEditor::InsertText(const char* aValue) { + if (aValue == nullptr) + return; + + auto pos = GetActualCursorCoordinates(); + auto start = std::min(pos, mState.mSelectionStart); + int totalLines = pos.mLine - start.mLine; + + totalLines += InsertTextAt(pos, aValue); + + SetSelection(pos, pos); + SetCursorPosition(pos); + Colorize(start.mLine - 1, totalLines + 2); +} + +void TextEditor::DeleteSelection() { + ASSERT(mState.mSelectionEnd >= mState.mSelectionStart); + + if (mState.mSelectionEnd == mState.mSelectionStart) + return; + + DeleteRange(mState.mSelectionStart, mState.mSelectionEnd); + + SetSelection(mState.mSelectionStart, mState.mSelectionStart); + SetCursorPosition(mState.mSelectionStart); + Colorize(mState.mSelectionStart.mLine, 1); +} + +void TextEditor::MoveUp(int aAmount, bool aSelect) { + auto oldPos = mState.mCursorPosition; + mState.mCursorPosition.mLine = std::max(0, mState.mCursorPosition.mLine - aAmount); + if (oldPos != mState.mCursorPosition) { + if (aSelect) { + if (oldPos == mInteractiveStart) + mInteractiveStart = mState.mCursorPosition; + else if (oldPos == mInteractiveEnd) + mInteractiveEnd = mState.mCursorPosition; + else { + mInteractiveStart = mState.mCursorPosition; + mInteractiveEnd = oldPos; + } + } else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd); + + EnsureCursorVisible(); + } +} + +void TextEditor::MoveDown(int aAmount, bool aSelect) { + ASSERT(mState.mCursorPosition.mColumn >= 0); + auto oldPos = mState.mCursorPosition; + mState.mCursorPosition.mLine = + std::max(0, std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + aAmount)); + + if (mState.mCursorPosition != oldPos) { + if (aSelect) { + if (oldPos == mInteractiveEnd) + mInteractiveEnd = mState.mCursorPosition; + else if (oldPos == mInteractiveStart) + mInteractiveStart = mState.mCursorPosition; + else { + mInteractiveStart = oldPos; + mInteractiveEnd = mState.mCursorPosition; + } + } else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd); + + EnsureCursorVisible(); + } +} + +static bool IsUTFSequence(char c) { + return (c & 0xC0) == 0x80; +} + +void TextEditor::MoveLeft(int aAmount, bool aSelect, bool aWordMode) { + if (mLines.empty()) + return; + + auto oldPos = mState.mCursorPosition; + mState.mCursorPosition = GetActualCursorCoordinates(); + auto line = mState.mCursorPosition.mLine; + auto cindex = GetCharacterIndex(mState.mCursorPosition); + + while (aAmount-- > 0) { + if (cindex == 0) { + if (line > 0) { + --line; + if ((int)mLines.size() > line) + cindex = (int)mLines[line].size(); + else + cindex = 0; + } + } else { + --cindex; + if (cindex > 0) { + if ((int)mLines.size() > line) { + while (cindex > 0 && IsUTFSequence(mLines[line][cindex].mChar)) + --cindex; + } + } + } + + mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex)); + if (aWordMode) { + mState.mCursorPosition = FindWordStart(mState.mCursorPosition); + cindex = GetCharacterIndex(mState.mCursorPosition); + } + } + + mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex)); + + ASSERT(mState.mCursorPosition.mColumn >= 0); + if (aSelect) { + if (oldPos == mInteractiveStart) + mInteractiveStart = mState.mCursorPosition; + else if (oldPos == mInteractiveEnd) + mInteractiveEnd = mState.mCursorPosition; + else { + mInteractiveStart = mState.mCursorPosition; + mInteractiveEnd = oldPos; + } + } else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd, + aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal); + + EnsureCursorVisible(); +} + +void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode) { + auto oldPos = mState.mCursorPosition; + + if (mLines.empty() || oldPos.mLine >= mLines.size()) + return; + + auto cindex = GetCharacterIndex(mState.mCursorPosition); + while (aAmount-- > 0) { + auto lindex = mState.mCursorPosition.mLine; + auto& line = mLines[lindex]; + + if (cindex >= line.size()) { + if (mState.mCursorPosition.mLine < mLines.size() - 1) { + mState.mCursorPosition.mLine = + std::max(0, std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + 1)); + mState.mCursorPosition.mColumn = 0; + } else + return; + } else { + cindex += UTF8CharLength(line[cindex].mChar); + mState.mCursorPosition = Coordinates(lindex, GetCharacterColumn(lindex, cindex)); + if (aWordMode) + mState.mCursorPosition = FindNextWord(mState.mCursorPosition); + } + } + + if (aSelect) { + if (oldPos == mInteractiveEnd) + mInteractiveEnd = SanitizeCoordinates(mState.mCursorPosition); + else if (oldPos == mInteractiveStart) + mInteractiveStart = mState.mCursorPosition; + else { + mInteractiveStart = oldPos; + mInteractiveEnd = mState.mCursorPosition; + } + } else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd, + aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal); + + EnsureCursorVisible(); +} + +void TextEditor::MoveTop(bool aSelect) { + auto oldPos = mState.mCursorPosition; + SetCursorPosition(Coordinates(0, 0)); + + if (mState.mCursorPosition != oldPos) { + if (aSelect) { + mInteractiveEnd = oldPos; + mInteractiveStart = mState.mCursorPosition; + } else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd); + } +} + +void TextEditor::TextEditor::MoveBottom(bool aSelect) { + auto oldPos = GetCursorPosition(); + auto newPos = Coordinates((int)mLines.size() - 1, 0); + SetCursorPosition(newPos); + if (aSelect) { + mInteractiveStart = oldPos; + mInteractiveEnd = newPos; + } else + mInteractiveStart = mInteractiveEnd = newPos; + SetSelection(mInteractiveStart, mInteractiveEnd); +} + +void TextEditor::MoveHome(bool aSelect) { + auto oldPos = mState.mCursorPosition; + SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, 0)); + + if (mState.mCursorPosition != oldPos) { + if (aSelect) { + if (oldPos == mInteractiveStart) + mInteractiveStart = mState.mCursorPosition; + else if (oldPos == mInteractiveEnd) + mInteractiveEnd = mState.mCursorPosition; + else { + mInteractiveStart = mState.mCursorPosition; + mInteractiveEnd = oldPos; + } + } else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd); + } +} + +void TextEditor::MoveEnd(bool aSelect) { + auto oldPos = mState.mCursorPosition; + SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, GetLineMaxColumn(oldPos.mLine))); + + if (mState.mCursorPosition != oldPos) { + if (aSelect) { + if (oldPos == mInteractiveEnd) + mInteractiveEnd = mState.mCursorPosition; + else if (oldPos == mInteractiveStart) + mInteractiveStart = mState.mCursorPosition; + else { + mInteractiveStart = oldPos; + mInteractiveEnd = mState.mCursorPosition; + } + } else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd); + } +} + +void TextEditor::Delete() { + ASSERT(!mReadOnly); + + if (mLines.empty()) + return; + + UndoRecord u; + u.mBefore = mState; + + if (HasSelection()) { + u.mRemoved = GetSelectedText(); + u.mRemovedStart = mState.mSelectionStart; + u.mRemovedEnd = mState.mSelectionEnd; + + DeleteSelection(); + } else { + auto pos = GetActualCursorCoordinates(); + SetCursorPosition(pos); + auto& line = mLines[pos.mLine]; + + if (pos.mColumn == GetLineMaxColumn(pos.mLine)) { + if (pos.mLine == (int)mLines.size() - 1) + return; + + u.mRemoved = '\n'; + u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); + Advance(u.mRemovedEnd); + + auto& nextLine = mLines[pos.mLine + 1]; + line.insert(line.end(), nextLine.begin(), nextLine.end()); + RemoveLine(pos.mLine + 1); + } else { + auto cindex = GetCharacterIndex(pos); + u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); + u.mRemovedEnd.mColumn++; + u.mRemoved = GetText(u.mRemovedStart, u.mRemovedEnd); + + auto d = UTF8CharLength(line[cindex].mChar); + while (d-- > 0 && cindex < (int)line.size()) + line.erase(line.begin() + cindex); + } + + mTextChanged = true; + + Colorize(pos.mLine, 1); + } + + u.mAfter = mState; + AddUndo(u); +} + +void TextEditor::Backspace() { + ASSERT(!mReadOnly); + + if (mLines.empty()) + return; + + UndoRecord u; + u.mBefore = mState; + + if (HasSelection()) { + u.mRemoved = GetSelectedText(); + u.mRemovedStart = mState.mSelectionStart; + u.mRemovedEnd = mState.mSelectionEnd; + + DeleteSelection(); + } else { + auto pos = GetActualCursorCoordinates(); + SetCursorPosition(pos); + + if (mState.mCursorPosition.mColumn == 0) { + if (mState.mCursorPosition.mLine == 0) + return; + + u.mRemoved = '\n'; + u.mRemovedStart = u.mRemovedEnd = + Coordinates(pos.mLine - 1, GetLineMaxColumn(pos.mLine - 1)); + Advance(u.mRemovedEnd); + + auto& line = mLines[mState.mCursorPosition.mLine]; + auto& prevLine = mLines[mState.mCursorPosition.mLine - 1]; + auto prevSize = GetLineMaxColumn(mState.mCursorPosition.mLine - 1); + prevLine.insert(prevLine.end(), line.begin(), line.end()); + + ErrorMarkers etmp; + for (auto& i : mErrorMarkers) + etmp.insert(ErrorMarkers::value_type( + i.first - 1 == mState.mCursorPosition.mLine ? i.first - 1 : i.first, i.second)); + mErrorMarkers = std::move(etmp); + + RemoveLine(mState.mCursorPosition.mLine); + --mState.mCursorPosition.mLine; + mState.mCursorPosition.mColumn = prevSize; + } else { + auto& line = mLines[mState.mCursorPosition.mLine]; + auto cindex = GetCharacterIndex(pos) - 1; + auto cend = cindex + 1; + while (cindex > 0 && IsUTFSequence(line[cindex].mChar)) + --cindex; + + // if (cindex > 0 && UTF8CharLength(line[cindex].mChar) > 1) + // --cindex; + + u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); + --u.mRemovedStart.mColumn; + --mState.mCursorPosition.mColumn; + + while (cindex < line.size() && cend-- > cindex) { + u.mRemoved += line[cindex].mChar; + line.erase(line.begin() + cindex); + } + } + + mTextChanged = true; + + EnsureCursorVisible(); + Colorize(mState.mCursorPosition.mLine, 1); + } + + u.mAfter = mState; + AddUndo(u); +} + +void TextEditor::SelectWordUnderCursor() { + auto c = GetCursorPosition(); + SetSelection(FindWordStart(c), FindWordEnd(c)); +} + +void TextEditor::SelectAll() { + SetSelection(Coordinates(0, 0), Coordinates((int)mLines.size(), 0)); +} + +bool TextEditor::HasSelection() const { + return mState.mSelectionEnd > mState.mSelectionStart; +} + +void TextEditor::Copy() { + if (HasSelection()) { + ImGui::SetClipboardText(GetSelectedText().c_str()); + } else { + if (!mLines.empty()) { + std::string str; + auto& line = mLines[GetActualCursorCoordinates().mLine]; + for (auto& g : line) + str.push_back(g.mChar); + ImGui::SetClipboardText(str.c_str()); + } + } +} + +void TextEditor::Cut() { + if (IsReadOnly()) { + Copy(); + } else { + if (HasSelection()) { + UndoRecord u; + u.mBefore = mState; + u.mRemoved = GetSelectedText(); + u.mRemovedStart = mState.mSelectionStart; + u.mRemovedEnd = mState.mSelectionEnd; + + Copy(); + DeleteSelection(); + + u.mAfter = mState; + AddUndo(u); + } + } +} + +void TextEditor::Paste() { + if (IsReadOnly()) + return; + + auto clipText = ImGui::GetClipboardText(); + if (clipText != nullptr && strlen(clipText) > 0) { + UndoRecord u; + u.mBefore = mState; + + if (HasSelection()) { + u.mRemoved = GetSelectedText(); + u.mRemovedStart = mState.mSelectionStart; + u.mRemovedEnd = mState.mSelectionEnd; + DeleteSelection(); + } + + u.mAdded = clipText; + u.mAddedStart = GetActualCursorCoordinates(); + + InsertText(clipText); + + u.mAddedEnd = GetActualCursorCoordinates(); + u.mAfter = mState; + AddUndo(u); + } +} + +bool TextEditor::CanUndo() const { + return !mReadOnly && mUndoIndex > 0; +} + +bool TextEditor::CanRedo() const { + return !mReadOnly && mUndoIndex < (int)mUndoBuffer.size(); +} + +void TextEditor::Undo(int aSteps) { + while (CanUndo() && aSteps-- > 0) + mUndoBuffer[--mUndoIndex].Undo(this); +} + +void TextEditor::Redo(int aSteps) { + while (CanRedo() && aSteps-- > 0) + mUndoBuffer[mUndoIndex++].Redo(this); +} + +const TextEditor::Palette& TextEditor::GetDarkPalette() { + const static Palette p = {{ + 0xff7f7f7f, // Default + 0xffd69c56, // Keyword + 0xff00ff00, // Number + 0xff7070e0, // String + 0xff70a0e0, // Char literal + 0xffffffff, // Punctuation + 0xff408080, // Preprocessor + 0xffaaaaaa, // Identifier + 0xff9bc64d, // Known identifier + 0xffc040a0, // Preproc identifier + 0xff206020, // Comment (single line) + 0xff406020, // Comment (multi line) + 0xff101010, // Background + 0xffe0e0e0, // Cursor + 0x80a06020, // Selection + 0x800020ff, // ErrorMarker + 0x40f08000, // Breakpoint + 0xff707000, // Line number + 0x40000000, // Current line fill + 0x40808080, // Current line fill (inactive) + 0x40a0a0a0, // Current line edge + }}; + return p; +} + +const TextEditor::Palette& TextEditor::GetLightPalette() { + const static Palette p = {{ + 0xff7f7f7f, // None + 0xffff0c06, // Keyword + 0xff008000, // Number + 0xff2020a0, // String + 0xff304070, // Char literal + 0xff000000, // Punctuation + 0xff406060, // Preprocessor + 0xff404040, // Identifier + 0xff606010, // Known identifier + 0xffc040a0, // Preproc identifier + 0xff205020, // Comment (single line) + 0xff405020, // Comment (multi line) + 0xffffffff, // Background + 0xff000000, // Cursor + 0x80600000, // Selection + 0xa00010ff, // ErrorMarker + 0x80f08000, // Breakpoint + 0xff505000, // Line number + 0x40000000, // Current line fill + 0x40808080, // Current line fill (inactive) + 0x40000000, // Current line edge + }}; + return p; +} + +const TextEditor::Palette& TextEditor::GetRetroBluePalette() { + const static Palette p = {{ + 0xff00ffff, // None + 0xffffff00, // Keyword + 0xff00ff00, // Number + 0xff808000, // String + 0xff808000, // Char literal + 0xffffffff, // Punctuation + 0xff008000, // Preprocessor + 0xff00ffff, // Identifier + 0xffffffff, // Known identifier + 0xffff00ff, // Preproc identifier + 0xff808080, // Comment (single line) + 0xff404040, // Comment (multi line) + 0xff800000, // Background + 0xff0080ff, // Cursor + 0x80ffff00, // Selection + 0xa00000ff, // ErrorMarker + 0x80ff8000, // Breakpoint + 0xff808000, // Line number + 0x40000000, // Current line fill + 0x40808080, // Current line fill (inactive) + 0x40000000, // Current line edge + }}; + return p; +} + +std::string TextEditor::GetText() const { + return GetText(Coordinates(), Coordinates((int)mLines.size(), 0)); +} + +std::vector TextEditor::GetTextLines() const { + std::vector result; + + result.reserve(mLines.size()); + + for (auto& line : mLines) { + std::string text; + + text.resize(line.size()); + + for (size_t i = 0; i < line.size(); ++i) + text[i] = line[i].mChar; + + result.emplace_back(std::move(text)); + } + + return result; +} + +std::string TextEditor::GetSelectedText() const { + return GetText(mState.mSelectionStart, mState.mSelectionEnd); +} + +std::string TextEditor::GetCurrentLineText() const { + auto lineLength = GetLineMaxColumn(mState.mCursorPosition.mLine); + return GetText(Coordinates(mState.mCursorPosition.mLine, 0), + Coordinates(mState.mCursorPosition.mLine, lineLength)); +} + +void TextEditor::ProcessInputs() {} + +void TextEditor::Colorize(int aFromLine, int aLines) { + int toLine = + aLines == -1 ? (int)mLines.size() : std::min((int)mLines.size(), aFromLine + aLines); + mColorRangeMin = std::min(mColorRangeMin, aFromLine); + mColorRangeMax = std::max(mColorRangeMax, toLine); + mColorRangeMin = std::max(0, mColorRangeMin); + mColorRangeMax = std::max(mColorRangeMin, mColorRangeMax); + mCheckComments = true; +} + +void TextEditor::ColorizeRange(int aFromLine, int aToLine) { + if (mLines.empty() || aFromLine >= aToLine) + return; + + std::string buffer; + std::cmatch results; + std::string id; + + int endLine = std::max(0, std::min((int)mLines.size(), aToLine)); + for (int i = aFromLine; i < endLine; ++i) { + auto& line = mLines[i]; + + if (line.empty()) + continue; + + buffer.resize(line.size()); + for (size_t j = 0; j < line.size(); ++j) { + auto& col = line[j]; + buffer[j] = col.mChar; + col.mColorIndex = PaletteIndex::Default; + } + + const char* bufferBegin = &buffer.front(); + const char* bufferEnd = bufferBegin + buffer.size(); + + auto last = bufferEnd; + + for (auto first = bufferBegin; first != last;) { + const char* token_begin = nullptr; + const char* token_end = nullptr; + PaletteIndex token_color = PaletteIndex::Default; + + bool hasTokenizeResult = false; + + if (mLanguageDefinition.mTokenize != nullptr) { + if (mLanguageDefinition.mTokenize(first, last, token_begin, token_end, token_color)) + hasTokenizeResult = true; + } + + if (hasTokenizeResult == false) { + // todo : remove + // printf("using regex for %.*s\n", first + 10 < last ? 10 : int(last - first), + // first); + + for (auto& p : mRegexList) { + if (std::regex_search(first, last, results, p.first, + std::regex_constants::match_continuous)) { + hasTokenizeResult = true; + + auto& v = *results.begin(); + token_begin = v.first; + token_end = v.second; + token_color = p.second; + break; + } + } + } + + if (hasTokenizeResult == false) { + first++; + } else { + const size_t token_length = token_end - token_begin; + + if (token_color == PaletteIndex::Identifier) { + id.assign(token_begin, token_end); + + // todo : allmost all language definitions use lower case to specify keywords, + // so shouldn't this use ::tolower ? + if (!mLanguageDefinition.mCaseSensitive) + std::transform(id.begin(), id.end(), id.begin(), ::toupper); + + if (!line[first - bufferBegin].mPreprocessor) { + if (mLanguageDefinition.mKeywords.count(id) != 0) + token_color = PaletteIndex::Keyword; + else if (mLanguageDefinition.mIdentifiers.count(id) != 0) + token_color = PaletteIndex::KnownIdentifier; + else if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0) + token_color = PaletteIndex::PreprocIdentifier; + } else { + if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0) + token_color = PaletteIndex::PreprocIdentifier; + } + } + + for (size_t j = 0; j < token_length; ++j) + line[(token_begin - bufferBegin) + j].mColorIndex = token_color; + + first = token_end; + } + } + } +} + +void TextEditor::ColorizeInternal() { + if (mLines.empty() || !mColorizerEnabled) + return; + + if (mCheckComments) { + auto endLine = mLines.size(); + auto endIndex = 0; + auto commentStartLine = endLine; + auto commentStartIndex = endIndex; + auto withinString = false; + auto withinSingleLineComment = false; + auto withinPreproc = false; + auto firstChar = true; // there is no other non-whitespace characters in the line before + auto concatenate = false; // '\' on the very end of the line + auto currentLine = 0; + auto currentIndex = 0; + while (currentLine < endLine || currentIndex < endIndex) { + auto& line = mLines[currentLine]; + + if (currentIndex == 0 && !concatenate) { + withinSingleLineComment = false; + withinPreproc = false; + firstChar = true; + } + + concatenate = false; + + if (!line.empty()) { + auto& g = line[currentIndex]; + auto c = g.mChar; + + if (c != mLanguageDefinition.mPreprocChar && !isspace(c)) + firstChar = false; + + if (currentIndex == (int)line.size() - 1 && line[line.size() - 1].mChar == '\\') + concatenate = true; + + bool inComment = + (commentStartLine < currentLine || + (commentStartLine == currentLine && commentStartIndex <= currentIndex)); + + if (withinString) { + line[currentIndex].mMultiLineComment = inComment; + + if (c == '\"') { + if (currentIndex + 1 < (int)line.size() && + line[currentIndex + 1].mChar == '\"') { + currentIndex += 1; + if (currentIndex < (int)line.size()) + line[currentIndex].mMultiLineComment = inComment; + } else + withinString = false; + } else if (c == '\\') { + currentIndex += 1; + if (currentIndex < (int)line.size()) + line[currentIndex].mMultiLineComment = inComment; + } + } else { + if (firstChar && c == mLanguageDefinition.mPreprocChar) + withinPreproc = true; + + if (c == '\"') { + withinString = true; + line[currentIndex].mMultiLineComment = inComment; + } else { + auto pred = [](const char& a, const Glyph& b) { return a == b.mChar; }; + auto from = line.begin() + currentIndex; + auto& startStr = mLanguageDefinition.mCommentStart; + auto& singleStartStr = mLanguageDefinition.mSingleLineComment; + + if (singleStartStr.size() > 0 && + currentIndex + singleStartStr.size() <= line.size() && + equals(singleStartStr.begin(), singleStartStr.end(), from, + from + singleStartStr.size(), pred)) { + withinSingleLineComment = true; + } else if (!withinSingleLineComment && + currentIndex + startStr.size() <= line.size() && + equals(startStr.begin(), startStr.end(), from, + from + startStr.size(), pred)) { + commentStartLine = currentLine; + commentStartIndex = currentIndex; + } + + inComment = inComment = + (commentStartLine < currentLine || (commentStartLine == currentLine && + commentStartIndex <= currentIndex)); + + line[currentIndex].mMultiLineComment = inComment; + line[currentIndex].mComment = withinSingleLineComment; + + auto& endStr = mLanguageDefinition.mCommentEnd; + if (currentIndex + 1 >= (int)endStr.size() && + equals(endStr.begin(), endStr.end(), from + 1 - endStr.size(), from + 1, + pred)) { + commentStartIndex = endIndex; + commentStartLine = endLine; + } + } + } + line[currentIndex].mPreprocessor = withinPreproc; + currentIndex += UTF8CharLength(c); + if (currentIndex >= (int)line.size()) { + currentIndex = 0; + ++currentLine; + } + } else { + currentIndex = 0; + ++currentLine; + } + } + mCheckComments = false; + } + + if (mColorRangeMin < mColorRangeMax) { + const int increment = (mLanguageDefinition.mTokenize == nullptr) ? 10 : 10000; + const int to = std::min(mColorRangeMin + increment, mColorRangeMax); + ColorizeRange(mColorRangeMin, to); + mColorRangeMin = to; + + if (mColorRangeMax == mColorRangeMin) { + mColorRangeMin = std::numeric_limits::max(); + mColorRangeMax = 0; + } + return; + } +} + +float TextEditor::TextDistanceToLineStart(const Coordinates& aFrom) const { + auto& line = mLines[aFrom.mLine]; + float distance = 0.0f; + float spaceSize = + ImGui::GetFont() + ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr) + .x; + int colIndex = GetCharacterIndex(aFrom); + for (size_t it = 0u; it < line.size() && it < colIndex;) { + if (line[it].mChar == '\t') { + distance = (1.0f + std::floor((1.0f + distance) / (float(mTabSize) * spaceSize))) * + (float(mTabSize) * spaceSize); + ++it; + } else { + auto d = UTF8CharLength(line[it].mChar); + char tempCString[7]; + int i = 0; + for (; i < 6 && d-- > 0 && it < (int)line.size(); i++, it++) + tempCString[i] = line[it].mChar; + + tempCString[i] = '\0'; + distance += ImGui::GetFont() + ->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, tempCString, + nullptr, nullptr) + .x; + } + } + + return distance; +} + +void TextEditor::EnsureCursorVisible() { + if (!mWithinRender) { + mScrollToCursor = true; + return; + } + + float scrollX = ImGui::GetScrollX(); + float scrollY = ImGui::GetScrollY(); + + auto height = ImGui::GetWindowHeight(); + auto width = ImGui::GetWindowWidth(); + + auto top = 1 + (int)ceil(scrollY / mCharAdvance.y); + auto bottom = (int)ceil((scrollY + height) / mCharAdvance.y); + + auto left = (int)ceil(scrollX / mCharAdvance.x); + auto right = (int)ceil((scrollX + width) / mCharAdvance.x); + + auto pos = GetActualCursorCoordinates(); + auto len = TextDistanceToLineStart(pos); + + if (pos.mLine < top) + ImGui::SetScrollY(std::max(0.0f, (pos.mLine - 1) * mCharAdvance.y)); + if (pos.mLine > bottom - 4) + ImGui::SetScrollY(std::max(0.0f, (pos.mLine + 4) * mCharAdvance.y - height)); + if (len + mTextStart < left + 4) + ImGui::SetScrollX(std::max(0.0f, len + mTextStart - 4)); + if (len + mTextStart > right - 4) + ImGui::SetScrollX(std::max(0.0f, len + mTextStart + 4 - width)); +} + +int TextEditor::GetPageSize() const { + auto height = ImGui::GetWindowHeight() - 20.0f; + return (int)floor(height / mCharAdvance.y); +} + +TextEditor::UndoRecord::UndoRecord( + const std::string& aAdded, const TextEditor::Coordinates aAddedStart, + const TextEditor::Coordinates aAddedEnd, const std::string& aRemoved, + const TextEditor::Coordinates aRemovedStart, const TextEditor::Coordinates aRemovedEnd, + TextEditor::EditorState& aBefore, TextEditor::EditorState& aAfter) + : mAdded(aAdded), mAddedStart(aAddedStart), mAddedEnd(aAddedEnd), mRemoved(aRemoved), + mRemovedStart(aRemovedStart), mRemovedEnd(aRemovedEnd), mBefore(aBefore), mAfter(aAfter) { + ASSERT(mAddedStart <= mAddedEnd); + ASSERT(mRemovedStart <= mRemovedEnd); +} + +void TextEditor::UndoRecord::Undo(TextEditor* aEditor) { + if (!mAdded.empty()) { + aEditor->DeleteRange(mAddedStart, mAddedEnd); + aEditor->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 2); + } + + if (!mRemoved.empty()) { + auto start = mRemovedStart; + aEditor->InsertTextAt(start, mRemoved.c_str()); + aEditor->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 2); + } + + aEditor->mState = mBefore; + aEditor->EnsureCursorVisible(); +} + +void TextEditor::UndoRecord::Redo(TextEditor* aEditor) { + if (!mRemoved.empty()) { + aEditor->DeleteRange(mRemovedStart, mRemovedEnd); + aEditor->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 1); + } + + if (!mAdded.empty()) { + auto start = mAddedStart; + aEditor->InsertTextAt(start, mAdded.c_str()); + aEditor->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 1); + } + + aEditor->mState = mAfter; + aEditor->EnsureCursorVisible(); +} + +const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::GLSL() { + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) { + static const char* const keywords[] = { + "auto", "break", "case", "char", "const", "continue", + "default", "do", "double", "else", "enum", "extern", + "float", "for", "goto", "if", "inline", "int", + "long", "register", "restrict", "return", "short", "signed", + "sizeof", "static", "struct", "switch", "typedef", "union", + "unsigned", "void", "volatile", "while", "_Alignas", "_Alignof", + "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary", "_Noreturn", + "_Static_assert", "_Thread_local"}; + for (auto& k : keywords) + langDef.mKeywords.insert(k); + + static const char* const identifiers[] = { + "abort", "abs", "acos", "asin", "atan", "atexit", "atof", + "atoi", "atol", "ceil", "clock", "cosh", "ctime", "div", + "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", + "isalpha", "isdigit", "isgraph", "ispunct", "isspace", "isupper", "kbhit", + "log10", "log2", "log", "memcmp", "modf", "pow", "putchar", + "putenv", "puts", "rand", "remove", "rename", "sinh", "sqrt", + "srand", "strcat", "strcmp", "strerror", "time", "tolower", "toupper"}; + for (auto& k : identifiers) { + Identifier id; + id.mDeclaration = "Built-in function"; + langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); + } + + langDef.mTokenRegexStrings.push_back(std::make_pair( + "[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor)); + langDef.mTokenRegexStrings.push_back(std::make_pair( + "L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); + langDef.mTokenRegexStrings.push_back(std::make_pair( + "\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); + langDef.mTokenRegexStrings.push_back(std::make_pair( + "[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair( + "[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair( + "0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair( + "0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair( + "[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); + langDef.mTokenRegexStrings.push_back(std::make_pair( + "[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", + PaletteIndex::Punctuation)); + + langDef.mCommentStart = "/*"; + langDef.mCommentEnd = "*/"; + langDef.mSingleLineComment = "//"; + + langDef.mCaseSensitive = true; + langDef.mAutoIndentation = true; + + langDef.mName = "GLSL"; + + inited = true; + } + return langDef; +} + +} // namespace Core::Devtools::Widget diff --git a/src/core/devtools/widget/text_editor.h b/src/core/devtools/widget/text_editor.h new file mode 100644 index 000000000..5c3f29f11 --- /dev/null +++ b/src/core/devtools/widget/text_editor.h @@ -0,0 +1,408 @@ +// SPDX-FileCopyrightText: Copyright (c) 2017 BalazsJako +// SPDX-License-Identifier: MIT + +// source: https://github.com/BalazsJako/ImGuiColorTextEdit + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common/assert.h" +#include "imgui.h" + +namespace Core::Devtools::Widget { + +class TextEditor { +public: + enum class PaletteIndex { + Default, + Keyword, + Number, + String, + CharLiteral, + Punctuation, + Preprocessor, + Identifier, + KnownIdentifier, + PreprocIdentifier, + Comment, + MultiLineComment, + Background, + Cursor, + Selection, + ErrorMarker, + Breakpoint, + LineNumber, + CurrentLineFill, + CurrentLineFillInactive, + CurrentLineEdge, + Max + }; + + enum class SelectionMode { Normal, Word, Line }; + + struct Breakpoint { + int mLine; + bool mEnabled; + std::string mCondition; + + Breakpoint() : mLine(-1), mEnabled(false) {} + }; + + // Represents a character coordinate from the user's point of view, + // i. e. consider an uniform grid (assuming fixed-width font) on the + // screen as it is rendered, and each cell has its own coordinate, starting from 0. + // Tabs are counted as [1..mTabSize] count empty spaces, depending on + // how many space is necessary to reach the next tab stop. + // For example, coordinate (1, 5) represents the character 'B' in a line "\tABC", when mTabSize + // = 4, because it is rendered as " ABC" on the screen. + struct Coordinates { + int mLine, mColumn; + Coordinates() : mLine(0), mColumn(0) {} + Coordinates(int aLine, int aColumn) : mLine(aLine), mColumn(aColumn) { + ASSERT(aLine >= 0); + ASSERT(aColumn >= 0); + } + static Coordinates Invalid() { + static Coordinates invalid(-1, -1); + return invalid; + } + + bool operator==(const Coordinates& o) const { + return mLine == o.mLine && mColumn == o.mColumn; + } + + bool operator!=(const Coordinates& o) const { + return mLine != o.mLine || mColumn != o.mColumn; + } + + bool operator<(const Coordinates& o) const { + if (mLine != o.mLine) + return mLine < o.mLine; + return mColumn < o.mColumn; + } + + bool operator>(const Coordinates& o) const { + if (mLine != o.mLine) + return mLine > o.mLine; + return mColumn > o.mColumn; + } + + bool operator<=(const Coordinates& o) const { + if (mLine != o.mLine) + return mLine < o.mLine; + return mColumn <= o.mColumn; + } + + bool operator>=(const Coordinates& o) const { + if (mLine != o.mLine) + return mLine > o.mLine; + return mColumn >= o.mColumn; + } + }; + + struct Identifier { + Coordinates mLocation; + std::string mDeclaration; + }; + + typedef std::string String; + typedef std::unordered_map Identifiers; + typedef std::unordered_set Keywords; + typedef std::map ErrorMarkers; + typedef std::unordered_set Breakpoints; + typedef std::array Palette; + typedef uint8_t Char; + + struct Glyph { + Char mChar; + PaletteIndex mColorIndex = PaletteIndex::Default; + bool mComment : 1; + bool mMultiLineComment : 1; + bool mPreprocessor : 1; + + Glyph(Char aChar, PaletteIndex aColorIndex) + : mChar(aChar), mColorIndex(aColorIndex), mComment(false), mMultiLineComment(false), + mPreprocessor(false) {} + }; + + typedef std::vector Line; + typedef std::vector Lines; + + struct LanguageDefinition { + typedef std::pair TokenRegexString; + typedef std::vector TokenRegexStrings; + typedef bool (*TokenizeCallback)(const char* in_begin, const char* in_end, + const char*& out_begin, const char*& out_end, + PaletteIndex& paletteIndex); + + std::string mName; + Keywords mKeywords; + Identifiers mIdentifiers; + Identifiers mPreprocIdentifiers; + std::string mCommentStart, mCommentEnd, mSingleLineComment; + char mPreprocChar; + bool mAutoIndentation; + + TokenizeCallback mTokenize; + + TokenRegexStrings mTokenRegexStrings; + + bool mCaseSensitive; + + LanguageDefinition() + : mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true) {} + + static const LanguageDefinition& GLSL(); + }; + + TextEditor(); + ~TextEditor(); + + void SetLanguageDefinition(const LanguageDefinition& aLanguageDef); + const LanguageDefinition& GetLanguageDefinition() const { + return mLanguageDefinition; + } + + const Palette& GetPalette() const { + return mPaletteBase; + } + void SetPalette(const Palette& aValue); + + void SetErrorMarkers(const ErrorMarkers& aMarkers) { + mErrorMarkers = aMarkers; + } + void SetBreakpoints(const Breakpoints& aMarkers) { + mBreakpoints = aMarkers; + } + + void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false); + void SetText(const std::string& aText); + std::string GetText() const; + + void SetTextLines(const std::vector& aLines); + std::vector GetTextLines() const; + + std::string GetSelectedText() const; + std::string GetCurrentLineText() const; + + int GetTotalLines() const { + return (int)mLines.size(); + } + bool IsOverwrite() const { + return mOverwrite; + } + + void SetReadOnly(bool aValue); + bool IsReadOnly() const { + return mReadOnly; + } + bool IsTextChanged() const { + return mTextChanged; + } + bool IsCursorPositionChanged() const { + return mCursorPositionChanged; + } + + bool IsColorizerEnabled() const { + return mColorizerEnabled; + } + void SetColorizerEnable(bool aValue); + + Coordinates GetCursorPosition() const { + return GetActualCursorCoordinates(); + } + void SetCursorPosition(const Coordinates& aPosition); + + inline void SetHandleMouseInputs(bool aValue) { + mHandleMouseInputs = aValue; + } + inline bool IsHandleMouseInputsEnabled() const { + return mHandleKeyboardInputs; + } + + inline void SetHandleKeyboardInputs(bool aValue) { + mHandleKeyboardInputs = aValue; + } + inline bool IsHandleKeyboardInputsEnabled() const { + return mHandleKeyboardInputs; + } + + inline void SetImGuiChildIgnored(bool aValue) { + mIgnoreImGuiChild = aValue; + } + inline bool IsImGuiChildIgnored() const { + return mIgnoreImGuiChild; + } + + inline void SetShowWhitespaces(bool aValue) { + mShowWhitespaces = aValue; + } + inline bool IsShowingWhitespaces() const { + return mShowWhitespaces; + } + + void SetTabSize(int aValue); + inline int GetTabSize() const { + return mTabSize; + } + + void InsertText(const std::string& aValue); + void InsertText(const char* aValue); + + void MoveUp(int aAmount = 1, bool aSelect = false); + void MoveDown(int aAmount = 1, bool aSelect = false); + void MoveLeft(int aAmount = 1, bool aSelect = false, bool aWordMode = false); + void MoveRight(int aAmount = 1, bool aSelect = false, bool aWordMode = false); + void MoveTop(bool aSelect = false); + void MoveBottom(bool aSelect = false); + void MoveHome(bool aSelect = false); + void MoveEnd(bool aSelect = false); + + void SetSelectionStart(const Coordinates& aPosition); + void SetSelectionEnd(const Coordinates& aPosition); + void SetSelection(const Coordinates& aStart, const Coordinates& aEnd, + SelectionMode aMode = SelectionMode::Normal); + void SelectWordUnderCursor(); + void SelectAll(); + bool HasSelection() const; + + void Copy(); + void Cut(); + void Paste(); + void Delete(); + + bool CanUndo() const; + bool CanRedo() const; + void Undo(int aSteps = 1); + void Redo(int aSteps = 1); + + static const Palette& GetDarkPalette(); + static const Palette& GetLightPalette(); + static const Palette& GetRetroBluePalette(); + +private: + typedef std::vector> RegexList; + + struct EditorState { + Coordinates mSelectionStart; + Coordinates mSelectionEnd; + Coordinates mCursorPosition; + }; + + class UndoRecord { + public: + UndoRecord() {} + ~UndoRecord() {} + + UndoRecord(const std::string& aAdded, const TextEditor::Coordinates aAddedStart, + const TextEditor::Coordinates aAddedEnd, + + const std::string& aRemoved, const TextEditor::Coordinates aRemovedStart, + const TextEditor::Coordinates aRemovedEnd, + + TextEditor::EditorState& aBefore, TextEditor::EditorState& aAfter); + + void Undo(TextEditor* aEditor); + void Redo(TextEditor* aEditor); + + std::string mAdded; + Coordinates mAddedStart; + Coordinates mAddedEnd; + + std::string mRemoved; + Coordinates mRemovedStart; + Coordinates mRemovedEnd; + + EditorState mBefore; + EditorState mAfter; + }; + + typedef std::vector UndoBuffer; + + void ProcessInputs(); + void Colorize(int aFromLine = 0, int aCount = -1); + void ColorizeRange(int aFromLine = 0, int aToLine = 0); + void ColorizeInternal(); + float TextDistanceToLineStart(const Coordinates& aFrom) const; + void EnsureCursorVisible(); + int GetPageSize() const; + std::string GetText(const Coordinates& aStart, const Coordinates& aEnd) const; + Coordinates GetActualCursorCoordinates() const; + Coordinates SanitizeCoordinates(const Coordinates& aValue) const; + void Advance(Coordinates& aCoordinates) const; + void DeleteRange(const Coordinates& aStart, const Coordinates& aEnd); + int InsertTextAt(Coordinates& aWhere, const char* aValue); + void AddUndo(UndoRecord& aValue); + Coordinates ScreenPosToCoordinates(const ImVec2& aPosition) const; + Coordinates FindWordStart(const Coordinates& aFrom) const; + Coordinates FindWordEnd(const Coordinates& aFrom) const; + Coordinates FindNextWord(const Coordinates& aFrom) const; + int GetCharacterIndex(const Coordinates& aCoordinates) const; + int GetCharacterColumn(int aLine, int aIndex) const; + int GetLineCharacterCount(int aLine) const; + int GetLineMaxColumn(int aLine) const; + bool IsOnWordBoundary(const Coordinates& aAt) const; + void RemoveLine(int aStart, int aEnd); + void RemoveLine(int aIndex); + Line& InsertLine(int aIndex); + void EnterCharacter(ImWchar aChar, bool aShift); + void Backspace(); + void DeleteSelection(); + std::string GetWordUnderCursor() const; + std::string GetWordAt(const Coordinates& aCoords) const; + ImU32 GetGlyphColor(const Glyph& aGlyph) const; + + void HandleKeyboardInputs(); + void HandleMouseInputs(); + void Render(); + + float mLineSpacing; + Lines mLines; + EditorState mState; + UndoBuffer mUndoBuffer; + int mUndoIndex; + + int mTabSize; + bool mOverwrite; + bool mReadOnly; + bool mWithinRender; + bool mScrollToCursor; + bool mScrollToTop; + bool mTextChanged; + bool mColorizerEnabled; + float mTextStart; // position (in pixels) where a code line starts relative to the left of the + // TextEditor. + int mLeftMargin; + bool mCursorPositionChanged; + int mColorRangeMin, mColorRangeMax; + SelectionMode mSelectionMode; + bool mHandleKeyboardInputs; + bool mHandleMouseInputs; + bool mIgnoreImGuiChild; + bool mShowWhitespaces; + + Palette mPaletteBase; + Palette mPalette; + LanguageDefinition mLanguageDefinition; + RegexList mRegexList; + + bool mCheckComments; + Breakpoints mBreakpoints; + ErrorMarkers mErrorMarkers; + ImVec2 mCharAdvance; + Coordinates mInteractiveStart, mInteractiveEnd; + std::string mLineBuffer; + uint64_t mStartTime; + + float mLastClick; +}; + +} // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/libraries/dialogs/error_dialog.cpp b/src/core/libraries/dialogs/error_dialog.cpp index b122e2d0a..811f2cb99 100644 --- a/src/core/libraries/dialogs/error_dialog.cpp +++ b/src/core/libraries/dialogs/error_dialog.cpp @@ -75,7 +75,7 @@ public: std::min(io.DisplaySize.y, 300.0f), }; - CentralizeWindow(); + CentralizeNextWindow(); SetNextWindowSize(window_size); SetNextWindowCollapsed(false); if (first_render || !io.NavActive) { diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index ce30895ca..4d8aa8817 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -519,10 +519,12 @@ void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) { // Dumping them using the current ring pointer would result in files containing only the // `IndirectBuffer` command. To access the actual command stream, we need to unwrap the IB. auto acb = acb_span; + auto base_addr = reinterpret_cast(acb_ptr); const auto* indirect_buffer = reinterpret_cast(acb_span.data()); if (indirect_buffer->header.opcode == PM4ItOpcode::IndirectBuffer) { - acb = {indirect_buffer->Address(), indirect_buffer->ib_size}; + base_addr = reinterpret_cast(indirect_buffer->Address()); + acb = {reinterpret_cast(base_addr), indirect_buffer->ib_size}; } using namespace DebugStateType; @@ -532,9 +534,9 @@ void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) { .submit_num = seq_num, .num2 = gnm_vqid, .data = {acb.begin(), acb.end()}, + .base_addr = base_addr, }); } - liverpool->SubmitAsc(vqid, acb_span); *asc_queue.read_addr += acb_size; @@ -1125,9 +1127,25 @@ int PS4_SYSV_ABI sceGnmInsertSetColorMarker() { return ORBIS_OK; } -int PS4_SYSV_ABI sceGnmInsertSetMarker() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceGnmInsertSetMarker(u32* cmdbuf, u32 size, const char* marker) { + LOG_TRACE(Lib_GnmDriver, "called"); + + if (cmdbuf && marker) { + const auto len = std::strlen(marker); + const u32 packet_size = ((len + 8) >> 2) + ((len + 0xc) >> 3) * 2; + if (packet_size + 2 == size) { + auto* nop = reinterpret_cast(cmdbuf); + nop->header = + PM4Type3Header{PM4ItOpcode::Nop, packet_size, PM4ShaderType::ShaderGraphics}; + nop->data_block[0] = PM4CmdNop::PayloadType::DebugSetMarker; + const auto marker_len = len + 1; + std::memcpy(&nop->data_block[1], marker, marker_len); + std::memset(reinterpret_cast(&nop->data_block[1]) + marker_len, 0, + packet_size * 4 - marker_len); + return ORBIS_OK; + } + } + return -1; } int PS4_SYSV_ABI sceGnmInsertThreadTraceMarker() { @@ -2163,15 +2181,16 @@ s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[ .submit_num = seq_num, .num2 = cbpair, .data = {dcb_span.begin(), dcb_span.end()}, + .base_addr = reinterpret_cast(dcb_gpu_addrs[cbpair]), }); DebugState.PushQueueDump({ .type = QueueType::ccb, .submit_num = seq_num, .num2 = cbpair, .data = {ccb_span.begin(), ccb_span.end()}, + .base_addr = reinterpret_cast(ccb), }); } - liverpool->SubmitGfx(dcb_span, ccb_span); } diff --git a/src/core/libraries/gnmdriver/gnmdriver.h b/src/core/libraries/gnmdriver/gnmdriver.h index 33bccf427..a95daa90d 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.h +++ b/src/core/libraries/gnmdriver/gnmdriver.h @@ -108,7 +108,7 @@ s32 PS4_SYSV_ABI sceGnmInsertPopMarker(u32* cmdbuf, u32 size); s32 PS4_SYSV_ABI sceGnmInsertPushColorMarker(u32* cmdbuf, u32 size, const char* marker, u32 color); s32 PS4_SYSV_ABI sceGnmInsertPushMarker(u32* cmdbuf, u32 size, const char* marker); int PS4_SYSV_ABI sceGnmInsertSetColorMarker(); -int PS4_SYSV_ABI sceGnmInsertSetMarker(); +s32 PS4_SYSV_ABI sceGnmInsertSetMarker(u32* cmdbuf, u32 size, const char* marker); int PS4_SYSV_ABI sceGnmInsertThreadTraceMarker(); s32 PS4_SYSV_ABI sceGnmInsertWaitFlipDone(u32* cmdbuf, u32 size, s32 vo_handle, u32 buf_idx); int PS4_SYSV_ABI sceGnmIsCoredumpValid(); diff --git a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp index c4bf84258..01e56f8b8 100644 --- a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp +++ b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp @@ -98,7 +98,7 @@ SaveDialogState::SaveDialogState(const OrbisSaveDataDialogParam& param) { param_sfo.Open(param_sfo_path); auto last_write = param_sfo.GetLastWrite(); -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__GNUC__) && !defined(__MINGW32__) && !defined(__MINGW64__) auto utc_time = std::chrono::file_clock::to_utc(last_write); #else auto utc_time = std::chrono::file_clock::to_sys(last_write); @@ -402,7 +402,7 @@ void SaveDialogUi::Draw() { }; } - CentralizeWindow(); + CentralizeNextWindow(); SetNextWindowSize(window_size); SetNextWindowCollapsed(false); if (first_render || !io.NavActive) { diff --git a/src/core/libraries/system/msgdialog_ui.cpp b/src/core/libraries/system/msgdialog_ui.cpp index ae1dced12..862f5a569 100644 --- a/src/core/libraries/system/msgdialog_ui.cpp +++ b/src/core/libraries/system/msgdialog_ui.cpp @@ -256,7 +256,7 @@ void MsgDialogUi::Draw() { std::min(io.DisplaySize.y, 300.0f), }; - CentralizeWindow(); + CentralizeNextWindow(); SetNextWindowSize(window_size); SetNextWindowCollapsed(false); if (first_render || !io.NavActive) { diff --git a/src/imgui/imgui_std.h b/src/imgui/imgui_std.h index 168204ea8..ce79da705 100644 --- a/src/imgui/imgui_std.h +++ b/src/imgui/imgui_std.h @@ -25,11 +25,16 @@ inline float FastInFastOutCubic(float x) { } // namespace Easing -inline void CentralizeWindow() { +inline void CentralizeNextWindow() { const auto display_size = GetIO().DisplaySize; SetNextWindowPos(display_size / 2.0f, ImGuiCond_Always, {0.5f}); } +inline void CentralizeWindow() { + const auto display_size = GetIO().DisplaySize; + SetWindowPos(display_size / 2.0f); +} + inline void KeepWindowInside(ImVec2 display_size = GetIO().DisplaySize) { const auto cur_pos = GetWindowPos(); if (cur_pos.x < 0.0f || cur_pos.y < 0.0f) { diff --git a/src/imgui/renderer/texture_manager.cpp b/src/imgui/renderer/texture_manager.cpp index ba4a05d01..7f9c69d49 100644 --- a/src/imgui/renderer/texture_manager.cpp +++ b/src/imgui/renderer/texture_manager.cpp @@ -7,6 +7,7 @@ #include #include "common/assert.h" +#include "common/config.h" #include "common/io_file.h" #include "common/polyfill_thread.h" #include "imgui_impl_vulkan.h" @@ -147,6 +148,11 @@ void WorkerLoop() { g_job_list.pop_front(); g_job_list_mtx.unlock(); + if (Config::vkCrashDiagnosticEnabled()) { + // FIXME: Crash diagnostic hangs when building the command buffer here + continue; + } + if (!path.empty()) { // Decode PNG from file Common::FS::IOFile file(path, Common::FS::FileAccessMode::Read); if (!file.IsOpen()) { diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index b3b718836..d4ebeb883 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -6,6 +6,7 @@ #include "common/debug.h" #include "common/polyfill_thread.h" #include "common/thread.h" +#include "core/debug_state.h" #include "core/libraries/videoout/driver.h" #include "video_core/amdgpu/liverpool.h" #include "video_core/amdgpu/pm4_cmds.h" @@ -187,6 +188,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(dcb.data()); while (!dcb.empty()) { const auto* header = reinterpret_cast(dcb.data()); const u32 type = header->type; @@ -359,6 +361,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spanindex_base_hi); regs.num_indices = draw_index->index_count; regs.draw_initiator = draw_index->draw_initiator; + if (DebugState.DumpingCurrentReg()) { + DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); + } if (rasterizer) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndex2", cmd_address)); @@ -373,6 +378,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spanmax_size; regs.num_indices = draw_index_off->index_count; regs.draw_initiator = draw_index_off->draw_initiator; + if (DebugState.DumpingCurrentReg()) { + DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); + } if (rasterizer) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin( @@ -386,6 +394,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); regs.num_indices = draw_index->index_count; regs.draw_initiator = draw_index->draw_initiator; + if (DebugState.DumpingCurrentReg()) { + DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); + } if (rasterizer) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndexAuto", cmd_address)); @@ -399,6 +410,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spandata_offset; const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; const auto size = sizeof(PM4CmdDrawIndirect::DrawInstancedArgs); + if (DebugState.DumpingCurrentReg()) { + DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); + } if (rasterizer) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:DrawIndirect", cmd_address)); @@ -413,6 +427,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spandata_offset; const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; const auto size = sizeof(PM4CmdDrawIndexIndirect::DrawIndexInstancedArgs); + if (DebugState.DumpingCurrentReg()) { + DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); + } if (rasterizer) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin( @@ -428,6 +445,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spandim_y; regs.cs_program.dim_z = dispatch_direct->dim_z; regs.cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator; + if (DebugState.DumpingCurrentReg()) { + DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); + } if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin(fmt::format("dcb:{}:Dispatch", cmd_address)); @@ -442,6 +462,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spandata_offset; const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; const auto size = sizeof(PM4CmdDispatchIndirect::GroupDimensions); + if (DebugState.DumpingCurrentReg()) { + DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); + } if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin( @@ -576,6 +599,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span acb, int vqid) { TracyFiberEnter(acb_task_name); + auto base_addr = reinterpret_cast(acb.data()); while (!acb.empty()) { const auto* header = reinterpret_cast(acb.data()); const u32 type = header->type; @@ -620,6 +644,9 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, int vqid) { regs.cs_program.dim_y = dispatch_direct->dim_y; regs.cs_program.dim_z = dispatch_direct->dim_z; regs.cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator; + if (DebugState.DumpingCurrentReg()) { + DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); + } if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); rasterizer->ScopeMarkerBegin(fmt::format("acb[{}]:{}:Dispatch", vqid, cmd_address)); diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 1c994d0a0..a4cf79334 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -772,6 +772,8 @@ struct Liverpool { BitField<27, 1, u32> fmask_compress_1frag_only; BitField<28, 1, u32> dcc_enable; BitField<29, 1, u32> cmask_addr_type; + + u32 u32all; } info; union Color0Attrib { BitField<0, 5, TilingMode> tile_mode_index; @@ -780,6 +782,8 @@ struct Liverpool { BitField<12, 3, u32> num_samples_log2; BitField<15, 2, u32> num_fragments_log2; BitField<17, 1, u32> force_dst_alpha_1; + + u32 u32all; } attrib; INSERT_PADDING_WORDS(1); u32 cmask_base_address; @@ -935,7 +939,7 @@ struct Liverpool { BitField<5, 1, u32> gs_en; BitField<6, 1, u32> vs_en; - bool IsStageEnabled(u32 stage) { + bool IsStageEnabled(u32 stage) const { switch (stage) { case 0: case 1: diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index b9fbfcb89..a7a862ea3 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -213,6 +213,7 @@ struct PM4CmdNop { enum PayloadType : u32 { DebugMarkerPush = 0x68750001u, ///< Begin of GPU event scope DebugMarkerPop = 0x68750002u, ///< End of GPU event scope + DebugSetMarker = 0x68750003u, ///< Set GPU event marker SetVsharpInUdata = 0x68750004u, ///< Indicates that V# will be set in the next packet SetTsharpInUdata = 0x68750005u, ///< Indicates that T# will be set in the next packet SetSsharpInUdata = 0x68750006u, ///< Indicates that S# will be set in the next packet From f0ee3919e038deefa6f3b57ec5b68730ee3902f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Sun, 13 Oct 2024 14:03:19 +0200 Subject: [PATCH 58/93] improved documentation + better toolbar icons (#1364) --- .gitmodules | 3 +- README.md | 17 ++++------ documents/Quickstart/Quickstart.md | 50 ++++++++++------------------- documents/changelog.txt | 2 +- externals/glslang | 2 +- externals/toml11 | 2 +- externals/vulkan-headers | 2 +- externals/xbyak | 2 +- externals/xxhash | 2 +- externals/zydis | 2 +- src/images/pause_icon.png | Bin 1113 -> 965 bytes src/images/settings_icon.png | Bin 4471 -> 2219 bytes src/images/stop_icon.png | Bin 1084 -> 658 bytes src/qt_gui/main_window_ui.h | 2 +- 14 files changed, 33 insertions(+), 53 deletions(-) diff --git a/.gitmodules b/.gitmodules index 6e4eac2b4..88635e645 100644 --- a/.gitmodules +++ b/.gitmodules @@ -97,4 +97,5 @@ shallow = true [submodule "externals/discord-rpc"] path = externals/discord-rpc - url = https://github.com/shadps4-emu/ext-discord-rpc + url = https://github.com/shadps4-emu/ext-discord-rpc.git + shallow = true \ No newline at end of file diff --git a/README.md b/README.md index 95428dfd0..18e69546c 100644 --- a/README.md +++ b/README.md @@ -36,14 +36,10 @@ SPDX-License-Identifier: GPL-2.0-or-later **shadPS4** is an early **PlayStation 4** emulator for **Windows**, **Linux** and **macOS** written in C++. -If you encounter problems or have doubts, do not hesitate to look at the [**Quickstart**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Quickstart/Quickstart.md). - -To verify that a game works, you can look at [**shadPS4 Game Compatibility**](https://github.com/shadps4-emu/shadps4-game-compatibility). - -To discuss shadPS4 development, suggest ideas or to ask for help, join our [**Discord server**](https://discord.gg/bFJxfftGW6). - -To get the latest news, go to our [**X (Twitter)**](https://x.com/shadps4) or our [**website**](https://shadps4.net/). - +If you encounter problems or have doubts, do not hesitate to look at the [**Quickstart**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Quickstart/Quickstart.md).\ +To verify that a game works, you can look at [**shadPS4 Game Compatibility**](https://github.com/shadps4-emu/shadps4-game-compatibility).\ +To discuss shadPS4 development, suggest ideas or to ask for help, join our [**Discord server**](https://discord.gg/bFJxfftGW6).\ +To get the latest news, go to our [**X (Twitter)**](https://x.com/shadps4) or our [**website**](https://shadps4.net/).\ For those who'd like to donate to the project, we now have a [**Kofi page**](https://ko-fi.com/shadps4)! # Status @@ -51,7 +47,7 @@ For those who'd like to donate to the project, we now have a [**Kofi page**](htt > [!IMPORTANT] > shadPS4 is early in development, don't expect a flawless experience. -Currently, the emulator successfully runs small games like [**Sonic Mania**](https://www.youtube.com/watch?v=AAHoNzhHyCU), [**Undertale**](https://youtu.be/5zIvdy65Ro4) and it can even run [**Bloodborne**](https://www.youtube.com/watch?v=wC6s0avpQRE). +Currently, the emulator can successfully run games like [**Bloodborne**](https://www.youtube.com/watch?v=wC6s0avpQRE), [**Dark Souls Remastered**](https://www.youtube.com/watch?v=-3PA-Xwszts), [**Red Dead Redemption**](https://www.youtube.com/watch?v=Al7yz_5nLag) and many other games. # Why @@ -123,8 +119,7 @@ Logo is done by [**Xphalnos**](https://github.com/Xphalnos) # Contributing -If you want to contribute, please look the [**CONTRIBUTING.md**](https://github.com/shadps4-emu/shadPS4/blob/main/CONTRIBUTING.md) file. - +If you want to contribute, please look the [**CONTRIBUTING.md**](https://github.com/shadps4-emu/shadPS4/blob/main/CONTRIBUTING.md) file.\ Open a PR and we'll check it :) # Contributors diff --git a/documents/Quickstart/Quickstart.md b/documents/Quickstart/Quickstart.md index 58549e067..b2931e51e 100644 --- a/documents/Quickstart/Quickstart.md +++ b/documents/Quickstart/Quickstart.md @@ -7,16 +7,16 @@ SPDX-License-Identifier: GPL-2.0-or-later ## Summary -- [PC Requirements](#pc-requirements) - - [CPU](#cpu) - - [GPU](#gpu) - - [RAM](#ram) - - [OS](#os) -- [Have the latest WIP version](#have-the-latest-wip-version) -- [Install PKG files (Games and Updates)](#install-pkg-files) -- [Configure the emulator](#configure-the-emulator) +- [**PC Requirements**](#minimum-pc-requirements) + - [**CPU**](#cpu) + - [**GPU**](#gpu) + - [**RAM**](#ram) + - [**OS**](#os) +- [**Have the latest WIP version**](#how-to-run-the-latest-work-in-progress-builds-of-shadps4) +- [**Install PKG files (Games and Updates)**](#install-pkg-files) +- [**Configure the emulator**](#configure-the-emulator) -## PC Requirements +## Minimum PC requirements ### CPU @@ -38,41 +38,25 @@ SPDX-License-Identifier: GPL-2.0-or-later - Windows 10 or Ubuntu 22.04 -## How to run the latest Work-in-Progress builds of ShadPS4 +## How to run the latest Work-in-Progress builds of shadPS4 1. Go to In the release identified as 'pre-release' click on the down arrow(Assets), select your operating system of choice (the "**qt**" versions have a user interface, which is probably the one you want. The others are SDL versions, which can only be run via command line). ![image](https://github.com/user-attachments/assets/af520c77-797c-41a0-8f67-d87f5de3e3df) -2. Once downloaded, extract to its own folder, and run ShadPS4's executable from the extracted folder. +2. Once downloaded, extract to its own folder, and run shadPS4's executable from the extracted folder. -3. Upon first launch, ShadPS4 will prompt you to select a folder to store your installed games in. Select "Browse" and then select a folder that ShadPS4 can use to install your PKG files to. +3. Upon first launch, shadPS4 will prompt you to select a folder to store your installed games in. Select "Browse" and then select a folder that shadPS4 can use to install your PKG files to. ## Install PKG files To install PKG files (game and updates), you will need the Qt application (with UI). You will have to go to "File" then to "Install Packages (PKG)", a window will open then you will have to select the files. You can install multiple PKG files at once. Once finished, the game should appear in the application. - + ## Configure the emulator -You can configure the emulator by editing the `config.toml` file found in the `user` folder created after starting the application.\ -Some settings may be related to more technical development and debugging. For more information on those, see [Debugging](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md#configuration). +To configure the emulator, you can go through the interface and go to "settings". -Here's a list of configuration entries that are worth changing: - -- `[General]` - - - `Fullscreen`: Display the game in a full screen borderless window. - - - `logType`: Configures logging synchronization (`sync`/`async`) - - It can be beneficial to set this to `sync` in order for the log to accurately maintain message order, at the cost of performance. - - Use when sending logs to developers. See more about [reporting issues](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md#reporting-and-communicating-about-issues). - - `logFilter`: Sets the logging category for various logging classes. - - Format: `: ...`, `: <*:level> ...` - - Valid log levels: `Trace, Debug, Info, Warning, Error, Critical` - in this order, setting a level silences all levels preceding it and logs every level after it. - - Examples: - - If the log is being spammed with messages coming from Lib.Pad, you can use `Lib.Pad:Critical` to only log critical-level messages. - - If you'd like to mute everything, but still want to receive messages from Vulkan rendering: `*:Error Render.Vulkan:Info` - -- `[GPU]` - - `screenWidth` and `screenHeight`: Configures the game window width and height. +You can also configure the emulator by editing the `config.toml` file located in the `user` folder created after the application is started (Mostly useful if you are using the SDL version). +Some settings may be related to more technical development and debugging.\ +For more information on this, see [**Debugging**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md#configuration). \ No newline at end of file diff --git a/documents/changelog.txt b/documents/changelog.txt index 33c3f77be..6df09472d 100644 --- a/documents/changelog.txt +++ b/documents/changelog.txt @@ -6,7 +6,7 @@ v0.3.0 23/09/2024 - codename broamic - New translations support (26 languages) - Support for unlocking trophies - Support for more controllers (Dualshock and Xbox) -- Many GUI imporovements +- Many GUI improvements - AVplayer v0.2.0 15/08/2024 - codename validptr diff --git a/externals/glslang b/externals/glslang index 46ef757e0..e61d7bb30 160000 --- a/externals/glslang +++ b/externals/glslang @@ -1 +1 @@ -Subproject commit 46ef757e048e760b46601e6e77ae0cb72c97bd2f +Subproject commit e61d7bb3006f451968714e2f653412081871e1ee diff --git a/externals/toml11 b/externals/toml11 index d050c6b13..f925e7f28 160000 --- a/externals/toml11 +++ b/externals/toml11 @@ -1 +1 @@ -Subproject commit d050c6b137199666cae75c2628a75d63b49b1c22 +Subproject commit f925e7f287c0008813c2294798cf9ca167fd9ffd diff --git a/externals/vulkan-headers b/externals/vulkan-headers index 29f979ee5..d91597a82 160000 --- a/externals/vulkan-headers +++ b/externals/vulkan-headers @@ -1 +1 @@ -Subproject commit 29f979ee5aa58b7b005f805ea8df7a855c39ff37 +Subproject commit d91597a82f881d473887b560a03a7edf2720b72c diff --git a/externals/xbyak b/externals/xbyak index ccdf68421..d067f0d3f 160000 --- a/externals/xbyak +++ b/externals/xbyak @@ -1 +1 @@ -Subproject commit ccdf68421bc8eb85693f573080fc0a5faad862db +Subproject commit d067f0d3f55696ae8bc9a25ad7012ee80f221d54 diff --git a/externals/xxhash b/externals/xxhash index 3e321b440..d4ad85e4a 160000 --- a/externals/xxhash +++ b/externals/xxhash @@ -1 +1 @@ -Subproject commit 3e321b4407318ac1348c0b80fb6fbae8c81ad5fa +Subproject commit d4ad85e4afaad5c780f54db1dc967fff5a869ffd diff --git a/externals/zydis b/externals/zydis index bd73bc03b..9d298eb80 160000 --- a/externals/zydis +++ b/externals/zydis @@ -1 +1 @@ -Subproject commit bd73bc03b0aacaa89c9c203b9b43cd08f1b1843b +Subproject commit 9d298eb8067ff62a237203d1e1470785033e185c diff --git a/src/images/pause_icon.png b/src/images/pause_icon.png index e4356949ae52a000982a1b055d8ce3ea5e530f3e..5375689b7880bb46d843eca0f8494e66bee69f19 100644 GIT binary patch literal 965 zcmeAS@N?(olHy`uVBq!ia0vp^DImtU-d1msLTBv`w>9f#KDkjb=i$8av~#YX{;OV|SJ{5L_TAEltb!0QbM7;y zxd$W{u}{pdTEZO}P* z=XB%hfAdnG|Kpj%CUzky_kag0|CjGy1vAzczRNw3!CSna!R(UK>%aW#0xy4Eyi7cG zy8Z5TJMDkOUs>|(>X&mFe;&`>wCK>zaK`yUXKd&EDt_wH)SmU@by{ojeJ_jehC)@r z1}9hyICxAdZ5fVnpPS7zEzF?!o9n~ygSE&0>YP1iYjIY*DO_dK)`Skl`K=M%jn%Br zZkAFq2JMXPm#^KX2PV@1u2pis@n4=i3V-r0m>{- zIU2ml$!l8Uq|^sf)>L?fd7Lx~*u*nulBoYlF3m_bpiJW z`Re&gZ|-bg7FqdaQcIsWPte!W#fNrQ3!kf;YA{&@cD64BQY zxBmzo-6i+Rdc)?nowu{&bS$6Qzlcs^{;;L3y^22vnA~Llv4?-Xqg=?&+KN6{`R67{1=U l`|swmAL-)I{E#;Lew559d%-E~fxvvp;OXk;vd$@?2>?c3y|VxS literal 1113 zcmeAS@N?(olHy`uVBq!ia0vp^6(G#P1|%(0%q{^b&H|6fVg?4j!ywFfJby(BP*AeO zHKHUqKdq!Zu_%?Hyu4g5GcUV1Ik6yBFTW^#_B$IX1_tJ5o-U3d6?5LsJew!tF5>1d z$geHn$RwiL#mg_?JYhq?9S#>q-JiS8`IP(mcX9OSu^;10u+tUO>0|9qKKLkNg9R3*POt0&Kd(&A z2xdP<^;Mtr3dDEF3a&qXitXw1-~57sM;P|8uVeh}VA4~Yz-{riMz^?MF5#Q*4n`mQ z4Fz8fS=TWi7q^YS*wpxbM(nLydNH*&4CnN+_L(V%svB8oE)9L4Ry41H!Y_52at9eZ6*T<@Hych>ww{3SWY z=?$J~=U8^|*yPRFy;b9<)!fq@Mdv5n-|&Xv{de2a?4)* zJ-7PV>~x;LI=@nuZIc!JUzQtk_GaEsx!jXybv5JD+r<8qE-TybzW>gh{Hib7wjhxj zjo|95i|?QIS$n>@^U~+iWA)-)I71hEm=7jV<1G)wXWm*U_m?j{>wGoTkAKRSZM(Gi z{z;#=iT!m;pT9nHq3L~kmbB(l&+BfQOPde4o&KKTotNkN(+a5le#73Vw;BbBeR1NA z%3E*QefTZ2_g+xdzIQH17BJsnT4W~2d|I|@4a4r4cKcU6e_dmz%wfmuW30aaa+=`k zqq`^1v}X;w`dL(eM^ww}nXDbJg6mmlq&$;hS!Y&b%yc7WN=>ZWszbb9UN0>Qy8q}2 zKal*O{UQBp73&YqADbTjs6G(oa^?N}Nk4ZPbR{%QxujJv`A_~)Jsb9G%8&g1hrHLY zjgNM#o+K(*&+z(;-QCZh7=C;V)`@%hGs^f_?ZUG6R>yLl&$@lXq*rqTr%d#h8u_#K zWewlOK9y|MsFsh|QNJp=?S8M=y!DwqW&C+cOTVxd@L90dgie(=+#PbSuj6IJ%>{pI pO1p$}{>A?(Ux+P-fn)oev`BP|6r<#y|G;8_!PC{xWt~$(696Gs`vL#} diff --git a/src/images/settings_icon.png b/src/images/settings_icon.png index 6fd024e361d62eb2a5ea66cac10d97fcfbc814e8..c88cd7a6f16cbc54bbb44730d3dac206f3affe2b 100644 GIT binary patch literal 2219 zcmV;c2vqlpP)8h7-~%dxtX<5B=S#lS&Y8J$ zXYP05n_n_X({^Ug%-lQ6IcLrV0)apv5C{YUfj}T2aUFnC0P_J<0C)?)QUL2YE#==; z@c)!@o`53;0eBO@X8?W!&}Nw^?+yTO02mlX$Bq#I{)jOMszhZD4}%h&Z5{?Ao3@4l ziOva+143m7B5)0WR*wVH%5?+`8Dh|>#{hf{pc=qxPSyOoV+Q%Dj({N(g^r}Zl^B#! zVo*-NkjE6Ac+#Q=RXep=VKmHHr0B%`7M=K`qAWT*1`JuH=)^jU9;{Q8wK9x`8ASjN zD?0IAi%wjkDC_4!84yxUECetYKwpde(}ab1wnce!h4LP>D6bEfxd6Zbq?{Q5&gL}u zJa0#DG0cBGfCfe9H3R5pArqC^B5a-e8 z0KW4q>pJ9_>7E<7pLaE`=qnr)T_sv`@&B90#CLb zFLV{=OWJ#P@_Yb2Y`8^L**-|E?~6{tne3D!O@pzWo6>7OD#U+|3Y%jLMEkUf4~9N? zU8vWK{eFj{GSZ?B_E6toR*kngP6t^#?V2HiM%l*ntmTZ*NK)Djbvzi29tI9xmrBHf-oJ-Vxcd zSyO&9>x#xGc260$;8hBy+iW8Jlr2|rTTS}pC3v8S?^oy=*~Xn=v(DebESrtXQDn8#hR_{F3TyVL}L>_Xz}p8V$l93?yE%T!-pL3g!q;4eic!OcnBv zaVY0Fp`QC=t{smfWvC8Dbc5E`+r7!LVlX54kC7T($1X))vNooU7ll)?jui=7-F7MJ zT5gb6wY#JTDs`w_g2=yD#|A~-wI1YDts``<&~aXo_aR(H140hTwgXa`ptnq%0im|1 zjxc4VjD3oyht&2uDinE-r)*K*smMzknpE*bOclx$A^%v1a*mI=?S0wtWg;(37HlOg zh6ls$f@kAehqA60PT1WH?H;Ow;Ti6Wyy04PQY#Fq=?yH2QG)6uikzRMQFUUXT%;%d z<_Kk$3uV+i99|>sf0XWtx%(nbHFma%O}BW?}{^X@`-Umc%sSZZc?OB`XS5CfxD7 zfa@L}@^-YPn7!unX2Laba&WQr>&aF;lZKvP-Bi}efu^Op2-qRK4#2w`%S5k7L^g&N{)p0c#Ri5rGf<1B) zbtUb}j>8>Q*yP#Sx-7!FqIxTAc2lNYEjZ}?<;&R`9P2~XXo`a1lMH)@)EZtOy}&2% z83|lRSeTFN2)gTR4GCV@>CVc)3byoZ;Iy2zw)Yw8rd-fP&X}NCP&-=SDNxw6ywEn}s5MTpwQ(6M z<9si`xlmn`xbA&9SN2+1#u}EnU&Mtx@xTGlDA#gBYmDZz#Kj0()ba%6FORU5ZXa6X zHce;^x*VH0*BS($*=yL~j?$KxHV_phF$4YUg3mPOEBFl9AF0h_I$#-RQAPg<0cUKJ zO<+x@m|R?mllHNeATHfF>YGnleK_`69nyR^^aGE#2}ahF1IkTyLo<9ZEHtrI)D~pTWUOqiFJ8p z!fPPqbF7dYk;xMv2led6P`w!d+_sw@@!P{eb`fT9=G+1 zr$`7K5Q6dNiB*aL37@OT6BUX9p_sc-IiM)35KRTv%u;kBeZD9v*+`@9)1w~bzG9gu zEX3iXU@84pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H15d=v@ zK~#90?VWqDUsZL-KkJ4YAPK?I1Vr97m_VYW0Wm;%gaFbpL6hJN5TK&1qw)}yM?38x zFQpNtkb|sH0T$3$lUF0LN9J=V;(>D$r93`b9S`BCCP-J5D_V+yT6lQRGPA z3}C$D+&Wb~IHSBqpwE*nB4dHyI8H4BKB1~_<&@iry&9%CPLEU7XY$Cc6aAqZAMH7Q zXB${xQc-4+WNr$om4D zR4YVekcj*fa5q**A0`2hiO6S4F{Fei`f~htZV$F>cqgzv;kuQ;ehE4{fD?e*fK86y zH|EL4USU5>sH?AhB{(+~68#D=x#iknz}$rER|EUU9F6fja1d}Ba340ok*1MeESc=yvK{y-aAHoPpT|ByD^4OI5jZl1j1J&m zbC4P@>wv3)&VXcJlF^2Tv8Ua4eg*J8hn^3YO?c6UxU?wIF~Yx>v0=9Z4+0kgqeBtw zux$pgg!HeGOhjiO{PJ4J#uo9Di2iU6nH|7)QZ(9f;ETXMWfAPLZ$E7QU`%0#*woZZ*Y(1_G>6wVmK=?(alZ}1rx|F{P92K}B2LnUvCN!n3r(<&i zt!lvW$-cm&d`8_3UUva@!v>3Y0@nb;1ISDw{AplNT3MNW&_rYw zJG<-rPF4HWe0Gx{BIAJvfcH2~pQft!r;(FqA;h&T`eqI}14s!~T?U+qJbu9)K26fd#U4?0C)AbrO5`u6?&99XfY4jPhg)Daf|{M zi^x7XWDcMWqM*fN9j8*qo@FLmZAACCp3|clAUrstShnwDGk9_QGOe6ko+_%k0P`(# zVGKt2d-KR1z@H-W%>4njG8RWS7PXQJMck4u!#prt7y~RBAiNkNqQ8%g#9g>eRoAB$ zSnz|t-CA_wD$hVkY}<~AXkXhtPR7~A!hwA)ci&9UCC>s!tLpz2Qj`-SG75{$8V&4? zja~<13prb{0nSQbrIzi=n-U_@yWD%e=aOf!snTq2*MaWBb}l$qSNKHi0i1_TS+;Ze ziI>gM@6H2;)$~LQ@Gl8At{~@pdq^V(VS@#ifp=BVNaL|F#MX=i`$`w~iW*-r(cen2 z={X9#7DF5RkjKaiN^GEUz`dB8atM;+r6-a{&se&ocap42~%zw?)C;z@r68R_yb^8(W>R z(l>Z?yjC^QF~Yk%cHIz2#{sxs8-)TEMO6`zcVS(nFM(VZEK${yiVBHdBTrzxO)C_b zrWJJEK15yme0ENA-3|P?2FQLWB98!n5j(sQxHjQuFJTKhZvD}nG>_s>bZ-gbKMpML z$PX5dThPAQ9{UyqlK-Uw9IOF83+xh;$uWUQUJraRP({b%?#9+q(x^L!jYF^*xVViy z1Xw)5a<3HSA1gT zzoEUiXG0L#Gs|&m2sWEn6xU+eA+zR7kK-H>nb3i)w!~5AJ{W)LIJO0we{Uk@11^`V z)L}V3sOmOVy;xQEQdLPRSXkCLz!mo;z^59Ol{iZEc%tLfy{h^~#pa}fs=kRiH^gyb zRG+Hs*rE2x^>J(mg*YZRifrJ|u*sb`mQ-y>Ml5z5+Z(G-rBF+#e~07L{!Jr$jOX~H zm0FPnVP{aw`(hG_t!K$M&q;ihLOEIM%2=Pnt%yD^P8}rf+r{+2@*eZq$A!O(T1i<5(vd zlEl=6_v6kYwX$kMlF)&>>vYXxRRP~Fdq-DlMRvGyoj?Q0c6}e7B_eeUjGo6m`%~D> zsboxY9NUc5r!nZjGUvrH7PwnP#zw*Rl^|L&T%wkO5wda81OFb)$tYD?_rjDplOMxmq6w83@((zK&gp(Y_xt#K&IMbH3*jCej z;`?7H!(kLlyq=vG0iFX6)gr>*){zvSy%jiDRZDj>j=Jt2;E&J#h^jsnKu-s@Mi6&a z+=T5$n1mOBRhksoSMV;p5%=A7O28*-Cdru(W>atGg|h?do9fZ?wZgIk7nlt6g9Y)MZ>J;Cldb@XOS za>a1P>ld2n<2}0O2azAh$SEEp=Y(vGNY_l#GPodh;(_-UMFDJ-C~RD zsa72ObJt1sULFG8%KL=1(RVo6GaPnfNobZ~84j04|95AUvmE$P33WQ%qwnEhIszCu z68B<&Vx!bt$>&n&9ZEmRU5b5a_+~AGWH&5KcJxF)ozOtHltFaNhOyYxYMEJy7f@nW zV&I~(pXx$nryzPR6$bQUizm0^pWVt@wqo!6(({c2b38UI1P0eicGHNC*)|Nzh|IfWLIqP_&Rn&ti)!X zYS|-1M1}*80f%`mS%_t!daID0EM!OiRuAI$_M8Wt)fA%p5~7Fb0o=|IU>OyrX@R5! zS^dUm;U{_yaOMEf`JlFw&t&Y&#)Xdp_e2j|E~I|4MdUzig5DLKF$e3qT`|f7$N&); zfu*5#xfTK3sj6q?k)7qqJ_9$=^B5pH7*zFT>gJzhi#I~NYX4^@2*TT_oC^B(0{$aaOZ2rySfj?Aif0GZ(W z+}f5^NEWgmz+%7Rco(oJ@)aut5g9Hb*NVs-5xKCb!F*LB>N#Dn%NH^W%Q%NS3HP>6 zig{h;UgyOIfX~Eat_Du8V~#zC{ks!*?H-%r^+kkaUWLnJz{MWrig`DV%OP_l$qOrs zf#d5Y)MNVuY&~EbwtjyL0TzL%A$+~K9|${Ng(KFBUZ8S1D$-Bt&##1G~p_5FNkEmjGv!NbqRjLTt6qH(v3T zIm9+)X`^c#)-(F#-9!Ob=@5-^U4n5hU_1B4f3qj>tAyYE40t$W!(I}Q;K9JxFo(n< zH=_bSNkcgBA#BX!lXGL?dIf5n>l2LJ2uzMWDgTvl{oO>}ANF4K?aLi6>jRSg%^ajA zxi{aHL^!s><%=OxU{;WN1#F;iv}ue@SoWYK$H04WxZq%+tMf_jM!N&nC8^vfIg*RLcPIhFtFYYT%xewU2jr;?#U4306)sSSj^CjPaxfPF! z{jS|7O+pq|bR}!wjE+{`Cg+&#(DF@N_lfl0n_*@NSzkqe`$CL2sM|)??^_Wv* zy8++T!MQ1cY{5Ru(gu_Nn2a+|HIe=K48Lzjc)ODO^D3zX&t0m9YcOw%UnN|+7C28; z&(?cR$V{|?-*s+rfPvtoekilC|+fPaokEofSD`LuAo>pMbZ zP>y7{+NIBrFZwFke{dY@Aj}SVhvIIRqC4Hzj=oCvbDra~ilo7g+@Nmsoc8T<&}UF6 z4_ciNk+r}VgP^!X5{zWUBaEi`yK^za>cWLdxmJqeaYUez~9^NjLra`DY8Sp;&0&QkN;qmtC9m^~iQ58_A_Neb2c6`26F&neh9wX8V@~tzW;(G}W2c zV$IJF8*WT}W%zvk+R3x{_4QmgG&`1`Hd{(ZYX zo~-abb2xIc_?6j@PP7%qypfAClUr`HE-8-JdPYH@YJY9&fr;+FOpnE$JACjEW5gWi zrSB!eE%{l08wvAA&-oIU`?=!$*IBc+T$q1+es!qgW6giFvT7LrzZTvs^#5YJ`tvQR mi{;-c?( zPRE%&dgn6y_upDl`&{B!`}65@j+WorSN!Sk<(b8|PWbp--}t=cNAbZK&S+mFez0`AE|b>}RX)=AS-%@{HkK->mrE&)b7|Yqmw+*F3ke zd297Aw^R?)4@sNlzMaoz+i-JD{A!++_fp>eTzDpDeseB^^PA28XRF`}SFC|OI0RQU zmjCMRurNHZHNxLzPL7}Lx-%=;{_TyNuLbe8JILG3X@BDi`()SLy&SLQys9zZ{I#~= z!}FgTU5+pn?6kc5?&rdg#W@U*@BOm*5Lh_v?RK^~uh-7M`1a?-sZ8n5#6Hv(|61Kq zvD@f;nd=ej1L9|2zBl?Dm>HKArk}#Oga6C>1@oD%H|84`2TA>Dnzc`N{l%lTJHIc@ zeQ@?nPJRx*`F5#_szB`nzTfr>-(kMiXni0p?cEmp?Ef}#^Vse!|9!^xdd+E;h_h!d z-8N>JX?dA3X7O(uKlx_4O%!hw{P0{R z=feN563q4t-Oi;mUmE|o?~}i8T4VQt*9U~E^N-|Q=i9*g$Lsr?-GO_f?M^&s%-#26 gy+3M(L65_F#&fmY$7YmX1eFgAp00i_>zopr0PC9oR{#J2 diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index 45b2ab4e0..373b2924e 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -190,7 +190,7 @@ public: settingsButton = new QPushButton(centralWidget); settingsButton->setFlat(true); settingsButton->setIcon(QIcon(":images/settings_icon.png")); - settingsButton->setIconSize(QSize(40, 40)); + settingsButton->setIconSize(QSize(44, 44)); controllerButton = new QPushButton(centralWidget); controllerButton->setFlat(true); controllerButton->setIcon(QIcon(":images/controller_icon.png")); From bd9f82df94847b4a5f3d2676ae938f064505c992 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Sun, 13 Oct 2024 09:49:39 -0300 Subject: [PATCH 59/93] fix descriptionText size | missing translations (#1319) * fix descriptionText size * + avoid 'blinking' * Update ru_RU.ts * TR * Update pt_BR.ts * emulatorLanguage alphabetical order --------- Co-authored-by: georgemoralis --- src/qt_gui/settings_dialog.cpp | 41 ++++-- src/qt_gui/settings_dialog.h | 2 + src/qt_gui/settings_dialog.ui | 8 +- src/qt_gui/translations/ar.ts | 127 +++++++++++++++++- src/qt_gui/translations/da_DK.ts | 127 +++++++++++++++++- src/qt_gui/translations/de.ts | 127 +++++++++++++++++- src/qt_gui/translations/el.ts | 127 +++++++++++++++++- src/qt_gui/translations/en.ts | 69 ++++++---- src/qt_gui/translations/es_ES.ts | 127 +++++++++++++++++- src/qt_gui/translations/fa_IR.ts | 127 +++++++++++++++++- src/qt_gui/translations/fi.ts | 127 +++++++++++++++++- src/qt_gui/translations/fr.ts | 213 ++++++++++++++++++++++++------- src/qt_gui/translations/hu_HU.ts | 127 +++++++++++++++++- src/qt_gui/translations/id.ts | 127 +++++++++++++++++- src/qt_gui/translations/it.ts | 127 +++++++++++++++++- src/qt_gui/translations/ja_JP.ts | 127 +++++++++++++++++- src/qt_gui/translations/ko_KR.ts | 127 +++++++++++++++++- src/qt_gui/translations/lt_LT.ts | 127 +++++++++++++++++- src/qt_gui/translations/nb.ts | 127 +++++++++++++++++- src/qt_gui/translations/nl.ts | 127 +++++++++++++++++- src/qt_gui/translations/pl_PL.ts | 127 +++++++++++++++++- src/qt_gui/translations/pt_BR.ts | 137 +++++++++++++++++++- src/qt_gui/translations/ro_RO.ts | 127 +++++++++++++++++- src/qt_gui/translations/ru_RU.ts | 127 +++++++++++++++++- src/qt_gui/translations/sq.ts | 120 ++++++++++++++++- src/qt_gui/translations/tr_TR.ts | 127 +++++++++++++++++- src/qt_gui/translations/vi_VN.ts | 127 +++++++++++++++++- src/qt_gui/translations/zh_CN.ts | 127 +++++++++++++++++- src/qt_gui/translations/zh_TW.ts | 127 +++++++++++++++++- 29 files changed, 3267 insertions(+), 117 deletions(-) diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index efc2cd3eb..b63f14c05 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -51,6 +51,7 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge : QDialog(parent), ui(new Ui::SettingsDialog) { ui->setupUi(this); ui->tabWidgetSettings->setUsesScrollButtons(false); + initialHeight = this->height(); const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); ui->buttonBox->button(QDialogButtonBox::StandardButton::Close)->setFocus(); @@ -268,6 +269,7 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge ui->fullscreenCheckBox->installEventFilter(this); ui->showSplashCheckBox->installEventFilter(this); ui->ps4proCheckBox->installEventFilter(this); + ui->discordRPCCheckbox->installEventFilter(this); ui->userName->installEventFilter(this); ui->logTypeGroupBox->installEventFilter(this); ui->logFilter->installEventFilter(this); @@ -275,7 +277,6 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge ui->GUIgroupBox->installEventFilter(this); // Input - ui->cursorGroupBox->installEventFilter(this); ui->hideCursorGroupBox->installEventFilter(this); ui->idleTimeoutGroupBox->installEventFilter(this); ui->backButtonBehaviorGroupBox->installEventFilter(this); @@ -361,15 +362,30 @@ void SettingsDialog::LoadValuesFromConfig() { void SettingsDialog::InitializeEmulatorLanguages() { QDirIterator it(QStringLiteral(":/translations"), QDirIterator::NoIteratorFlags); - int idx = 0; + QVector> languagesList; + while (it.hasNext()) { QString locale = it.next(); locale.truncate(locale.lastIndexOf(QLatin1Char{'.'})); locale.remove(0, locale.lastIndexOf(QLatin1Char{'/'}) + 1); const QString lang = QLocale::languageToString(QLocale(locale).language()); const QString country = QLocale::territoryToString(QLocale(locale).territory()); - ui->emulatorLanguageComboBox->addItem(QStringLiteral("%1 (%2)").arg(lang, country), locale); + QString displayName = QStringLiteral("%1 (%2)").arg(lang, country); + languagesList.append(qMakePair(locale, displayName)); + } + + std::sort(languagesList.begin(), languagesList.end(), + [](const QPair& a, const QPair& b) { + return a.second < b.second; + }); + + int idx = 0; + for (const auto& pair : languagesList) { + const QString& locale = pair.first; + const QString& displayName = pair.second; + + ui->emulatorLanguageComboBox->addItem(displayName, locale); languages[locale.toStdString()] = idx; idx++; } @@ -419,6 +435,8 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("showSplashCheckBox"); } else if (elementName == "ps4proCheckBox") { text = tr("ps4proCheckBox"); + } else if (elementName == "discordRPCCheckbox") { + text = tr("discordRPCCheckbox"); } else if (elementName == "userName") { text = tr("userName"); } else if (elementName == "logTypeGroupBox") { @@ -432,9 +450,7 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { } // Input - if (elementName == "cursorGroupBox") { - text = tr("cursorGroupBox"); - } else if (elementName == "hideCursorGroupBox") { + if (elementName == "hideCursorGroupBox") { text = tr("hideCursorGroupBox"); } else if (elementName == "idleTimeoutGroupBox") { text = tr("idleTimeoutGroupBox"); @@ -493,15 +509,22 @@ bool SettingsDialog::eventFilter(QObject* obj, QEvent* event) { } // if the text exceeds the size of the box, it will increase the size + QRect currentGeometry = this->geometry(); + int newWidth = currentGeometry.width(); + int documentHeight = ui->descriptionText->document()->size().height(); int visibleHeight = ui->descriptionText->viewport()->height(); if (documentHeight > visibleHeight) { - ui->descriptionText->setMinimumHeight(90); + ui->descriptionText->setMaximumSize(16777215, 110); + this->setGeometry(currentGeometry.x(), currentGeometry.y(), newWidth, + currentGeometry.height() + 40); } else { - ui->descriptionText->setMinimumHeight(70); + ui->descriptionText->setMaximumSize(16777215, 70); + this->setGeometry(currentGeometry.x(), currentGeometry.y(), newWidth, + initialHeight); } return true; } } return QDialog::eventFilter(obj, event); -} +} \ No newline at end of file diff --git a/src/qt_gui/settings_dialog.h b/src/qt_gui/settings_dialog.h index d09617ec3..8cdded980 100644 --- a/src/qt_gui/settings_dialog.h +++ b/src/qt_gui/settings_dialog.h @@ -40,4 +40,6 @@ private: std::map languages; QString defaultTextEdit; + + int initialHeight; }; diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 9743e51bd..b98fe228d 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -12,7 +12,7 @@ 0 0 854 - 605 + 630 @@ -1133,7 +1133,7 @@ 100 360 - 80 + 91 24 @@ -1144,9 +1144,9 @@ - 210 + 199 360 - 80 + 91 24 diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index ec03c04b5..8efacc063 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -414,6 +414,11 @@ Is PS4 Pro PS4 Pro هل هو + + + Enable Discord Rich Presence + تفعيل حالة الثراء في ديسكورد + Username @@ -434,6 +439,36 @@ Log Filter مرشح السجل + + + Input + إدخال + + + + Cursor + مؤشر + + + + Hide Cursor + إخفاء المؤشر + + + + Hide Cursor Idle Timeout + مهلة إخفاء المؤشر عند الخمول + + + + Controller + التحكم + + + + Back Button Behavior + سلوك زر العودة + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU تمكين وحدة معالجة الرسومات الفارغة + + + Paths + المسارات + + + + Game Folders + مجلدات اللعبة + + + + Add... + إضافة... + + + + Remove + إزالة + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox هل هو PS4 Pro:\nيجعل المحاكي يعمل كـ PS4 PRO، مما قد يتيح ميزات خاصة في الألعاب التي تدعمه. + + + discordRPCCheckbox + تفعيل حالة الثراء في ديسكورد:\nيعرض أيقونة المحاكي ومعلومات ذات صلة على ملفك الشخصي في ديسكورد. + userName @@ -1011,7 +1071,7 @@ logFilter - فلتر السجل: يقوم بتصفية السجل لطباعة معلومات محددة فقط. أمثلة: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" المستويات: Trace, Debug, Info, Warning, Error, Critical - بالترتيب، مستوى محدد يخفي جميع المستويات التي تسبقه ويعرض جميع المستويات بعده. + فلتر السجل:\nيقوم بتصفية السجل لطباعة معلومات محددة فقط.\nأمثلة: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" المستويات: Trace, Debug, Info, Warning, Error, Critical - بالترتيب، مستوى محدد يخفي جميع المستويات التي تسبقه ويعرض جميع المستويات بعده. @@ -1023,6 +1083,56 @@ GUIgroupBox تشغيل موسيقى العنوان:\nإذا كانت اللعبة تدعم ذلك، قم بتمكين تشغيل موسيقى خاصة عند اختيار اللعبة في واجهة المستخدم. + + + hideCursorGroupBox + إخفاء المؤشر:\nاختر متى سيختفي المؤشر:\nأبداً: سترى الفأرة دائماً.\nعاطل: حدد وقتاً لاختفائه بعد أن يكون غير مستخدم.\nدائماً: لن ترى الفأرة أبداً. + + + + idleTimeoutGroupBox + حدد وقتاً لاختفاء الفأرة بعد أن تكون غير مستخدم. + + + + backButtonBehaviorGroupBox + سلوك زر العودة:\nيضبط زر العودة في وحدة التحكم ليحاكي الضغط على الموضع المحدد على لوحة اللمس في PS4. + + + + Never + أبداً + + + + Idle + خامل + + + + Always + دائماً + + + + Touchpad Left + لوحة اللمس اليسرى + + + + Touchpad Right + لوحة اللمس اليمنى + + + + Touchpad Center + وسط لوحة اللمس + + + + None + لا شيء + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox تمكين GPU الافتراضية:\nلأغراض تصحيح الأخطاء التقنية، يقوم بتعطيل عرض اللعبة كما لو لم يكن هناك بطاقة رسومات. + + + gameFoldersBox + مجلدات اللعبة:\nقائمة بالمجلدات للتحقق من الألعاب المثبتة. + + + + addFolderButton + إضافة:\nأضف مجلداً إلى القائمة. + + + + removeFolderButton + إزالة:\nأزل مجلداً من القائمة. + debugDump diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index c7edfb102..9abe22c3d 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -414,6 +414,11 @@ Is PS4 Pro Is PS4 Pro + + + Enable Discord Rich Presence + Aktiver Discord Rich Presence + Username @@ -434,6 +439,36 @@ Log Filter Log Filter + + + Input + Indtastning + + + + Cursor + Markør + + + + Hide Cursor + Skjul markør + + + + Hide Cursor Idle Timeout + Timeout for skjul markør ved inaktivitet + + + + Controller + Controller + + + + Back Button Behavior + Tilbageknap adfærd + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU Enable NULL GPU + + + Paths + Stier + + + + Game Folders + Spilmapper + + + + Add... + Tilføj... + + + + Remove + Fjern + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox Er det en PS4 Pro:\nGør det muligt for emulatoren at fungere som en PS4 PRO, hvilket kan aktivere visse funktioner i spil, der understøtter det. + + + discordRPCCheckbox + Aktiver Discord Rich Presence:\nViser emulatorikonet og relevante oplysninger på din Discord-profil. + userName @@ -1011,7 +1071,7 @@ logFilter - Logfilter: Filtrerer loggen for kun at udskrive bestemte oplysninger. Eksempler: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Niveaus: Trace, Debug, Info, Warning, Error, Critical - i rækkefølge, et valgt niveau skjuler alle forudgående niveauer og viser alle efterfølgende niveauer. + Logfilter:\nFiltrerer loggen for kun at udskrive bestemte oplysninger.\nEksempler: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Niveaus: Trace, Debug, Info, Warning, Error, Critical - i rækkefølge, et valgt niveau skjuler alle forudgående niveauer og viser alle efterfølgende niveauer. @@ -1023,6 +1083,56 @@ GUIgroupBox Titelsmusikafspilning:\nHvis spillet understøtter det, aktiver speciel musik, når spillet vælges i brugergrænsefladen. + + + hideCursorGroupBox + Skjul Cursor:\nVælg hvornår cursoren skal forsvinde:\nAldrig: Du vil altid se musen.\nInaktiv: Indstil en tid for, hvornår den skal forsvinde efter at være inaktiv.\nAltid: du vil aldrig se musen. + + + + idleTimeoutGroupBox + Indstil en tid for, at musen skal forsvinde efter at være inaktiv. + + + + backButtonBehaviorGroupBox + Tilbageknap Adfærd:\nIndstiller controllerens tilbageknap til at efterligne tryk på den angivne position på PS4 berøringsflade. + + + + Never + Aldrig + + + + Idle + Inaktiv + + + + Always + Altid + + + + Touchpad Left + Berøringsplade Venstre + + + + Touchpad Right + Berøringsplade Højre + + + + Touchpad Center + Berøringsplade Center + + + + None + Ingen + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Aktiver virtuel GPU:\nTil teknisk fejlfinding deaktiverer det spilvisning, som om der ikke var et grafikkort. + + + gameFoldersBox + Spilmappen:\nListen over mapper til at tjekke for installerede spil. + + + + addFolderButton + Tilføj:\nTilføj en mappe til listen. + + + + removeFolderButton + Fjern:\nFjern en mappe fra listen. + debugDump diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index 43e19a37f..a6904d9a1 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -414,6 +414,11 @@ Is PS4 Pro Ist PS4 Pro + + + Enable Discord Rich Presence + Discord Rich Presence aktivieren + Username @@ -434,6 +439,36 @@ Log Filter Log-Filter + + + Input + Eingabe + + + + Cursor + Cursor + + + + Hide Cursor + Cursor ausblenden + + + + Hide Cursor Idle Timeout + Inaktivitätszeitüberschreitung zum Ausblenden des Cursors + + + + Controller + Controller + + + + Back Button Behavior + Verhalten der Zurück-Taste + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU NULL GPU aktivieren + + + Paths + Pfad + + + + Game Folders + Spieleordner + + + + Add... + Hinzufügen... + + + + Remove + Entfernen + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox Ist es eine PS4 Pro:\nErmöglicht es dem Emulator, als PS4 PRO zu arbeiten, was in Spielen, die dies unterstützen, spezielle Funktionen aktivieren kann. + + + discordRPCCheckbox + Discord Rich Presence aktivieren:\nZeigt das Emulator-Icon und relevante Informationen in deinem Discord-Profil an. + userName @@ -1011,7 +1071,7 @@ logFilter - Protokollfilter: Filtert das Protokoll so, dass nur bestimmte Informationen ausgegeben werden. Beispiele: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Ebenen: Trace, Debug, Info, Warning, Error, Critical - in dieser Reihenfolge, ein ausgewähltes Level blendet alle vorherigen Ebenen aus und zeigt alle nachfolgenden an. + Protokollfilter:\nFiltert das Protokoll so, dass nur bestimmte Informationen ausgegeben werden.\nBeispiele: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Ebenen: Trace, Debug, Info, Warning, Error, Critical - in dieser Reihenfolge, ein ausgewähltes Level blendet alle vorherigen Ebenen aus und zeigt alle nachfolgenden an. @@ -1023,6 +1083,56 @@ GUIgroupBox Wiedergabe der Titelmusik:\nWenn das Spiel dies unterstützt, wird beim Auswählen des Spiels in der Benutzeroberfläche spezielle Musik abgespielt. + + + hideCursorGroupBox + Maus ausblenden:\nWählen Sie, wann der Cursor verschwinden soll:\nNie: Sie sehen die Maus immer.\nInaktiv: Legen Sie eine Zeit fest, nach der sie nach Inaktivität verschwindet.\nImmer: Sie sehen die Maus niemals. + + + + idleTimeoutGroupBox + Stellen Sie eine Zeit ein, nach der die Maus nach Inaktivität verschwinden soll. + + + + backButtonBehaviorGroupBox + Zurück-Button Verhalten:\nStellt die Zurück-Taste des Controllers so ein, dass sie das Antippen der angegebenen Position auf dem PS4-Touchpad emuliert. + + + + Never + Niemals + + + + Idle + Im Leerlauf + + + + Always + Immer + + + + Touchpad Left + Touchpad Links + + + + Touchpad Right + Touchpad Rechts + + + + Touchpad Center + Touchpad Mitte + + + + None + Keine + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Virtuelle GPU aktivieren:\nFür das technische Debugging deaktiviert es die Spielanzeige, als ob keine Grafikkarte vorhanden wäre. + + + gameFoldersBox + Spieleordner:\nDie Liste der Ordner, in denen nach installierten Spielen gesucht wird. + + + + addFolderButton + Hinzufügen:\nFügen Sie einen Ordner zur Liste hinzu. + + + + removeFolderButton + Entfernen:\nEntfernen Sie einen Ordner aus der Liste. + debugDump diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index 8009dfd88..963ca0ab1 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -414,6 +414,11 @@ Is PS4 Pro Is PS4 Pro + + + Enable Discord Rich Presence + Ενεργοποίηση Discord Rich Presence + Username @@ -434,6 +439,36 @@ Log Filter Log Filter + + + Input + Είσοδος + + + + Cursor + Δείκτης + + + + Hide Cursor + Απόκρυψη δείκτη + + + + Hide Cursor Idle Timeout + Χρόνος αδράνειας απόκρυψης δείκτη + + + + Controller + Controller + + + + Back Button Behavior + Συμπεριφορά κουμπιού επιστροφής + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU Enable NULL GPU + + + Paths + Διαδρομές + + + + Game Folders + Φάκελοι παιχνιδιών + + + + Add... + Προσθήκη... + + + + Remove + Αφαίρεση + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox Είναι PS4 Pro:\nΕπιτρέπει στον εξομοιωτή να λειτουργεί σαν PS4 PRO, κάτι που μπορεί να ενεργοποιήσει συγκεκριμένες λειτουργίες σε παιχνίδια που το υποστηρίζουν. + + + discordRPCCheckbox + Ενεργοποίηση Discord Rich Presence:\nΕμφανίζει το εικονίδιο του emulator και σχετικές πληροφορίες στο προφίλ σας στο Discord. + userName @@ -1011,7 +1071,7 @@ logFilter - Φίλτρο Καταγραφής: Φιλτράρει τις καταγραφές ώστε να εκτυπώνονται μόνο συγκεκριμένες πληροφορίες. Παραδείγματα: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Επίπεδα: Trace, Debug, Info, Warning, Error, Critical - με τη σειρά αυτή, κάθε επίπεδο που επιλέγεται αποκλείει τα προηγούμενα και εμφανίζει τα επόμενα επίπεδα. + Φίλτρο Καταγραφής:\nΦιλτράρει τις καταγραφές ώστε να εκτυπώνονται μόνο συγκεκριμένες πληροφορίες.\nΠαραδείγματα: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Επίπεδα: Trace, Debug, Info, Warning, Error, Critical - με τη σειρά αυτή, κάθε επίπεδο που επιλέγεται αποκλείει τα προηγούμενα και εμφανίζει τα επόμενα επίπεδα. @@ -1023,6 +1083,56 @@ GUIgroupBox Αναπαραγωγή Μουσικής Τίτλων:\nΕάν το παιχνίδι το υποστηρίζει, ενεργοποιεί ειδική μουσική κατά την επιλογή του παιχνιδιού από τη διεπαφή χρήστη. + + + hideCursorGroupBox + Απόκρυψη Κέρσορα:\nΕπιλέξτε πότε θα εξαφανιστεί ο κέρσορας:\nΠοτέ: θα βλέπετε πάντα το ποντίκι.\nΑδρανές: ορίστε έναν χρόνο για να εξαφανιστεί μετά από αδράνεια.\nΠάντα: δεν θα δείτε ποτέ το ποντίκι. + + + + idleTimeoutGroupBox + Ορίστε έναν χρόνο για να εξαφανιστεί το ποντίκι μετά από αδράνεια. + + + + backButtonBehaviorGroupBox + Συμπεριφορά Κουμπιού Επιστροφής:\nΟρίζει το κουμπί επιστροφής του ελεγκτή να προσομοιώνει το πάτημα της καθορισμένης θέσης στην οθόνη αφής PS4. + + + + Never + Ποτέ + + + + Idle + Αδρανής + + + + Always + Πάντα + + + + Touchpad Left + Touchpad Αριστερά + + + + Touchpad Right + Touchpad Δεξιά + + + + Touchpad Center + Κέντρο Touchpad + + + + None + Κανένα + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Ενεργοποίηση Εικονικής GPU:\nΓια τεχνικό εντοπισμό σφαλμάτων, απενεργοποιεί την εμφάνιση του παιχνιδιού σαν να μην υπάρχει κάρτα γραφικών. + + + gameFoldersBox + Φάκελοι Παιχνιδιών:\nΗ λίστα των φακέλων για έλεγχο των εγκατεστημένων παιχνιδιών. + + + + addFolderButton + Προσθήκη:\nΠροσθέστε έναν φάκελο στη λίστα. + + + + removeFolderButton + Αφαίρεση:\nΑφαιρέστε έναν φάκελο από τη λίστα. + debugDump diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 974045de1..d781bc2fe 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -414,6 +414,11 @@ Is PS4 Pro Is PS4 Pro + + + Enable Discord Rich Presence + Enable Discord Rich Presence + Username @@ -454,11 +459,6 @@ Hide Cursor Idle Timeout Hide Cursor Idle Timeout - - - Input - Input - Controller @@ -509,6 +509,26 @@ Enable NULL GPU Enable NULL GPU + + + Paths + Paths + + + + Game Folders + Game Folders + + + + Add... + Add... + + + + Remove + Remove + Debug @@ -1033,6 +1053,11 @@ ps4proCheckBox Is PS4 Pro:\nMakes the emulator act as a PS4 PRO, which may enable special features in games that support it. + + + discordRPCCheckbox + Enable Discord Rich Presence:\nDisplays the emulator icon and relevant information on your Discord profile. + userName @@ -1046,7 +1071,7 @@ logFilter - Log Filter: Filters the log to only print specific information. Examples: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Levels: Trace, Debug, Info, Warning, Error, Critical - in this order, a specific level silences all levels preceding it in the list and logs every level after it. + Log Filter:\nFilters the log to only print specific information.\nExamples: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nLevels: Trace, Debug, Info, Warning, Error, Critical - in this order, a specific level silences all levels preceding it in the list and logs every level after it. @@ -1059,20 +1084,20 @@ Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. - - cursorGroupBox - Cursor:\nChange settings related to the cursor. - - - + hideCursorGroupBox - Hide Cursor:\nSet cursor hiding behavior. + Hide Cursor:\nChoose when the cursor will disappear:\nNever: You will always see the mouse.\nidle: Set a time for it to disappear after being idle.\nAlways: you will never see the mouse. - + idleTimeoutGroupBox Hide Idle Cursor Timeout:\nThe duration (seconds) after which the cursor that has been idle hides itself. + + + backButtonBehaviorGroupBox + Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad. + Never @@ -1088,16 +1113,6 @@ Always Always - - - backButtonBehaviorGroupBox - Back Button Behavior:\nAllows setting which part of the touchpad the back button will emulate a touch on. - - - - backButtonBehaviorGroupBox - Back Button Behavior:\nAllows setting which part of the touchpad the back button will emulate a touch on. - Touchpad Left @@ -1146,17 +1161,17 @@ gameFoldersBox - Game Folders: The list of folders to check for installed games. + Game Folders:\nThe list of folders to check for installed games. addFolderButton - Add: Add a folder to the list. + Add:\nAdd a folder to the list. removeFolderButton - Remove: Remove a folder from the list. + Remove:\nRemove a folder from the list. diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 0c6591a5f..1095cd9cd 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -414,6 +414,11 @@ Is PS4 Pro Modo PS4 Pro + + + Enable Discord Rich Presence + Habilitar Discord Rich Presence + Username @@ -434,6 +439,36 @@ Log Filter Filtro de registro + + + Input + Entrada + + + + Cursor + Cursor + + + + Hide Cursor + Ocultar cursor + + + + Hide Cursor Idle Timeout + Tiempo de espera para ocultar cursor inactivo + + + + Controller + Controlador + + + + Back Button Behavior + Comportamiento del botón de retroceso + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU Habilitar GPU NULL + + + Paths + Rutas + + + + Game Folders + Carpetas de juego + + + + Add... + Añadir... + + + + Remove + Eliminar + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox Es PS4 Pro:\nHace que el emulador actúe como una PS4 PRO, lo que puede habilitar funciones especiales en los juegos que lo admitan. + + + discordRPCCheckbox + Habilitar Discord Rich Presence:\nMuestra el ícono del emulador y la información relevante en tu perfil de Discord. + userName @@ -1011,7 +1071,7 @@ logFilter - Filtro de Registro: Filtra el registro para imprimir solo información específica. Ejemplos: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Niveles: Trace, Debug, Info, Warning, Error, Critical - en este orden, un nivel específico silencia todos los niveles anteriores en la lista y registra cada nivel posterior. + Filtro de Registro:\nFiltra el registro para imprimir solo información específica.\nEjemplos: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Niveles: Trace, Debug, Info, Warning, Error, Critical - en este orden, un nivel específico silencia todos los niveles anteriores en la lista y registra cada nivel posterior. @@ -1023,6 +1083,56 @@ GUIgroupBox Reproducir Música del Título:\nSi un juego lo admite, habilita la reproducción de música especial al seleccionar el juego en la interfaz gráfica. + + + hideCursorGroupBox + Ocultar Cursor:\nElija cuándo desaparecerá el cursor:\nNunca: Siempre verá el mouse.\nInactivo: Establezca un tiempo para que desaparezca después de estar inactivo.\nSiempre: nunca verá el mouse. + + + + idleTimeoutGroupBox + Establezca un tiempo para que el mouse desaparezca después de estar inactivo. + + + + backButtonBehaviorGroupBox + Comportamiento del Botón Atrás:\nEstablece el botón atrás del controlador para emular el toque en la posición especificada en el touchpad del PS4. + + + + Never + Nunca + + + + Idle + Inactivo + + + + Always + Siempre + + + + Touchpad Left + Touchpad Izquierda + + + + Touchpad Right + Touchpad Derecha + + + + Touchpad Center + Centro del Touchpad + + + + None + Ninguno + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Habilitar GPU Nula:\nPor el bien de la depuración técnica, desactiva el renderizado del juego como si no hubiera tarjeta gráfica. + + + gameFoldersBox + Carpetas de Juegos:\nLa lista de carpetas para verificar los juegos instalados. + + + + addFolderButton + Añadir:\nAgregar una carpeta a la lista. + + + + removeFolderButton + Eliminar:\nEliminar una carpeta de la lista. + debugDump diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 60d8be89e..e86001b9a 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -414,6 +414,11 @@ Is PS4 Pro PS4 Pro حالت + + + Enable Discord Rich Presence + Discord Rich Presence را فعال کنید + Username @@ -434,6 +439,36 @@ Log Filter Log فیلتر + + + Input + ورودی + + + + Cursor + نشانگر + + + + Hide Cursor + پنهان کردن نشانگر + + + + Hide Cursor Idle Timeout + مخفی کردن زمان توقف مکان نما + + + + Controller + کنترل کننده + + + + Back Button Behavior + رفتار دکمه بازگشت + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU NULL GPU فعال کردن + + + Paths + مسیرها + + + + Game Folders + پوشه های بازی + + + + Add... + افزودن... + + + + Remove + حذف + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox Is PS4 Pro:\nMakes the emulator act as a PS4 PRO, which may enable special features in games that support it. + + + discordRPCCheckbox + فعال کردن Discord Rich Presence:\nآیکون شبیه ساز و اطلاعات مربوطه را در نمایه Discord شما نمایش می دهد. + userName @@ -1011,7 +1071,7 @@ logFilter - Log Filter: Filters the log to only print specific information. Examples: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Levels: Trace, Debug, Info, Warning, Error, Critical - in this order, a specific level silences all levels preceding it in the list and logs every level after it. + Log Filter:\nFilters the log to only print specific information.\nExamples: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Levels: Trace, Debug, Info, Warning, Error, Critical - in this order, a specific level silences all levels preceding it in the list and logs every level after it. @@ -1023,6 +1083,56 @@ GUIgroupBox Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. + + + hideCursorGroupBox + پنهان کردن نشانگر:\nانتخاب کنید که نشانگر چه زمانی ناپدید شود:\nهرگز: شما همیشه ماوس را خواهید دید.\nغیرفعال: زمانی را برای ناپدید شدن بعد از غیرفعالی تعیین کنید.\nهمیشه: شما هرگز ماوس را نخواهید دید. + + + + idleTimeoutGroupBox + زمانی را برای ناپدید شدن ماوس بعد از غیرفعالی تعیین کنید. + + + + backButtonBehaviorGroupBox + رفتار دکمه برگشت:\nدکمه برگشت کنترلر را طوری تنظیم می کند که ضربه زدن روی موقعیت مشخص شده روی صفحه لمسی PS4 را شبیه سازی کند. + + + + Never + هرگز + + + + Idle + بیکار + + + + Always + همیشه + + + + Touchpad Left + پد لمسی سمت چپ + + + + Touchpad Right + صفحه لمسی سمت راست + + + + Touchpad Center + مرکز تاچ پد + + + + None + هیچ کدام + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card. + + + gameFoldersBox + پوشه های بازی:\nلیست پوشه هایی که باید بازی های نصب شده را بررسی کنید. + + + + addFolderButton + اضافه کردن:\nیک پوشه به لیست اضافه کنید. + + + + removeFolderButton + حذف:\nیک پوشه را از لیست حذف کنید. + debugDump diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index 30c60af81..fe972de9a 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -414,6 +414,11 @@ Is PS4 Pro Is PS4 Pro + + + Enable Discord Rich Presence + Ota käyttöön Discord Rich Presence + Username @@ -434,6 +439,36 @@ Log Filter Log Filter + + + Input + Syöttö + + + + Cursor + Kursori + + + + Hide Cursor + Piilota kursor + + + + Hide Cursor Idle Timeout + Inaktiivisuuden aikaraja kursorin piilottamiselle + + + + Controller + Ohjain + + + + Back Button Behavior + Takaisin-painikkeen käyttäytyminen + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU Enable NULL GPU + + + Paths + Polut + + + + Game Folders + Pelihakemistot + + + + Add... + Lisää... + + + + Remove + Poista + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox Onko PS4 Pro:\nAsettaa emulaattorin toimimaan PS4 PRO:na, mikä voi mahdollistaa erityisiä ominaisuuksia peleissä, jotka tukevat sitä. + + + discordRPCCheckbox + Ota käyttöön Discord Rich Presence:\nNäyttää emulaattorin kuvakkeen ja asiaankuuluvat tiedot Discord-profiilissasi. + userName @@ -1011,7 +1071,7 @@ logFilter - Lokifiltteri: Suodattaa lokia tulostamaan vain erityistä tietoa. Esimerkkejä: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Tasot: Jälki, Virheenkorjaus, Tieto, Varoitus, Virhe, Kriittinen - tällä järjestyksellä, tietty taso vaientaa kaikki edeltävät tasot luettelossa ja kirjaa kaikki tasot sen jälkeen. + Lokifiltteri:\nSuodattaa lokia tulostamaan vain erityistä tietoa.\nEsimerkkejä: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Tasot: Jälki, Virheenkorjaus, Tieto, Varoitus, Virhe, Kriittinen - tällä järjestyksellä, tietty taso vaientaa kaikki edeltävät tasot luettelossa ja kirjaa kaikki tasot sen jälkeen. @@ -1023,6 +1083,56 @@ GUIgroupBox Soita Otsikkomusiikkia:\nJos peli tukee sitä, ota käyttöön erityisen musiikin soittaminen pelin valinnan yhteydessä käyttöliittymässä. + + + hideCursorGroupBox + Piilota kursori:\nValitse, milloin kursori häviää:\nEi koskaan: Näet hiiren aina.\nAktiivinen: Aseta aika, jolloin se häviää oltuaan aktiivinen.\nAina: et koskaan näe hiirtä. + + + + idleTimeoutGroupBox + Aseta aika, jolloin hiiri häviää oltuaan aktiivinen. + + + + backButtonBehaviorGroupBox + Takaisin-napin käyttäytyminen:\nAsettaa ohjaimen takaisin-napin jäljittelemään kosketusta PS4:n kosketuslevyn määritettyyn kohtaan. + + + + Never + Ei koskaan + + + + Idle + Odotustila + + + + Always + aina + + + + Touchpad Left + Kosketuslevy Vasemmalla + + + + Touchpad Right + Kosketuslevy Oikealla + + + + Touchpad Center + Kosketuslevy Keskellä + + + + None + Ei mitään + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Ota Null GPU käyttöön:\nTeknistä vianetsintää varten pelin renderöinti estetään niin, että ikään kuin grafiikkakorttia ei olisi. + + + gameFoldersBox + Pelihakemistot:\nLuettelo hakemistoista asennettujen pelien tarkistamiseksi. + + + + addFolderButton + Lisää:\nLisää hakemisto luetteloon. + + + + removeFolderButton + Poista:\nPoista hakemisto luettelosta. + debugDump diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index 54e4b0e82..95158d73e 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -414,6 +414,11 @@ Is PS4 Pro Mode PS4 Pro + + + Enable Discord Rich Presence + Activer Discord Rich Presence + Username @@ -434,6 +439,36 @@ Log Filter Filtre + + + Input + Entrée + + + + Cursor + Curseur + + + + Hide Cursor + Masquer le curseur + + + + Hide Cursor Idle Timeout + Délai d'inactivité pour masquer le curseur + + + + Controller + Manette + + + + Back Button Behavior + Comportement du bouton retour + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU NULL GPU + + + Paths + Chemins + + + + Game Folders + Dossiers de jeu + + + + Add... + Ajouter... + + + + Remove + Supprimer + Debug @@ -580,7 +635,7 @@ Games: - Jeux : + Jeux: @@ -615,7 +670,7 @@ PKG and Game versions match: - Les versions PKG et jeu correspondent : + Les versions PKG et jeu correspondent: @@ -625,17 +680,17 @@ PKG Version %1 is older than installed version: - La version PKG %1 est plus ancienne que la version installée : + La version PKG %1 est plus ancienne que la version installée: Game is installed: - Jeu installé : + Jeu installé: Would you like to install Patch: - Souhaitez-vous installer le patch : + Souhaitez-vous installer le patch: @@ -645,12 +700,12 @@ Would you like to install DLC: %1? - Souhaitez-vous installer le DLC : %1 ? + Souhaitez-vous installer le DLC: %1 ? DLC already installed: - DLC déjà installé : + DLC déjà installé: @@ -698,7 +753,7 @@ defaultTextEdit_MSG - Les Cheats/Patchs sont expérimentaux.\nUtilisez-les avec précaution.\n\nTéléchargez les Cheats individuellement en sélectionnant le dépôt et en cliquant sur le bouton de téléchargement.\nDans l'onglet Patchs, vous pouvez télécharger tous les patchs en une seule fois, choisir lesquels vous souhaitez utiliser et enregistrer votre sélection.\n\nComme nous ne développons pas les Cheats/Patches,\nmerci de signaler les problèmes à l'auteur du Cheat.\n\nVous avez créé un nouveau cheat ? Visitez :\nhttps://github.com/shadps4-emu/ps4_cheats + Les Cheats/Patchs sont expérimentaux.\nUtilisez-les avec précaution.\n\nTéléchargez les Cheats individuellement en sélectionnant le dépôt et en cliquant sur le bouton de téléchargement.\nDans l'onglet Patchs, vous pouvez télécharger tous les patchs en une seule fois, choisir lesquels vous souhaitez utiliser et enregistrer votre sélection.\n\nComme nous ne développons pas les Cheats/Patches,\nmerci de signaler les problèmes à l'auteur du Cheat.\n\nVous avez créé un nouveau cheat ? Visitez:\nhttps://github.com/shadps4-emu/ps4_cheats @@ -708,27 +763,27 @@ Serial: - Série : + Série: Version: - Version : + Version: Size: - Taille : + Taille: Select Cheat File: - Sélectionner le fichier de Cheat : + Sélectionner le fichier de Cheat: Repository: - Dépôt : + Dépôt: @@ -758,7 +813,7 @@ Select Patch File: - Sélectionner le fichier de patch : + Sélectionner le fichier de patch: @@ -813,7 +868,7 @@ Failed to parse XML: - Échec de l'analyse XML : + Échec de l'analyse XML: @@ -848,12 +903,12 @@ Failed to save file: - Échec de l'enregistrement du fichier : + Échec de l'enregistrement du fichier: Failed to download file: - Échec du téléchargement du fichier : + Échec du téléchargement du fichier: @@ -878,12 +933,12 @@ Failed to save: - Échec de l'enregistrement : + Échec de l'enregistrement: Failed to download: - Échec du téléchargement : + Échec du téléchargement: @@ -908,12 +963,12 @@ Failed to open file: - Échec de l'ouverture du fichier : + Échec de l'ouverture du fichier: XML ERROR: - Erreur XML : + Erreur XML: @@ -923,12 +978,12 @@ Author: - Auteur : + Auteur: Directory does not exist: - Répertoire n'existe pas : + Répertoire n'existe pas: @@ -938,7 +993,7 @@ Name: - Nom : + Nom: @@ -976,97 +1031,167 @@ consoleLanguageGroupBox - Langue de la console :\nDéfinit la langue utilisée par le jeu PS4.\nIl est recommandé de le définir sur une langue que le jeu prend en charge, ce qui variera selon la région. + Langue de la console:\nDéfinit la langue utilisée par le jeu PS4.\nIl est recommandé de le définir sur une langue que le jeu prend en charge, ce qui variera selon la région. emulatorLanguageGroupBox - Langue de l'émulateur :\nDéfinit la langue de l'interface utilisateur de l'émulateur. + Langue de l'émulateur:\nDéfinit la langue de l'interface utilisateur de l'émulateur. fullscreenCheckBox - Activer le mode plein écran :\nMet automatiquement la fenêtre du jeu en mode plein écran.\nCela peut être activé en appuyant sur la touche F11. + Activer le mode plein écran:\nMet automatiquement la fenêtre du jeu en mode plein écran.\nCela peut être activé en appuyant sur la touche F11. showSplashCheckBox - Afficher l'écran de démarrage :\nAffiche l'écran de démarrage du jeu (une image spéciale) lors du démarrage du jeu. + Afficher l'écran de démarrage:\nAffiche l'écran de démarrage du jeu (une image spéciale) lors du démarrage du jeu. ps4proCheckBox - Est-ce un PS4 Pro :\nFait en sorte que l'émulateur se comporte comme un PS4 PRO, ce qui peut activer des fonctionnalités spéciales dans les jeux qui le prennent en charge. + Est-ce un PS4 Pro:\nFait en sorte que l'émulateur se comporte comme un PS4 PRO, ce qui peut activer des fonctionnalités spéciales dans les jeux qui le prennent en charge. + + + + discordRPCCheckbox + Activer Discord Rich Presence:\nAffiche l'icône de l'émulateur et les informations pertinentes sur votre profil Discord. userName - Nom d'utilisateur :\nDéfinit le nom d'utilisateur du compte PS4, qui peut être affiché par certains jeux. + Nom d'utilisateur:\nDéfinit le nom d'utilisateur du compte PS4, qui peut être affiché par certains jeux. logTypeGroupBox - Type de journal :\nDétermine si la sortie de la fenêtre de journalisation est synchronisée pour des raisons de performance. Cela peut avoir un impact négatif sur l'émulation. + Type de journal:\nDétermine si la sortie de la fenêtre de journalisation est synchronisée pour des raisons de performance. Cela peut avoir un impact négatif sur l'émulation. logFilter - Filtre de journal : n'imprime que des informations spécifiques. Exemples : "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Niveaux : Trace, Debug, Info, Avertissement, Erreur, Critique - dans cet ordre, un niveau particulier désactive tous les niveaux précédents de la liste et enregistre tous les niveaux suivants. + Filtre de journal:\n n'imprime que des informations spécifiques.\nExemples: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Niveaux: Trace, Debug, Info, Avertissement, Erreur, Critique - dans cet ordre, un niveau particulier désactive tous les niveaux précédents de la liste et enregistre tous les niveaux suivants. updaterGroupBox - Mise à jour :\nRelease: versions officielles publiées chaque mois qui peuvent être très anciennes, mais plus fiables et testées.\nNightly: versions de développement avec toutes les dernières fonctionnalités et correctifs, mais pouvant avoir des bogues et être moins stables. + Mise à jour:\nRelease: versions officielles publiées chaque mois qui peuvent être très anciennes, mais plus fiables et testées.\nNightly: versions de développement avec toutes les dernières fonctionnalités et correctifs, mais pouvant avoir des bogues et être moins stables. GUIgroupBox - Jouer de la musique de titre :\nSi le jeu le prend en charge, cela active la musique spéciale lorsque vous sélectionnez le jeu dans l'interface utilisateur. + Jouer de la musique de titre:\nSi le jeu le prend en charge, cela active la musique spéciale lorsque vous sélectionnez le jeu dans l'interface utilisateur. + + + + hideCursorGroupBox + Masquer le curseur:\nChoisissez quand le curseur disparaîtra:\nJamais: Vous verrez toujours la souris.\nInactif: Définissez un temps pour qu'il disparaisse après inactivité.\nToujours: vous ne verrez jamais la souris. + + + + idleTimeoutGroupBox + Définissez un temps pour que la souris disparaisse après être inactif. + + + + backButtonBehaviorGroupBox + Comportement du bouton retour:\nDéfinit le bouton de retour de la manette pour imiter le toucher de la position spécifiée sur le pavé tactile PS4. + + + + Never + Jamais + + + + Idle + Inactif + + + + Always + Toujours + + + + Touchpad Left + Pavé Tactile Gauche + + + + Touchpad Right + Pavé Tactile Droit + + + + Touchpad Center + Centre du Pavé Tactile + + + + None + Aucun graphicsAdapterGroupBox - Adaptateur graphique :\nSélectionnez le GPU que l'émulateur utilisera dans les systèmes multi-GPU à partir de la liste déroulante,\nou choisissez "Auto Select" pour le déterminer automatiquement. + Adaptateur graphique:\nSélectionnez le GPU que l'émulateur utilisera dans les systèmes multi-GPU à partir de la liste déroulante,\nou choisissez "Auto Select" pour le déterminer automatiquement. resolutionLayout - Largeur/Hauteur :\nDéfinit la taille de la fenêtre de l'émulateur au démarrage, qui peut être redimensionnée pendant le jeu.\nCela diffère de la résolution interne du jeu. + Largeur/Hauteur:\nDéfinit la taille de la fenêtre de l'émulateur au démarrage, qui peut être redimensionnée pendant le jeu.\nCela diffère de la résolution interne du jeu. heightDivider - Diviseur Vblank :\nLe taux de rafraîchissement de l'émulateur est multiplié par ce nombre. Changer cela peut avoir des effets négatifs, tels qu'une augmentation de la vitesse du jeu ou la rupture de fonctionnalités critiques du jeu qui ne s'attendent pas à ce changement ! + Diviseur Vblank:\nLe taux de rafraîchissement de l'émulateur est multiplié par ce nombre. Changer cela peut avoir des effets négatifs, tels qu'une augmentation de la vitesse du jeu ou la rupture de fonctionnalités critiques du jeu qui ne s'attendent pas à ce changement ! dumpShadersCheckBox - Activer l'exportation de shaders :\nPour le débogage technique, les shaders du jeu sont enregistrés dans un dossier lors du rendu. + Activer l'exportation de shaders:\nPour le débogage technique, les shaders du jeu sont enregistrés dans un dossier lors du rendu. nullGpuCheckBox - Activer le GPU nul :\nPour le débogage technique, désactive le rendu du jeu comme s'il n'y avait pas de carte graphique. + Activer le GPU nul:\nPour le débogage technique, désactive le rendu du jeu comme s'il n'y avait pas de carte graphique. + + + + gameFoldersBox + Dossiers de jeux:\nLa liste des dossiers à vérifier pour les jeux installés. + + + + addFolderButton + Ajouter:\nAjouter un dossier à la liste. + + + + removeFolderButton + Supprimer:\nSupprimer un dossier de la liste. debugDump - Activer l'exportation de débogage :\nEnregistre les symboles d'importation et d'exportation et les informations d'en-tête du fichier du programme PS4 actuel dans un répertoire. + Activer l'exportation de débogage:\nEnregistre les symboles d'importation et d'exportation et les informations d'en-tête du fichier du programme PS4 actuel dans un répertoire. vkValidationCheckBox - Activer les couches de validation Vulkan :\nActive un système qui valide l'état du rendu Vulkan et enregistre des informations sur son état interne. Cela réduit les performances et peut changer le comportement de l'émulation. + Activer les couches de validation Vulkan:\nActive un système qui valide l'état du rendu Vulkan et enregistre des informations sur son état interne. Cela réduit les performances et peut changer le comportement de l'émulation. vkSyncValidationCheckBox - Activer la validation de synchronisation Vulkan :\nActive un système qui valide la planification des tâches de rendu Vulkan. Cela réduit les performances et peut changer le comportement de l'émulation. + Activer la validation de synchronisation Vulkan:\nActive un système qui valide la planification des tâches de rendu Vulkan. Cela réduit les performances et peut changer le comportement de l'émulation. rdocCheckBox - Activer le débogage RenderDoc :\nS'il est activé, l'émulateur fournit une compatibilité avec Renderdoc, permettant d'enregistrer et d'analyser la trame rendue actuelle. + Activer le débogage RenderDoc:\nS'il est activé, l'émulateur fournit une compatibilité avec Renderdoc, permettant d'enregistrer et d'analyser la trame rendue actuelle. @@ -1132,7 +1257,7 @@ Network error: - Erreur réseau : + Erreur réseau: diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index a6481d5a2..7337b54c9 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -414,6 +414,11 @@ Is PS4 Pro PS4 Pro + + + Enable Discord Rich Presence + Engedélyezze a Discord Rich Presence-t + Username @@ -434,6 +439,36 @@ Log Filter Naplózási Filter + + + Input + Bemenet + + + + Cursor + Kurzor + + + + Hide Cursor + Kurzor elrejtése + + + + Hide Cursor Idle Timeout + Kurzor inaktivitási időtúllépés + + + + Controller + Kontroller + + + + Back Button Behavior + Vissza gomb viselkedése + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU NULL GPU Engedélyezése + + + Paths + Útvonalak + + + + Game Folders + Játékmappák + + + + Add... + Hozzáadás... + + + + Remove + Eltávolítás + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox PS4 Pro:\nAz emulátort PS4 PRO-ként kezeli, ami engedélyezheti a speciális funkciókat olyan játékokban, amelyek támogatják. + + + discordRPCCheckbox + Engedélyezze a Discord Rich Presence-t:\nMegjeleníti az emulator ikonját és a kapcsolódó információkat a Discord profilján. + userName @@ -1011,7 +1071,7 @@ logFilter - Napló szűrő: Csak bizonyos információk megjelenítésére szűri a naplót. Példák: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Szintek: Trace, Debug, Info, Warning, Error, Critical - ebben a sorrendben, egy konkrét szint elnémítja az előtte lévő összes szintet, és naplózza az utána következő szinteket. + Napló szűrő:\nCsak bizonyos információk megjelenítésére szűri a naplót.\nPéldák: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Szintek: Trace, Debug, Info, Warning, Error, Critical - ebben a sorrendben, egy konkrét szint elnémítja az előtte lévő összes szintet, és naplózza az utána következő szinteket. @@ -1023,6 +1083,56 @@ GUIgroupBox Játék címzene lejátszása:\nHa a játék támogatja, engedélyezze a speciális zene lejátszását, amikor a játékot kiválasztja a GUI-ban. + + + hideCursorGroupBox + Akurátor elrejtése:\nVálassza ki, mikor tűnjön el az egérkurzor:\nSoha: Az egér mindig látható.\nInaktív: Állítson be egy időt, amikor inaktív állapotban eltűnik.\nMindig: soha nem látja az egeret. + + + + idleTimeoutGroupBox + Állítson be egy időt, ameddig az egér inaktív állapot után eltűnik. + + + + backButtonBehaviorGroupBox + Vissza gomb viselkedés:\nBeállítja a vezérlő vissza gombját, hogy utánozza a PS4 érintőpadján megadott pozíció megérintését. + + + + Never + Soha + + + + Idle + Inaktív + + + + Always + Mindig + + + + Touchpad Left + Érintőpad Balra + + + + Touchpad Right + Érintőpad Jobbra + + + + Touchpad Center + Érintőpad Középen + + + + None + Semmi + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Null GPU engedélyezése:\nMűszaki hibaelhárítás céljából letiltja a játék renderelését, mintha nem lenne grafikus kártya. + + + gameFoldersBox + Játék mappák:\nA mappák listája az telepített játékok ellenőrzésére. + + + + addFolderButton + Hozzáadás:\nHozzon létre egy mappát a listában. + + + + removeFolderButton + Eltávolítás:\nTávolítson el egy mappát a listából. + debugDump diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index 2fdc9b68c..f64762671 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -414,6 +414,11 @@ Is PS4 Pro Is PS4 Pro + + + Enable Discord Rich Presence + Aktifkan Discord Rich Presence + Username @@ -434,6 +439,36 @@ Log Filter Log Filter + + + Input + Masukan + + + + Cursor + Kursor + + + + Hide Cursor + Sembunyikan kursor + + + + Hide Cursor Idle Timeout + Batas waktu sembunyikan kursor tidak aktif + + + + Controller + Pengontrol + + + + Back Button Behavior + Perilaku tombol kembali + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU Enable NULL GPU + + + Paths + Jalur + + + + Game Folders + Folder Permainan + + + + Add... + Tambah... + + + + Remove + Hapus + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox Adalah PS4 Pro:\nMembuat emulator berfungsi sebagai PS4 PRO, yang mungkin mengaktifkan fitur khusus dalam permainan yang mendukungnya. + + + discordRPCCheckbox + Aktifkan Discord Rich Presence:\nMenampilkan ikon emulator dan informasi relevan di profil Discord Anda. + userName @@ -1011,7 +1071,7 @@ logFilter - Filter Log: Menyaring log untuk hanya mencetak informasi tertentu. Contoh: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Tingkatan: Trace, Debug, Info, Warning, Error, Critical - dalam urutan ini, tingkat tertentu membungkam semua tingkat sebelumnya dalam daftar dan mencatat setiap tingkat setelahnya. + Filter Log:\nMenyaring log untuk hanya mencetak informasi tertentu.\nContoh: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Tingkatan: Trace, Debug, Info, Warning, Error, Critical - dalam urutan ini, tingkat tertentu membungkam semua tingkat sebelumnya dalam daftar dan mencatat setiap tingkat setelahnya. @@ -1023,6 +1083,56 @@ GUIgroupBox Putar Musik Judul Permainan:\nJika permainan mendukungnya, aktifkan pemutaran musik khusus saat memilih permainan di GUI. + + + hideCursorGroupBox + Sembunyikan Kursor:\nPilih kapan kursor akan menghilang:\nTidak Pernah: Anda akan selalu melihat mouse.\nTidak Aktif: Tetapkan waktu untuk menghilang setelah tidak aktif.\nSelalu: Anda tidak akan pernah melihat mouse. + + + + idleTimeoutGroupBox + Tetapkan waktu untuk mouse menghilang setelah tidak aktif. + + + + backButtonBehaviorGroupBox + Perilaku Tombol Kembali:\nMengatur tombol kembali pada pengontrol untuk meniru ketukan di posisi yang ditentukan di touchpad PS4. + + + + Never + Tidak Pernah + + + + Idle + Diam + + + + Always + Selalu + + + + Touchpad Left + Touchpad Kiri + + + + Touchpad Right + Touchpad Kanan + + + + Touchpad Center + Pusat Touchpad + + + + None + Tidak Ada + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Aktifkan GPU Null:\nUntuk tujuan debugging teknis, menonaktifkan rendering permainan seolah-olah tidak ada kartu grafis. + + + gameFoldersBox + Folder Permainan:\nDaftar folder untuk memeriksa permainan yang diinstal. + + + + addFolderButton + Tambah:\nTambahkan folder ke daftar. + + + + removeFolderButton + Hapus:\nHapus folder dari daftar. + debugDump diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index aba38a290..1da50ec5b 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -414,6 +414,11 @@ Is PS4 Pro Modalità Ps4Pro + + + Enable Discord Rich Presence + Abilita Discord Rich Presence + Username @@ -434,6 +439,36 @@ Log Filter Filtro Log + + + Input + Input + + + + Cursor + Cursore + + + + Hide Cursor + Nascondi cursore + + + + Hide Cursor Idle Timeout + Timeout inattivo per nascondere il cursore + + + + Controller + Controller + + + + Back Button Behavior + Comportamento del pulsante Indietro + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU Abilita NULL GPU + + + Paths + Percorsi + + + + Game Folders + Cartelle di gioco + + + + Add... + Aggiungi... + + + + Remove + Rimuovi + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox È PS4 Pro:\nFa sì che l'emulatore si comporti come una PS4 PRO, il che può abilitare funzionalità speciali in giochi che la supportano. + + + discordRPCCheckbox + Abilita Discord Rich Presence:\nMostra l'icona dell'emulatore e informazioni pertinenti sul tuo profilo Discord. + userName @@ -1011,7 +1071,7 @@ logFilter - Filtro Log: Filtra il log per stampare solo informazioni specifiche. Esempi: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Livelli: Trace, Debug, Info, Warning, Error, Critical - in questo ordine, un livello specifico silenzia tutti i livelli precedenti nell'elenco e registra ogni livello successivo. + Filtro Log:\nFiltra il log per stampare solo informazioni specifiche.\nEsempi: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Livelli: Trace, Debug, Info, Warning, Error, Critical - in questo ordine, un livello specifico silenzia tutti i livelli precedenti nell'elenco e registra ogni livello successivo. @@ -1023,6 +1083,56 @@ GUIgroupBox Riproduci Musica del Titolo:\nSe un gioco lo supporta, attiva la riproduzione di musica speciale quando selezioni il gioco nell'interfaccia grafica. + + + hideCursorGroupBox + Nascondi cursore:\nScegli quando il cursore scomparirà:\nMai: Vedrai sempre il mouse.\nInattivo: Imposta un tempo per farlo scomparire dopo essere stato inattivo.\nSempre: non vedrai mai il mouse. + + + + idleTimeoutGroupBox + Imposta un tempo affinché il mouse scompaia dopo essere stato inattivo. + + + + backButtonBehaviorGroupBox + Comportamento del pulsante Indietro:\nImposta il pulsante Indietro del controller per emulare il tocco sulla posizione specificata sul touchpad PS4. + + + + Never + Mai + + + + Idle + Inattivo + + + + Always + Sempre + + + + Touchpad Left + Touchpad Sinistra + + + + Touchpad Right + Touchpad Destra + + + + Touchpad Center + Centro del Touchpad + + + + None + Nessuno + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Abilita GPU Null:\nPer scopi di debug tecnico, disabilita il rendering del gioco come se non ci fosse alcuna scheda grafica. + + + gameFoldersBox + Cartelle di Gioco:\nL'elenco delle cartelle da controllare per i giochi installati. + + + + addFolderButton + Aggiungi:\nAggiungi una cartella all'elenco. + + + + removeFolderButton + Rimuovi:\nRimuovi una cartella dall'elenco. + debugDump diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index 7524250f5..4fd819452 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -414,6 +414,11 @@ Is PS4 Pro PS4 Proモード + + + Enable Discord Rich Presence + Discord Rich Presenceを有効にする + Username @@ -434,6 +439,36 @@ Log Filter ログフィルター + + + Input + 入力 + + + + Cursor + カーソル + + + + Hide Cursor + カーソルを隠す + + + + Hide Cursor Idle Timeout + カーソル非アクティブタイムアウト + + + + Controller + コントローラー + + + + Back Button Behavior + 戻るボタンの動作 + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU NULL GPUを有効にする + + + Paths + パス + + + + Game Folders + ゲームフォルダ + + + + Add... + 追加... + + + + Remove + 削除 + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox PS4 Proです:\nエミュレーターがPS4 PROとして動作するようにし、これをサポートするゲームで特別な機能を有効にする場合があります。 + + + discordRPCCheckbox + Discord Rich Presenceを有効にする:\nエミュレーターのアイコンと関連情報をDiscordプロフィールに表示します。 + userName @@ -1011,7 +1071,7 @@ logFilter - ログフィルター: 特定の情報のみを印刷するようにログをフィルタリングします。例: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" レベル: Trace, Debug, Info, Warning, Error, Critical - この順序で、特定のレベルはリスト内のすべての前のレベルをサイレンスし、その後のすべてのレベルをログに記録します。 + ログフィルター:\n特定の情報のみを印刷するようにログをフィルタリングします。\n例: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" レベル: Trace, Debug, Info, Warning, Error, Critical - この順序で、特定のレベルはリスト内のすべての前のレベルをサイレンスし、その後のすべてのレベルをログに記録します。 @@ -1023,6 +1083,56 @@ GUIgroupBox タイトルミュージックを再生:\nゲームがそれをサポートしている場合、GUIでゲームを選択したときに特別な音楽を再生することを有効にします。 + + + hideCursorGroupBox + カーソルを隠す:\nカーソルが消えるタイミングを選択してください:\n決して: いつでもマウスが見えます。\nアイドル: アイダルの後に消えるまでの時間を設定します。\n常に: マウスは決して見えません。 + + + + idleTimeoutGroupBox + アイドル後にマウスが消えるまでの時間を設定します。 + + + + backButtonBehaviorGroupBox + 戻るボタンの動作:\nコントローラーの戻るボタンを、PS4のタッチパッドの指定された位置をタッチするように設定します。 + + + + Never + 決して + + + + Idle + アイドル + + + + Always + 常に + + + + Touchpad Left + タッチパッド左 + + + + Touchpad Right + タッチパッド右 + + + + Touchpad Center + タッチパッド中央 + + + + None + なし + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Null GPUを有効にする:\n技術的なデバッグの目的で、グラフィックスカードがないかのようにゲームのレンダリングを無効にします。 + + + gameFoldersBox + ゲームフォルダ:\nインストールされたゲームを確認するためのフォルダのリスト。 + + + + addFolderButton + 追加:\nリストにフォルダを追加します。 + + + + removeFolderButton + 削除:\nリストからフォルダを削除します。 + debugDump diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index 4805a4d0e..74d307c3e 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -414,6 +414,11 @@ Is PS4 Pro Is PS4 Pro + + + Enable Discord Rich Presence + Enable Discord Rich Presence + Username @@ -434,6 +439,36 @@ Log Filter Log Filter + + + Input + Input + + + + Cursor + Cursor + + + + Hide Cursor + Hide Cursor + + + + Hide Cursor Idle Timeout + Hide Cursor Idle Timeout + + + + Controller + Controller + + + + Back Button Behavior + Back Button Behavior + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU Enable NULL GPU + + + Paths + Paths + + + + Game Folders + Game Folders + + + + Add... + Add... + + + + Remove + Remove + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox Is PS4 Pro:\nMakes the emulator act as a PS4 PRO, which may enable special features in games that support it. + + + discordRPCCheckbox + Discord Rich Presence 활성화:\nDiscord 프로필에 에뮬레이터 아이콘과 관련 정보를 표시합니다. + userName @@ -1011,7 +1071,7 @@ logFilter - Log Filter: Filters the log to only print specific information. Examples: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Levels: Trace, Debug, Info, Warning, Error, Critical - in this order, a specific level silences all levels preceding it in the list and logs every level after it. + Log Filter:\nFilters the log to only print specific information.\nExamples: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Levels: Trace, Debug, Info, Warning, Error, Critical - in this order, a specific level silences all levels preceding it in the list and logs every level after it. @@ -1023,6 +1083,56 @@ GUIgroupBox Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. + + + hideCursorGroupBox + Hide Cursor:\nChoose when the cursor will disappear:\nNever: You will always see the mouse.\nidle: Set a time for it to disappear after being idle.\nAlways: you will never see the mouse. + + + + idleTimeoutGroupBox + Set a time for the mouse to disappear after being after being idle. + + + + backButtonBehaviorGroupBox + Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad. + + + + Never + Never + + + + Idle + Idle + + + + Always + Always + + + + Touchpad Left + Touchpad Left + + + + Touchpad Right + Touchpad Right + + + + Touchpad Center + Touchpad Center + + + + None + None + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card. + + + gameFoldersBox + Game Folders:\nThe list of folders to check for installed games. + + + + addFolderButton + Add:\nAdd a folder to the list. + + + + removeFolderButton + Remove:\nRemove a folder from the list. + debugDump diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index beaaa116a..2924d65ba 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -414,6 +414,11 @@ Is PS4 Pro Is PS4 Pro + + + Enable Discord Rich Presence + Įjungti Discord Rich Presence + Username @@ -434,6 +439,36 @@ Log Filter Log Filter + + + Input + Įvestis + + + + Cursor + Žymeklis + + + + Hide Cursor + Slėpti žymeklį + + + + Hide Cursor Idle Timeout + Žymeklio paslėpimo neveikimo laikas + + + + Controller + Valdiklis + + + + Back Button Behavior + Atgal mygtuko elgsena + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU Enable NULL GPU + + + Paths + Keliai + + + + Game Folders + Žaidimų aplankai + + + + Add... + Pridėti... + + + + Remove + Pašalinti + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox Ar PS4 Pro:\nPadaro, kad emuliatorius veiktų kaip PS4 PRO, kas gali įjungti specialias funkcijas žaidimuose, kurie tai palaiko. + + + discordRPCCheckbox + Įjungti Discord Rich Presence:\nRodo emuliatoriaus ikoną ir susijusią informaciją jūsų Discord profilyje. + userName @@ -1011,7 +1071,7 @@ logFilter - Žurnalo filtras: Filtruojamas žurnalas, kad būtų spausdinama tik konkreti informacija. Pavyzdžiai: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Lygiai: Trace, Debug, Info, Warning, Error, Critical - šia tvarka, konkretus lygis nutildo visus ankstesnius lygius sąraše ir registruoja visus vėlesnius. + Žurnalo filtras:\nFiltruojamas žurnalas, kad būtų spausdinama tik konkreti informacija.\nPavyzdžiai: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Lygiai: Trace, Debug, Info, Warning, Error, Critical - šia tvarka, konkretus lygis nutildo visus ankstesnius lygius sąraše ir registruoja visus vėlesnius. @@ -1023,6 +1083,56 @@ GUIgroupBox Groti antraščių muziką:\nJei žaidimas tai palaiko, įjungia specialios muzikos grojimą, kai pasirinkite žaidimą GUI. + + + hideCursorGroupBox + Slėpti žymeklį:\nPasirinkite, kada žymeklis dings:\nNiekuomet: Visada matysite pelę.\nNeaktyvus: Nustatykite laiką, po kurio ji dings, kai bus neaktyvi.\nVisada: niekada nematysite pelės. + + + + idleTimeoutGroupBox + Nustatykite laiką, po kurio pelė dings, kai bus neaktyvi. + + + + backButtonBehaviorGroupBox + Atgal mygtuko elgesys:\nNustato valdiklio atgal mygtuką imituoti paspaudimą nurodytoje vietoje PS4 jutiklinėje plokštėje. + + + + Never + Niekada + + + + Idle + Neaktyvus + + + + Always + Visada + + + + Touchpad Left + Jutiklinis Paviršius Kairėje + + + + Touchpad Right + Jutiklinis Paviršius Dešinėje + + + + Touchpad Center + Jutiklinis Paviršius Centre + + + + None + Nieko + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Įjungti tuščią GPU:\nTechninio derinimo tikslais išjungia žaidimo renderiavimą, tarsi nebūtų grafikos plokštės. + + + gameFoldersBox + Žaidimų aplankai:\nAplankų sąrašas, kurį reikia patikrinti, ar yra įdiegtų žaidimų. + + + + addFolderButton + Pridėti:\nPridėti aplanką į sąrašą. + + + + removeFolderButton + Pašalinti:\nPašalinti aplanką iš sąrašo. + debugDump diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index c193e83dd..c224c4f8f 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -414,6 +414,11 @@ Is PS4 Pro Is PS4 Pro + + + Enable Discord Rich Presence + Aktiver Discord Rich Presence + Username @@ -434,6 +439,36 @@ Log Filter Log Filter + + + Input + Inndata + + + + Cursor + Markør + + + + Hide Cursor + Skjul markør + + + + Hide Cursor Idle Timeout + Timeout for å skjule markør ved inaktivitet + + + + Controller + Kontroller + + + + Back Button Behavior + Tilbakeknapp atferd + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU Enable NULL GPU + + + Paths + Stier + + + + Game Folders + Spillmapper + + + + Add... + Legg til... + + + + Remove + Fjern + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox Er PS4 Pro:\nFår emulatoren til å fungere som en PS4 PRO, noe som kan aktivere spesielle funksjoner i spill som støtter det. + + + discordRPCCheckbox + Aktiver Discord Rich Presence:\nViser emulatorikonet og relevant informasjon på Discord-profilen din. + userName @@ -1011,7 +1071,7 @@ logFilter - Loggfilter: Filtrerer loggen for å kun skrive ut spesifikk informasjon. Eksempler: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Nivåer: Trace, Debug, Info, Warning, Error, Critical - i denne rekkefølgen, et spesifikt nivå demper alle tidligere nivåer i listen og logger alle nivåer etter det. + Loggfilter:\nFiltrerer loggen for å kun skrive ut spesifikk informasjon.\nEksempler: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Nivåer: Trace, Debug, Info, Warning, Error, Critical - i denne rekkefølgen, et spesifikt nivå demper alle tidligere nivåer i listen og logger alle nivåer etter det. @@ -1023,6 +1083,56 @@ GUIgroupBox Spille tittelmusikk:\nHvis et spill støtter det, aktiverer det å spille spesiell musikk når du velger spillet i GUI. + + + hideCursorGroupBox + Skjul musepeker:\nVelg når musepekeren skal forsvinne:\nAldri: Du vil alltid se musen.\nInaktiv: Sett en tid for at den skal forsvinne etter å ha vært inaktiv.\nAlltid: du vil aldri se musen. + + + + idleTimeoutGroupBox + Sett en tid for når musen forsvinner etter å ha vært inaktiv. + + + + backButtonBehaviorGroupBox + Atferd for tilbaketasten:\nSetter tilbaketasten på kontrolleren til å imitere et trykk på den angitte posisjonen på PS4s berøringsplate. + + + + Never + Aldri + + + + Idle + Inaktiv + + + + Always + Alltid + + + + Touchpad Left + Berøringsplate Venstre + + + + Touchpad Right + Berøringsplate Høyre + + + + Touchpad Center + Berøringsplate Midt + + + + None + Ingen + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Aktiver Null GPU:\nFor teknisk feilsøking deaktiverer spillrendering som om det ikke var noe grafikkort. + + + gameFoldersBox + Spillmapper:\nListen over mapper for å sjekke installerte spill. + + + + addFolderButton + Legg til:\nLegg til en mappe til listen. + + + + removeFolderButton + Fjern:\nFjern en mappe fra listen. + debugDump diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index 6b0befa92..b733610d1 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -414,6 +414,11 @@ Is PS4 Pro Is PS4 Pro + + + Enable Discord Rich Presence + Discord Rich Presence inschakelen + Username @@ -434,6 +439,36 @@ Log Filter Log Filter + + + Input + Invoer + + + + Cursor + Cursor + + + + Hide Cursor + Cursor verbergen + + + + Hide Cursor Idle Timeout + Inactiviteit timeout voor het verbergen van de cursor + + + + Controller + Controller + + + + Back Button Behavior + Achterknop gedrag + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU Enable NULL GPU + + + Paths + Pad + + + + Game Folders + Spelmappen + + + + Add... + Toevoegen... + + + + Remove + Verwijderen + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox Is PS4 Pro:\nLaat de emulator zich gedragen als een PS4 PRO, wat speciale functies kan inschakelen in games die dit ondersteunen. + + + discordRPCCheckbox + Discord Rich Presence inschakelen:\nToont het emulatoricoon en relevante informatie op je Discord-profiel. + userName @@ -1011,7 +1071,7 @@ logFilter - Logfilter: Filtert het logboek om alleen specifieke informatie af te drukken. Voorbeelden: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Niveaus: Trace, Debug, Info, Waarschuwing, Fout, Kritiek - in deze volgorde, een specifiek niveau dempt alle voorgaande niveaus in de lijst en logt alle niveaus daarna. + Logfilter:\nFiltert het logboek om alleen specifieke informatie af te drukken.\nVoorbeelden: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Niveaus: Trace, Debug, Info, Waarschuwing, Fout, Kritiek - in deze volgorde, een specifiek niveau dempt alle voorgaande niveaus in de lijst en logt alle niveaus daarna. @@ -1023,6 +1083,56 @@ GUIgroupBox Speel titelsong:\nAls een game dit ondersteunt, wordt speciale muziek afgespeeld wanneer je het spel in de GUI selecteert. + + + hideCursorGroupBox + Verberg cursor:\nKies wanneer de cursor verdwijnt:\nNooit: Je ziet altijd de muis.\nInactief: Stel een tijd in waarna deze verdwijnt na inactiviteit.\nAltijd: je ziet de muis nooit. + + + + idleTimeoutGroupBox + Stel een tijd in voor wanneer de muis verdwijnt na inactiviteit. + + + + backButtonBehaviorGroupBox + Gedrag van de terugknop:\nStelt de terugknop van de controller in om een aanraking op de opgegeven positie op de PS4-touchpad na te bootsen. + + + + Never + Nooit + + + + Idle + Inactief + + + + Always + Altijd + + + + Touchpad Left + Touchpad Links + + + + Touchpad Right + Touchpad Rechts + + + + Touchpad Center + Touchpad Midden + + + + None + Geen + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Null GPU inschakelen:\nVoor technische foutopsporing schakelt de game-rendering uit alsof er geen grafische kaart is. + + + gameFoldersBox + Spelmap:\nDe lijst met mappen om te controleren op geïnstalleerde spellen. + + + + addFolderButton + Toevoegen:\nVoeg een map toe aan de lijst. + + + + removeFolderButton + Verwijderen:\nVerwijder een map uit de lijst. + debugDump diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index ba8b4f9e4..f81647f35 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -414,6 +414,11 @@ Is PS4 Pro Emulacja PS4 Pro + + + Enable Discord Rich Presence + Włącz Discord Rich Presence + Username @@ -434,6 +439,36 @@ Log Filter Filtrowanie dziennika + + + Input + Wejście + + + + Cursor + Kursor + + + + Hide Cursor + Ukryj kursor + + + + Hide Cursor Idle Timeout + Czas oczekiwania na ukrycie kursora przy bezczynności + + + + Controller + Kontroler + + + + Back Button Behavior + Zachowanie przycisku wstecz + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU Wyłącz kartę graficzną + + + Paths + Ścieżki + + + + Game Folders + Foldery gier + + + + Add... + Dodaj... + + + + Remove + Usuń + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox Czy PS4 Pro:\nSprawia, że emulator działa jak PS4 PRO, co może aktywować specjalne funkcje w grach, które to obsługują. + + + discordRPCCheckbox + Włącz Discord Rich Presence:\nWyświetla ikonę emuladora i odpowiednie informacje na twoim profilu Discord. + userName @@ -1011,7 +1071,7 @@ logFilter - Filtr logu: Filtruje dziennik, aby drukować tylko określone informacje. Przykłady: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Poziomy: Trace, Debug, Info, Warning, Error, Critical - w tej kolejności, konkretny poziom wycisza wszystkie wcześniejsze poziomy w liście i rejestruje wszystkie poziomy później. + Filtr logu:\nFiltruje dziennik, aby drukować tylko określone informacje.\nPrzykłady: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Poziomy: Trace, Debug, Info, Warning, Error, Critical - w tej kolejności, konkretny poziom wycisza wszystkie wcześniejsze poziomy w liście i rejestruje wszystkie poziomy później. @@ -1023,6 +1083,56 @@ GUIgroupBox Odtwórz muzykę tytułową:\nJeśli gra to obsługuje, aktywuje odtwarzanie specjalnej muzyki podczas wybierania gry w GUI. + + + hideCursorGroupBox + Ukryj kursor:\nWybierz, kiedy kursor zniknie:\nNigdy: Zawsze będziesz widział myszkę.\nNieaktywny: Ustaw czas, po którym zniknie po bezczynności.\nZawsze: nigdy nie zobaczysz myszki. + + + + idleTimeoutGroupBox + Ustaw czas, po którym mysz zniknie po bezczynności. + + + + backButtonBehaviorGroupBox + Zachowanie przycisku Wstecz:\nUstawia przycisk Wstecz kontrolera tak, aby emulował dotknięcie określonego miejsca na panelu dotykowym PS4. + + + + Never + Nigdy + + + + Idle + Bezczynny + + + + Always + Zawsze + + + + Touchpad Left + Touchpad Lewy + + + + Touchpad Right + Touchpad Prawy + + + + Touchpad Center + Touchpad Środkowy + + + + None + Brak + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Włącz Null GPU:\nDla technicznego debugowania dezaktywuje renderowanie gry tak, jakby nie było karty graficznej. + + + gameFoldersBox + Foldery gier:\nLista folderów do sprawdzenia zainstalowanych gier. + + + + addFolderButton + Dodaj:\nDodaj folder do listy. + + + + removeFolderButton + Usuń:\nUsuń folder z listy. + debugDump diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index 2e859f12b..cf673b298 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -414,6 +414,11 @@ Is PS4 Pro Modo PS4 Pro + + + Enable Discord Rich Presence + Ativar Discord Rich Presence + Username @@ -434,6 +439,36 @@ Log Filter Filtro do Registro + + + Input + Entradas + + + + Cursor + Cursor + + + + Hide Cursor + Ocultar Cursor + + + + Hide Cursor Idle Timeout + Tempo de Inatividade para Ocultar Cursor + + + + Controller + Controle + + + + Back Button Behavior + Comportamento do botão Voltar + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU Ativar GPU NULA + + + Paths + Pastas + + + + Game Folders + Pastas dos Jogos + + + + Add... + Adicionar... + + + + Remove + Remover + Debug @@ -986,17 +1041,22 @@ fullscreenCheckBox - Ativar modo tela cheia:\nMove automaticamente a janela do jogo para o modo tela cheia.\nIsso pode ser alterado pressionando a tecla F11. + Ativar Tela Cheia:\nMove automaticamente a janela do jogo para o modo tela cheia.\nIsso pode ser alterado pressionando a tecla F11. showSplashCheckBox - Mostrar tela inicial:\nExibe a tela inicial do jogo (imagem especial) ao iniciar o jogo. + Mostrar Splash Inicial:\nExibe a tela inicial do jogo (imagem especial) ao iniciar o jogo. ps4proCheckBox - É um PS4 Pro:\nFaz o emulador agir como um PS4 PRO, o que pode ativar recursos especiais em jogos que o suportam. + Modo PS4 Pro:\nFaz o emulador agir como um PS4 PRO, o que pode ativar recursos especiais em jogos que o suportam. + + + + discordRPCCheckbox + Ativar Discord Rich Presence:\nExibe o ícone do emulador e informações relevantes no seu perfil do Discord. @@ -1006,12 +1066,12 @@ logTypeGroupBox - Tipo de log:\nDefine se a saída da janela de log deve ser sincronizada para melhorar o desempenho. Isso pode impactar negativamente a emulação. + Tipo de Registro:\nDefine se a saída da janela de log deve ser sincronizada para melhorar o desempenho. Isso pode impactar negativamente a emulação. logFilter - Filtro de log: Imprime apenas informações específicas. Exemplos: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Níveis: Trace, Debug, Info, Warning, Error, Critical - assim, um nível específico desativa todos os níveis anteriores na lista e registra todos os níveis subsequentes. + Filtro de Registro:\nImprime apenas informações específicas.\nExemplos: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nNíveis: Trace, Debug, Info, Warning, Error, Critical - assim, um nível específico desativa todos os níveis anteriores na lista e registra todos os níveis subsequentes. @@ -1023,6 +1083,56 @@ GUIgroupBox Reproduzir música de abertura:\nSe o jogo suportar, ativa a reprodução de uma música especial ao selecionar o jogo na interface do menu. + + + hideCursorGroupBox + Ocultar Cursor:\nEscolha quando o cursor desaparecerá:\nNunca: Você sempre verá o mouse.\nParado: Defina um tempo para ele desaparecer após ficar inativo.\nSempre: Você nunca verá o mouse. + + + + idleTimeoutGroupBox + Defina um tempo em segundos para o mouse desaparecer após ficar inativo. + + + + backButtonBehaviorGroupBox + Comportamento do botão Voltar:\nDefine o botão Voltar do controle para emular o toque na posição especificada no touchpad do PS4. + + + + Never + Nunca + + + + Idle + Parado + + + + Always + Sempre + + + + Touchpad Left + Touchpad Esquerdo + + + + Touchpad Right + Touchpad Direito + + + + Touchpad Center + Touchpad Centro + + + + None + Nenhum + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Ativar GPU NULA:\nDesativa a renderização do jogo para fins de depuração técnica, como se não houvesse nenhuma placa gráfica. + + + gameFoldersBox + Pastas dos jogos:\nA lista de pastas para verificar se há jogos instalados. + + + + addFolderButton + Adicionar:\nAdicione uma pasta à lista. + + + + removeFolderButton + Remover:\nRemove uma pasta da lista. + debugDump @@ -1114,7 +1239,7 @@ Play Time - Horas Jogadas + Tempo Jogado diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index b36647c1c..33a57792f 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -414,6 +414,11 @@ Is PS4 Pro Is PS4 Pro + + + Enable Discord Rich Presence + Activați Discord Rich Presence + Username @@ -434,6 +439,36 @@ Log Filter Log Filter + + + Input + Introducere + + + + Cursor + Cursor + + + + Hide Cursor + Ascunde cursorul + + + + Hide Cursor Idle Timeout + Timeout pentru ascunderea cursorului inactiv + + + + Controller + Controler + + + + Back Button Behavior + Comportament buton înapoi + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU Enable NULL GPU + + + Paths + Trasee + + + + Game Folders + Dosare de joc + + + + Add... + Adaugă... + + + + Remove + Eliminare + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox Este PS4 Pro:\nFace ca emulatorul să se comporte ca un PS4 PRO, ceea ce poate activa funcții speciale în jocurile care o suportă. + + + discordRPCCheckbox + Activați Discord Rich Presence:\nAfișează pictograma emulatorului și informații relevante pe profilul dumneavoastră Discord. + userName @@ -1011,7 +1071,7 @@ logFilter - Filtrul jurnalului: Filtrează jurnalul pentru a imprima doar informații specifice. Exemple: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Niveluri: Trace, Debug, Info, Warning, Error, Critical - în această ordine, un nivel specific reduce toate nivelurile anterioare din listă și înregistrează toate nivelurile ulterioare. + Filtrul jurnalului:\nFiltrează jurnalul pentru a imprima doar informații specifice.\nExemple: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Niveluri: Trace, Debug, Info, Warning, Error, Critical - în această ordine, un nivel specific reduce toate nivelurile anterioare din listă și înregistrează toate nivelurile ulterioare. @@ -1023,6 +1083,56 @@ GUIgroupBox Redă muzica titlului:\nDacă un joc o suportă, activează redarea muzicii speciale când selectezi jocul în GUI. + + + hideCursorGroupBox + Ascunde cursorul:\nAlegeți când va dispărea cursorul:\nNiciodată: Vei vedea întotdeauna mouse-ul.\nInactiv: Setează un timp pentru a dispărea după inactivitate.\nÎntotdeauna: nu vei vedea niciodată mouse-ul. + + + + idleTimeoutGroupBox + Setați un timp pentru ca mouse-ul să dispară după ce a fost inactiv. + + + + backButtonBehaviorGroupBox + Comportamentul butonului înapoi:\nSetează butonul înapoi al controlerului să imite atingerea poziției specificate pe touchpad-ul PS4. + + + + Never + Niciodată + + + + Idle + Inactiv + + + + Always + Întotdeauna + + + + Touchpad Left + Touchpad Stânga + + + + Touchpad Right + Touchpad Dreapta + + + + Touchpad Center + Centru Touchpad + + + + None + Niciunul + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Activează GPU Null:\nÎn scopuri de depanare tehnică, dezactivează redarea jocului ca și cum nu ar exista o placă grafică. + + + gameFoldersBox + Folderele jocurilor:\nLista folderelor pentru a verifica jocurile instalate. + + + + addFolderButton + Adăugați:\nAdăugați un folder la listă. + + + + removeFolderButton + Eliminați:\nÎndepărtați un folder din listă. + debugDump diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 224342f34..e53ec2bc8 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -414,6 +414,11 @@ Is PS4 Pro Режим PS4 Pro + + + Enable Discord Rich Presence + Включить Discord Rich Presence + Username @@ -434,6 +439,36 @@ Log Filter Фильтр логов + + + Input + Ввод + + + + Cursor + Курсор + + + + Hide Cursor + Скрыть курсор + + + + Hide Cursor Idle Timeout + Тайм-аут скрытия курсора при неактивности + + + + Controller + Контроллер + + + + Back Button Behavior + Поведение кнопки назад + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU Включить NULL GPU + + + Paths + Пути + + + + Game Folders + Игровые папки + + + + Add... + Добавить... + + + + Remove + Удалить + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox Режим PS4 Pro:\nЗаставляет эмулятор работать как PS4 Pro, что может включить специальные функции в играх, поддерживающих это. + + + discordRPCCheckbox + Включить Discord Rich Presence:\nОтображает значок эмулятора и соответствующую информацию в вашем профиле Discord. + userName @@ -1011,7 +1071,7 @@ logFilter - Фильтр логов: Фильтрует логи, чтобы показывать только определенную информацию. Примеры: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Уровни: Trace, Debug, Info, Warning, Error, Critical - в этом порядке, конкретный уровень глушит все предыдущие уровни в списке и показывает все последующие уровни. + Фильтр логов:\nФильтрует логи, чтобы показывать только определенную информацию.\nПримеры: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Уровни: Trace, Debug, Info, Warning, Error, Critical - в этом порядке, конкретный уровень глушит все предыдущие уровни в списке и показывает все последующие уровни. @@ -1023,6 +1083,56 @@ GUIgroupBox Воспроизведение заглавной музыки:\nЕсли игра это поддерживает, включает воспроизведение специальной музыки при выборе игры в интерфейсе. + + + hideCursorGroupBox + Скрыть курсор:\nВыберите, когда курсор исчезнет:\nНикогда: Вы всегда будете видеть мышь.\nНеактивный: Yстановите время, через которое она исчезнет после бездействия.\nВсегда: вы никогда не увидите мышь. + + + + idleTimeoutGroupBox + Установите время, через которое курсор исчезнет после бездействия. + + + + backButtonBehaviorGroupBox + Поведение кнопки «Назад»:\nУстанавливает кнопку «Назад» на контроллере для имитации нажатия в указанной позиции на сенсорной панели PS4. + + + + Never + Никогда + + + + Idle + Неактивный + + + + Always + Всегда + + + + Touchpad Left + Тачпад Слева + + + + Touchpad Right + Тачпад Справа + + + + Touchpad Center + Центр Тачпада + + + + None + Нет + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Включить NULL GPU:\nДля технической отладки отключает рендеринг игры так, как будто графической карты нет. + + + gameFoldersBox + Игровые папки:\nСписок папок для проверки установленных игр. + + + + addFolderButton + Добавить:\nДобавить папку в список. + + + + removeFolderButton + Удалить:\nУдалить папку из списка. + debugDump diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index dae5525e8..09f1714e1 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -414,6 +414,11 @@ Is PS4 Pro Mënyra PS4 Pro + + + Enable Discord Rich Presence + Aktivizo Discord Rich Presence + Username @@ -434,6 +439,36 @@ Log Filter Filtri i Ditarit + + + Input + Hyrje + + + + Cursor + Kursori + + + + Hide Cursor + Fshih kursorin + + + + Hide Cursor Idle Timeout + Koha e pritjes për fshehjen e kursorit + + + + Controller + Kontrollues + + + + Back Button Behavior + Sjellja e butonit të kthimit + Input @@ -509,6 +544,26 @@ Enable NULL GPU Aktivizo GPU-në NULL + + + Paths + Rrugët + + + + Game Folders + Dosjet e lojës + + + + Add... + Shtoni... + + + + Remove + Hiqni + Debug @@ -1033,6 +1088,11 @@ ps4proCheckBox Është PS4 Pro:\nBën që emulatori të veprojë si një PS4 PRO, gjë që mund të aktivizojë veçori të veçanta në lojrat që e mbështesin. + + + discordRPCCheckbox + Aktivizo Discord Rich Presence:\nShfaq ikonën e emulatorit dhe informacionin përkatës në profilin tuaj në Discord. + userName @@ -1046,7 +1106,7 @@ logFilter - Filtri i ditarit: Filtron ditarin për të shfaqur vetëm informacione specifike. Shembuj: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Nivelet: Trace, Debug, Info, Warning, Error, Critical - në këtë rend, një nivel specifik hesht të gjitha nivelet përpara në listë dhe regjistron çdo nivel pas atij. + Filtri i ditarit:\nFiltron ditarin për të shfaqur vetëm informacione specifike.\nShembuj: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Nivelet: Trace, Debug, Info, Warning, Error, Critical - në këtë rend, një nivel specifik hesht të gjitha nivelet përpara në listë dhe regjistron çdo nivel pas atij. @@ -1058,6 +1118,56 @@ GUIgroupBox Luaj muzikën e titullit:\nNëse një lojë e mbështet, aktivizohet luajtja e muzikës të veçantë kur të zgjidhësh lojën në GUI. + + + hideCursorGroupBox + Fsheh kursori:\nZgjidhni kur do të zhduket kursori:\nKurrë: Do ta shihni gjithmonë maus.\nInaktiv: Vendosni një kohë për të zhdukur pas inaktivitetit.\nGjithmonë: nuk do ta shihni kurrë maus. + + + + idleTimeoutGroupBox + Vendosni një kohë për të zhdukur maus pas inaktivitetit. + + + + backButtonBehaviorGroupBox + Sjellja e butonit mbrapa:\nVendos butonin mbrapa të kontrollorit për të imituar prekjen në pozicionin e caktuar në touchpad-in PS4. + + + + Never + Kurrë + + + + Idle + Pasiv + + + + Always + Gjithmonë + + + + Touchpad Left + Touchpad Majtas + + + + Touchpad Right + Touchpad Djathtas + + + + Touchpad Center + Qendra e Touchpad + + + + None + Asnjë + cursorGroupBox @@ -1146,17 +1256,17 @@ gameFoldersBox - Dosjet e lojërave: Lista e dosjeve për t'u kontrolluar për lojëra të instaluara. + Folderët e lojërave:\nLista e folderëve për të kontrolluar lojërat e instaluara. addFolderButton - Shto: Shto një dosje në listë. + Shto:\nShto një folder në listë. removeFolderButton - Hiq: Hiq një dosje nga lista. + Hiq:\nHiq një folder nga lista. @@ -1355,4 +1465,4 @@ Krijimi i skedarit skript të përditësimit dështoi - + \ No newline at end of file diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 3e9173c7c..d00f5fcf9 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -414,6 +414,11 @@ Is PS4 Pro PS4 Pro mu + + + Enable Discord Rich Presence + Discord Rich Presence'i etkinleştir + Username @@ -434,6 +439,36 @@ Log Filter Kayıt Filtresi + + + Input + Girdi + + + + Cursor + İmleç + + + + Hide Cursor + İmleci gizle + + + + Hide Cursor Idle Timeout + İmleç için hareketsizlik zaman aşımı + + + + Controller + Kontrolcü + + + + Back Button Behavior + Geri Dön Butonu Davranışı + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU NULL GPU'yu Etkinleştir + + + Paths + Yollar + + + + Game Folders + Oyun Klasörleri + + + + Add... + Ekle... + + + + Remove + Kaldır + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox PS4 Pro Mu:\nEmülatörü bir PS4 PRO gibi çalıştırır; bu, bunu destekleyen oyunlarda özel özellikleri etkinleştirebilir. + + + discordRPCCheckbox + Discord Rich Presence'i etkinleştir:\nEmülatör simgesini ve Discord profilinizdeki ilgili bilgileri gösterir. + userName @@ -1011,7 +1071,7 @@ logFilter - Günlük Filtre: Sadece belirli bilgileri yazdırmak için günlüğü filtreler. Örnekler: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Düzeyler: Trace, Debug, Info, Warning, Error, Critical - bu sırada, belirli bir seviye listede önceki tüm seviyeleri susturur ve sonraki tüm seviyeleri kaydeder. + Günlük Filtre:\nSadece belirli bilgileri yazdırmak için günlüğü filtreler.\nÖrnekler: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Düzeyler: Trace, Debug, Info, Warning, Error, Critical - bu sırada, belirli bir seviye listede önceki tüm seviyeleri susturur ve sonraki tüm seviyeleri kaydeder. @@ -1023,6 +1083,56 @@ GUIgroupBox Başlık Müziklerini Çal:\nEğer bir oyun bunu destekliyorsa, GUI'de oyunu seçtiğinizde özel müziklerin çalmasını etkinleştirir. + + + hideCursorGroupBox + İmleci gizle:\nİmlecin ne zaman kaybolacağını seçin:\nAsla: Fareyi her zaman göreceksiniz.\nPasif: Hareketsiz kaldıktan sonra kaybolması için bir süre belirleyin.\nHer zaman: fareyi asla göremeyeceksiniz. + + + + idleTimeoutGroupBox + Hareket etmeden sonra imlecin kaybolacağı süreyi ayarlayın. + + + + backButtonBehaviorGroupBox + Geri düğmesi davranışı:\nKontrol cihazındaki geri düğmesini, PS4'ün dokunmatik panelindeki belirlenen noktaya dokunmak için ayarlar. + + + + Never + Asla + + + + Idle + Boşta + + + + Always + Her zaman + + + + Touchpad Left + Dokunmatik Yüzey Sol + + + + Touchpad Right + Dokunmatik Yüzey Sağ + + + + Touchpad Center + Dokunmatik Yüzey Orta + + + + None + Yok + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Null GPU'yu Etkinleştir:\nTeknik hata ayıklama amacıyla, oyunun render edilmesini grafik kartı yokmuş gibi devre dışı bırakır. + + + gameFoldersBox + Oyun klasörleri:\nYüklenmiş oyunları kontrol etmek için klasörlerin listesi. + + + + addFolderButton + Ekle:\nListeye bir klasör ekle. + + + + removeFolderButton + Kaldır:\nListeden bir klasörü kaldır. + debugDump diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index af8d218a6..cf58ee5ba 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -414,6 +414,11 @@ Is PS4 Pro Is PS4 Pro + + + Enable Discord Rich Presence + Bật Discord Rich Presence + Username @@ -434,6 +439,36 @@ Log Filter Log Filter + + + Input + Đầu vào + + + + Cursor + Con trỏ + + + + Hide Cursor + Ẩn con trỏ + + + + Hide Cursor Idle Timeout + Thời gian chờ ẩn con trỏ + + + + Controller + Điều khiển + + + + Back Button Behavior + Hành vi nút quay lại + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU Enable NULL GPU + + + Paths + Đường dẫn + + + + Game Folders + Thư mục trò chơi + + + + Add... + Thêm... + + + + Remove + Xóa + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox Là PS4 Pro:\nKhiến trình giả lập hoạt động như một PS4 PRO, điều này có thể kích hoạt các tính năng đặc biệt trong các trò chơi hỗ trợ điều này. + + + discordRPCCheckbox + Bật Discord Rich Presence:\nHiển thị biểu tượng trình giả lập và thông tin liên quan trên hồ sơ Discord của bạn. + userName @@ -1011,7 +1071,7 @@ logFilter - Bộ lọc nhật ký: Lọc nhật ký để in chỉ thông tin cụ thể. Ví dụ: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Các mức: Trace, Debug, Info, Warning, Error, Critical - theo thứ tự này, một mức cụ thể làm tắt tất cả các mức trước trong danh sách và ghi lại tất cả các mức sau đó. + Bộ lọc nhật ký:\nLọc nhật ký để in chỉ thông tin cụ thể.\nVí dụ: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" Các mức: Trace, Debug, Info, Warning, Error, Critical - theo thứ tự này, một mức cụ thể làm tắt tất cả các mức trước trong danh sách và ghi lại tất cả các mức sau đó. @@ -1023,6 +1083,56 @@ GUIgroupBox Phát nhạc tiêu đề trò chơi:\nNếu một trò chơi hỗ trợ điều này, hãy kích hoạt phát nhạc đặc biệt khi bạn chọn trò chơi trong GUI. + + + hideCursorGroupBox + Ẩn con trỏ:\nChọn khi nào con trỏ sẽ biến mất:\nKhông bao giờ: Bạn sẽ luôn thấy chuột.\nKhông hoạt động: Đặt một khoảng thời gian để nó biến mất sau khi không hoạt động.\nLuôn luôn: bạn sẽ không bao giờ thấy chuột. + + + + idleTimeoutGroupBox + Đặt thời gian để chuột biến mất sau khi không hoạt động. + + + + backButtonBehaviorGroupBox + Hành vi nút quay lại:\nĐặt nút quay lại của tay cầm để mô phỏng việc chạm vào vị trí đã chỉ định trên touchpad của PS4. + + + + Never + Không bao giờ + + + + Idle + Nhàn rỗi + + + + Always + Luôn luôn + + + + Touchpad Left + Touchpad Trái + + + + Touchpad Right + Touchpad Phải + + + + Touchpad Center + Giữa Touchpad + + + + None + Không có + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox Bật GPU Null:\nĐể mục đích gỡ lỗi kỹ thuật, vô hiệu hóa việc kết xuất trò chơi như thể không có card đồ họa. + + + gameFoldersBox + Thư mục trò chơi:\nDanh sách các thư mục để kiểm tra các trò chơi đã cài đặt. + + + + addFolderButton + Thêm:\nThêm một thư mục vào danh sách. + + + + removeFolderButton + Xóa:\nXóa một thư mục khỏi danh sách. + debugDump diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 5eef55641..ac4117edc 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -414,6 +414,11 @@ Is PS4 Pro 是否是 PS4 Pro + + + Enable Discord Rich Presence + 启用 Discord Rich Presence + Username @@ -434,6 +439,36 @@ Log Filter 日志过滤 + + + Input + 输入 + + + + Cursor + 光标 + + + + Hide Cursor + 隐藏光标 + + + + Hide Cursor Idle Timeout + 光标空闲超时隐藏 + + + + Controller + 控制器 + + + + Back Button Behavior + 返回按钮行为 + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU 启用 NULL GPU + + + Paths + 路径 + + + + Game Folders + 游戏文件夹 + + + + Add... + 添加... + + + + Remove + 删除 + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox 这是 PS4 Pro:\n使模拟器作为 PS4 PRO 运行,可以在支持的游戏中激活特殊功能。 + + + discordRPCCheckbox + 启用 Discord Rich Presence:\n在您的 Discord 个人资料上显示仿真器图标和相关信息。 + userName @@ -1011,7 +1071,7 @@ logFilter - 日志过滤器: 过滤日志,仅打印特定信息。例如:"Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" 级别: Trace, Debug, Info, Warning, Error, Critical - 按此顺序,特定级别将静默列表中所有先前的级别,并记录所有后续级别。 + 日志过滤器:\n过滤日志,仅打印特定信息。\n例如:"Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" 级别: Trace, Debug, Info, Warning, Error, Critical - 按此顺序,特定级别将静默列表中所有先前的级别,并记录所有后续级别。 @@ -1023,6 +1083,56 @@ GUIgroupBox 播放标题音乐:\n如果游戏支持,在图形界面选择游戏时启用播放特殊音乐。 + + + hideCursorGroupBox + 隐藏光标:\n选择光标何时消失:\n从不: 您将始终看到鼠标。\n空闲: 设置光标在空闲后消失的时间。\n始终: 您将永远看不到鼠标。 + + + + idleTimeoutGroupBox + 设置鼠标在空闲后消失的时间。 + + + + backButtonBehaviorGroupBox + 返回按钮行为:\n设置控制器的返回按钮以模拟在 PS4 触控板上指定位置的点击。 + + + + Never + 从不 + + + + Idle + 空闲 + + + + Always + 始终 + + + + Touchpad Left + 触控板左侧 + + + + Touchpad Right + 触控板右侧 + + + + Touchpad Center + 触控板中间 + + + + None + + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox 启用空 GPU:\n为了技术调试,将游戏渲染禁用,仿佛没有图形卡。 + + + gameFoldersBox + 游戏文件夹:\n检查已安装游戏的文件夹列表。 + + + + addFolderButton + 添加:\n将文件夹添加到列表。 + + + + removeFolderButton + 移除:\n从列表中移除文件夹。 + debugDump diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 7e3585a07..c114c69c7 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -414,6 +414,11 @@ Is PS4 Pro Is PS4 Pro + + + Enable Discord Rich Presence + 啟用 Discord Rich Presence + Username @@ -434,6 +439,36 @@ Log Filter Log Filter + + + Input + 輸入 + + + + Cursor + 游標 + + + + Hide Cursor + 隱藏游標 + + + + Hide Cursor Idle Timeout + 游標空閒超時隱藏 + + + + Controller + 控制器 + + + + Back Button Behavior + 返回按鈕行為 + Graphics @@ -474,6 +509,26 @@ Enable NULL GPU Enable NULL GPU + + + Paths + 路徑 + + + + Game Folders + 遊戲資料夾 + + + + Add... + 添加... + + + + Remove + 刪除 + Debug @@ -998,6 +1053,11 @@ ps4proCheckBox 為PS4 Pro:\n讓模擬器像PS4 PRO一樣運作,這可能啟用支持此功能的遊戲中的特殊功能。 + + + discordRPCCheckbox + 啟用 Discord Rich Presence:\n在您的 Discord 個人檔案上顯示模擬器圖標和相關信息。 + userName @@ -1011,7 +1071,7 @@ logFilter - 日誌過濾器: 過濾日誌以僅打印特定信息。範例:"Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" 等級: Trace, Debug, Info, Warning, Error, Critical - 以此順序,特定級別靜音所有前面的級別,並記錄其後的每個級別。 + 日誌過濾器:\n過濾日誌以僅打印特定信息。\n範例:"Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" 等級: Trace, Debug, Info, Warning, Error, Critical - 以此順序,特定級別靜音所有前面的級別,並記錄其後的每個級別。 @@ -1023,6 +1083,56 @@ GUIgroupBox 播放標題音樂:\n如果遊戲支持,啟用在GUI中選擇遊戲時播放特殊音樂。 + + + hideCursorGroupBox + 隱藏游標:\n選擇游標何時消失:\n從不: 您將始終看到滑鼠。\n閒置: 設定在閒置後消失的時間。\n始終: 您將永遠看不到滑鼠。 + + + + idleTimeoutGroupBox + 設定滑鼠在閒置後消失的時間。 + + + + backButtonBehaviorGroupBox + 返回按鈕行為:\n設定控制器的返回按鈕模擬在 PS4 觸控板上指定位置的觸碰。 + + + + Never + 從不 + + + + Idle + 閒置 + + + + Always + 始終 + + + + Touchpad Left + 觸控板左側 + + + + Touchpad Right + 觸控板右側 + + + + Touchpad Center + 觸控板中間 + + + + None + + graphicsAdapterGroupBox @@ -1048,6 +1158,21 @@ nullGpuCheckBox 啟用空GPU:\n為了技術調試,禁用遊戲渲染,彷彿沒有顯示卡。 + + + gameFoldersBox + 遊戲資料夾:\n檢查已安裝遊戲的資料夾列表。 + + + + addFolderButton + 添加:\n將資料夾添加到列表。 + + + + removeFolderButton + 移除:\n從列表中移除資料夾。 + debugDump From b965b094b7174b91e38bd611d482db2e6d494696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Mon, 14 Oct 2024 18:34:52 +0700 Subject: [PATCH 60/93] test (#1371) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ee09163fd..6b87ec418 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -368,7 +368,7 @@ jobs: run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel3 - name: Run AppImage packaging script run: ./.github/linux-appimage-qt.sh From 9b54e8231409c3210405a38c03f11240d76b4c1e Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Mon, 14 Oct 2024 15:00:55 +0300 Subject: [PATCH 61/93] revert qt-issue fixed linux-qt build --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6b87ec418..ee09163fd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -368,7 +368,7 @@ jobs: run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel3 + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel - name: Run AppImage packaging script run: ./.github/linux-appimage-qt.sh From a3df2448ec848f8b264e058a002e841fee26959b Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Mon, 14 Oct 2024 15:11:21 +0300 Subject: [PATCH 62/93] Small Np + trophy fixes (#1363) * sceNpGetOnlineId returns sign out code * return -1 if trophy xml not found . Fixes undertale --- src/core/libraries/np_manager/np_manager.cpp | 6 +++--- src/core/libraries/np_manager/np_manager.h | 2 +- src/core/libraries/np_trophy/np_trophy.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp index e1aaee814..907244b22 100644 --- a/src/core/libraries/np_manager/np_manager.cpp +++ b/src/core/libraries/np_manager/np_manager.cpp @@ -986,9 +986,9 @@ int PS4_SYSV_ABI sceNpGetNpReachabilityState() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpGetOnlineId() { - LOG_ERROR(Lib_NpManager, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNpGetOnlineId(s32 userId, OrbisNpOnlineId* onlineId) { + LOG_DEBUG(Lib_NpManager, "called returned sign out"); + return ORBIS_NP_ERROR_SIGNED_OUT; } int PS4_SYSV_ABI sceNpGetParentalControlInfo() { diff --git a/src/core/libraries/np_manager/np_manager.h b/src/core/libraries/np_manager/np_manager.h index 861d91e39..7e906cdc8 100644 --- a/src/core/libraries/np_manager/np_manager.h +++ b/src/core/libraries/np_manager/np_manager.h @@ -233,7 +233,7 @@ int PS4_SYSV_ABI sceNpGetGamePresenceStatus(); int PS4_SYSV_ABI sceNpGetGamePresenceStatusA(); int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId userId, OrbisNpId* npId); int PS4_SYSV_ABI sceNpGetNpReachabilityState(); -int PS4_SYSV_ABI sceNpGetOnlineId(); +int PS4_SYSV_ABI sceNpGetOnlineId(s32 userId, OrbisNpOnlineId* onlineId); int PS4_SYSV_ABI sceNpGetParentalControlInfo(); int PS4_SYSV_ABI sceNpGetParentalControlInfoA(); int PS4_SYSV_ABI sceNpGetState(s32 userId, OrbisNpState* state); diff --git a/src/core/libraries/np_trophy/np_trophy.cpp b/src/core/libraries/np_trophy/np_trophy.cpp index 548d1af69..e8fd57ef1 100644 --- a/src/core/libraries/np_trophy/np_trophy.cpp +++ b/src/core/libraries/np_trophy/np_trophy.cpp @@ -520,7 +520,7 @@ s32 PS4_SYSV_ABI sceNpTrophyGetTrophyUnlockState(OrbisNpTrophyContext context, if (!result) { LOG_ERROR(Lib_NpTrophy, "Failed to open trophy xml : {}", result.description()); - return ORBIS_OK; + return -1; } int num_trophies = 0; From 09725bd921088b73746605e672abf6ff40171880 Mon Sep 17 00:00:00 2001 From: psucien Date: Mon, 14 Oct 2024 22:33:06 +0200 Subject: [PATCH 63/93] hot-fix: unexpected pass break on indirect args buffer obtaining --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 293dfbe6a..b3c42fcb6 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -100,12 +100,12 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr address, u32 offset, u32 si buffer_cache.BindVertexBuffers(vs_info); const u32 num_indices = buffer_cache.BindIndexBuffer(is_indexed, 0); - BeginRendering(*pipeline); - UpdateDynamicState(*pipeline); - const auto [buffer, base] = buffer_cache.ObtainBuffer(address, size, true); const auto total_offset = base + offset; + BeginRendering(*pipeline); + UpdateDynamicState(*pipeline); + // We can safely ignore both SGPR UD indices and results of fetch shader parsing, as vertex and // instance offsets will be automatically applied by Vulkan from indirect args buffer. From 7e27afb37dbebecc00ac80035bdcf3eaf7eabc29 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Tue, 15 Oct 2024 03:30:10 -0300 Subject: [PATCH 64/93] fix tr sq.ts (#1380) * Update sq.ts * InstallDirSelect --- src/qt_gui/translations/ar.ts | 23 ++++-- src/qt_gui/translations/da_DK.ts | 23 ++++-- src/qt_gui/translations/de.ts | 23 ++++-- src/qt_gui/translations/el.ts | 23 ++++-- src/qt_gui/translations/en.ts | 17 ++++- src/qt_gui/translations/es_ES.ts | 23 ++++-- src/qt_gui/translations/fa_IR.ts | 23 ++++-- src/qt_gui/translations/fi.ts | 23 ++++-- src/qt_gui/translations/fr.ts | 23 ++++-- src/qt_gui/translations/hu_HU.ts | 23 ++++-- src/qt_gui/translations/id.ts | 23 ++++-- src/qt_gui/translations/it.ts | 23 ++++-- src/qt_gui/translations/ja_JP.ts | 23 ++++-- src/qt_gui/translations/ko_KR.ts | 21 ++++-- src/qt_gui/translations/lt_LT.ts | 23 ++++-- src/qt_gui/translations/nb.ts | 23 ++++-- src/qt_gui/translations/nl.ts | 23 ++++-- src/qt_gui/translations/pl_PL.ts | 23 ++++-- src/qt_gui/translations/pt_BR.ts | 13 ++++ src/qt_gui/translations/ro_RO.ts | 23 ++++-- src/qt_gui/translations/ru_RU.ts | 23 ++++-- src/qt_gui/translations/sq.ts | 118 +++++-------------------------- src/qt_gui/translations/tr_TR.ts | 23 ++++-- src/qt_gui/translations/vi_VN.ts | 23 ++++-- src/qt_gui/translations/zh_CN.ts | 23 ++++-- src/qt_gui/translations/zh_TW.ts | 23 ++++-- 26 files changed, 459 insertions(+), 216 deletions(-) diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index 8efacc063..463fd9716 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -52,6 +52,19 @@ ...جارٍ التحميل + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - اختر المجلد + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter مرشح السجل - + Input إدخال - + Cursor مؤشر @@ -509,7 +522,7 @@ Enable NULL GPU تمكين وحدة معالجة الرسومات الفارغة - + Paths المسارات @@ -1083,7 +1096,7 @@ GUIgroupBox تشغيل موسيقى العنوان:\nإذا كانت اللعبة تدعم ذلك، قم بتمكين تشغيل موسيقى خاصة عند اختيار اللعبة في واجهة المستخدم. - + hideCursorGroupBox إخفاء المؤشر:\nاختر متى سيختفي المؤشر:\nأبداً: سترى الفأرة دائماً.\nعاطل: حدد وقتاً لاختفائه بعد أن يكون غير مستخدم.\nدائماً: لن ترى الفأرة أبداً. @@ -1289,7 +1302,7 @@ Update Available تحديث متاح - + Update Channel قناة التحديث diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 9abe22c3d..b3a6b9001 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -52,6 +52,19 @@ Loading... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Log Filter - + Input Indtastning - + Cursor Markør @@ -509,7 +522,7 @@ Enable NULL GPU Enable NULL GPU - + Paths Stier @@ -1083,7 +1096,7 @@ GUIgroupBox Titelsmusikafspilning:\nHvis spillet understøtter det, aktiver speciel musik, når spillet vælges i brugergrænsefladen. - + hideCursorGroupBox Skjul Cursor:\nVælg hvornår cursoren skal forsvinde:\nAldrig: Du vil altid se musen.\nInaktiv: Indstil en tid for, hvornår den skal forsvinde efter at være inaktiv.\nAltid: du vil aldrig se musen. @@ -1289,7 +1302,7 @@ Update Available Opdatering tilgængelig - + Update Channel Opdateringskanal diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index a6904d9a1..75a14b348 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -52,6 +52,19 @@ Lade... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Wähle Ordner + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Log-Filter - + Input Eingabe - + Cursor Cursor @@ -509,7 +522,7 @@ Enable NULL GPU NULL GPU aktivieren - + Paths Pfad @@ -1083,7 +1096,7 @@ GUIgroupBox Wiedergabe der Titelmusik:\nWenn das Spiel dies unterstützt, wird beim Auswählen des Spiels in der Benutzeroberfläche spezielle Musik abgespielt. - + hideCursorGroupBox Maus ausblenden:\nWählen Sie, wann der Cursor verschwinden soll:\nNie: Sie sehen die Maus immer.\nInaktiv: Legen Sie eine Zeit fest, nach der sie nach Inaktivität verschwindet.\nImmer: Sie sehen die Maus niemals. @@ -1289,7 +1302,7 @@ Update Available Aktualisierung verfügbar - + Update Channel Update-Kanal diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index 963ca0ab1..871709297 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -52,6 +52,19 @@ Loading... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Log Filter - + Input Είσοδος - + Cursor Δείκτης @@ -509,7 +522,7 @@ Enable NULL GPU Enable NULL GPU - + Paths Διαδρομές @@ -1083,7 +1096,7 @@ GUIgroupBox Αναπαραγωγή Μουσικής Τίτλων:\nΕάν το παιχνίδι το υποστηρίζει, ενεργοποιεί ειδική μουσική κατά την επιλογή του παιχνιδιού από τη διεπαφή χρήστη. - + hideCursorGroupBox Απόκρυψη Κέρσορα:\nΕπιλέξτε πότε θα εξαφανιστεί ο κέρσορας:\nΠοτέ: θα βλέπετε πάντα το ποντίκι.\nΑδρανές: ορίστε έναν χρόνο για να εξαφανιστεί μετά από αδράνεια.\nΠάντα: δεν θα δείτε ποτέ το ποντίκι. @@ -1289,7 +1302,7 @@ Update Available Διαθέσιμη Ενημέρωση - + Update Channel Κανάλι Ενημέρωσης diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index d781bc2fe..7c44fbdf4 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -52,6 +52,19 @@ Loading... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -509,7 +522,7 @@ Enable NULL GPU Enable NULL GPU - + Paths Paths @@ -1289,7 +1302,7 @@ Update Available Update Available - + Update Channel Update Channel diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 1095cd9cd..1a5a8c887 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -52,6 +52,19 @@ Cargando... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Elegir carpeta + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Filtro de registro - + Input Entrada - + Cursor Cursor @@ -509,7 +522,7 @@ Enable NULL GPU Habilitar GPU NULL - + Paths Rutas @@ -1083,7 +1096,7 @@ GUIgroupBox Reproducir Música del Título:\nSi un juego lo admite, habilita la reproducción de música especial al seleccionar el juego en la interfaz gráfica. - + hideCursorGroupBox Ocultar Cursor:\nElija cuándo desaparecerá el cursor:\nNunca: Siempre verá el mouse.\nInactivo: Establezca un tiempo para que desaparezca después de estar inactivo.\nSiempre: nunca verá el mouse. @@ -1289,7 +1302,7 @@ Update Available Actualización disponible - + Update Channel Canal de Actualización diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index e86001b9a..0a6b21916 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -52,6 +52,19 @@ ...درحال بارگیری + + InstallDirSelect + + + shadPS4 - Choose directory + ShadPS4 - انتخاب محل نصب بازی + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Log فیلتر - + Input ورودی - + Cursor نشانگر @@ -509,7 +522,7 @@ Enable NULL GPU NULL GPU فعال کردن - + Paths مسیرها @@ -1083,7 +1096,7 @@ GUIgroupBox Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. - + hideCursorGroupBox پنهان کردن نشانگر:\nانتخاب کنید که نشانگر چه زمانی ناپدید شود:\nهرگز: شما همیشه ماوس را خواهید دید.\nغیرفعال: زمانی را برای ناپدید شدن بعد از غیرفعالی تعیین کنید.\nهمیشه: شما هرگز ماوس را نخواهید دید. @@ -1289,7 +1302,7 @@ Update Available به روز رسانی موجود است - + Update Channel کانال بروزرسانی diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index fe972de9a..98efe9c8f 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -52,6 +52,19 @@ Loading... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Log Filter - + Input Syöttö - + Cursor Kursori @@ -509,7 +522,7 @@ Enable NULL GPU Enable NULL GPU - + Paths Polut @@ -1083,7 +1096,7 @@ GUIgroupBox Soita Otsikkomusiikkia:\nJos peli tukee sitä, ota käyttöön erityisen musiikin soittaminen pelin valinnan yhteydessä käyttöliittymässä. - + hideCursorGroupBox Piilota kursori:\nValitse, milloin kursori häviää:\nEi koskaan: Näet hiiren aina.\nAktiivinen: Aseta aika, jolloin se häviää oltuaan aktiivinen.\nAina: et koskaan näe hiirtä. @@ -1289,7 +1302,7 @@ Update Available Päivitys saatavilla - + Update Channel Päivityskanava diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index 95158d73e..8e371908b 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -52,6 +52,19 @@ Chargement... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Choisir un répertoire + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Filtre - + Input Entrée - + Cursor Curseur @@ -509,7 +522,7 @@ Enable NULL GPU NULL GPU - + Paths Chemins @@ -1083,7 +1096,7 @@ GUIgroupBox Jouer de la musique de titre:\nSi le jeu le prend en charge, cela active la musique spéciale lorsque vous sélectionnez le jeu dans l'interface utilisateur. - + hideCursorGroupBox Masquer le curseur:\nChoisissez quand le curseur disparaîtra:\nJamais: Vous verrez toujours la souris.\nInactif: Définissez un temps pour qu'il disparaisse après inactivité.\nToujours: vous ne verrez jamais la souris. @@ -1289,7 +1302,7 @@ Update Available Mise à jour disponible - + Update Channel Canal de Mise à Jour diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 7337b54c9..8104a3d98 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -52,6 +52,19 @@ Betöltés.. + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Mappa kiválasztása + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Naplózási Filter - + Input Bemenet - + Cursor Kurzor @@ -509,7 +522,7 @@ Enable NULL GPU NULL GPU Engedélyezése - + Paths Útvonalak @@ -1083,7 +1096,7 @@ GUIgroupBox Játék címzene lejátszása:\nHa a játék támogatja, engedélyezze a speciális zene lejátszását, amikor a játékot kiválasztja a GUI-ban. - + hideCursorGroupBox Akurátor elrejtése:\nVálassza ki, mikor tűnjön el az egérkurzor:\nSoha: Az egér mindig látható.\nInaktív: Állítson be egy időt, amikor inaktív állapotban eltűnik.\nMindig: soha nem látja az egeret. @@ -1289,7 +1302,7 @@ Update Available Frissítés elérhető - + Update Channel Frissítési Csatorna diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index f64762671..288149346 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -52,6 +52,19 @@ Loading... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Log Filter - + Input Masukan - + Cursor Kursor @@ -509,7 +522,7 @@ Enable NULL GPU Enable NULL GPU - + Paths Jalur @@ -1083,7 +1096,7 @@ GUIgroupBox Putar Musik Judul Permainan:\nJika permainan mendukungnya, aktifkan pemutaran musik khusus saat memilih permainan di GUI. - + hideCursorGroupBox Sembunyikan Kursor:\nPilih kapan kursor akan menghilang:\nTidak Pernah: Anda akan selalu melihat mouse.\nTidak Aktif: Tetapkan waktu untuk menghilang setelah tidak aktif.\nSelalu: Anda tidak akan pernah melihat mouse. @@ -1289,7 +1302,7 @@ Update Available Pembaruan Tersedia - + Update Channel Saluran Pembaruan diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index 1da50ec5b..47000d57f 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -52,6 +52,19 @@ Caricamento... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Scegli cartella + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Filtro Log - + Input Input - + Cursor Cursore @@ -509,7 +522,7 @@ Enable NULL GPU Abilita NULL GPU - + Paths Percorsi @@ -1083,7 +1096,7 @@ GUIgroupBox Riproduci Musica del Titolo:\nSe un gioco lo supporta, attiva la riproduzione di musica speciale quando selezioni il gioco nell'interfaccia grafica. - + hideCursorGroupBox Nascondi cursore:\nScegli quando il cursore scomparirà:\nMai: Vedrai sempre il mouse.\nInattivo: Imposta un tempo per farlo scomparire dopo essere stato inattivo.\nSempre: non vedrai mai il mouse. @@ -1289,7 +1302,7 @@ Update Available Aggiornamento disponibile - + Update Channel Canale di Aggiornamento diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index 4fd819452..952ceb5ab 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -52,6 +52,19 @@ 読み込み中... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - ディレクトリを選択 + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter ログフィルター - + Input 入力 - + Cursor カーソル @@ -509,7 +522,7 @@ Enable NULL GPU NULL GPUを有効にする - + Paths パス @@ -1083,7 +1096,7 @@ GUIgroupBox タイトルミュージックを再生:\nゲームがそれをサポートしている場合、GUIでゲームを選択したときに特別な音楽を再生することを有効にします。 - + hideCursorGroupBox カーソルを隠す:\nカーソルが消えるタイミングを選択してください:\n決して: いつでもマウスが見えます。\nアイドル: アイダルの後に消えるまでの時間を設定します。\n常に: マウスは決して見えません。 @@ -1289,7 +1302,7 @@ Update Available アップデートがあります - + Update Channel アップデートチャネル diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index 74d307c3e..d7d200672 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -52,6 +52,19 @@ Loading... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Log Filter - + Input Input - + Cursor Cursor @@ -509,7 +522,7 @@ Enable NULL GPU Enable NULL GPU - + Paths Paths @@ -1289,7 +1302,7 @@ Update Available Update Available - + Update Channel Update Channel diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index 2924d65ba..f82a20a9d 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -52,6 +52,19 @@ Loading... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Log Filter - + Input Įvestis - + Cursor Žymeklis @@ -509,7 +522,7 @@ Enable NULL GPU Enable NULL GPU - + Paths Keliai @@ -1083,7 +1096,7 @@ GUIgroupBox Groti antraščių muziką:\nJei žaidimas tai palaiko, įjungia specialios muzikos grojimą, kai pasirinkite žaidimą GUI. - + hideCursorGroupBox Slėpti žymeklį:\nPasirinkite, kada žymeklis dings:\nNiekuomet: Visada matysite pelę.\nNeaktyvus: Nustatykite laiką, po kurio ji dings, kai bus neaktyvi.\nVisada: niekada nematysite pelės. @@ -1289,7 +1302,7 @@ Update Available Prieinama atnaujinimas - + Update Channel Atnaujinimo Kanalas diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index c224c4f8f..6fdf5ff8e 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -52,6 +52,19 @@ Loading... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Log Filter - + Input Inndata - + Cursor Markør @@ -509,7 +522,7 @@ Enable NULL GPU Enable NULL GPU - + Paths Stier @@ -1083,7 +1096,7 @@ GUIgroupBox Spille tittelmusikk:\nHvis et spill støtter det, aktiverer det å spille spesiell musikk når du velger spillet i GUI. - + hideCursorGroupBox Skjul musepeker:\nVelg når musepekeren skal forsvinne:\nAldri: Du vil alltid se musen.\nInaktiv: Sett en tid for at den skal forsvinne etter å ha vært inaktiv.\nAlltid: du vil aldri se musen. @@ -1289,7 +1302,7 @@ Update Available Oppdatering tilgjengelig - + Update Channel Oppdateringskanal diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index b733610d1..f99ad54da 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -52,6 +52,19 @@ Loading... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Log Filter - + Input Invoer - + Cursor Cursor @@ -509,7 +522,7 @@ Enable NULL GPU Enable NULL GPU - + Paths Pad @@ -1083,7 +1096,7 @@ GUIgroupBox Speel titelsong:\nAls een game dit ondersteunt, wordt speciale muziek afgespeeld wanneer je het spel in de GUI selecteert. - + hideCursorGroupBox Verberg cursor:\nKies wanneer de cursor verdwijnt:\nNooit: Je ziet altijd de muis.\nInactief: Stel een tijd in waarna deze verdwijnt na inactiviteit.\nAltijd: je ziet de muis nooit. @@ -1289,7 +1302,7 @@ Update Available Update beschikbaar - + Update Channel Updatekanaal diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index f81647f35..a1fd3dc6a 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -52,6 +52,19 @@ Ładowanie... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Wybierz katalog + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Filtrowanie dziennika - + Input Wejście - + Cursor Kursor @@ -509,7 +522,7 @@ Enable NULL GPU Wyłącz kartę graficzną - + Paths Ścieżki @@ -1083,7 +1096,7 @@ GUIgroupBox Odtwórz muzykę tytułową:\nJeśli gra to obsługuje, aktywuje odtwarzanie specjalnej muzyki podczas wybierania gry w GUI. - + hideCursorGroupBox Ukryj kursor:\nWybierz, kiedy kursor zniknie:\nNigdy: Zawsze będziesz widział myszkę.\nNieaktywny: Ustaw czas, po którym zniknie po bezczynności.\nZawsze: nigdy nie zobaczysz myszki. @@ -1289,7 +1302,7 @@ Update Available Dostępna aktualizacja - + Update Channel Kanał Aktualizacji diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index cf673b298..50c575623 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -52,6 +52,19 @@ Carregando... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Escolha o diretório + + + + Select which directory you want to install to. + Selecione o diretório em que você deseja instalar. + + GameInstallDialog diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 33a57792f..ba2952976 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -52,6 +52,19 @@ Loading... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Log Filter - + Input Introducere - + Cursor Cursor @@ -509,7 +522,7 @@ Enable NULL GPU Enable NULL GPU - + Paths Trasee @@ -1083,7 +1096,7 @@ GUIgroupBox Redă muzica titlului:\nDacă un joc o suportă, activează redarea muzicii speciale când selectezi jocul în GUI. - + hideCursorGroupBox Ascunde cursorul:\nAlegeți când va dispărea cursorul:\nNiciodată: Vei vedea întotdeauna mouse-ul.\nInactiv: Setează un timp pentru a dispărea după inactivitate.\nÎntotdeauna: nu vei vedea niciodată mouse-ul. @@ -1289,7 +1302,7 @@ Update Available Actualizare disponibilă - + Update Channel Canal de Actualizare diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index e53ec2bc8..5b29bf7c7 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -52,6 +52,19 @@ Загрузка... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Выберите папку + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Фильтр логов - + Input Ввод - + Cursor Курсор @@ -509,7 +522,7 @@ Enable NULL GPU Включить NULL GPU - + Paths Пути @@ -1083,7 +1096,7 @@ GUIgroupBox Воспроизведение заглавной музыки:\nЕсли игра это поддерживает, включает воспроизведение специальной музыки при выборе игры в интерфейсе. - + hideCursorGroupBox Скрыть курсор:\nВыберите, когда курсор исчезнет:\nНикогда: Вы всегда будете видеть мышь.\nНеактивный: Yстановите время, через которое она исчезнет после бездействия.\nВсегда: вы никогда не увидите мышь. @@ -1289,7 +1302,7 @@ Update Available Доступно обновление - + Update Channel Канал обновления diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 09f1714e1..28e32aa07 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -52,6 +52,19 @@ Duke ngarkuar... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Përzgjidh dosjen + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,36 +452,6 @@ Log Filter Filtri i Ditarit - - - Input - Hyrje - - - - Cursor - Kursori - - - - Hide Cursor - Fshih kursorin - - - - Hide Cursor Idle Timeout - Koha e pritjes për fshehjen e kursorit - - - - Controller - Kontrollues - - - - Back Button Behavior - Sjellja e butonit të kthimit - Input @@ -489,11 +472,6 @@ Hide Cursor Idle Timeout Koha për fshehjen e kursorit joaktiv - - - Input - Hyrja - Controller @@ -544,7 +522,7 @@ Enable NULL GPU Aktivizo GPU-në NULL - + Paths Rrugët @@ -1118,7 +1096,7 @@ GUIgroupBox Luaj muzikën e titullit:\nNëse një lojë e mbështet, aktivizohet luajtja e muzikës të veçantë kur të zgjidhësh lojën në GUI. - + hideCursorGroupBox Fsheh kursori:\nZgjidhni kur do të zhduket kursori:\nKurrë: Do ta shihni gjithmonë maus.\nInaktiv: Vendosni një kohë për të zhdukur pas inaktivitetit.\nGjithmonë: nuk do ta shihni kurrë maus. @@ -1126,62 +1104,12 @@ idleTimeoutGroupBox - Vendosni një kohë për të zhdukur maus pas inaktivitetit. + Koha për fshehjen e kursorit joaktiv:\Kohëzgjatja (në sekonda) pas së cilës kursori që ka nuk ka qënë në veprim fshihet. backButtonBehaviorGroupBox - Sjellja e butonit mbrapa:\nVendos butonin mbrapa të kontrollorit për të imituar prekjen në pozicionin e caktuar në touchpad-in PS4. - - - - Never - Kurrë - - - - Idle - Pasiv - - - - Always - Gjithmonë - - - - Touchpad Left - Touchpad Majtas - - - - Touchpad Right - Touchpad Djathtas - - - - Touchpad Center - Qendra e Touchpad - - - - None - Asnjë - - - - cursorGroupBox - Kursori:\nNdrysho cilësimet në lidhje me kursorin. - - - - hideCursorGroupBox - Fshih kursorin:\nCakto sjelljen e fshehjes së kursorit. - - - - idleTimeoutGroupBox - Koha për fshehjen e kursorit joaktiv:\Kohëzgjatja (në sekonda) pas së cilës kursori që ka nuk ka qënë në veprim fshihet. + Sjellja e butonit mbrapa:\nLejon të përcaktohet se në cilën pjesë të tastierës prekëse do të imitojë një prekje butoni prapa. @@ -1198,16 +1126,6 @@ Always Gjithmonë - - - backButtonBehaviorGroupBox - Back Button Behavior:\nAllows setting which part of the touchpad the back button will emulate a touch on. - - - - backButtonBehaviorGroupBox - Sjellja e butonit mbrapa:\nLejon të përcaktohet se në cilën pjesë të tastierës prekëse do të imitojë një prekje butoni prapa. - Touchpad Left @@ -1384,7 +1302,7 @@ Update Available Ofrohet një përditësim - + Update Channel Kanali i përditësimit diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index d00f5fcf9..91ede7319 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -52,6 +52,19 @@ Yükleniyor... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Klasörü Seç + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Kayıt Filtresi - + Input Girdi - + Cursor İmleç @@ -509,7 +522,7 @@ Enable NULL GPU NULL GPU'yu Etkinleştir - + Paths Yollar @@ -1083,7 +1096,7 @@ GUIgroupBox Başlık Müziklerini Çal:\nEğer bir oyun bunu destekliyorsa, GUI'de oyunu seçtiğinizde özel müziklerin çalmasını etkinleştirir. - + hideCursorGroupBox İmleci gizle:\nİmlecin ne zaman kaybolacağını seçin:\nAsla: Fareyi her zaman göreceksiniz.\nPasif: Hareketsiz kaldıktan sonra kaybolması için bir süre belirleyin.\nHer zaman: fareyi asla göremeyeceksiniz. @@ -1289,7 +1302,7 @@ Update Available Güncelleme Mevcut - + Update Channel Güncelleme Kanalı diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index cf58ee5ba..ebdee76e5 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -52,6 +52,19 @@ Loading... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Log Filter - + Input Đầu vào - + Cursor Con trỏ @@ -509,7 +522,7 @@ Enable NULL GPU Enable NULL GPU - + Paths Đường dẫn @@ -1083,7 +1096,7 @@ GUIgroupBox Phát nhạc tiêu đề trò chơi:\nNếu một trò chơi hỗ trợ điều này, hãy kích hoạt phát nhạc đặc biệt khi bạn chọn trò chơi trong GUI. - + hideCursorGroupBox Ẩn con trỏ:\nChọn khi nào con trỏ sẽ biến mất:\nKhông bao giờ: Bạn sẽ luôn thấy chuột.\nKhông hoạt động: Đặt một khoảng thời gian để nó biến mất sau khi không hoạt động.\nLuôn luôn: bạn sẽ không bao giờ thấy chuột. @@ -1289,7 +1302,7 @@ Update Available Có bản cập nhật - + Update Channel Kênh Cập Nhật diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index ac4117edc..8533c5456 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -52,6 +52,19 @@ 加载中... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - 选择文件目录 + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter 日志过滤 - + Input 输入 - + Cursor 光标 @@ -509,7 +522,7 @@ Enable NULL GPU 启用 NULL GPU - + Paths 路径 @@ -1083,7 +1096,7 @@ GUIgroupBox 播放标题音乐:\n如果游戏支持,在图形界面选择游戏时启用播放特殊音乐。 - + hideCursorGroupBox 隐藏光标:\n选择光标何时消失:\n从不: 您将始终看到鼠标。\n空闲: 设置光标在空闲后消失的时间。\n始终: 您将永远看不到鼠标。 @@ -1289,7 +1302,7 @@ Update Available 可用更新 - + Update Channel 更新频道 diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index c114c69c7..f3db48971 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -52,6 +52,19 @@ Loading... + + InstallDirSelect + + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + + Select which directory you want to install to. + Select which directory you want to install to. + + GameInstallDialog @@ -439,12 +452,12 @@ Log Filter Log Filter - + Input 輸入 - + Cursor 游標 @@ -509,7 +522,7 @@ Enable NULL GPU Enable NULL GPU - + Paths 路徑 @@ -1083,7 +1096,7 @@ GUIgroupBox 播放標題音樂:\n如果遊戲支持,啟用在GUI中選擇遊戲時播放特殊音樂。 - + hideCursorGroupBox 隱藏游標:\n選擇游標何時消失:\n從不: 您將始終看到滑鼠。\n閒置: 設定在閒置後消失的時間。\n始終: 您將永遠看不到滑鼠。 @@ -1289,7 +1302,7 @@ Update Available 可用更新 - + Update Channel 更新頻道 From 168ba8dbcd945a17949799ded49471e74b23e22f Mon Sep 17 00:00:00 2001 From: RDN000 <109141852+RDN000@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:35:19 +0200 Subject: [PATCH 65/93] Fix sq.ts (#1383) --- src/qt_gui/translations/sq.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 28e32aa07..fabc79cca 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -62,7 +62,7 @@ Select which directory you want to install to. - Select which directory you want to install to. + Përzgjidh në cilën dosje do që të instalosh. @@ -440,7 +440,7 @@ Logger - Regjistruesi i të dhënave + Regjistruesi i ditarit @@ -525,7 +525,7 @@ Paths - Rrugët + Shtigjet @@ -535,12 +535,12 @@ Add... - Shtoni... + Shto... Remove - Hiqni + Hiq @@ -1069,7 +1069,7 @@ discordRPCCheckbox - Aktivizo Discord Rich Presence:\nShfaq ikonën e emulatorit dhe informacionin përkatës në profilin tuaj në Discord. + Aktivizo Discord Rich Presence:\nShfaq ikonën e emulatorit dhe informacionin përkatës në profilin tënd në Discord. @@ -1089,7 +1089,7 @@ updaterGroupBox - Aktualizimi:\nRelease: Versionet zyrtare të lëshuara çdo muaj që mund të jenë shumë të vjetra, por janë më të besueshme dhe të provuara.\nNightly: Versionet e zhvillimit që kanë të gjitha veçoritë dhe rregullimet më të fundit, por mund të përmbajnë gabime dhe janë më pak të qëndrueshme. + Përditësimi:\nRelease: Versionet zyrtare të lëshuara çdo muaj që mund të jenë shumë të vjetra, por janë më të besueshme dhe të provuara.\nNightly: Versionet e zhvillimit që kanë të gjitha veçoritë dhe rregullimet më të fundit, por mund të përmbajnë gabime dhe janë më pak të qëndrueshme. @@ -1099,17 +1099,17 @@ hideCursorGroupBox - Fsheh kursori:\nZgjidhni kur do të zhduket kursori:\nKurrë: Do ta shihni gjithmonë maus.\nInaktiv: Vendosni një kohë për të zhdukur pas inaktivitetit.\nGjithmonë: nuk do ta shihni kurrë maus. + Fsheh kursorin:\nZgjidh kur do të fshihet kursori:\nKurrë: Do ta shohësh gjithmonë miun.\nInaktiv: Vendos një kohë për ta fshehur pas mosveprimit.\nGjithmonë: nuk do ta shohësh kurrë miun. idleTimeoutGroupBox - Koha për fshehjen e kursorit joaktiv:\Kohëzgjatja (në sekonda) pas së cilës kursori që ka nuk ka qënë në veprim fshihet. + Koha për fshehjen e kursorit joaktiv:\nKohëzgjatja (në sekonda) pas së cilës kursori që nuk ka qënë në veprim fshihet. backButtonBehaviorGroupBox - Sjellja e butonit mbrapa:\nLejon të përcaktohet se në cilën pjesë të tastierës prekëse do të imitojë një prekje butoni prapa. + Sjellja e butonit mbrapa:\nLejon të përcaktohet se në cilën pjesë të tastierës prekëse do të imitojë një prekje butoni mprapa. @@ -1119,7 +1119,7 @@ Idle - Pa vepruar + Joaktiv @@ -1174,17 +1174,17 @@ gameFoldersBox - Folderët e lojërave:\nLista e folderëve për të kontrolluar lojërat e instaluara. + Dosjet e lojërave:\nLista e dosjeve për të kontrolluar lojërat e instaluara. addFolderButton - Shto:\nShto një folder në listë. + Shto:\nShto një dosje në listë. removeFolderButton - Hiq:\nHiq një folder nga lista. + Hiq:\nHiq një dosje nga lista. @@ -1383,4 +1383,4 @@ Krijimi i skedarit skript të përditësimit dështoi - \ No newline at end of file + From 29ad2eca622cd75c15fb8416f7fb6939e638d62b Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Tue, 15 Oct 2024 11:40:37 -0300 Subject: [PATCH 66/93] Fix translation InstallDirSelect (#1386) --- src/qt_gui/install_dir_select.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/qt_gui/install_dir_select.h b/src/qt_gui/install_dir_select.h index fdadf2fe0..e3e81575a 100644 --- a/src/qt_gui/install_dir_select.h +++ b/src/qt_gui/install_dir_select.h @@ -12,6 +12,8 @@ class QLineEdit; class InstallDirSelect final : public QDialog { + Q_OBJECT + public: InstallDirSelect(); ~InstallDirSelect(); @@ -20,9 +22,6 @@ public: return selected_dir; } -private slots: - void BrowseGamesDirectory(); - private: QWidget* SetupInstallDirList(); QWidget* SetupDialogActions(); From a588bc5da85cc8413782148cca06d3d0c83d480a Mon Sep 17 00:00:00 2001 From: ElBread3 <92335081+ElBread3@users.noreply.github.com> Date: Tue, 15 Oct 2024 10:49:42 -0500 Subject: [PATCH 67/93] Separate Updates from Game Folder (#1026) * separate updates implementation * clang format clang format clang format clang format clang hates me work please * hotfix: check for sfo file instead of the folder * tiny change * refactor * forgot to change this over * add review changes * use operator --- src/common/config.cpp | 11 ++++++ src/common/config.h | 2 + src/core/file_sys/fs.cpp | 10 ++++- src/emulator.cpp | 5 ++- src/qt_gui/game_info.cpp | 2 +- src/qt_gui/game_info.h | 8 +++- src/qt_gui/gui_context_menus.h | 69 +++++++++++++++++++++++++++++++++- src/qt_gui/main_window.cpp | 26 +++++++++---- src/qt_gui/settings_dialog.cpp | 7 ++++ src/qt_gui/settings_dialog.ui | 7 ++++ 10 files changed, 135 insertions(+), 12 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 17862e6aa..1dde7223c 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -60,6 +60,7 @@ static bool vkMarkers = false; static bool vkCrashDiagnostic = false; static s16 cursorState = HideCursorState::Idle; static int cursorHideTimeout = 5; // 5 seconds (default) +static bool separateupdatefolder = false; // Gui std::vector settings_install_dirs = {}; @@ -207,6 +208,10 @@ bool vkCrashDiagnosticEnabled() { return vkCrashDiagnostic; } +bool getSeparateUpdateEnabled() { + return separateupdatefolder; +} + void setGpuId(s32 selectedGpuId) { gpuId = selectedGpuId; } @@ -319,6 +324,10 @@ void setSpecialPadClass(int type) { specialPadClass = type; } +void setSeparateUpdateEnabled(bool use) { + separateupdatefolder = use; +} + void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) { main_window_geometry_x = x; main_window_geometry_y = y; @@ -483,6 +492,7 @@ void load(const std::filesystem::path& path) { } isShowSplash = toml::find_or(general, "showSplash", true); isAutoUpdate = toml::find_or(general, "autoUpdate", false); + separateupdatefolder = toml::find_or(general, "separateUpdateEnabled", false); } if (data.contains("Input")) { @@ -597,6 +607,7 @@ void save(const std::filesystem::path& path) { data["General"]["updateChannel"] = updateChannel; data["General"]["showSplash"] = isShowSplash; data["General"]["autoUpdate"] = isAutoUpdate; + data["General"]["separateUpdateEnabled"] = separateupdatefolder; data["Input"]["cursorState"] = cursorState; data["Input"]["cursorHideTimeout"] = cursorHideTimeout; data["Input"]["backButtonBehavior"] = backButtonBehavior; diff --git a/src/common/config.h b/src/common/config.h index 591d6dced..9c71c96a8 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -19,6 +19,7 @@ bool isFullscreenMode(); bool getPlayBGM(); int getBGMvolume(); bool getEnableDiscordRPC(); +bool getSeparateUpdateEnabled(); std::string getLogFilter(); std::string getLogType(); @@ -62,6 +63,7 @@ void setLanguage(u32 language); void setNeoMode(bool enable); void setUserName(const std::string& type); void setUpdateChannel(const std::string& type); +void setSeparateUpdateEnabled(bool use); void setCursorState(s16 cursorState); void setCursorHideTimeout(int newcursorHideTimeout); diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index 3b060dd83..8e6d74622 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include "common/config.h" #include "common/string_util.h" #include "core/file_sys/fs.h" @@ -53,7 +54,14 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view guest_directory, b // Remove device (e.g /app0) from path to retrieve relative path. pos = mount->mount.size() + 1; const auto rel_path = std::string_view(corrected_path).substr(pos); - const auto host_path = mount->host_path / rel_path; + std::filesystem::path host_path = mount->host_path / rel_path; + + std::filesystem::path patch_path = mount->host_path; + patch_path += "-UPDATE"; + if (corrected_path.starts_with("/app0/") && std::filesystem::exists(patch_path / rel_path)) { + host_path = patch_path / rel_path; + } + if (!NeedsCaseInsensitiveSearch) { return host_path; } diff --git a/src/emulator.cpp b/src/emulator.cpp index 67aaa0492..46bcfea37 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -114,7 +114,10 @@ void Emulator::Run(const std::filesystem::path& file) { std::string app_version; u32 fw_version; - std::filesystem::path sce_sys_folder = file.parent_path() / "sce_sys"; + std::filesystem::path game_patch_folder = file.parent_path().concat("-UPDATE"); + bool use_game_patch = std::filesystem::exists(game_patch_folder / "sce_sys"); + std::filesystem::path sce_sys_folder = + use_game_patch ? game_patch_folder / "sce_sys" : file.parent_path() / "sce_sys"; if (std::filesystem::is_directory(sce_sys_folder)) { for (const auto& entry : std::filesystem::directory_iterator(sce_sys_folder)) { if (entry.path().filename() == "param.sfo") { diff --git a/src/qt_gui/game_info.cpp b/src/qt_gui/game_info.cpp index d82f43f20..48643f8ed 100644 --- a/src/qt_gui/game_info.cpp +++ b/src/qt_gui/game_info.cpp @@ -17,7 +17,7 @@ void GameInfoClass::GetGameInfo(QWidget* parent) { QDir parentFolder(installDir); QFileInfoList fileList = parentFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); for (const auto& fileInfo : fileList) { - if (fileInfo.isDir()) { + if (fileInfo.isDir() && !fileInfo.filePath().endsWith("-UPDATE")) { filePaths.append(fileInfo.absoluteFilePath()); } } diff --git a/src/qt_gui/game_info.h b/src/qt_gui/game_info.h index 8f65803bf..640b25e49 100644 --- a/src/qt_gui/game_info.h +++ b/src/qt_gui/game_info.h @@ -25,9 +25,15 @@ public: static GameInfo readGameInfo(const std::filesystem::path& filePath) { GameInfo game; game.path = filePath; + std::filesystem::path sce_folder_path = filePath / "sce_sys" / "param.sfo"; + std::filesystem::path game_update_path = + std::filesystem::path(filePath.string() + "-UPDATE"); + if (std::filesystem::exists(game_update_path / "sce_sys" / "param.sfo")) { + sce_folder_path = game_update_path / "sce_sys" / "param.sfo"; + } PSF psf; - if (psf.Open(game.path / "sce_sys" / "param.sfo")) { + if (psf.Open(sce_folder_path)) { game.icon_path = game.path / "sce_sys" / "icon0.png"; QString iconpath; Common::FS::PathToQString(iconpath, game.icon_path); diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 4eb657572..fba8616af 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -68,6 +68,18 @@ public: menu.addMenu(copyMenu); + // "Delete..." submenu. + QMenu* deleteMenu = new QMenu(tr("Delete..."), widget); + QAction* deleteGame = new QAction(tr("Delete Game"), widget); + QAction* deleteUpdate = new QAction(tr("Delete Update"), widget); + QAction* deleteDLC = new QAction(tr("Delete DLC"), widget); + + deleteMenu->addAction(deleteGame); + deleteMenu->addAction(deleteUpdate); + deleteMenu->addAction(deleteDLC); + + menu.addMenu(deleteMenu); + // Show menu. auto selected = menu.exec(global_pos); if (!selected) { @@ -82,7 +94,13 @@ public: if (selected == &openSfoViewer) { PSF psf; - if (psf.Open(std::filesystem::path(m_games[itemID].path) / "sce_sys" / "param.sfo")) { + QString game_update_path; + Common::FS::PathToQString(game_update_path, m_games[itemID].path.concat("-UPDATE")); + std::filesystem::path game_folder_path = m_games[itemID].path; + if (std::filesystem::exists(Common::FS::PathFromQString(game_update_path))) { + game_folder_path = Common::FS::PathFromQString(game_update_path); + } + if (psf.Open(game_folder_path / "sce_sys" / "param.sfo")) { int rows = psf.GetEntries().size(); QTableWidget* tableWidget = new QTableWidget(rows, 2); tableWidget->setAttribute(Qt::WA_DeleteOnClose); @@ -269,6 +287,55 @@ public: .arg(QString::fromStdString(m_games[itemID].size)); clipboard->setText(combinedText); } + + if (selected == deleteGame || selected == deleteUpdate || selected == deleteDLC) { + bool error = false; + QString folder_path, game_update_path; + Common::FS::PathToQString(folder_path, m_games[itemID].path); + Common::FS::PathToQString(game_update_path, m_games[itemID].path.concat("-UPDATE")); + QString message_type = tr("Game"); + if (selected == deleteUpdate) { + if (!Config::getSeparateUpdateEnabled()) { + QMessageBox::critical( + nullptr, tr("Error"), + QString(tr("This feature requires the 'Enable Separate Update Folder' " + "config option " + "to work. If you want to use this feature, please enable it."))); + error = true; + } else if (!std::filesystem::exists(m_games[itemID].path.concat("-UPDATE"))) { + QMessageBox::critical(nullptr, tr("Error"), + QString(tr("This game has no update to delete!"))); + error = true; + } else { + folder_path = game_update_path; + message_type = tr("Update"); + } + } else if (selected == deleteDLC) { + std::filesystem::path addon_path = + Config::getAddonInstallDir() / + Common::FS::PathFromQString(folder_path).parent_path().filename(); + if (!std::filesystem::exists(addon_path)) { + QMessageBox::critical(nullptr, tr("Error"), + QString(tr("This game has no DLC to delete!"))); + error = true; + } else { + folder_path = QString::fromStdString(addon_path.string()); + message_type = tr("DLC"); + } + } + if (!error) { + QString gameName = QString::fromStdString(m_games[itemID].name); + QDir dir(folder_path); + QMessageBox::StandardButton reply = QMessageBox::question( + nullptr, QString(tr("Delete %1")).arg(message_type), + QString(tr("Are you sure you want to delete %1's %2 directory?")) + .arg(gameName, message_type), + QMessageBox::Yes | QMessageBox::No); + if (reply == QMessageBox::Yes) { + dir.removeRecursively(); + } + } + } } int GetRowIndex(QTreeWidget* treeWidget, QTreeWidgetItem* item) { diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 025749dd4..d80102ff4 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -676,10 +676,17 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int InstallDirSelect ids; ids.exec(); auto game_install_dir = ids.getSelectedDirectory(); - auto extract_path = game_install_dir / pkg.GetTitleID(); + auto game_folder_path = game_install_dir / pkg.GetTitleID(); QString pkgType = QString::fromStdString(pkg.GetPkgFlags()); + bool use_game_update = pkgType.contains("Patch") && Config::getSeparateUpdateEnabled(); + auto game_update_path = use_game_update + ? game_install_dir / (std::string(pkg.GetTitleID()) + "-UPDATE") + : game_folder_path; + if (!std::filesystem::exists(game_update_path)) { + std::filesystem::create_directory(game_update_path); + } QString gameDirPath; - Common::FS::PathToQString(gameDirPath, extract_path); + Common::FS::PathToQString(gameDirPath, game_folder_path); QDir game_dir(gameDirPath); if (game_dir.exists()) { QMessageBox msgBox; @@ -715,7 +722,11 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int QMessageBox::critical(this, tr("PKG ERROR"), "PSF file there is no APP_VER"); return; } - psf.Open(extract_path / "sce_sys" / "param.sfo"); + std::filesystem::path sce_folder_path = + std::filesystem::exists(game_update_path / "sce_sys" / "param.sfo") + ? game_update_path / "sce_sys" / "param.sfo" + : game_folder_path / "sce_sys" / "param.sfo"; + psf.Open(sce_folder_path); QString game_app_version; if (auto app_ver = psf.GetString("APP_VER"); app_ver.has_value()) { game_app_version = QString::fromStdString(std::string{*app_ver}); @@ -764,7 +775,7 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int addonMsgBox.setDefaultButton(QMessageBox::No); int result = addonMsgBox.exec(); if (result == QMessageBox::Yes) { - extract_path = addon_extract_path; + game_update_path = addon_extract_path; } else { return; } @@ -775,12 +786,14 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int msgBox.setDefaultButton(QMessageBox::No); int result = msgBox.exec(); if (result == QMessageBox::Yes) { - extract_path = addon_extract_path; + game_update_path = addon_extract_path; } else { return; } } } else { + QString gameDirPath; + Common::FS::PathToQString(gameDirPath, game_folder_path); msgBox.setText(QString(tr("Game already installed") + "\n" + gameDirPath + "\n" + tr("Would you like to overwrite?"))); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); @@ -801,8 +814,7 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int } // what else? } - - if (!pkg.Extract(file, extract_path, failreason)) { + if (!pkg.Extract(file, game_update_path, failreason)) { QMessageBox::critical(this, tr("PKG ERROR"), QString::fromStdString(failreason)); } else { int nfiles = pkg.GetNumberOfFiles(); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index b63f14c05..77701f221 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -133,6 +133,9 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge connect(ui->ps4proCheckBox, &QCheckBox::stateChanged, this, [](int val) { Config::setNeoMode(val); }); + connect(ui->separateUpdatesCheckBox, &QCheckBox::stateChanged, this, + [](int val) { Config::setSeparateUpdateEnabled(val); }); + connect(ui->logTypeComboBox, &QComboBox::currentTextChanged, this, [](const QString& text) { Config::setLogType(text.toStdString()); }); @@ -270,6 +273,7 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge ui->showSplashCheckBox->installEventFilter(this); ui->ps4proCheckBox->installEventFilter(this); ui->discordRPCCheckbox->installEventFilter(this); + ui->separateUpdatesCheckBox->installEventFilter(this); ui->userName->installEventFilter(this); ui->logTypeGroupBox->installEventFilter(this); ui->logFilter->installEventFilter(this); @@ -328,6 +332,7 @@ void SettingsDialog::LoadValuesFromConfig() { ui->logTypeComboBox->setCurrentText(QString::fromStdString(Config::getLogType())); ui->logFilterLineEdit->setText(QString::fromStdString(Config::getLogFilter())); ui->userNameLineEdit->setText(QString::fromStdString(Config::getUserName())); + ui->separateUpdatesCheckBox->setChecked(Config::getSeparateUpdateEnabled()); ui->debugDump->setChecked(Config::debugDump()); ui->vkValidationCheckBox->setChecked(Config::vkValidationEnabled()); @@ -437,6 +442,8 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("ps4proCheckBox"); } else if (elementName == "discordRPCCheckbox") { text = tr("discordRPCCheckbox"); + } else if (elementName == "separateUpdatesCheckBox") { + text = tr("separateUpdatesCheckBox"); } else if (elementName == "userName") { text = tr("userName"); } else if (elementName == "logTypeGroupBox") { diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index b98fe228d..5d1225a1e 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -134,6 +134,13 @@ + + + + Enable Separate Update Folder + + + From 08343faf01b98888ab0fb97a205b78982140ab4f Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Tue, 15 Oct 2024 20:10:49 +0300 Subject: [PATCH 68/93] hot fix : put linux-qt in parallel3 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ee09163fd..6b87ec418 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -368,7 +368,7 @@ jobs: run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel3 - name: Run AppImage packaging script run: ./.github/linux-appimage-qt.sh From 6fc7b3993dad0cde955104b65b345411083d470e Mon Sep 17 00:00:00 2001 From: ElBread3 <92335081+ElBread3@users.noreply.github.com> Date: Tue, 15 Oct 2024 14:29:27 -0500 Subject: [PATCH 69/93] Minor Fixes for Separate Update Folder (#1387) * description + string fixes * fix use condition * clang format * updates now fully extract to game update folder * don't guarantee the overwrite game condition --- src/core/file_format/pkg.cpp | 4 +++- src/qt_gui/game_info.h | 4 ++-- src/qt_gui/gui_context_menus.h | 16 +++++++++------- src/qt_gui/main_window.cpp | 7 +------ src/qt_gui/translations/en.ts | 5 +++++ 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/core/file_format/pkg.cpp b/src/core/file_format/pkg.cpp index 7d36b019a..0ae9f57eb 100644 --- a/src/core/file_format/pkg.cpp +++ b/src/core/file_format/pkg.cpp @@ -3,6 +3,7 @@ #include #include "common/io_file.h" +#include "common/logging/formatter.h" #include "core/file_format/pkg.h" #include "core/file_format/pkg_type.h" @@ -349,7 +350,8 @@ bool PKG::Extract(const std::filesystem::path& filepath, const std::filesystem:: auto parent_path = extract_path.parent_path(); auto title_id = GetTitleID(); - if (parent_path.filename() != title_id) { + if (parent_path.filename() != title_id && + !fmt::UTF(extract_path.u8string()).data.ends_with("-UPDATE")) { extractPaths[ndinode_counter] = parent_path / title_id; } else { // DLCs path has different structure diff --git a/src/qt_gui/game_info.h b/src/qt_gui/game_info.h index 640b25e49..99805cd52 100644 --- a/src/qt_gui/game_info.h +++ b/src/qt_gui/game_info.h @@ -26,8 +26,8 @@ public: GameInfo game; game.path = filePath; std::filesystem::path sce_folder_path = filePath / "sce_sys" / "param.sfo"; - std::filesystem::path game_update_path = - std::filesystem::path(filePath.string() + "-UPDATE"); + std::filesystem::path game_update_path = filePath; + game_update_path += "-UPDATE"; if (std::filesystem::exists(game_update_path / "sce_sys" / "param.sfo")) { sce_folder_path = game_update_path / "sce_sys" / "param.sfo"; } diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index fba8616af..da3e32077 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -290,10 +290,14 @@ public: if (selected == deleteGame || selected == deleteUpdate || selected == deleteDLC) { bool error = false; - QString folder_path, game_update_path; + QString folder_path, game_update_path, dlc_path; Common::FS::PathToQString(folder_path, m_games[itemID].path); Common::FS::PathToQString(game_update_path, m_games[itemID].path.concat("-UPDATE")); + Common::FS::PathToQString( + dlc_path, Config::getAddonInstallDir() / + Common::FS::PathFromQString(folder_path).parent_path().filename()); QString message_type = tr("Game"); + if (selected == deleteUpdate) { if (!Config::getSeparateUpdateEnabled()) { QMessageBox::critical( @@ -302,7 +306,8 @@ public: "config option " "to work. If you want to use this feature, please enable it."))); error = true; - } else if (!std::filesystem::exists(m_games[itemID].path.concat("-UPDATE"))) { + } else if (!std::filesystem::exists( + Common::FS::PathFromQString(game_update_path))) { QMessageBox::critical(nullptr, tr("Error"), QString(tr("This game has no update to delete!"))); error = true; @@ -311,15 +316,12 @@ public: message_type = tr("Update"); } } else if (selected == deleteDLC) { - std::filesystem::path addon_path = - Config::getAddonInstallDir() / - Common::FS::PathFromQString(folder_path).parent_path().filename(); - if (!std::filesystem::exists(addon_path)) { + if (!std::filesystem::exists(Common::FS::PathFromQString(dlc_path))) { QMessageBox::critical(nullptr, tr("Error"), QString(tr("This game has no DLC to delete!"))); error = true; } else { - folder_path = QString::fromStdString(addon_path.string()); + folder_path = dlc_path; message_type = tr("DLC"); } } diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index d80102ff4..e52820102 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -678,13 +678,10 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int auto game_install_dir = ids.getSelectedDirectory(); auto game_folder_path = game_install_dir / pkg.GetTitleID(); QString pkgType = QString::fromStdString(pkg.GetPkgFlags()); - bool use_game_update = pkgType.contains("Patch") && Config::getSeparateUpdateEnabled(); + bool use_game_update = pkgType.contains("PATCH") && Config::getSeparateUpdateEnabled(); auto game_update_path = use_game_update ? game_install_dir / (std::string(pkg.GetTitleID()) + "-UPDATE") : game_folder_path; - if (!std::filesystem::exists(game_update_path)) { - std::filesystem::create_directory(game_update_path); - } QString gameDirPath; Common::FS::PathToQString(gameDirPath, game_folder_path); QDir game_dir(gameDirPath); @@ -792,8 +789,6 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int } } } else { - QString gameDirPath; - Common::FS::PathToQString(gameDirPath, game_folder_path); msgBox.setText(QString(tr("Game already installed") + "\n" + gameDirPath + "\n" + tr("Would you like to overwrite?"))); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 7c44fbdf4..ab7232978 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -1056,6 +1056,11 @@ fullscreenCheckBox Enable Full Screen:\nAutomatically puts the game window into full-screen mode.\nThis can be toggled by pressing the F11 key. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox From 437ebc1e02755c50a2b87f42d0d15d832a488011 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Tue, 15 Oct 2024 22:31:11 +0300 Subject: [PATCH 70/93] AvPlayer: Do not align w/h to 16 with vdec2 (#1388) --- src/core/libraries/avplayer/avplayer_impl.cpp | 2 +- src/core/libraries/avplayer/avplayer_impl.h | 1 - .../libraries/avplayer/avplayer_source.cpp | 61 +++++++++++++------ src/core/libraries/avplayer/avplayer_source.h | 3 +- .../libraries/avplayer/avplayer_state.cpp | 8 ++- src/core/libraries/avplayer/avplayer_state.h | 2 + 6 files changed, 54 insertions(+), 23 deletions(-) diff --git a/src/core/libraries/avplayer/avplayer_impl.cpp b/src/core/libraries/avplayer/avplayer_impl.cpp index d193e765f..1c414c961 100644 --- a/src/core/libraries/avplayer/avplayer_impl.cpp +++ b/src/core/libraries/avplayer/avplayer_impl.cpp @@ -112,7 +112,7 @@ AvPlayer::AvPlayer(const SceAvPlayerInitData& data) m_state(std::make_unique(m_init_data)) {} s32 AvPlayer::PostInit(const SceAvPlayerPostInitData& data) { - m_post_init_data = data; + m_state->PostInit(data); return ORBIS_OK; } diff --git a/src/core/libraries/avplayer/avplayer_impl.h b/src/core/libraries/avplayer/avplayer_impl.h index b6ad940c9..d7f28094e 100644 --- a/src/core/libraries/avplayer/avplayer_impl.h +++ b/src/core/libraries/avplayer/avplayer_impl.h @@ -56,7 +56,6 @@ private: SceAvPlayerInitData m_init_data{}; SceAvPlayerInitData m_init_data_original{}; - SceAvPlayerPostInitData m_post_init_data{}; std::mutex m_file_io_mutex{}; std::atomic_bool m_has_source{}; diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index fcae180e7..19925ba0c 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -37,7 +37,8 @@ namespace Libraries::AvPlayer { using namespace Kernel; -AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state) : m_state(state) {} +AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state, bool use_vdec2) + : m_state(state), m_use_vdec2(use_vdec2) {} AvPlayerSource::~AvPlayerSource() { Stop(); @@ -129,18 +130,25 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info LOG_WARNING(Lib_AvPlayer, "Stream {} language is unknown", stream_index); } switch (info.type) { - case SCE_AVPLAYER_VIDEO: + case SCE_AVPLAYER_VIDEO: { LOG_INFO(Lib_AvPlayer, "Stream {} is a video stream.", stream_index); info.details.video.aspect_ratio = f32(p_stream->codecpar->width) / p_stream->codecpar->height; - info.details.video.width = Common::AlignUp(u32(p_stream->codecpar->width), 16); - info.details.video.height = Common::AlignUp(u32(p_stream->codecpar->height), 16); + auto width = u32(p_stream->codecpar->width); + auto height = u32(p_stream->codecpar->height); + if (!m_use_vdec2) { + width = Common::AlignUp(width, 16); + height = Common::AlignUp(height, 16); + } + info.details.video.width = width; + info.details.video.height = height; if (p_lang_node != nullptr) { std::memcpy(info.details.video.language_code, p_lang_node->value, std::min(strlen(p_lang_node->value), size_t(3))); } break; - case SCE_AVPLAYER_AUDIO: + } + case SCE_AVPLAYER_AUDIO: { LOG_INFO(Lib_AvPlayer, "Stream {} is an audio stream.", stream_index); info.details.audio.channel_count = p_stream->codecpar->ch_layout.nb_channels; info.details.audio.sample_rate = p_stream->codecpar->sample_rate; @@ -150,7 +158,8 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info std::min(strlen(p_lang_node->value), size_t(3))); } break; - case SCE_AVPLAYER_TIMEDTEXT: + } + case SCE_AVPLAYER_TIMEDTEXT: { LOG_WARNING(Lib_AvPlayer, "Stream {} is a timedtext stream.", stream_index); info.details.subs.font_size = 12; info.details.subs.text_size = 12; @@ -159,10 +168,12 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info std::min(strlen(p_lang_node->value), size_t(3))); } break; - default: + } + default: { LOG_ERROR(Lib_AvPlayer, "Stream {} type is unknown: {}.", stream_index, info.type); return false; } + } return true; } @@ -189,8 +200,12 @@ bool AvPlayerSource::EnableStream(u32 stream_index) { LOG_ERROR(Lib_AvPlayer, "Could not open avcodec for video stream {}.", stream_index); return false; } - const auto width = Common::AlignUp(u32(m_video_codec_context->width), 16); - const auto height = Common::AlignUp(u32(m_video_codec_context->height), 16); + auto width = u32(m_video_codec_context->width); + auto height = u32(m_video_codec_context->height); + if (!m_use_vdec2) { + width = Common::AlignUp(width, 16); + height = Common::AlignUp(height, 16); + } const auto size = (width * height * 3) / 2; for (u64 index = 0; index < m_num_output_video_framebuffers; ++index) { m_video_buffers.Push(FrameBuffer(m_memory_replacement, 0x100, size)); @@ -316,7 +331,7 @@ bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) { auto frame = m_video_frames.Pop(); if (!frame.has_value()) { - LOG_WARNING(Lib_AvPlayer, "Could get video frame. EOF reached."); + LOG_TRACE(Lib_AvPlayer, "Could get video frame. EOF reached."); return false; } @@ -351,7 +366,7 @@ bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) { auto frame = m_audio_frames.Pop(); if (!frame.has_value()) { - LOG_WARNING(Lib_AvPlayer, "Could get audio frame. EOF reached."); + LOG_TRACE(Lib_AvPlayer, "Could get audio frame. EOF reached."); return false; } @@ -537,9 +552,13 @@ AvPlayerSource::AVFramePtr AvPlayerSource::ConvertVideoFrame(const AVFrame& fram return nv12_frame; } -static void CopyNV12Data(u8* dst, const AVFrame& src) { - const auto width = Common::AlignUp(u32(src.width), 16); - const auto height = Common::AlignUp(u32(src.height), 16); +static void CopyNV12Data(u8* dst, const AVFrame& src, bool use_vdec2) { + auto width = u32(src.width); + auto height = u32(src.height); + if (!use_vdec2) { + width = Common::AlignUp(width, 16); + height = Common::AlignUp(height, 16); + } if (src.width == width) { std::memcpy(dst, src.data[0], src.width * src.height); @@ -561,7 +580,7 @@ Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame ASSERT(frame.format == AV_PIX_FMT_NV12); auto p_buffer = buffer.GetBuffer(); - CopyNV12Data(p_buffer, frame); + CopyNV12Data(p_buffer, frame, m_use_vdec2); const auto pkt_dts = u64(frame.pkt_dts) * 1000; const auto stream = m_avformat_context->streams[m_video_stream_index.value()]; @@ -570,8 +589,12 @@ Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame const auto num = time_base.num; const auto timestamp = (num != 0 && den > 1) ? (pkt_dts * num) / den : pkt_dts; - const auto width = Common::AlignUp(u32(frame.width), 16); - const auto height = Common::AlignUp(u32(frame.height), 16); + auto width = u32(frame.width); + auto height = u32(frame.height); + if (!m_use_vdec2) { + width = Common::AlignUp(width, 16); + height = Common::AlignUp(height, 16); + } return Frame{ .buffer = std::move(buffer), @@ -583,8 +606,8 @@ Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame { .video = { - .width = u32(width), - .height = u32(height), + .width = width, + .height = height, .aspect_ratio = AVRationalToF32(frame.sample_aspect_ratio), .crop_left_offset = u32(frame.crop_left), .crop_right_offset = u32(frame.crop_right + (width - frame.width)), diff --git a/src/core/libraries/avplayer/avplayer_source.h b/src/core/libraries/avplayer/avplayer_source.h index 906122142..505d74465 100644 --- a/src/core/libraries/avplayer/avplayer_source.h +++ b/src/core/libraries/avplayer/avplayer_source.h @@ -120,7 +120,7 @@ private: class AvPlayerSource { public: - AvPlayerSource(AvPlayerStateCallback& state); + AvPlayerSource(AvPlayerStateCallback& state, bool use_vdec2); ~AvPlayerSource(); bool Init(const SceAvPlayerInitData& init_data, std::string_view path); @@ -168,6 +168,7 @@ private: Frame PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame); AvPlayerStateCallback& m_state; + bool m_use_vdec2 = false; SceAvPlayerMemAllocator m_memory_replacement{}; u32 m_num_output_video_framebuffers{}; diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index 5cabdba2c..e66100679 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -130,6 +130,10 @@ AvPlayerState::~AvPlayerState() { m_event_queue.Clear(); } +void AvPlayerState::PostInit(const SceAvPlayerPostInitData& post_init_data) { + m_post_init_data = post_init_data; +} + // Called inside GAME thread bool AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType source_type) { if (path.empty()) { @@ -144,7 +148,9 @@ bool AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType sourc return false; } - m_up_source = std::make_unique(*this); + m_up_source = std::make_unique( + *this, m_post_init_data.video_decoder_init.decoderType.video_type == + SCE_AVPLAYER_VIDEO_DECODER_TYPE_SOFTWARE2); if (!m_up_source->Init(m_init_data, path)) { SetState(AvState::Error); m_up_source.reset(); diff --git a/src/core/libraries/avplayer/avplayer_state.h b/src/core/libraries/avplayer/avplayer_state.h index 151eea52c..d106127e4 100644 --- a/src/core/libraries/avplayer/avplayer_state.h +++ b/src/core/libraries/avplayer/avplayer_state.h @@ -24,6 +24,7 @@ public: AvPlayerState(const SceAvPlayerInitData& init_data); ~AvPlayerState(); + void PostInit(const SceAvPlayerPostInitData& post_init_data); bool AddSource(std::string_view filename, SceAvPlayerSourceType source_type); s32 GetStreamCount(); bool GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info); @@ -68,6 +69,7 @@ private: std::unique_ptr m_up_source; SceAvPlayerInitData m_init_data{}; + SceAvPlayerPostInitData m_post_init_data{}; SceAvPlayerEventReplacement m_event_replacement{}; bool m_auto_start{}; u8 m_default_language[4]{}; From 170db22d9c8a6072f07839727e96b0831c56b31d Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Tue, 15 Oct 2024 14:55:03 -0500 Subject: [PATCH 71/93] Improve sceSystemServiceReceiveEvent stub (#992) --- src/core/libraries/error_codes.h | 1 + src/core/libraries/system/systemservice.cpp | 9 ++- src/core/libraries/system/systemservice.h | 63 ++++++++++++++++++++- 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/src/core/libraries/error_codes.h b/src/core/libraries/error_codes.h index 041870ed7..dae285664 100644 --- a/src/core/libraries/error_codes.h +++ b/src/core/libraries/error_codes.h @@ -438,6 +438,7 @@ constexpr int ORBIS_USER_SERVICE_ERROR_BUFFER_TOO_SHORT = 0x8096000A; // SystemService library constexpr int ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER = 0x80A10003; +constexpr int ORBIS_SYSTEM_SERVICE_ERROR_NO_EVENT = 0x80A10004; // NpTrophy library constexpr int ORBIS_NP_TROPHY_ERROR_UNKNOWN = 0x80551600; diff --git a/src/core/libraries/system/systemservice.cpp b/src/core/libraries/system/systemservice.cpp index 8002e2bfc..9c6fe8f2e 100644 --- a/src/core/libraries/system/systemservice.cpp +++ b/src/core/libraries/system/systemservice.cpp @@ -1942,9 +1942,12 @@ int PS4_SYSV_ABI sceSystemServiceRaiseExceptionLocalProcess() { return ORBIS_OK; } -int PS4_SYSV_ABI sceSystemServiceReceiveEvent() { - LOG_ERROR(Lib_SystemService, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceSystemServiceReceiveEvent(OrbisSystemServiceEvent* event) { + LOG_ERROR(Lib_SystemService, "(STUBBED) called, event type = {:#x}", (int)event->eventType); + if (event == nullptr) { + return ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER; + } + return ORBIS_SYSTEM_SERVICE_ERROR_NO_EVENT; } int PS4_SYSV_ABI sceSystemServiceReenableMusicPlayer() { diff --git a/src/core/libraries/system/systemservice.h b/src/core/libraries/system/systemservice.h index 1976606a8..56583c97e 100644 --- a/src/core/libraries/system/systemservice.h +++ b/src/core/libraries/system/systemservice.h @@ -87,6 +87,32 @@ enum OrbisSystemParamLanguage { ORBIS_SYSTEM_PARAM_LANG_INDONESIAN = 29 }; +enum OrbisSystemServiceEventType { + ORBIS_SYSTEM_SERVICE_EVENT_INVALID = -1, + ORBIS_SYSTEM_SERVICE_EVENT_ON_RESUME = 0x10000000, + ORBIS_SYSTEM_SERVICE_EVENT_GAME_LIVE_STREAMING_STATUS_UPDATE = 0x10000001, + ORBIS_SYSTEM_SERVICE_EVENT_SESSION_INVITATION = 0x10000002, + ORBIS_SYSTEM_SERVICE_EVENT_ENTITLEMENT_UPDATE = 0x10000003, + ORBIS_SYSTEM_SERVICE_EVENT_GAME_CUSTOM_DATA = 0x10000004, + ORBIS_SYSTEM_SERVICE_EVENT_DISPLAY_SAFE_AREA_UPDATE = 0x10000005, + ORBIS_SYSTEM_SERVICE_EVENT_URL_OPEN = 0x10000006, + ORBIS_SYSTEM_SERVICE_EVENT_LAUNCH_APP = 0x10000007, + ORBIS_SYSTEM_SERVICE_EVENT_APP_LAUNCH_LINK = 0x10000008, + ORBIS_SYSTEM_SERVICE_EVENT_ADDCONTENT_INSTALL = 0x10000009, + ORBIS_SYSTEM_SERVICE_EVENT_RESET_VR_POSITION = 0x1000000a, + ORBIS_SYSTEM_SERVICE_EVENT_JOIN_EVENT = 0x1000000b, + ORBIS_SYSTEM_SERVICE_EVENT_PLAYGO_LOCUS_UPDATE = 0x1000000c, + ORBIS_SYSTEM_SERVICE_EVENT_PLAY_TOGETHER_HOST = 0x1000000d, + ORBIS_SYSTEM_SERVICE_EVENT_SERVICE_ENTITLEMENT_UPDATE = 0x1000000e, + ORBIS_SYSTEM_SERVICE_EVENT_EYE_TO_EYE_DISTANCE_UPDATE = 0x1000000f, + ORBIS_SYSTEM_SERVICE_EVENT_JOIN_MATCH_EVENT = 0x10000010, + ORBIS_SYSTEM_SERVICE_EVENT_PLAY_TOGETHER_HOST_A = 0x10000011, + ORBIS_SYSTEM_SERVICE_EVENT_WEBBROWSER_CLOSED = 0x10000012, + ORBIS_SYSTEM_SERVICE_EVENT_CONTROLLER_SETTINGS_CLOSED = 0x10000013, + ORBIS_SYSTEM_SERVICE_EVENT_JOIN_TEAM_ON_TEAM_MATCH_EVENT = 0x10000014, + ORBIS_SYSTEM_SERVICE_EVENT_OPEN_SHARE_MENU = 0x30000000 +}; + struct OrbisSystemServiceStatus { s32 eventNum; bool isSystemUiOverlaid; @@ -102,6 +128,41 @@ struct OrbisSystemServiceDisplaySafeAreaInfo { uint8_t reserved[128]; }; +struct OrbisSystemServiceEvent { + OrbisSystemServiceEventType eventType; + union { + char param[8192]; + struct { + char source[1024]; + char url[4096]; + } urlOpen; + struct { + u32 size; + u8 arg[8188]; + } launchApp; + struct { + u32 size; + u8 arg[2020]; + } appLaunchLink; + struct { + s32 userId; + char eventId[37]; + char bootArgument[7169]; + } joinEvent; + struct { + s32 userId; + u32 npServiceLabel; + u8 reserved[8184]; + } serviceEntitlementUpdate; + struct { + s32 userId; + u32 npServiceLabel; + u8 reserved[8184]; + } unifiedEntitlementUpdate; + u8 reserved[8192]; + }; +}; + bool IsSplashVisible(); int PS4_SYSV_ABI sceAppMessagingClearEventFlag(); @@ -480,7 +541,7 @@ s32 PS4_SYSV_ABI sceSystemServiceParamGetInt(int param_id, int* value); int PS4_SYSV_ABI sceSystemServiceParamGetString(); int PS4_SYSV_ABI sceSystemServicePowerTick(); int PS4_SYSV_ABI sceSystemServiceRaiseExceptionLocalProcess(); -int PS4_SYSV_ABI sceSystemServiceReceiveEvent(); +s32 PS4_SYSV_ABI sceSystemServiceReceiveEvent(OrbisSystemServiceEvent* event); int PS4_SYSV_ABI sceSystemServiceReenableMusicPlayer(); int PS4_SYSV_ABI sceSystemServiceRegisterDaemon(); int PS4_SYSV_ABI sceSystemServiceReleaseFb0(); From 877cda9b9a93ae3652b394b655ab5f19263eed92 Mon Sep 17 00:00:00 2001 From: Lander Gallastegi Date: Wed, 16 Oct 2024 11:55:45 +0200 Subject: [PATCH 72/93] video_core: Rework clear values (#1381) * Clear color convertion * Add missing formats * Add swap handling * Format bits and offsets * clang-format * Make num_components const * Initialize alpha to 1 * Handle SnormNz as Snorm * Don0t leave accidental nonzero values * parallel3 for linux-qt * Move number_utils to common --- CMakeLists.txt | 7 +- externals/CMakeLists.txt | 8 +- src/common/number_utils.cpp | 161 ++++++++ src/common/number_utils.h | 28 ++ src/video_core/amdgpu/pixel_format.cpp | 36 +- .../renderer_vulkan/liverpool_to_vk.cpp | 383 ++++++++++++++++-- 6 files changed, 561 insertions(+), 62 deletions(-) create mode 100644 src/common/number_utils.cpp create mode 100644 src/common/number_utils.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 781e93e10..ff7717c76 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -432,6 +432,8 @@ set(COMMON src/common/logging/backend.cpp src/common/version.h src/common/ntapi.h src/common/ntapi.cpp + src/common/number_utils.h + src/common/number_utils.cpp src/common/memory_patcher.h src/common/memory_patcher.cpp src/common/scm_rev.cpp @@ -771,7 +773,7 @@ endif() create_target_directory_groups(shadps4) -target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn) +target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half) target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml) target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") @@ -794,9 +796,6 @@ if (APPLE) # Replacement for std::chrono::time_zone target_link_libraries(shadps4 PRIVATE date::date-tz) - - # Half float conversions for F16C patches - target_link_libraries(shadps4 PRIVATE half) endif() if (NOT ENABLE_QT_GUI) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 2f9336c21..9cae34381 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -141,11 +141,11 @@ if (WIN32) target_compile_options(sirit PUBLIC "-Wno-error=unused-command-line-argument") endif() -if (APPLE) - # half - add_library(half INTERFACE) - target_include_directories(half INTERFACE half/include) +# half +add_library(half INTERFACE) +target_include_directories(half INTERFACE half/include) +if (APPLE) # date if (NOT TARGET date::date-tz) option(BUILD_TZ_LIB "" ON) diff --git a/src/common/number_utils.cpp b/src/common/number_utils.cpp new file mode 100644 index 000000000..af29e5cd3 --- /dev/null +++ b/src/common/number_utils.cpp @@ -0,0 +1,161 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +#include "common/number_utils.h" +#include "video_core/amdgpu/pixel_format.h" +#include "video_core/amdgpu/types.h" + +#define UF11_EXPONENT_SHIFT 6 +#define UF10_EXPONENT_SHIFT 5 + +#define RGB9E5_MANTISSA_BITS 9 +#define RGB9E5_EXP_BIAS 1 + +#define F32_INFINITY 0x7f800000 + +namespace NumberUtils { + +float Uf11ToF32(u16 val) { + union { + float f; + u32 ui; + } f32; + + int exponent = (val & 0x07c0) >> UF11_EXPONENT_SHIFT; + int mantissa = (val & 0x003f); + + f32.f = 0.0; + + if (exponent == 0) { + if (mantissa != 0) { + const float scale = 1.0 / (1 << 20); + f32.f = scale * mantissa; + } + } else if (exponent == 31) { + f32.ui = F32_INFINITY | mantissa; + } else { + float scale, decimal; + exponent -= 15; + if (exponent < 0) { + scale = 1.0f / (1 << -exponent); + } else { + scale = (float)(1 << exponent); + } + decimal = 1.0f + (float)mantissa / 64; + f32.f = scale * decimal; + } + + return f32.f; +} + +float Uf10ToF32(u16 val) { + union { + float f; + u32 ui; + } f32; + + int exponent = (val & 0x03e0) >> UF10_EXPONENT_SHIFT; + int mantissa = (val & 0x001f); + + f32.f = 0.0; + + if (exponent == 0) { + if (mantissa != 0) { + const float scale = 1.0 / (1 << 19); + f32.f = scale * mantissa; + } + } else if (exponent == 31) { + f32.ui = F32_INFINITY | mantissa; + } else { + float scale, decimal; + exponent -= 15; + if (exponent < 0) { + scale = 1.0f / (1 << -exponent); + } else { + scale = (float)(1 << exponent); + } + decimal = 1.0f + (float)mantissa / 32; + f32.f = scale * decimal; + } + + return f32.f; +} + +float Uf16ToF32(u16 val) { + return half_float::half_cast(reinterpret_cast(val)); +} + +float U2ToUnorm(u8 val) { + static constexpr auto c = 1.0f / 3.0f; + return float(val * c); +} + +float S2ToSnorm(s8 val) { + static constexpr auto c = 1.0f / 1.0f; + return float(val * c); +} + +float U4ToUnorm(u8 val) { + static constexpr auto c = 1.0f / 15.0f; + return float(val * c); +} + +float S4ToSnorm(s8 val) { + static constexpr auto c = 1.0f / 7.0f; + return float(val * c); +} + +float U5ToUnorm(u8 val) { + static constexpr auto c = 1.0f / 31.0f; + return float(val * c); +} + +float S5ToSnorm(s8 val) { + static constexpr auto c = 1.0f / 15.0f; + return float(val * c); +} + +float U6ToUnorm(u8 val) { + static constexpr auto c = 1.0f / 63.0f; + return float(val * c); +} + +float S6ToSnorm(s8 val) { + static constexpr auto c = 1.0f / 31.0f; + return float(val * c); +} + +float U8ToUnorm(u8 val) { + static constexpr auto c = 1.0f / 255.0f; + return float(val * c); +} + +float S8ToSnorm(s8 val) { + static constexpr auto c = 1.0f / 127.0f; + return float(val * c); +} + +float U10ToUnorm(u16 val) { + static constexpr auto c = 1.0f / 1023.0f; + return float(val * c); +} + +float S10ToSnorm(s16 val) { + static constexpr auto c = 1.0f / 511.0f; + return float(val * c); +} + +float U16ToUnorm(u16 val) { + static constexpr auto c = 1.0f / 65535.0f; + return float(val * c); +} + +float S16ToSnorm(s16 val) { + static constexpr auto c = 1.0f / 32767.0f; + return float(val * c); +} + +} // namespace NumberUtils \ No newline at end of file diff --git a/src/common/number_utils.h b/src/common/number_utils.h new file mode 100644 index 000000000..05c62a1cb --- /dev/null +++ b/src/common/number_utils.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace NumberUtils { + +float Uf11ToF32(u16 val); +float Uf10ToF32(u16 val); +float Uf16ToF32(u16 val); +float U2ToUnorm(u8 val); +float S2ToSnorm(s8 val); +float U4ToUnorm(u8 val); +float S4ToSnorm(s8 val); +float U5ToUnorm(u8 val); +float S5ToSnorm(s8 val); +float U6ToUnorm(u8 val); +float S6ToSnorm(s8 val); +float U8ToUnorm(u8 val); +float S8ToSnorm(s8 val); +float U10ToUnorm(u16 val); +float S10ToSnorm(s16 val); +float U16ToUnorm(u16 val); +float S16ToSnorm(s16 val); + +} // namespace NumberUtils diff --git a/src/video_core/amdgpu/pixel_format.cpp b/src/video_core/amdgpu/pixel_format.cpp index 6744891a6..b13fc2d11 100644 --- a/src/video_core/amdgpu/pixel_format.cpp +++ b/src/video_core/amdgpu/pixel_format.cpp @@ -144,10 +144,10 @@ static constexpr std::array component_bits = { std::array{8, 8, 0, 0}, // 3 Format8_8 std::array{32, 0, 0, 0}, // 4 Format32 std::array{16, 16, 0, 0}, // 5 Format16_16 - std::array{10, 11, 11, 0}, // 6 Format10_11_11 - std::array{11, 11, 10, 0}, // 7 Format11_11_10 - std::array{10, 10, 10, 2}, // 8 Format10_10_10_2 - std::array{2, 10, 10, 10}, // 9 Format2_10_10_10 + std::array{11, 11, 10, 0}, // 6 Format10_11_11 + std::array{10, 11, 11, 0}, // 7 Format11_11_10 + std::array{2, 10, 10, 10}, // 8 Format10_10_10_2 + std::array{10, 10, 10, 2}, // 9 Format2_10_10_10 std::array{8, 8, 8, 8}, // 10 Format8_8_8_8 std::array{32, 32, 0, 0}, // 11 Format32_32 std::array{16, 16, 16, 16}, // 12 Format16_16_16_16 @@ -155,12 +155,12 @@ static constexpr std::array component_bits = { std::array{32, 32, 32, 32}, // 14 Format32_32_32_32 std::array{0, 0, 0, 0}, // 15 std::array{5, 6, 5, 0}, // 16 Format5_6_5 - std::array{1, 5, 5, 5}, // 17 Format1_5_5_5 - std::array{5, 5, 5, 1}, // 18 Format5_5_5_1 + std::array{5, 5, 5, 1}, // 17 Format1_5_5_5 + std::array{1, 5, 5, 5}, // 18 Format5_5_5_1 std::array{4, 4, 4, 4}, // 19 Format4_4_4_4 - std::array{8, 24, 0, 0}, // 20 Format8_24 - std::array{24, 8, 0, 0}, // 21 Format24_8 - std::array{24, 8, 0, 0}, // 22 FormatX24_8_32 + std::array{24, 8, 0, 0}, // 20 Format8_24 + std::array{8, 24, 0, 0}, // 21 Format24_8 + std::array{8, 24, 0, 0}, // 22 FormatX24_8_32 std::array{0, 0, 0, 0}, // 23 std::array{0, 0, 0, 0}, // 24 std::array{0, 0, 0, 0}, // 25 @@ -197,10 +197,10 @@ static constexpr std::array component_offset = { std::array{0, 8, -1, -1}, // 3 Format8_8 std::array{0, -1, -1, -1}, // 4 Format32 std::array{0, 16, -1, -1}, // 5 Format16_16 - std::array{0, 10, 21, -1}, // 6 Format10_11_11 - std::array{0, 11, 22, -1}, // 7 Format11_11_10 - std::array{0, 10, 20, 30}, // 8 Format10_10_10_2 - std::array{0, 2, 12, 22}, // 9 Format2_10_10_10 + std::array{0, 11, 22, -1}, // 6 Format10_11_11 + std::array{0, 10, 21, -1}, // 7 Format11_11_10 + std::array{0, 2, 12, 22}, // 8 Format10_10_10_2 + std::array{0, 10, 20, 30}, // 9 Format2_10_10_10 std::array{0, 8, 16, 24}, // 10 Format8_8_8_8 std::array{0, 32, -1, -1}, // 11 Format32_32 std::array{0, 16, 32, 48}, // 12 Format16_16_16_16 @@ -208,12 +208,12 @@ static constexpr std::array component_offset = { std::array{0, 32, 64, 96}, // 14 Format32_32_32_32 std::array{-1, -1, -1, -1}, // 15 std::array{0, 5, 11, -1}, // 16 Format5_6_5 - std::array{0, 1, 6, 11}, // 17 Format1_5_5_5 - std::array{0, 5, 10, 15}, // 18 Format5_5_5_1 + std::array{0, 5, 10, 15}, // 17 Format1_5_5_5 + std::array{0, 1, 6, 11}, // 18 Format5_5_5_1 std::array{0, 4, 8, 12}, // 19 Format4_4_4_4 - std::array{0, 8, -1, -1}, // 20 Format8_24 - std::array{0, 24, -1, -1}, // 21 Format24_8 - std::array{0, 24, -1, -1}, // 22 FormatX24_8_32 + std::array{0, 24, -1, -1}, // 20 Format8_24 + std::array{0, 8, -1, -1}, // 21 Format24_8 + std::array{0, 8, -1, -1}, // 22 FormatX24_8_32 std::array{-1, -1, -1, -1}, // 23 std::array{-1, -1, -1, -1}, // 24 std::array{-1, -1, -1, -1}, // 25 diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index a68ec1e74..3e43c50e5 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -2,11 +2,15 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" +#include "common/number_utils.h" #include "video_core/amdgpu/pixel_format.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" #include +#define INVALID_NUMBER_FORMAT_COMBO \ + LOG_ERROR(Render_Vulkan, "Unsupported number type {} for format {}", number_type, format); + namespace Vulkan::LiverpoolToVK { using DepthBuffer = Liverpool::DepthBuffer; @@ -725,55 +729,362 @@ void EmitQuadToTriangleListIndices(u8* out_ptr, u32 num_vertices) { } } -static constexpr float U8ToUnorm(u8 v) { - static constexpr auto c = 1.0f / 255.0f; - return float(v * c); -} - vk::ClearValue ColorBufferClearValue(const AmdGpu::Liverpool::ColorBuffer& color_buffer) { const auto comp_swap = color_buffer.info.comp_swap.Value(); - ASSERT_MSG(comp_swap == Liverpool::ColorBuffer::SwapMode::Standard || - comp_swap == Liverpool::ColorBuffer::SwapMode::Alternate, - "Unsupported component swap mode {}", static_cast(comp_swap)); - - const bool comp_swap_alt = comp_swap == Liverpool::ColorBuffer::SwapMode::Alternate; + const auto format = color_buffer.info.format.Value(); + const auto number_type = color_buffer.info.number_type.Value(); const auto& c0 = color_buffer.clear_word0; const auto& c1 = color_buffer.clear_word1; const auto num_bits = AmdGpu::NumBits(color_buffer.info.format); + const auto num_components = AmdGpu::NumComponents(format); + + const bool comp_swap_alt = + comp_swap == AmdGpu::Liverpool::ColorBuffer::SwapMode::Alternate || + comp_swap == AmdGpu::Liverpool::ColorBuffer::SwapMode::AlternateReverse; + const bool comp_swap_reverse = + comp_swap == AmdGpu::Liverpool::ColorBuffer::SwapMode::StandardReverse || + comp_swap == AmdGpu::Liverpool::ColorBuffer::SwapMode::AlternateReverse; vk::ClearColorValue color{}; - switch (color_buffer.info.number_type) { - case AmdGpu::NumberFormat::Snorm: - [[fallthrough]]; - case AmdGpu::NumberFormat::SnormNz: - [[fallthrough]]; - case AmdGpu::NumberFormat::Unorm: - [[fallthrough]]; - case AmdGpu::NumberFormat::Srgb: { - switch (num_bits) { - case 32: { - color.float32 = std::array{ - U8ToUnorm((c0 >> (comp_swap_alt ? 16 : 0)) & 0xff), - U8ToUnorm((c0 >> 8) & 0xff), - U8ToUnorm((c0 >> (comp_swap_alt ? 0 : 16)) & 0xff), - U8ToUnorm((c0 >> 24) & 0xff), - }; - break; - } - default: { - LOG_ERROR(Render_Vulkan, "Missing clear color conversion for bits {}", num_bits); - break; - } - } + + switch (number_type) { + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[3] = 1; + break; + default: + color.float32[3] = 1.0f; break; } - default: { - LOG_ERROR(Render_Vulkan, "Missing clear color conversion for type {}", - color_buffer.info.number_type.Value()); + + switch (format) { + case AmdGpu::DataFormat::Format8: + switch (number_type) { + case AmdGpu::NumberFormat::Unorm: + case AmdGpu::NumberFormat::Srgb: // Should we handle gamma correction here? + color.float32[0] = NumberUtils::U8ToUnorm(c0 & 0xff); + break; + break; + case AmdGpu::NumberFormat::Snorm: + case AmdGpu::NumberFormat::SnormNz: + color.float32[0] = NumberUtils::S8ToSnorm(c0 & 0xff); + break; + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0; + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format16: + switch (number_type) { + case AmdGpu::NumberFormat::Unorm: + color.float32[0] = NumberUtils::U16ToUnorm(c0 & 0xffff); + break; + case AmdGpu::NumberFormat::Snorm: + case AmdGpu::NumberFormat::SnormNz: + color.float32[0] = NumberUtils::S16ToSnorm(c0 & 0xffff); + break; + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0; + break; + case AmdGpu::NumberFormat::Float: + color.float32[0] = NumberUtils::Uf16ToF32(c0 & 0xffff); + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format8_8: + switch (number_type) { + case AmdGpu::NumberFormat::Unorm: + case AmdGpu::NumberFormat::Srgb: // Should we handle gamma correction here? + color.float32[0] = NumberUtils::U8ToUnorm(c0 & 0xff); + color.float32[1] = NumberUtils::U8ToUnorm((c0 >> 8) & 0xff); + break; + case AmdGpu::NumberFormat::Snorm: + case AmdGpu::NumberFormat::SnormNz: + color.float32[0] = NumberUtils::S8ToSnorm(c0 & 0xff); + color.float32[1] = NumberUtils::S8ToSnorm((c0 >> 8) & 0xff); + break; + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0 & 0xff; + color.uint32[1] = (c0 >> 8) & 0xff; + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format32: + switch (number_type) { + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0; + break; + case AmdGpu::NumberFormat::Float: + color.float32[0] = *(reinterpret_cast(&c0)); + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format16_16: + switch (number_type) { + case AmdGpu::NumberFormat::Unorm: + color.float32[0] = NumberUtils::U16ToUnorm(c0 & 0xffff); + color.float32[1] = NumberUtils::U16ToUnorm((c0 >> 16) & 0xffff); + break; + case AmdGpu::NumberFormat::Snorm: + case AmdGpu::NumberFormat::SnormNz: + color.float32[0] = NumberUtils::S16ToSnorm(c0 & 0xffff); + color.float32[1] = NumberUtils::S16ToSnorm((c0 >> 16) & 0xffff); + break; + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0 & 0xffff; + color.uint32[1] = (c0 >> 16) & 0xffff; + break; + case AmdGpu::NumberFormat::Float: + color.float32[0] = NumberUtils::Uf16ToF32(c0 & 0xffff); + color.float32[1] = NumberUtils::Uf16ToF32((c0 >> 16) & 0xffff); + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format10_11_11: + color.float32[0] = NumberUtils::Uf11ToF32(c0 & 0x7ff); + color.float32[1] = NumberUtils::Uf11ToF32((c0 >> 11) & 0x7ff); + color.float32[2] = NumberUtils::Uf10ToF32((c0 >> 22) & 0x3ff); + break; + case AmdGpu::DataFormat::Format11_11_10: + color.float32[0] = NumberUtils::Uf10ToF32(c0 & 0x3ff); + color.float32[1] = NumberUtils::Uf11ToF32((c0 >> 10) & 0x7ff); + color.float32[2] = NumberUtils::Uf11ToF32((c0 >> 21) & 0x7ff); + break; + case AmdGpu::DataFormat::Format5_9_9_9: { + int exponent; + union { + float f; + u32 u; + } scale; + + exponent = (c0 >> 27) - 10; + scale.u = (exponent + 127) << 23; + + color.float32[0] = (c0 & 0x1ff) * scale.f; + color.float32[1] = ((c0 >> 9) & 0x1ff) * scale.f; + color.float32[2] = ((c0 >> 18) & 0x1ff) * scale.f; break; } + case AmdGpu::DataFormat::Format10_10_10_2: + switch (number_type) { + case AmdGpu::NumberFormat::Unorm: + color.float32[0] = NumberUtils::U2ToUnorm(c0 & 0x3); + color.float32[1] = NumberUtils::U10ToUnorm((c0 >> 2) & 0x3ff); + color.float32[2] = NumberUtils::U10ToUnorm((c0 >> 12) & 0x3ff); + color.float32[3] = NumberUtils::U10ToUnorm(c0 >> 22); + break; + case AmdGpu::NumberFormat::Snorm: + case AmdGpu::NumberFormat::SnormNz: + color.float32[0] = NumberUtils::S2ToSnorm(c0 & 0x3); + color.float32[1] = NumberUtils::S10ToSnorm((c0 >> 2) & 0x3ff); + color.float32[2] = NumberUtils::S10ToSnorm((c0 >> 12) & 0x3ff); + color.float32[3] = NumberUtils::S2ToSnorm(c0 >> 22); + break; + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0 & 0x3; + color.uint32[1] = (c0 >> 2) & 0x3ff; + color.uint32[2] = (c0 >> 12) & 0x3ff; + color.uint32[3] = c0 >> 22; + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format2_10_10_10: + switch (number_type) { + case AmdGpu::NumberFormat::Unorm: + color.float32[0] = NumberUtils::U10ToUnorm(c0 & 0x3ff); + color.float32[1] = NumberUtils::U10ToUnorm((c0 >> 10) & 0x3ff); + color.float32[2] = NumberUtils::U10ToUnorm((c0 >> 20) & 0x3ff); + color.float32[3] = NumberUtils::U2ToUnorm(c0 >> 30); + break; + case AmdGpu::NumberFormat::Snorm: + case AmdGpu::NumberFormat::SnormNz: + color.float32[0] = NumberUtils::S10ToSnorm(c0 & 0x3ff); + color.float32[1] = NumberUtils::S10ToSnorm((c0 >> 10) & 0x3ff); + color.float32[2] = NumberUtils::S10ToSnorm((c0 >> 20) & 0x3ff); + color.float32[3] = NumberUtils::S2ToSnorm(c0 >> 30); + break; + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0 & 0x3ff; + color.uint32[1] = (c0 >> 10) & 0x3ff; + color.uint32[2] = (c0 >> 20) & 0x3ff; + color.uint32[3] = c0 >> 30; + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format8_8_8_8: + switch (number_type) { + case AmdGpu::NumberFormat::Unorm: + case AmdGpu::NumberFormat::Srgb: // Should we handle gamma correction here? + color.float32[0] = NumberUtils::U8ToUnorm(c0 & 0xff); + color.float32[1] = NumberUtils::U8ToUnorm((c0 >> 8) & 0xff); + color.float32[2] = NumberUtils::U8ToUnorm((c0 >> 16) & 0xff); + color.float32[3] = NumberUtils::U8ToUnorm(c0 >> 24); + break; + case AmdGpu::NumberFormat::Snorm: + case AmdGpu::NumberFormat::SnormNz: + color.float32[0] = NumberUtils::S8ToSnorm(c0 & 0xff); + color.float32[1] = NumberUtils::S8ToSnorm((c0 >> 8) & 0xff); + color.float32[2] = NumberUtils::S8ToSnorm((c0 >> 16) & 0xff); + color.float32[3] = NumberUtils::S8ToSnorm(c0 >> 24); + break; + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0 & 0xff; + color.uint32[1] = (c0 >> 8) & 0xff; + color.uint32[2] = (c0 >> 16) & 0xff; + color.uint32[3] = c0 >> 24; + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format32_32: + switch (number_type) { + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0; + color.uint32[1] = c1; + break; + case AmdGpu::NumberFormat::Float: + color.float32[0] = *(reinterpret_cast(&c0)); + color.float32[1] = *(reinterpret_cast(&c1)); + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format16_16_16_16: + switch (number_type) { + case AmdGpu::NumberFormat::Unorm: + color.float32[0] = NumberUtils::U16ToUnorm(c0 & 0xffff); + color.float32[1] = NumberUtils::U16ToUnorm((c0 >> 16) & 0xffff); + color.float32[2] = NumberUtils::U16ToUnorm(c1 & 0xffff); + color.float32[3] = NumberUtils::U16ToUnorm((c1 >> 16) & 0xffff); + break; + case AmdGpu::NumberFormat::Snorm: + case AmdGpu::NumberFormat::SnormNz: + color.float32[0] = NumberUtils::S16ToSnorm(c0 & 0xffff); + color.float32[1] = NumberUtils::S16ToSnorm((c0 >> 16) & 0xffff); + color.float32[2] = NumberUtils::S16ToSnorm(c1 & 0xffff); + color.float32[3] = NumberUtils::S16ToSnorm((c1 >> 16) & 0xffff); + break; + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0 & 0xffff; + color.uint32[1] = (c0 >> 16) & 0xffff; + color.uint32[2] = c1 & 0xffff; + color.uint32[3] = (c1 >> 16) & 0xffff; + break; + case AmdGpu::NumberFormat::Float: + color.float32[0] = NumberUtils::Uf16ToF32(c0 & 0xffff); + color.float32[1] = NumberUtils::Uf16ToF32((c0 >> 16) & 0xffff); + color.float32[2] = NumberUtils::Uf16ToF32(c1 & 0xffff); + color.float32[3] = NumberUtils::Uf16ToF32((c1 >> 16) & 0xffff); + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format32_32_32_32: + switch (number_type) { + case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberFormat::Sint: + color.uint32[0] = c0; + color.uint32[1] = c0; + color.uint32[2] = c0; + color.uint32[3] = c1; + break; + case AmdGpu::NumberFormat::Float: + color.float32[0] = *(reinterpret_cast(&c0)); + color.float32[1] = *(reinterpret_cast(&c0)); + color.float32[2] = *(reinterpret_cast(&c0)); + color.float32[3] = *(reinterpret_cast(&c1)); + break; + default: + INVALID_NUMBER_FORMAT_COMBO; + break; + } + break; + case AmdGpu::DataFormat::Format5_6_5: + color.float32[0] = NumberUtils::U5ToUnorm(c0 & 0x1f); + color.float32[1] = NumberUtils::U6ToUnorm((c0 >> 5) & 0x3f); + color.float32[2] = NumberUtils::U5ToUnorm(c0 >> 11); + break; + case AmdGpu::DataFormat::Format1_5_5_5: + color.float32[0] = NumberUtils::U5ToUnorm(c0 & 0x1f); + color.float32[1] = NumberUtils::U5ToUnorm((c0 >> 5) & 0x1f); + color.float32[2] = NumberUtils::U5ToUnorm((c0 >> 10) & 0x1f); + color.float32[3] = (c0 >> 15) ? 1.0f : 0.0f; + break; + case AmdGpu::DataFormat::Format5_5_5_1: + color.float32[0] = (c0 & 0x1) ? 1.0f : 0.0f; + color.float32[1] = NumberUtils::U5ToUnorm((c0 >> 1) & 0x1f); + color.float32[2] = NumberUtils::U5ToUnorm((c0 >> 6) & 0x1f); + color.float32[3] = NumberUtils::U5ToUnorm((c0 >> 11) & 0x1f); + break; + case AmdGpu::DataFormat::Format4_4_4_4: + color.float32[0] = NumberUtils::U4ToUnorm(c0 & 0xf); + color.float32[1] = NumberUtils::U4ToUnorm((c0 >> 4) & 0xf); + color.float32[2] = NumberUtils::U4ToUnorm((c0 >> 8) & 0xf); + color.float32[3] = NumberUtils::U4ToUnorm(c0 >> 12); + break; + default: + LOG_ERROR(Render_Vulkan, "Unsupported color buffer format: {}", format); + break; } + + if (num_components == 1) { + if (comp_swap != Liverpool::ColorBuffer::SwapMode::Standard) { + color.float32[static_cast(comp_swap)] = color.float32[0]; + color.float32[0] = 0.0f; + } + } else { + if (comp_swap_alt && num_components == 4) { + std::swap(color.float32[0], color.float32[2]); + } + + if (comp_swap_reverse) { + std::reverse(std::begin(color.float32), std::begin(color.float32) + num_components); + } + + if (comp_swap_alt && num_components != 4) { + color.float32[3] = color.float32[num_components - 1]; + color.float32[num_components - 1] = 0.0f; + } + } + return {.color = color}; } From 25de4d6b653187386e4f8da0532525fcaca28f57 Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Wed, 16 Oct 2024 07:12:46 -0300 Subject: [PATCH 73/93] Devtools improvements I (#1392) * devtools: fix showing entire depth instead of bits * devtools: show button for stage instead of menu bar - fix batch view dockspace not rendering when window collapsed * devtools: removed useless "Batch" collapse & don't collapse last batch * devtools: refactor DrawRow to templating * devtools: reg popup size adjusted to the content * devtools: better window names * devtools: regview layout compacted * devtools: option to show collapsed frame dump keep most popups open when selection changes best popup windows positioning * devtools: show compute shader regs * devtools: tips popup --- src/common/bit_field.h | 5 +- src/core/debug_state.cpp | 33 ++- src/core/debug_state.h | 16 +- src/core/devtools/help.txt | 8 + src/core/devtools/layer.cpp | 35 +++ src/core/devtools/options.cpp | 6 + src/core/devtools/options.h | 3 +- src/core/devtools/widget/cmd_list.cpp | 47 ++-- src/core/devtools/widget/cmd_list.h | 4 +- src/core/devtools/widget/common.h | 23 +- src/core/devtools/widget/frame_dump.cpp | 63 ++++-- src/core/devtools/widget/frame_dump.h | 1 + src/core/devtools/widget/reg_popup.cpp | 172 ++++++++------- src/core/devtools/widget/reg_popup.h | 18 +- src/core/devtools/widget/reg_view.cpp | 218 ++++++++++++++----- src/core/devtools/widget/reg_view.h | 11 +- src/core/libraries/dialogs/ime_dialog_ui.cpp | 2 +- src/imgui/imgui_std.h | 4 +- src/video_core/amdgpu/liverpool.cpp | 8 +- 19 files changed, 474 insertions(+), 203 deletions(-) create mode 100644 src/core/devtools/help.txt diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 72af7b5cd..bc8a2763c 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h @@ -81,7 +81,9 @@ #pragma pack(1) template struct BitField { -private: + + using Type = T; + // UnderlyingType is T for non-enum types and the underlying type of T if // T is an enumeration. Note that T is wrapped within an enable_if in the // former case to workaround compile errors which arise when using @@ -92,7 +94,6 @@ private: // We store the value as the unsigned type to avoid undefined behaviour on value shifting using StorageType = std::make_unsigned_t; -public: /// Constants to allow limited introspection of fields if needed static constexpr std::size_t position = Position; static constexpr std::size_t bits = Bits; diff --git a/src/core/debug_state.cpp b/src/core/debug_state.cpp index 93b00285d..adcb0cadb 100644 --- a/src/core/debug_state.cpp +++ b/src/core/debug_state.cpp @@ -102,6 +102,10 @@ void DebugStateImpl::RequestFrameDump(s32 count) { gnm_frame_dump_request_count = count; frame_dump_list.clear(); frame_dump_list.resize(count); + const auto f = gnm_frame_count.load() + 1; + for (size_t i = 0; i < count; ++i) { + frame_dump_list[i].frame_id = f + i; + } waiting_submit_pause = true; } @@ -139,7 +143,7 @@ void DebugStateImpl::PushQueueDump(QueueDump dump) { } void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, - const AmdGpu::Liverpool::Regs& regs) { + const AmdGpu::Liverpool::Regs& regs, bool is_compute) { std::scoped_lock lock{frame_dump_list_mutex}; const auto it = waiting_reg_dumps.find(header_addr); if (it == waiting_reg_dumps.end()) { @@ -150,15 +154,24 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, waiting_reg_dumps_dbg.erase(waiting_reg_dumps_dbg.find(header_addr)); auto& dump = frame.regs[header_addr - base_addr]; dump.regs = regs; - for (int i = 0; i < RegDump::MaxShaderStages; i++) { - if (regs.stage_enable.IsStageEnabled(i)) { - auto stage = regs.ProgramForStage(i); - if (stage->address_lo != 0) { - auto code = stage->Code(); - dump.stages[i] = ShaderDump{ - .user_data = *stage, - .code = std::vector{code.begin(), code.end()}, - }; + if (is_compute) { + dump.is_compute = true; + const auto& cs = dump.regs.cs_program; + dump.cs_data = ComputerShaderDump{ + .cs_program = cs, + .code = std::vector{cs.Code().begin(), cs.Code().end()}, + }; + } else { + for (int i = 0; i < RegDump::MaxShaderStages; i++) { + if (regs.stage_enable.IsStageEnabled(i)) { + auto stage = regs.ProgramForStage(i); + if (stage->address_lo != 0) { + auto code = stage->Code(); + dump.stages[i] = ShaderDump{ + .user_data = *stage, + .code = std::vector{code.begin(), code.end()}, + }; + } } } } diff --git a/src/core/debug_state.h b/src/core/debug_state.h index 26dfa202e..cd1c6aa93 100644 --- a/src/core/debug_state.h +++ b/src/core/debug_state.h @@ -36,9 +36,9 @@ class FrameGraph; namespace DebugStateType { enum class QueueType { - acb, - dcb, - ccb, + dcb = 0, + ccb = 1, + acb = 2, }; struct QueueDump { @@ -54,13 +54,21 @@ struct ShaderDump { std::vector code{}; }; +struct ComputerShaderDump { + Vulkan::Liverpool::ComputeProgram cs_program{}; + std::vector code{}; +}; + struct RegDump { + bool is_compute{false}; static constexpr size_t MaxShaderStages = 5; Vulkan::Liverpool::Regs regs{}; std::array stages{}; + ComputerShaderDump cs_data{}; }; struct FrameDump { + u32 frame_id; std::vector queues; std::unordered_map regs; // address -> reg dump }; @@ -143,7 +151,7 @@ public: void PushQueueDump(QueueDump dump); void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, - const AmdGpu::Liverpool::Regs& regs); + const AmdGpu::Liverpool::Regs& regs, bool is_compute = false); }; } // namespace DebugStateType diff --git a/src/core/devtools/help.txt b/src/core/devtools/help.txt new file mode 100644 index 000000000..9670c5cea --- /dev/null +++ b/src/core/devtools/help.txt @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +R"( +* If you hold shift, you can move the window without docking it. +* You don't need to close every window you open. When a parent window is closed, all its children will be closed too. +* If you want to inspect or compare more than 1 frame dump without undocking, there's a option to keep showing opened popups even when in hide/minimize the frame dump window. +* To use the disassembly viewer, you need to set up a cli to use a external disassembler and use "{src}" as a placeholder for the source code file, e.g. dis.exe --some-opt "{src}" +)" \ No newline at end of file diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index 17ef43bbc..38b17c863 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -31,6 +31,12 @@ static float debug_popup_timing = 3.0f; static bool just_opened_options = false; +// clang-format off +static std::string help_text = +#include "help.txt" + ; +// clang-format on + void L::DrawMenuBar() { const auto& ctx = *GImGui; const auto& io = ctx.IO; @@ -38,6 +44,7 @@ void L::DrawMenuBar() { auto isSystemPaused = DebugState.IsGuestThreadsPaused(); bool open_popup_options = false; + bool open_popup_help = false; if (BeginMainMenuBar()) { if (BeginMenu("Options")) { @@ -60,6 +67,7 @@ void L::DrawMenuBar() { ImGui::EndMenu(); } open_popup_options = MenuItem("Options"); + open_popup_help = MenuItem("Help & Tips"); ImGui::EndMenu(); } EndMainMenuBar(); @@ -84,6 +92,9 @@ void L::DrawMenuBar() { OpenPopup("GPU Tools Options"); just_opened_options = true; } + if (open_popup_help) { + OpenPopup("HelpTips"); + } } void L::DrawAdvanced() { @@ -154,25 +165,49 @@ void L::DrawAdvanced() { if (BeginPopupModal("GPU Tools Options", &close_popup_options, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) { static char disassembly_cli[512]; + static bool frame_dump_render_on_collapse; if (just_opened_options) { just_opened_options = false; auto s = Options.disassembly_cli.copy(disassembly_cli, sizeof(disassembly_cli) - 1); disassembly_cli[s] = '\0'; + frame_dump_render_on_collapse = Options.frame_dump_render_on_collapse; } InputText("Shader disassembler: ", disassembly_cli, sizeof(disassembly_cli)); if (IsItemHovered()) { SetTooltip(R"(Command to disassemble shaders. Example "dis.exe" --raw "{src}")"); } + Checkbox("Show frame dump popups even when collapsed", &frame_dump_render_on_collapse); + if (IsItemHovered()) { + SetTooltip("When a frame dump is collapsed, it will keep\n" + "showing all opened popups related to it"); + } if (Button("Save")) { Options.disassembly_cli = disassembly_cli; + Options.frame_dump_render_on_collapse = frame_dump_render_on_collapse; SaveIniSettingsToDisk(io.IniFilename); CloseCurrentPopup(); } EndPopup(); } + + if (BeginPopup("HelpTips", ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove)) { + CentralizeWindow(); + + PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{10.0f}); + PushTextWrapPos(600.0f); + + const char* begin = help_text.data(); + TextUnformatted(begin, begin + help_text.size()); + + PopTextWrapPos(); + PopStyleVar(); + + EndPopup(); + } } void L::DrawSimple() { diff --git a/src/core/devtools/options.cpp b/src/core/devtools/options.cpp index 82fa6e87e..1b49da76b 100644 --- a/src/core/devtools/options.cpp +++ b/src/core/devtools/options.cpp @@ -11,14 +11,20 @@ TOptions Options; void LoadOptionsConfig(const char* line) { char str[512]; + int i; if (sscanf(line, "disassembly_cli=%511[^\n]", str) == 1) { Options.disassembly_cli = str; return; } + if (sscanf(line, "frame_dump_render_on_collapse=%d", &i) == 1) { + Options.frame_dump_render_on_collapse = i != 0; + return; + } } void SerializeOptionsConfig(ImGuiTextBuffer* buf) { buf->appendf("disassembly_cli=%s\n", Options.disassembly_cli.c_str()); + buf->appendf("frame_dump_render_on_collapse=%d\n", Options.frame_dump_render_on_collapse); } } // namespace Core::Devtools diff --git a/src/core/devtools/options.h b/src/core/devtools/options.h index 9d291d768..c3a8aaf31 100644 --- a/src/core/devtools/options.h +++ b/src/core/devtools/options.h @@ -10,7 +10,8 @@ struct ImGuiTextBuffer; namespace Core::Devtools { struct TOptions { - std::string disassembly_cli; + std::string disassembly_cli{}; + bool frame_dump_render_on_collapse{false}; }; extern TOptions Options; diff --git a/src/core/devtools/widget/cmd_list.cpp b/src/core/devtools/widget/cmd_list.cpp index f5d31efef..9a42f8238 100644 --- a/src/core/devtools/widget/cmd_list.cpp +++ b/src/core/devtools/widget/cmd_list.cpp @@ -1173,7 +1173,7 @@ CmdListViewer::CmdListViewer(DebugStateType::FrameDump* _frame_dump, } } -void CmdListViewer::Draw() { +void CmdListViewer::Draw(bool only_batches_view) { const auto& ctx = *GetCurrentContext(); if (batch_view.open) { @@ -1188,6 +1188,10 @@ void CmdListViewer::Draw() { ++it; } + if (only_batches_view) { + return; + } + if (cmdb_view.Open) { MemoryEditor::Sizes s; cmdb_view.CalcSizes(s, cmdb_size, cmdb_addr); @@ -1228,7 +1232,7 @@ void CmdListViewer::Draw() { Text("size : %04llX", cmdb_size); Separator(); - if (TreeNode("Batches")) { + { int tree_depth = 0; int tree_depth_show = 0; @@ -1283,9 +1287,10 @@ void CmdListViewer::Draw() { auto const* pm4_hdr = reinterpret_cast(cmdb_addr + batch.start_addr); + bool ignore_header = false; char batch_hdr[128]; if (batch.type == static_cast(0xFF)) { - snprintf(batch_hdr, sizeof(batch_hdr), "State batch"); + ignore_header = true; } else if (!batch.marker.empty()) { snprintf(batch_hdr, sizeof(batch_hdr), "%08llX: batch-%03d %s | %s", cmdb_addr + batch.start_addr, batch.id, @@ -1309,22 +1314,35 @@ void CmdListViewer::Draw() { auto data = frame_dump->regs.at(batch.command_addr); if (GetIO().KeyShift) { auto& pop = extra_batch_view.emplace_back(); - pop.SetData(data, batch_id); + pop.SetData(data, name, batch_id); pop.open = true; } else { - batch_view.SetData(data, batch_id); - batch_view.open = true; + if (batch_view.open && + this->last_selected_batch == static_cast(batch_id)) { + batch_view.open = false; + } else { + this->last_selected_batch = static_cast(batch_id); + batch_view.SetData(data, name, batch_id); + if (!batch_view.open || !batch_view.moved) { + batch_view.open = true; + const auto pos = GetItemRectMax() + ImVec2{5.0f, 0.0f}; + batch_view.SetPos(pos); + } + } } } }; bool show_batch_content = true; - if (group_batches) { + if (group_batches && !ignore_header) { show_batch_content = CollapsingHeader(batch_hdr, ImGuiTreeNodeFlags_AllowOverlap); SameLine(GetContentRegionAvail().x - 40.0f); - if (Button("->", {40.0f, 0.0f})) { + const char* text = + last_selected_batch == static_cast(batch_id) && batch_view.open ? "X" + : "->"; + if (Button(text, {40.0f, 0.0f})) { open_batch_view(); } } @@ -1332,7 +1350,7 @@ void CmdListViewer::Draw() { if (show_batch_content) { auto processed_size = 0ull; auto bb = ctx.LastItemData.Rect; - if (group_batches) { + if (group_batches && !ignore_header) { Indent(); } auto const batch_sz = batch.end_addr - batch.start_addr; @@ -1354,7 +1372,12 @@ void CmdListViewer::Draw() { if (!group_batches) { if (IsDrawCall(op)) { SameLine(GetContentRegionAvail().x - 40.0f); - if (Button("->", {40.0f, 0.0f})) { + const char* text = + last_selected_batch == static_cast(batch_id) && + batch_view.open + ? "X" + : "->"; + if (Button(text, {40.0f, 0.0f})) { open_batch_view(); } } @@ -1426,7 +1449,7 @@ void CmdListViewer::Draw() { processed_size += processed_len; } - if (group_batches) { + if (group_batches && !ignore_header) { Unindent(); }; bb = {{0.0f, bb.Max.y}, ctx.LastItemData.Rect.Max}; @@ -1450,8 +1473,6 @@ void CmdListViewer::Draw() { PopID(); highlight_batch = current_highlight_batch; - - TreePop(); } } EndChild(); diff --git a/src/core/devtools/widget/cmd_list.h b/src/core/devtools/widget/cmd_list.h index 971c8fffe..ed71d0b76 100644 --- a/src/core/devtools/widget/cmd_list.h +++ b/src/core/devtools/widget/cmd_list.h @@ -53,6 +53,8 @@ class CmdListViewer { u32 highlight_batch{~0u}; RegView batch_view; + int last_selected_batch{-1}; + std::vector extra_batch_view; static void OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body); @@ -68,7 +70,7 @@ public: explicit CmdListViewer(DebugStateType::FrameDump* frame_dump, const std::vector& cmd_list, uintptr_t base_addr = 0, std::string name = ""); - void Draw(); + void Draw(bool only_batches_view = false); }; } // namespace Core::Devtools::Widget diff --git a/src/core/devtools/widget/common.h b/src/core/devtools/widget/common.h index 701d16399..e650f5fc7 100644 --- a/src/core/devtools/widget/common.h +++ b/src/core/devtools/widget/common.h @@ -4,15 +4,16 @@ #pragma once #include +#include #include #include +#include "common/bit_field.h" #include "common/types.h" #include "video_core/amdgpu/pm4_opcodes.h" namespace Core::Devtools::Widget { - /* * Generic PM4 header */ @@ -57,16 +58,24 @@ void DrawRow(const char* text, const char* fmt, Args... args) { ImGui::TextUnformatted(buf); } -template -void DrawEnumRow(const char* text, T value) { - DrawRow(text, "%X (%s)", V(value), magic_enum::enum_name(value).data()); +template +void DrawValueRow(const char* text, T value) { + if constexpr (std::is_enum_v) { + return DrawRow(text, "%X (%s)", value, magic_enum::enum_name(value).data()); + } else if constexpr (std::is_integral_v) { + return DrawRow(text, "%X", value); + } else if constexpr (std::is_base_of_v, T>) { + return DrawValueRow(text, value.Value()); + } else { + static_assert(false, "Unsupported type"); + } } template -void DrawMultipleRow(const char* text, const char* fmt, V arg, Extra&&... extra_args) { - DrawRow(text, fmt, arg); +void DrawValueRowList(const char* text, V arg, Extra&&... extra_args) { + DrawValueRow(text, arg); if constexpr (sizeof...(extra_args) > 0) { - DrawMultipleRow(std::forward(extra_args)...); + DrawValueRowList(std::forward(extra_args)...); } } diff --git a/src/core/devtools/widget/frame_dump.cpp b/src/core/devtools/widget/frame_dump.cpp index 29b5cb8ee..86ba7b86e 100644 --- a/src/core/devtools/widget/frame_dump.cpp +++ b/src/core/devtools/widget/frame_dump.cpp @@ -7,6 +7,7 @@ #include #include "common/io_file.h" +#include "core/devtools/options.h" #include "frame_dump.h" #include "imgui_internal.h" #include "imgui_memory_editor.h" @@ -45,11 +46,14 @@ FrameDumpViewer::FrameDumpViewer(const FrameDump& _frame_dump) selected_submit_num = 0; selected_queue_num2 = 0; + has_queue_type.fill(false); cmd_list_viewer.reserve(frame_dump->queues.size()); for (const auto& cmd : frame_dump->queues) { - const auto fname = - fmt::format("{}_{}_{:02}_{:02}", id, magic_enum::enum_name(selected_queue_type), - selected_submit_num, selected_queue_num2); + if (!cmd.data.empty()) { + has_queue_type[static_cast(cmd.type)] = true; + } + const auto fname = fmt::format("F{} {}_{:02}_{:02}", frame_dump->frame_id, + magic_enum::enum_name(cmd.type), cmd.submit_num, cmd.num2); cmd_list_viewer.emplace_back(frame_dump.get(), cmd.data, cmd.base_addr, fname); if (cmd.type == QueueType::dcb && cmd.submit_num == 0 && cmd.num2 == 0) { selected_cmd = static_cast(cmd_list_viewer.size() - 1); @@ -64,9 +68,28 @@ void FrameDumpViewer::Draw() { return; } + const auto try_select = [&, this] { + const auto it = std::ranges::find_if(frame_dump->queues, [&](const auto& cmd) { + return cmd.type == selected_queue_type && + (selected_submit_num == -1 || cmd.submit_num == selected_submit_num) && + (selected_queue_num2 == -1 || cmd.num2 == selected_queue_num2); + }); + if (it != frame_dump->queues.end()) { + selected_cmd = static_cast(std::distance(frame_dump->queues.begin(), it)); + selected_submit_num = static_cast(frame_dump->queues[selected_cmd].submit_num); + selected_queue_num2 = static_cast(frame_dump->queues[selected_cmd].num2); + } + }; + + bool is_showing = Options.frame_dump_render_on_collapse; + bool is_collapsed = true; + char name[32]; - snprintf(name, sizeof(name), "Frame #%d dump", id); + snprintf(name, sizeof(name), "Frame #%d dump", frame_dump->frame_id); if (Begin(name, &is_open, ImGuiWindowFlags_NoSavedSettings)) { + is_showing = true; + is_collapsed = false; + if (IsWindowAppearing()) { auto window = GetCurrentWindow(); static ImGuiID dock_id = ImHashStr("FrameDumpDock"); @@ -79,12 +102,15 @@ void FrameDumpViewer::Draw() { if (BeginCombo("##select_queue_type", magic_enum::enum_name(selected_queue_type).data(), ImGuiComboFlags_WidthFitPreview)) { bool selected = false; -#define COMBO(x) C_V(magic_enum::enum_name(x).data(), x, selected_queue_type, selected) - COMBO(QueueType::acb) +#define COMBO(x) \ + if (has_queue_type[static_cast(x)]) \ + C_V(magic_enum::enum_name(x).data(), x, selected_queue_type, selected) COMBO(QueueType::dcb); COMBO(QueueType::ccb); + COMBO(QueueType::acb); if (selected) { selected_submit_num = selected_queue_num2 = -1; + try_select(); } EndCombo(); } @@ -111,9 +137,9 @@ void FrameDumpViewer::Draw() { SameLine(); if (BeginCombo("##select_submit_num", small_int_to_str(selected_submit_num).data(), ImGuiComboFlags_WidthFitPreview)) { - std::array available_submits{}; + std::array available_submits{false}; for (const auto& cmd : frame_dump->queues) { - if (cmd.type == selected_queue_type) { + if (cmd.type == selected_queue_type && !cmd.data.empty()) { available_submits[cmd.submit_num] = true; } } @@ -128,6 +154,7 @@ void FrameDumpViewer::Draw() { } if (selected) { selected_queue_num2 = -1; + try_select(); } EndCombo(); } @@ -136,9 +163,10 @@ void FrameDumpViewer::Draw() { SameLine(); if (BeginCombo("##select_queue_num2", small_int_to_str(selected_queue_num2).data(), ImGuiComboFlags_WidthFitPreview)) { - std::array available_queues{}; + std::array available_queues{false}; for (const auto& cmd : frame_dump->queues) { - if (cmd.type == selected_queue_type && cmd.submit_num == selected_submit_num) { + if (cmd.type == selected_queue_type && cmd.submit_num == selected_submit_num && + !cmd.data.empty()) { available_queues[cmd.num2] = true; } } @@ -152,21 +180,14 @@ void FrameDumpViewer::Draw() { } } if (selected) { - const auto it = std::ranges::find_if(frame_dump->queues, [&](const auto& cmd) { - return cmd.type == selected_queue_type && - cmd.submit_num == selected_submit_num && cmd.num2 == selected_queue_num2; - }); - if (it != frame_dump->queues.end()) { - selected_cmd = static_cast(std::distance(frame_dump->queues.begin(), it)); - } + try_select(); } EndCombo(); } EndGroup(); - - if (selected_cmd != -1) { - cmd_list_viewer[selected_cmd].Draw(); - } + } + if (is_showing && selected_cmd != -1) { + cmd_list_viewer[selected_cmd].Draw(is_collapsed); } End(); } diff --git a/src/core/devtools/widget/frame_dump.h b/src/core/devtools/widget/frame_dump.h index 2b3ff2411..cc4fe6381 100644 --- a/src/core/devtools/widget/frame_dump.h +++ b/src/core/devtools/widget/frame_dump.h @@ -20,6 +20,7 @@ class FrameDumpViewer { int id; std::vector cmd_list_viewer; + std::array has_queue_type; DebugStateType::QueueType selected_queue_type; s32 selected_submit_num; diff --git a/src/core/devtools/widget/reg_popup.cpp b/src/core/devtools/widget/reg_popup.cpp index d012437c3..0633e76e6 100644 --- a/src/core/devtools/widget/reg_popup.cpp +++ b/src/core/devtools/widget/reg_popup.cpp @@ -9,6 +9,7 @@ #include "cmd_list.h" #include "common.h" +#include "imgui/imgui_std.h" using namespace ImGui; using magic_enum::enum_name; @@ -21,13 +22,13 @@ void RegPopup::DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer) { // clang-format off - DrawMultipleRow( - "BASE_ADDR", "%X", buffer.base_address, - "PITCH.TILE_MAX", "%X", buffer.pitch.tile_max, - "PITCH.FMASK_TILE_MAX", "%X", buffer.pitch.fmask_tile_max, - "SLICE.TILE_MAX", "%X", buffer.slice.tile_max, - "VIEW.SLICE_START", "%X", buffer.view.slice_start, - "VIEW.SLICE_MAX", "%X", buffer.view.slice_max + DrawValueRowList( + "BASE_ADDR", buffer.base_address, + "PITCH.TILE_MAX", buffer.pitch.tile_max, + "PITCH.FMASK_TILE_MAX", buffer.pitch.fmask_tile_max, + "SLICE.TILE_MAX", buffer.slice.tile_max, + "VIEW.SLICE_START", buffer.view.slice_start, + "VIEW.SLICE_MAX", buffer.view.slice_max ); TableNextRow(); @@ -49,31 +50,25 @@ void RegPopup::DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer) { } TableNextRow(); - DrawMultipleRow( - "CMASK_BASE_EXT", "%X", buffer.cmask_base_address, - "FMASK_BASE_EXT", "%X", buffer.fmask_base_address, - "FMASK_SLICE.TILE_MAX", "%X", buffer.fmask_slice.tile_max, - "CLEAR_WORD0", "%X", buffer.clear_word0, - "CLEAR_WORD1", "%X", buffer.clear_word1 + DrawValueRowList( + "CMASK_BASE_EXT", buffer.cmask_base_address, + "FMASK_BASE_EXT", buffer.fmask_base_address, + "FMASK_SLICE.TILE_MAX", buffer.fmask_slice.tile_max, + "CLEAR_WORD0", buffer.clear_word0, + "CLEAR_WORD1", buffer.clear_word1, + "Pitch()", buffer.Pitch(), + "Height()", buffer.Height(), + "Address()", buffer.Address(), + "CmaskAddress", buffer.CmaskAddress(), + "FmaskAddress", buffer.FmaskAddress(), + "NumSamples()", buffer.NumSamples(), + "NumSlices()", buffer.NumSlices(), + "GetColorSliceSize()", buffer.GetColorSliceSize(), + "GetTilingMode()", buffer.GetTilingMode(), + "IsTiled()", buffer.IsTiled(), + "NumFormat()", buffer.NumFormat() ); - DrawMultipleRow( - "Pitch()", "%X", buffer.Pitch(), - "Height()", "%X", buffer.Height(), - "Address()", "%X", buffer.Address(), - "CmaskAddress", "%X", buffer.CmaskAddress(), - "FmaskAddress", "%X", buffer.FmaskAddress(), - "NumSamples()", "%X", buffer.NumSamples(), - "NumSlices()", "%X", buffer.NumSlices(), - "GetColorSliceSize()", "%X", buffer.GetColorSliceSize() - ); - - auto tiling_mode = buffer.GetTilingMode(); - auto num_format = buffer.NumFormat(); - DrawEnumRow("GetTilingMode()", tiling_mode); - DrawRow("IsTiled()", "%X", buffer.IsTiled()); - DrawEnumRow("NumFormat()", num_format); - // clang-format on EndTable(); @@ -89,37 +84,34 @@ void RegPopup::DrawDepthBuffer(const DepthBuffer& depth_data) { TableNextRow(); // clang-format off - DrawEnumRow("Z_INFO.FORMAT", depth_buffer.z_info.format.Value()); - DrawMultipleRow( - "Z_INFO.NUM_SAMPLES", "%X", depth_buffer.z_info.num_samples, - "Z_INFO.TILE_SPLIT", "%X", depth_buffer.z_info.tile_split, - "Z_INFO.TILE_MODE_INDEX", "%X", depth_buffer.z_info.tile_mode_index, - "Z_INFO.DECOMPRESS_ON_N_ZPLANES", "%X", depth_buffer.z_info.decompress_on_n_zplanes, - "Z_INFO.ALLOW_EXPCLEAR", "%X", depth_buffer.z_info.allow_expclear, - "Z_INFO.READ_SIZE", "%X", depth_buffer.z_info.read_size, - "Z_INFO.TILE_SURFACE_EN", "%X", depth_buffer.z_info.tile_surface_en, - "Z_INFO.CLEAR_DISALLOWED", "%X", depth_buffer.z_info.clear_disallowed, - "Z_INFO.ZRANGE_PRECISION", "%X", depth_buffer.z_info.zrange_precision - ); - - DrawEnumRow("STENCIL_INFO.FORMAT", depth_buffer.stencil_info.format.Value()); - - DrawMultipleRow( - "Z_READ_BASE", "%X", depth_buffer.z_read_base, - "STENCIL_READ_BASE", "%X", depth_buffer.stencil_read_base, - "Z_WRITE_BASE", "%X", depth_buffer.z_write_base, - "STENCIL_WRITE_BASE", "%X", depth_buffer.stencil_write_base, - "DEPTH_SIZE.PITCH_TILE_MAX", "%X", depth_buffer.depth_size.pitch_tile_max, - "DEPTH_SIZE.HEIGHT_TILE_MAX", "%X", depth_buffer.depth_size.height_tile_max, - "DEPTH_SLICE.TILE_MAX", "%X", depth_buffer.depth_slice.tile_max, - "Pitch()", "%X", depth_buffer.Pitch(), - "Height()", "%X", depth_buffer.Height(), - "Address()", "%X", depth_buffer.Address(), - "NumSamples()", "%X", depth_buffer.NumSamples(), - "NumBits()", "%X", depth_buffer.NumBits(), - "GetDepthSliceSize()", "%X", depth_buffer.GetDepthSliceSize() + DrawValueRowList( + "Z_INFO.FORMAT", depth_buffer.z_info.format, + "Z_INFO.NUM_SAMPLES", depth_buffer.z_info.num_samples, + "Z_INFO.TILE_SPLIT", depth_buffer.z_info.tile_split, + "Z_INFO.TILE_MODE_INDEX", depth_buffer.z_info.tile_mode_index, + "Z_INFO.DECOMPRESS_ON_N_ZPLANES", depth_buffer.z_info.decompress_on_n_zplanes, + "Z_INFO.ALLOW_EXPCLEAR", depth_buffer.z_info.allow_expclear, + "Z_INFO.READ_SIZE", depth_buffer.z_info.read_size, + "Z_INFO.TILE_SURFACE_EN", depth_buffer.z_info.tile_surface_en, + "Z_INFO.CLEAR_DISALLOWED", depth_buffer.z_info.clear_disallowed, + "Z_INFO.ZRANGE_PRECISION", depth_buffer.z_info.zrange_precision, + "STENCIL_INFO.FORMAT", depth_buffer.stencil_info.format, + "Z_READ_BASE", depth_buffer.z_read_base, + "STENCIL_READ_BASE", depth_buffer.stencil_read_base, + "Z_WRITE_BASE", depth_buffer.z_write_base, + "STENCIL_WRITE_BASE", depth_buffer.stencil_write_base, + "DEPTH_SIZE.PITCH_TILE_MAX", depth_buffer.depth_size.pitch_tile_max, + "DEPTH_SIZE.HEIGHT_TILE_MAX", depth_buffer.depth_size.height_tile_max, + "DEPTH_SLICE.TILE_MAX", depth_buffer.depth_slice.tile_max, + "Pitch()", depth_buffer.Pitch(), + "Height()", depth_buffer.Height(), + "Address()", depth_buffer.Address(), + "NumSamples()", depth_buffer.NumSamples(), + "NumBits()", depth_buffer.NumBits(), + "GetDepthSliceSize()", depth_buffer.GetDepthSliceSize() ); // clang-format on + EndTable(); } SeparatorText("Depth control"); @@ -127,19 +119,17 @@ void RegPopup::DrawDepthBuffer(const DepthBuffer& depth_data) { TableNextRow(); // clang-format off - DrawMultipleRow( - "STENCIL_ENABLE", "%X", depth_control.stencil_enable, - "DEPTH_ENABLE", "%X", depth_control.depth_enable, - "DEPTH_WRITE_ENABLE", "%X", depth_control.depth_write_enable, - "DEPTH_BOUNDS_ENABLE", "%X", depth_control.depth_bounds_enable - ); - DrawEnumRow("DEPTH_FUNC", depth_control.depth_func.Value()); - DrawRow("BACKFACE_ENABLE", "%X", depth_control.backface_enable); - DrawEnumRow("STENCIL_FUNC", depth_control.stencil_ref_func.Value()); - DrawEnumRow("STENCIL_FUNC_BF", depth_control.stencil_bf_func.Value()); - DrawMultipleRow( - "ENABLE_COLOR_WRITES_ON_DEPTH_FAIL", "%X", depth_control.enable_color_writes_on_depth_fail, - "DISABLE_COLOR_WRITES_ON_DEPTH_PASS", "%X", depth_control.disable_color_writes_on_depth_pass + DrawValueRowList( + "STENCIL_ENABLE", depth_control.stencil_enable, + "DEPTH_ENABLE", depth_control.depth_enable, + "DEPTH_WRITE_ENABLE", depth_control.depth_write_enable, + "DEPTH_BOUNDS_ENABLE", depth_control.depth_bounds_enable, + "DEPTH_FUNC", depth_control.depth_func, + "BACKFACE_ENABLE", depth_control.backface_enable, + "STENCIL_FUNC", depth_control.stencil_ref_func, + "STENCIL_FUNC_BF", depth_control.stencil_bf_func, + "ENABLE_COLOR_WRITES_ON_DEPTH_FAIL", depth_control.enable_color_writes_on_depth_fail, + "DISABLE_COLOR_WRITES_ON_DEPTH_PASS", depth_control.disable_color_writes_on_depth_pass ); // clang-format on @@ -152,24 +142,45 @@ RegPopup::RegPopup() { id = unique_id++; } -void RegPopup::SetData(AmdGpu::Liverpool::ColorBuffer color_buffer, u32 batch_id, u32 cb_id) { +void RegPopup::SetData(const std::string& base_title, AmdGpu::Liverpool::ColorBuffer color_buffer, + u32 cb_id) { this->data = color_buffer; - this->title = fmt::format("Batch #{} CB #{}", batch_id, cb_id); + this->title = fmt::format("{}/CB #{}", base_title, cb_id); } -void RegPopup::SetData(AmdGpu::Liverpool::DepthBuffer depth_buffer, - AmdGpu::Liverpool::DepthControl depth_control, u32 batch_id) { +void RegPopup::SetData(const std::string& base_title, AmdGpu::Liverpool::DepthBuffer depth_buffer, + AmdGpu::Liverpool::DepthControl depth_control) { this->data = std::make_tuple(depth_buffer, depth_control); - this->title = fmt::format("Batch #{} Depth", batch_id); + this->title = fmt::format("{}/Depth", base_title); +} + +void RegPopup::SetPos(ImVec2 pos, bool auto_resize) { + char name[128]; + snprintf(name, sizeof(name), "%s###reg_popup_%d", title.c_str(), id); + Begin(name, &open, flags); + SetWindowPos(pos); + if (auto_resize) { + if (std::holds_alternative(data)) { + SetWindowSize({365.0f, 520.0f}); + KeepWindowInside(); + } else if (std::holds_alternative(data)) { + SetWindowSize({404.0f, 543.0f}); + KeepWindowInside(); + } + } + last_pos = GetWindowPos(); + moved = false; + End(); } void RegPopup::Draw() { - char name[128]; snprintf(name, sizeof(name), "%s###reg_popup_%d", title.c_str(), id); + if (Begin(name, &open, flags)) { + if (GetWindowPos() != last_pos) { + moved = true; + } - SetNextWindowSize({250.0f, 300.0f}, ImGuiCond_FirstUseEver); - if (Begin(name, &open, ImGuiWindowFlags_NoSavedSettings)) { if (const auto* buffer = std::get_if(&data)) { DrawColorBuffer(*buffer); } else if (const auto* depth_data = std::get_if(&data)) { @@ -178,5 +189,4 @@ void RegPopup::Draw() { } End(); } - } // namespace Core::Devtools::Widget diff --git a/src/core/devtools/widget/reg_popup.h b/src/core/devtools/widget/reg_popup.h index ba4224d73..9ccd60ac0 100644 --- a/src/core/devtools/widget/reg_popup.h +++ b/src/core/devtools/widget/reg_popup.h @@ -5,6 +5,8 @@ #include +#include + #include "common/types.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" @@ -12,25 +14,31 @@ namespace Core::Devtools::Widget { class RegPopup { int id; + ImGuiWindowFlags flags{ImGuiWindowFlags_NoSavedSettings}; using DepthBuffer = std::tuple; + ImVec2 last_pos; std::variant data; std::string title{}; - void DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer); + static void DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer); - void DrawDepthBuffer(const DepthBuffer& depth_data); + static void DrawDepthBuffer(const DepthBuffer& depth_data); public: bool open = false; + bool moved = false; RegPopup(); - void SetData(AmdGpu::Liverpool::ColorBuffer color_buffer, u32 batch_id, u32 cb_id); + void SetData(const std::string& base_title, AmdGpu::Liverpool::ColorBuffer color_buffer, + u32 cb_id); - void SetData(AmdGpu::Liverpool::DepthBuffer depth_buffer, - AmdGpu::Liverpool::DepthControl depth_control, u32 batch_id); + void SetData(const std::string& base_title, AmdGpu::Liverpool::DepthBuffer depth_buffer, + AmdGpu::Liverpool::DepthControl depth_control); + + void SetPos(ImVec2 pos, bool auto_resize = false); void Draw(); }; diff --git a/src/core/devtools/widget/reg_view.cpp b/src/core/devtools/widget/reg_view.cpp index 2e8bb8f54..10cc88085 100644 --- a/src/core/devtools/widget/reg_view.cpp +++ b/src/core/devtools/widget/reg_view.cpp @@ -11,6 +11,7 @@ #include "common.h" #include "common/io_file.h" #include "core/devtools/options.h" +#include "imgui/imgui_std.h" #include "imgui_internal.h" #include "reg_view.h" @@ -22,6 +23,8 @@ using namespace ImGui; using magic_enum::enum_name; +constexpr auto depth_id = 0xF3; + static std::optional exec_cli(const char* cli) { std::array buffer{}; std::string output; @@ -40,7 +43,16 @@ static std::optional exec_cli(const char* cli) { namespace Core::Devtools::Widget { void RegView::ProcessShader(int shader_id) { - auto shader = data.stages[shader_id]; + std::vector shader_code; + Vulkan::Liverpool::UserData user_data; + if (data.is_compute) { + shader_code = data.cs_data.code; + user_data = data.cs_data.cs_program.user_data; + } else { + const auto& s = data.stages[shader_id]; + shader_code = s.code; + user_data = s.user_data.user_data; + } std::string shader_dis; @@ -57,7 +69,7 @@ void RegView::ProcessShader(int shader_id) { } else { cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\""); Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write); - file.Write(shader.code); + file.Write(shader_code); file.Close(); auto result = exec_cli(cli.c_str()); @@ -73,8 +85,9 @@ void RegView::ProcessShader(int shader_id) { MemoryEditor hex_view; hex_view.Open = true; hex_view.ReadOnly = true; - hex_view.Cols = 16; + hex_view.Cols = 8; hex_view.OptShowAscii = false; + hex_view.OptShowOptions = false; TextEditor dis_view; dis_view.SetPalette(TextEditor::GetDarkPalette()); @@ -84,7 +97,7 @@ void RegView::ProcessShader(int shader_id) { ShaderCache cache{ .hex_view = hex_view, .dis_view = dis_view, - .user_data = shader.user_data.user_data, + .user_data = user_data, }; shader_decomp.emplace(shader_id, std::move(cache)); } @@ -95,34 +108,64 @@ void RegView::SelectShader(int id) { } } -void RegView::DrawRegs() { +void RegView::DrawComputeRegs() { + const auto& cs = data.cs_data.cs_program; + + if (BeginTable("CREGS", 2, ImGuiTableFlags_Borders)) { + TableNextRow(); + + // clang-format off + DrawValueRowList( + "DISPATCH_INITIATOR", cs.dispatch_initiator, + "DIM_X", cs.dim_x, + "DIM_Y", cs.dim_y, + "DIM_Z", cs.dim_z, + "START_X", cs.start_x, + "START_Y", cs.start_y, + "START_Z", cs.start_z, + "NUM_THREAD_X.FULL", cs.num_thread_x.full, + "NUM_THREAD_X.PARTIAL", cs.num_thread_x.partial, + "NUM_THREAD_Y.FULL", cs.num_thread_y.full, + "NUM_THREAD_Y.PARTIAL", cs.num_thread_y.partial, + "NUM_THREAD_Z.FULL", cs.num_thread_z.full, + "NUM_THREAD_Z.PARTIAL", cs.num_thread_z.partial, + "MAX_WAVE_ID", cs.max_wave_id, + "SETTINGS.NUM_VGPRS", cs.settings.num_vgprs, + "SETTINGS.NUM_SGPRS", cs.settings.num_sgprs, + "SETTINGS.NUM_USER_REGS", cs.settings.num_user_regs, + "SETTINGS.TGID_ENABLE", cs.settings.tgid_enable, + "SETTINGS.LDS_DWORDS", cs.settings.lds_dwords, + "RESOURCE_LIMITS", cs.resource_limits + ); + // clang-format on + + EndTable(); + } +} + +void RegView::DrawGraphicsRegs() { const auto& regs = data.regs; if (BeginTable("REGS", 2, ImGuiTableFlags_Borders)) { + TableNextRow(); - auto& s = regs.screen_scissor; - DrawRow("Scissor", "(%d, %d, %d, %d)", s.top_left_x, s.top_left_y, s.bottom_right_x, - s.bottom_right_y); - - auto cc_mode = regs.color_control.mode.Value(); - DrawRow("Color control", "%X (%s)", cc_mode, enum_name(cc_mode).data()); + DrawValueRow("Primitive type", regs.primitive_type); const auto open_new_popup = [&](int cb, auto... args) { + const auto pos = GetItemRectMax() + ImVec2(5.0f, 0.0f); if (GetIO().KeyShift) { auto& pop = extra_reg_popup.emplace_back(); - pop.SetData(args...); + pop.SetData(title, args...); pop.open = true; + pop.SetPos(pos, true); } else if (last_selected_cb == cb && default_reg_popup.open) { default_reg_popup.open = false; } else { last_selected_cb = cb; - default_reg_popup.SetData(args...); - if (!default_reg_popup.open) { + default_reg_popup.SetData(title, args...); + if (!default_reg_popup.open || !default_reg_popup.moved) { default_reg_popup.open = true; - auto popup_pos = - GetCurrentContext()->LastItemData.Rect.Max + ImVec2(5.0f, 0.0f); - SetNextWindowPos(popup_pos, ImGuiCond_Always); - default_reg_popup.Draw(); + default_reg_popup.SetPos(pos, true); } } }; @@ -142,7 +185,7 @@ void RegView::DrawRegs() { } else { const char* text = last_selected_cb == cb && default_reg_popup.open ? "x" : "->"; if (SmallButton(text)) { - open_new_popup(cb, buffer, batch_id, cb); + open_new_popup(cb, buffer, cb); } } @@ -156,13 +199,30 @@ void RegView::DrawRegs() { if (regs.depth_buffer.Address() == 0 || !regs.depth_control.depth_enable) { TextUnformatted("N/A"); } else { - constexpr auto depth_id = 0xF3; const char* text = last_selected_cb == depth_id && default_reg_popup.open ? "x" : "->"; if (SmallButton(text)) { - open_new_popup(depth_id, regs.depth_buffer, regs.depth_control, batch_id); + open_new_popup(depth_id, regs.depth_buffer, regs.depth_control); } } + auto& s = regs.screen_scissor; + DrawRow("Scissor", "(%d, %d, %d, %d)", s.top_left_x, s.top_left_y, s.bottom_right_x, + s.bottom_right_y); + + DrawValueRow("Color control", regs.color_control.mode); + + DrawRow("Primitive restart", "%X (IDX: %X)", regs.enable_primitive_restart & 1, + regs.primitive_restart_index); + // clang-format off + DrawValueRowList( + "Polygon mode", regs.polygon_control.PolyMode(), + "Cull mode", regs.polygon_control.CullingMode(), + "Clip Space", regs.clipper_control.clip_space, + "Front face", regs.polygon_control.front_face, + "Num Samples", regs.aa_config.NumSamples() + ); + // clang-format on + EndTable(); } } @@ -172,9 +232,9 @@ RegView::RegView() { id = unique_id++; char name[128]; - snprintf(name, sizeof(name), "BatchView###reg_dump_%d", id); + snprintf(name, sizeof(name), "###reg_dump_%d", id); SetNextWindowPos({400.0f, 200.0f}); - SetNextWindowSize({450.0f, 500.0f}); + SetNextWindowSize({290.0f, 435.0f}); ImGuiID root_dock_id; Begin(name); { @@ -188,7 +248,7 @@ RegView::RegView() { ImGuiID up1, down1; DockBuilderRemoveNodeChildNodes(root_dock_id); - DockBuilderSplitNode(root_dock_id, ImGuiDir_Up, 0.2f, &up1, &down1); + DockBuilderSplitNode(root_dock_id, ImGuiDir_Up, 0.19f, &up1, &down1); snprintf(name, sizeof(name), "User data###reg_dump_%d/user_data", id); DockBuilderDockWindow(name, up1); @@ -202,35 +262,68 @@ RegView::RegView() { DockBuilderFinish(root_dock_id); } -void RegView::SetData(DebugStateType::RegDump data, u32 batch_id) { - this->data = std::move(data); +void RegView::SetData(DebugStateType::RegDump _data, const std::string& base_title, u32 batch_id) { + this->data = std::move(_data); this->batch_id = batch_id; + this->title = fmt::format("{}/Batch {}", base_title, batch_id); // clear cache - selected_shader = -1; shader_decomp.clear(); - default_reg_popup.open = false; + if (data.is_compute) { + selected_shader = -2; + last_selected_cb = -1; + default_reg_popup.open = false; + ProcessShader(-2); + } else { + const auto& regs = data.regs; + if (selected_shader >= 0 && !regs.stage_enable.IsStageEnabled(selected_shader)) { + selected_shader = -1; + } + if (default_reg_popup.open) { + default_reg_popup.open = false; + if (last_selected_cb == depth_id) { + const auto& has_depth = + regs.depth_buffer.Address() != 0 && regs.depth_control.depth_enable; + if (has_depth) { + default_reg_popup.SetData(title, regs.depth_buffer, regs.depth_control); + default_reg_popup.open = true; + } + } else if (last_selected_cb >= 0 && + last_selected_cb < AmdGpu::Liverpool::NumColorBuffers) { + const auto& buffer = regs.color_buffers[last_selected_cb]; + const bool has_cb = buffer && regs.color_target_mask.GetMask(last_selected_cb); + if (has_cb) { + default_reg_popup.SetData(title, buffer, last_selected_cb); + default_reg_popup.open = true; + } + } + } + } extra_reg_popup.clear(); } -void RegView::Draw() { - +void RegView::SetPos(ImVec2 pos) { char name[128]; - snprintf(name, sizeof(name), "BatchView %u###reg_dump_%d", batch_id, id); + snprintf(name, sizeof(name), "%s###reg_dump_%d", title.c_str(), id); + Begin(name, &open, ImGuiWindowFlags_MenuBar); + SetWindowPos(pos); + KeepWindowInside(); + last_pos = GetWindowPos(); + moved = false; + End(); +} + +void RegView::Draw() { + char name[128]; + snprintf(name, sizeof(name), "%s###reg_dump_%d", title.c_str(), id); + if (Begin(name, &open, ImGuiWindowFlags_MenuBar)) { + if (GetWindowPos() != last_pos) { + moved = true; + } + const char* names[] = {"vs", "ps", "gs", "es", "hs", "ls"}; if (BeginMenuBar()) { - if (BeginMenu("Stage")) { - for (int i = 0; i < DebugStateType::RegDump::MaxShaderStages; i++) { - if (data.regs.stage_enable.IsStageEnabled(i)) { - bool selected = selected_shader == i; - if (Selectable(names[i], &selected)) { - SelectShader(i); - } - } - } - ImGui::EndMenu(); - } if (BeginMenu("Windows")) { Checkbox("Registers", &show_registers); Checkbox("User data", &show_user_data); @@ -240,11 +333,31 @@ void RegView::Draw() { EndMenuBar(); } - char dock_name[64]; - snprintf(dock_name, sizeof(dock_name), "BatchView###reg_dump_%d/dock_space", id); - auto root_dock_id = ImHashStr(dock_name); - DockSpace(root_dock_id); + if (!data.is_compute && + BeginChild("STAGES", {}, + ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeY)) { + for (int i = 0; i < DebugStateType::RegDump::MaxShaderStages; i++) { + if (data.regs.stage_enable.IsStageEnabled(i)) { + const bool selected = selected_shader == i; + if (selected) { + PushStyleColor(ImGuiCol_Button, ImVec4{1.0f, 0.7f, 0.7f, 1.0f}); + } + if (Button(names[i], {40.0f, 40.0f})) { + SelectShader(i); + } + if (selected) { + PopStyleColor(); + } + } + SameLine(); + } + EndChild(); + } } + char dock_name[64]; + snprintf(dock_name, sizeof(dock_name), "BatchView###reg_dump_%d/dock_space", id); + auto root_dock_id = ImHashStr(dock_name); + DockSpace(root_dock_id); End(); auto get_shader = [&]() -> ShaderCache* { @@ -257,10 +370,11 @@ void RegView::Draw() { if (show_user_data) { snprintf(name, sizeof(name), "User data###reg_dump_%d/user_data", id); - if (Begin(name, &show_user_data)) { + + if (Begin(name, &show_user_data, ImGuiWindowFlags_NoScrollbar)) { auto shader = get_shader(); if (!shader) { - Text("Select a stage"); + Text("Stage not selected"); } else { shader->hex_view.DrawContents(shader->user_data.data(), shader->user_data.size()); } @@ -273,7 +387,7 @@ void RegView::Draw() { if (Begin(name, &show_disassembly)) { auto shader = get_shader(); if (!shader) { - Text("Select a stage"); + Text("Stage not selected"); } else { shader->dis_view.Render("Disassembly", GetContentRegionAvail()); } @@ -284,7 +398,11 @@ void RegView::Draw() { if (show_registers) { snprintf(name, sizeof(name), "Regs###reg_dump_%d/regs", id); if (Begin(name, &show_registers)) { - DrawRegs(); + if (data.is_compute) { + DrawComputeRegs(); + } else { + DrawGraphicsRegs(); + } } End(); } diff --git a/src/core/devtools/widget/reg_view.h b/src/core/devtools/widget/reg_view.h index 67ab1e04f..3ac8ec077 100644 --- a/src/core/devtools/widget/reg_view.h +++ b/src/core/devtools/widget/reg_view.h @@ -18,8 +18,10 @@ struct ShaderCache { class RegView { int id; + std::string title; DebugStateType::RegDump data; u32 batch_id{~0u}; + ImVec2 last_pos; std::unordered_map shader_decomp; int selected_shader{-1}; @@ -35,14 +37,19 @@ class RegView { void SelectShader(int shader_id); - void DrawRegs(); + void DrawComputeRegs(); + + void DrawGraphicsRegs(); public: bool open = false; + bool moved = false; RegView(); - void SetData(DebugStateType::RegDump data, u32 batch_id); + void SetData(DebugStateType::RegDump data, const std::string& base_title, u32 batch_id); + + void SetPos(ImVec2 pos); void Draw(); }; diff --git a/src/core/libraries/dialogs/ime_dialog_ui.cpp b/src/core/libraries/dialogs/ime_dialog_ui.cpp index 48f5d75dc..9d50d2fbb 100644 --- a/src/core/libraries/dialogs/ime_dialog_ui.cpp +++ b/src/core/libraries/dialogs/ime_dialog_ui.cpp @@ -245,7 +245,7 @@ void ImeDialogUi::Draw() { window_size = {500.0f, 150.0f}; } - CentralizeWindow(); + CentralizeNextWindow(); SetNextWindowSize(window_size); SetNextWindowCollapsed(false); diff --git a/src/imgui/imgui_std.h b/src/imgui/imgui_std.h index ce79da705..cd7208064 100644 --- a/src/imgui/imgui_std.h +++ b/src/imgui/imgui_std.h @@ -31,7 +31,7 @@ inline void CentralizeNextWindow() { } inline void CentralizeWindow() { - const auto display_size = GetIO().DisplaySize; + const auto display_size = GetIO().DisplaySize - GetCurrentWindowRead()->SizeFull; SetWindowPos(display_size / 2.0f); } @@ -41,7 +41,7 @@ inline void KeepWindowInside(ImVec2 display_size = GetIO().DisplaySize) { SetWindowPos(ImMax(cur_pos, ImVec2(0.0f, 0.0f))); return; } - const auto cur_size = GetWindowSize(); + const auto cur_size = GetCurrentWindowRead()->SizeFull; const auto bottom_right = cur_pos + cur_size; if (bottom_right.x > display_size.x || bottom_right.y > display_size.y) { const auto max_pos = display_size - cur_size; diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index d4ebeb883..7b4727088 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -446,7 +446,8 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spandim_z; regs.cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator; if (DebugState.DumpingCurrentReg()) { - DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); + DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs, + true); } if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); @@ -463,7 +464,8 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header), regs); + DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs, + true); } if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); @@ -645,7 +647,7 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, int vqid) { regs.cs_program.dim_z = dispatch_direct->dim_z; regs.cs_program.dispatch_initiator = dispatch_direct->dispatch_initiator; if (DebugState.DumpingCurrentReg()) { - DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); + DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs, true); } if (rasterizer && (regs.cs_program.dispatch_initiator & 1)) { const auto cmd_address = reinterpret_cast(header); From b62d2f839de9d096995cb5b1f6d74a4d5d863500 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Wed, 16 Oct 2024 13:05:35 -0300 Subject: [PATCH 74/93] missing translations (#1395) * missing translations * clang --- src/qt_gui/gui_context_menus.h | 7 +--- src/qt_gui/settings_dialog.cpp | 14 +++---- src/qt_gui/settings_dialog.ui | 2 +- src/qt_gui/translations/ar.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/da_DK.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/de.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/el.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/en.ts | 67 ++++++++++++++++++++++++++++- src/qt_gui/translations/es_ES.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/fa_IR.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/fi.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/fr.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/hu_HU.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/id.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/it.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/ja_JP.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/ko_KR.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/lt_LT.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/nb.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/nl.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/pl_PL.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/pt_BR.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/ro_RO.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/ru_RU.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/sq.ts | 72 +++++++++++++++++++++++++++++++- src/qt_gui/translations/tr_TR.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/vi_VN.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/zh_CN.ts | 70 +++++++++++++++++++++++++++++++ src/qt_gui/translations/zh_TW.ts | 70 +++++++++++++++++++++++++++++++ 29 files changed, 1827 insertions(+), 15 deletions(-) diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index da3e32077..8d7018522 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -300,11 +300,8 @@ public: if (selected == deleteUpdate) { if (!Config::getSeparateUpdateEnabled()) { - QMessageBox::critical( - nullptr, tr("Error"), - QString(tr("This feature requires the 'Enable Separate Update Folder' " - "config option " - "to work. If you want to use this feature, please enable it."))); + QMessageBox::critical(nullptr, tr("Error"), + QString(tr("requiresEnableSeparateUpdateFolder_MSG"))); error = true; } else if (!std::filesystem::exists( Common::FS::PathFromQString(game_update_path))) { diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 77701f221..309b40b46 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -127,15 +127,15 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge connect(ui->fullscreenCheckBox, &QCheckBox::stateChanged, this, [](int val) { Config::setFullscreenMode(val); }); + connect(ui->separateUpdatesCheckBox, &QCheckBox::stateChanged, this, + [](int val) { Config::setSeparateUpdateEnabled(val); }); + connect(ui->showSplashCheckBox, &QCheckBox::stateChanged, this, [](int val) { Config::setShowSplash(val); }); connect(ui->ps4proCheckBox, &QCheckBox::stateChanged, this, [](int val) { Config::setNeoMode(val); }); - connect(ui->separateUpdatesCheckBox, &QCheckBox::stateChanged, this, - [](int val) { Config::setSeparateUpdateEnabled(val); }); - connect(ui->logTypeComboBox, &QComboBox::currentTextChanged, this, [](const QString& text) { Config::setLogType(text.toStdString()); }); @@ -270,10 +270,10 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge ui->consoleLanguageGroupBox->installEventFilter(this); ui->emulatorLanguageGroupBox->installEventFilter(this); ui->fullscreenCheckBox->installEventFilter(this); + ui->separateUpdatesCheckBox->installEventFilter(this); ui->showSplashCheckBox->installEventFilter(this); ui->ps4proCheckBox->installEventFilter(this); ui->discordRPCCheckbox->installEventFilter(this); - ui->separateUpdatesCheckBox->installEventFilter(this); ui->userName->installEventFilter(this); ui->logTypeGroupBox->installEventFilter(this); ui->logFilter->installEventFilter(this); @@ -327,12 +327,12 @@ void SettingsDialog::LoadValuesFromConfig() { ui->BGMVolumeSlider->setValue((Config::getBGMvolume())); ui->discordRPCCheckbox->setChecked(Config::getEnableDiscordRPC()); ui->fullscreenCheckBox->setChecked(Config::isFullscreenMode()); + ui->separateUpdatesCheckBox->setChecked(Config::getSeparateUpdateEnabled()); ui->showSplashCheckBox->setChecked(Config::showSplash()); ui->ps4proCheckBox->setChecked(Config::isNeoMode()); ui->logTypeComboBox->setCurrentText(QString::fromStdString(Config::getLogType())); ui->logFilterLineEdit->setText(QString::fromStdString(Config::getLogFilter())); ui->userNameLineEdit->setText(QString::fromStdString(Config::getUserName())); - ui->separateUpdatesCheckBox->setChecked(Config::getSeparateUpdateEnabled()); ui->debugDump->setChecked(Config::debugDump()); ui->vkValidationCheckBox->setChecked(Config::vkValidationEnabled()); @@ -436,14 +436,14 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("emulatorLanguageGroupBox"); } else if (elementName == "fullscreenCheckBox") { text = tr("fullscreenCheckBox"); + } else if (elementName == "separateUpdatesCheckBox") { + text = tr("separateUpdatesCheckBox"); } else if (elementName == "showSplashCheckBox") { text = tr("showSplashCheckBox"); } else if (elementName == "ps4proCheckBox") { text = tr("ps4proCheckBox"); } else if (elementName == "discordRPCCheckbox") { text = tr("discordRPCCheckbox"); - } else if (elementName == "separateUpdatesCheckBox") { - text = tr("separateUpdatesCheckBox"); } else if (elementName == "userName") { text = tr("userName"); } else if (elementName == "logTypeGroupBox") { diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 5d1225a1e..cce728f65 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -12,7 +12,7 @@ 0 0 854 - 630 + 660 diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index 463fd9716..f8f7f5336 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -140,6 +140,26 @@ Copy All نسخ الكل + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG PKG تثبيت + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen تمكين ملء الشاشة + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox تمكين وضع ملء الشاشة:\nيجعل نافذة اللعبة تنتقل تلقائيًا إلى وضع ملء الشاشة.\nيمكن التبديل بالضغط على المفتاح F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index b3a6b9001..4bdc75828 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Aktiver fuld skærm:\nSætter automatisk spilvinduet i fuld skærm.\nDette kan skiftes ved at trykke på F11-tasten. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index 75a14b348..2a436ec2c 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -140,6 +140,26 @@ Copy All Alles kopieren + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG PKG installieren + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Vollbild aktivieren + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Vollbildmodus aktivieren:\nSchaltet das Spielfenster automatisch in den Vollbildmodus.\nKann durch Drücken der F11-Taste umgeschaltet werden. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index 871709297..b7d51bfb3 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Ενεργοποίηση Πλήρους Οθόνης:\nΑυτόματα μετατρέπει το παράθυρο του παιχνιδιού σε λειτουργία πλήρους οθόνης.\nΜπορεί να ενεργοποιηθεί/απενεργοποιηθεί πατώντας το πλήκτρο F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index ab7232978..5b58d865f 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,7 +1121,7 @@ fullscreenCheckBox Enable Full Screen:\nAutomatically puts the game window into full-screen mode.\nThis can be toggled by pressing the F11 key. - + separateUpdatesCheckBox Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 1a5a8c887..032579f91 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -140,6 +140,26 @@ Copy All Copiar todo + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Instalar PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Habilitar pantalla completa + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Habilitar Pantalla Completa:\nColoca automáticamente la ventana del juego en modo de pantalla completa.\nEsto se puede alternar presionando la tecla F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 0a6b21916..8976d185c 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -140,6 +140,26 @@ Copy All کپی کردن تمامی مقادیر + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG نصب PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen تمام صفحه + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Enable Full Screen:\nAutomatically puts the game window into full-screen mode.\nThis can be toggled by pressing the F11 key. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index 98efe9c8f..c2cb6c5e8 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Ota Täysikokoisuus käyttöön:\nSiirtää pelin ikkunan automaattisesti täysikokoiseen tilaan.\nTätä voidaan vaihtaa painamalla F11-näppäintä. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index 8e371908b..f891b7475 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -140,6 +140,26 @@ Copy All Copier tout + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Installer un PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Plein écran + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Activer le mode plein écran:\nMet automatiquement la fenêtre du jeu en mode plein écran.\nCela peut être activé en appuyant sur la touche F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 8104a3d98..500629682 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -140,6 +140,26 @@ Copy All Összes Másolása + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG PKG telepítése + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Teljesképernyő Engedélyezése + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Teljes képernyő engedélyezése:\nAutomatikusan teljes képernyőre állítja a játék ablakát.\nEz a F11 billentyű megnyomásával kapcsolható ki/be. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index 288149346..8721e3a8a 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Aktifkan Mode Layar Penuh:\nSecara otomatis menempatkan jendela permainan dalam mode layar penuh.\nIni dapat dinonaktifkan dengan menekan tombol F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index 47000d57f..747148b6e 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -140,6 +140,26 @@ Copy All Copia Tutto + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Installa PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Abilita Schermo Intero + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Abilita Schermo Intero:\nMetti automaticamente la finestra di gioco in modalità schermo intero.\nQuesto può essere disattivato premendo il tasto F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index 952ceb5ab..a020b1687 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -140,6 +140,26 @@ Copy All すべてコピー + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG PKGをインストール + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen フルスクリーンを有効にする + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox 全画面モードを有効にする:\nゲームウィンドウを自動的に全画面モードにします。\nF11キーを押すことで切り替えることができます。 + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index d7d200672..03ab49ad6 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Enable Full Screen:\nAutomatically puts the game window into full-screen mode.\nThis can be toggled by pressing the F11 key. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index f82a20a9d..aecf5aec9 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Įjungti visą ekraną:\nAutomatiškai perjungia žaidimo langą į viso ekrano režimą.\nTai galima išjungti paspaudus F11 klavišą. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index 6fdf5ff8e..303b5f831 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Aktiver fullskjerm:\nSetter automatisk spillvinduet i fullskjermmodus.\nDette kan slås av ved å trykke på F11-tasten. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index f99ad54da..c4f49b93e 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Volledig scherm inschakelen:\nZet het gamevenster automatisch in de volledig scherm modus.\nDit kan worden omgeschakeld door op de F11-toets te drukken. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index a1fd3dc6a..0f6a928b4 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -140,6 +140,26 @@ Copy All Kopiuj wszystko + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Zainstaluj PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Włącz pełny ekran + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Włącz tryb pełnoekranowy:\nAutomatycznie przełącza okno gry w tryb pełnoekranowy.\nMożna to wyłączyć naciskając klawisz F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index 50c575623..c1c2c438b 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -140,6 +140,26 @@ Copy All Copiar Tudo + + + Delete... + Deletar... + + + + Delete Game + Deletar Jogo + + + + Delete Update + Deletar Atualização + + + + Delete DLC + Deletar DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Instalar PKG + + + Game + Jogo + + + + requiresEnableSeparateUpdateFolder_MSG + Este recurso requer a opção de configuração 'Habilitar Pasta de Atualização Separada' para funcionar. Se você quiser usar este recurso, habilite-o. + + + + This game has no update to delete! + Este jogo não tem atualização para excluir! + + + + Update + Atualização + + + + This game has no DLC to delete! + Este jogo não tem DLC para excluir! + + + + DLC + DLC + + + + Delete %1 + Deletar %1 + + + + Are you sure you want to delete %1's %2 directory? + Tem certeza de que deseja excluir o diretório %2 de %1 ? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Ativar Tela Cheia + + + Enable Separate Update Folder + Habilitar pasta de atualização separada + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Ativar Tela Cheia:\nMove automaticamente a janela do jogo para o modo tela cheia.\nIsso pode ser alterado pressionando a tecla F11. + + + separateUpdatesCheckBox + Habilitar pasta de atualização separada:\nPermite instalar atualizações de jogos em uma pasta separada para fácil gerenciamento. + showSplashCheckBox diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index ba2952976..ffb6b6547 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Activează modul pe ecran complet:\nPune automat fereastra jocului în modul pe ecran complet.\nAceasta poate fi dezactivată apăsând tasta F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 5b29bf7c7..c7722635c 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -140,6 +140,26 @@ Copy All Копировать все + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Установить PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Полноэкранный режим + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Полноэкранный режим:\nАвтоматически переводит игровое окно в полноэкранный режим.\nВы можете отключить это, нажав клавишу F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index fabc79cca..fd835cb81 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -140,6 +140,26 @@ Copy All Kopjo të Gjitha + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Instalo PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Aktivizo Ekranin e plotë + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Aktivizo ekranin e plotë:\nVendos automatikisht dritaren e lojës në mënyrën e ekranit të plotë.\nKjo mund të aktivizohet duke shtypur tastin F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox @@ -1383,4 +1453,4 @@ Krijimi i skedarit skript të përditësimit dështoi - + \ No newline at end of file diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 91ede7319..86930764f 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -140,6 +140,26 @@ Copy All Tümünü Kopyala + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG PKG Yükle + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Tam Ekranı Etkinleştir + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Tam Ekranı Etkinleştir:\nOyun penceresini otomatik olarak tam ekran moduna alır.\nBu, F11 tuşuna basarak geçiş yapılabilir. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index ebdee76e5..8bc241ece 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox Bật chế độ toàn màn hình:\nTự động đặt cửa sổ trò chơi ở chế độ toàn màn hình.\nĐiều này có thể bị vô hiệu hóa bằng cách nhấn phím F11. + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 8533c5456..bc943c860 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -140,6 +140,26 @@ Copy All 复制全部 + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG 安装 PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen 启用全屏 + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox 启用全屏模式:\n自动将游戏窗口设置为全屏模式。\n您可以按 F11 键禁用此选项。 + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index f3db48971..c7b923f83 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -140,6 +140,26 @@ Copy All Copy All + + + Delete... + Delete... + + + + Delete Game + Delete Game + + + + Delete Update + Delete Update + + + + Delete DLC + Delete DLC + Shortcut creation @@ -165,6 +185,46 @@ Install PKG Install PKG + + + Game + Game + + + + requiresEnableSeparateUpdateFolder_MSG + This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + + + + This game has no update to delete! + This game has no update to delete! + + + + Update + Update + + + + This game has no DLC to delete! + This game has no DLC to delete! + + + + DLC + DLC + + + + Delete %1 + Delete %1 + + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + MainWindow @@ -417,6 +477,11 @@ Enable Fullscreen Enable Fullscreen + + + Enable Separate Update Folder + Enable Separate Update Folder + Show Splash @@ -1056,6 +1121,11 @@ fullscreenCheckBox 啟用全螢幕:\n自動將遊戲視窗設置為全螢幕模式。\n可以按F11鍵進行切換。 + + + separateUpdatesCheckBox + Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + showSplashCheckBox From 70430313e3da4d5454afc7b6b5c6d0914e83fb9e Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Wed, 16 Oct 2024 19:19:10 +0300 Subject: [PATCH 75/93] hot fix: fixed sceNpGetOnlineId function --- src/core/libraries/np_manager/np_manager.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp index 907244b22..4fff59003 100644 --- a/src/core/libraries/np_manager/np_manager.cpp +++ b/src/core/libraries/np_manager/np_manager.cpp @@ -987,8 +987,12 @@ int PS4_SYSV_ABI sceNpGetNpReachabilityState() { } int PS4_SYSV_ABI sceNpGetOnlineId(s32 userId, OrbisNpOnlineId* onlineId) { - LOG_DEBUG(Lib_NpManager, "called returned sign out"); - return ORBIS_NP_ERROR_SIGNED_OUT; + LOG_DEBUG(Lib_NpManager, "userId {}", userId); + std::string name = Config::getUserName(); + // Fill the unused stuffs to 0 + memset(onlineId, 0, sizeof(*onlineId)); + strcpy(onlineId->data, name.c_str()); + return ORBIS_OK; } int PS4_SYSV_ABI sceNpGetParentalControlInfo() { From 0f0d9740c2943aa20ea187880fd251a6d3c67355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Wed, 16 Oct 2024 18:34:17 +0200 Subject: [PATCH 76/93] Update CMakeLists.txt (#1396) --- externals/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 9cae34381..df9050a69 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -186,6 +186,7 @@ endif() # Discord RPC set(BUILD_EXAMPLES OFF) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -w") add_subdirectory(discord-rpc/) target_include_directories(discord-rpc INTERFACE discord-rpc/include) From f79b7f10ab5ac083affa3cd90d311504153f12e6 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Wed, 16 Oct 2024 19:51:16 +0300 Subject: [PATCH 77/93] Revert "Update CMakeLists.txt (#1396)" This reverts commit 0f0d9740c2943aa20ea187880fd251a6d3c67355. --- externals/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index df9050a69..9cae34381 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -186,7 +186,6 @@ endif() # Discord RPC set(BUILD_EXAMPLES OFF) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -w") add_subdirectory(discord-rpc/) target_include_directories(discord-rpc INTERFACE discord-rpc/include) From b4ced58acc7a5d7e3043acb6bb449d6252957184 Mon Sep 17 00:00:00 2001 From: psucien Date: Wed, 16 Oct 2024 23:46:20 +0200 Subject: [PATCH 78/93] hot-fix: a fallback when overlap resolve fails --- src/video_core/texture_cache/texture_cache.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 4813a3c57..00e6bea82 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -237,6 +237,16 @@ ImageId TextureCache::FindImage(const ImageInfo& info, FindFlags flags) { } } + if (image_id) { + Image& image_resoved = slot_images[image_id]; + + if (image_resoved.info.resources < info.resources) { + // The image was clearly picked up wrong. + FreeImage(image_id); + image_id = {}; + LOG_WARNING(Render_Vulkan, "Image overlap resolve failed"); + } + } // Create and register a new image if (!image_id) { image_id = slot_images.insert(instance, scheduler, info); From aa958c4d19ff119e4edef3348b7f78a860676ea7 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Wed, 16 Oct 2024 21:24:10 -0700 Subject: [PATCH 79/93] build: Make sure pkg-config is configured correctly for macOS cross compile. (#1399) --- CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ff7717c76..27b11aa28 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,9 +47,15 @@ else() message(FATAL_ERROR "Unsupported CPU architecture: ${BASE_ARCHITECTURE}") endif() -if (APPLE AND ARCHITECTURE STREQUAL "x86_64") +if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64") # Exclude ARM homebrew path to avoid conflicts when cross compiling. list(APPEND CMAKE_IGNORE_PREFIX_PATH "/opt/homebrew") + + # Need to reconfigure pkg-config to use the right architecture library paths. + # It's not ideal to override these but otherwise the build breaks just by having pkg-config installed. + set(ENV{PKG_CONFIG_DIR} "") + set(ENV{PKG_CONFIG_LIBDIR} "${CMAKE_SYSROOT}/usr/lib/pkgconfig:${CMAKE_SYSROOT}/usr/share/pkgconfig:${CMAKE_SYSROOT}/usr/local/lib/pkgconfig:${CMAKE_SYSROOT}/usr/local/share/pkgconfig") + set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT}) endif() # This function should be passed a list of all files in a target. It will automatically generate file groups From 88c37048acb26ddda9dddf4451f1f6b6cb6df661 Mon Sep 17 00:00:00 2001 From: ElBread3 <92335081+ElBread3@users.noreply.github.com> Date: Thu, 17 Oct 2024 03:49:29 -0500 Subject: [PATCH 80/93] Load Eboot/Modules from Separate Update Folder (#1397) * load eboot from separate update folder * clarify --------- Co-authored-by: georgemoralis --- src/core/file_sys/fs.cpp | 3 ++- src/emulator.cpp | 27 ++++++++++++++++++--------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index 8e6d74622..afa231c15 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -58,7 +58,8 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view guest_directory, b std::filesystem::path patch_path = mount->host_path; patch_path += "-UPDATE"; - if (corrected_path.starts_with("/app0/") && std::filesystem::exists(patch_path / rel_path)) { + if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) && + std::filesystem::exists(patch_path / rel_path)) { host_path = patch_path / rel_path; } diff --git a/src/emulator.cpp b/src/emulator.cpp index 46bcfea37..a3019c9ca 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -100,6 +100,12 @@ Emulator::~Emulator() { } void Emulator::Run(const std::filesystem::path& file) { + + // Use the eboot from the separated updates folder if it's there + std::filesystem::path game_patch_folder = file.parent_path().concat("-UPDATE"); + bool use_game_patch = std::filesystem::exists(game_patch_folder / "sce_sys"); + std::filesystem::path eboot_path = use_game_patch ? game_patch_folder / file.filename() : file; + // Applications expect to be run from /app0 so mount the file's parent path as app0. auto* mnt = Common::Singleton::Instance(); mnt->Mount(file.parent_path(), "/app0"); @@ -114,10 +120,7 @@ void Emulator::Run(const std::filesystem::path& file) { std::string app_version; u32 fw_version; - std::filesystem::path game_patch_folder = file.parent_path().concat("-UPDATE"); - bool use_game_patch = std::filesystem::exists(game_patch_folder / "sce_sys"); - std::filesystem::path sce_sys_folder = - use_game_patch ? game_patch_folder / "sce_sys" : file.parent_path() / "sce_sys"; + std::filesystem::path sce_sys_folder = eboot_path.parent_path() / "sce_sys"; if (std::filesystem::is_directory(sce_sys_folder)) { for (const auto& entry : std::filesystem::directory_iterator(sce_sys_folder)) { if (entry.path().filename() == "param.sfo") { @@ -132,7 +135,7 @@ void Emulator::Run(const std::filesystem::path& file) { Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / id / "TrophyFiles"; if (!std::filesystem::exists(trophyDir)) { TRP trp; - if (!trp.Extract(file.parent_path(), id)) { + if (!trp.Extract(eboot_path.parent_path(), id)) { LOG_ERROR(Loader, "Couldn't extract trophies"); } } @@ -221,17 +224,23 @@ void Emulator::Run(const std::filesystem::path& file) { Libraries::InitHLELibs(&linker->GetHLESymbols()); // Load the module with the linker - linker->LoadModule(file); + linker->LoadModule(eboot_path); // check if we have system modules to load - LoadSystemModules(file); + LoadSystemModules(eboot_path); // Load all prx from game's sce_module folder std::filesystem::path sce_module_folder = file.parent_path() / "sce_module"; if (std::filesystem::is_directory(sce_module_folder)) { for (const auto& entry : std::filesystem::directory_iterator(sce_module_folder)) { - LOG_INFO(Loader, "Loading {}", fmt::UTF(entry.path().u8string())); - linker->LoadModule(entry.path()); + std::filesystem::path module_path = entry.path(); + std::filesystem::path update_module_path = + eboot_path.parent_path() / "sce_module" / entry.path().filename(); + if (std::filesystem::exists(update_module_path) && use_game_patch) { + module_path = update_module_path; + } + LOG_INFO(Loader, "Loading {}", fmt::UTF(module_path.u8string())); + linker->LoadModule(module_path); } } From ac6b4a625da34811c56d64b9c89b1ffff9a572af Mon Sep 17 00:00:00 2001 From: psucien Date: Thu, 17 Oct 2024 23:06:58 +0200 Subject: [PATCH 81/93] hot-fix: address check in mips overlap heuristic --- src/video_core/texture_cache/image_info.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 521e4118f..efb74ffae 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -329,6 +329,11 @@ bool ImageInfo::IsMipOf(const ImageInfo& info) const { return false; } + // Ensure that address matches too. + if ((info.guest_address + info.mips_layout[mip].offset) != guest_address) { + return false; + } + return true; } From 181b00563652932e916d3df21af604765b3d149b Mon Sep 17 00:00:00 2001 From: Yury <27062841+f1amy@users.noreply.github.com> Date: Fri, 18 Oct 2024 09:51:22 +0500 Subject: [PATCH 82/93] New translations and fixes for ru_RU (#1402) --- src/qt_gui/translations/ru_RU.ts | 60 ++++++++++++++++---------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index c7722635c..73263587d 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -62,7 +62,7 @@ Select which directory you want to install to. - Select which directory you want to install to. + Выберите папку, в которую вы хотите установить. @@ -143,22 +143,22 @@ Delete... - Delete... + Удаление... Delete Game - Delete Game + Удалить игру Delete Update - Delete Update + Удалить обновление Delete DLC - Delete DLC + Удалить DLC @@ -188,27 +188,27 @@ Game - Game + Игры requiresEnableSeparateUpdateFolder_MSG - This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it. + Эта функция требует включения настройки 'Отдельная папка обновлений'. Если вы хотите использовать эту функцию, пожалуйста включите её. This game has no update to delete! - This game has no update to delete! + У этой игры нет обновлений для удаления! Update - Update + Обновления This game has no DLC to delete! - This game has no DLC to delete! + У этой игры нет DLC для удаления! @@ -218,12 +218,12 @@ Delete %1 - Delete %1 + Удалить %1 Are you sure you want to delete %1's %2 directory? - Are you sure you want to delete %1's %2 directory? + Вы уверены, что хотите удалить папку %2 %1? @@ -480,7 +480,7 @@ Enable Separate Update Folder - Enable Separate Update Folder + Отдельная папка обновлений @@ -525,17 +525,17 @@ Cursor - Курсор + Курсор мыши Hide Cursor - Скрыть курсор + Скрывать курсор Hide Cursor Idle Timeout - Тайм-аут скрытия курсора при неактивности + Тайм-аут скрытия курсора при бездействии @@ -640,7 +640,7 @@ Check for Updates at Startup - Проверка обновлений при запуске + Проверка при запуске @@ -655,12 +655,12 @@ GUI Settings - Настройки интерфейса + Интерфейс Play title music - Воспроизведение заглавной музыки + Играть заглавную музыку @@ -1124,7 +1124,7 @@ separateUpdatesCheckBox - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + Отдельная папка обновлений:\nПозволяет устанавливать обновления игры в отдельную папку для удобства. @@ -1164,22 +1164,22 @@ GUIgroupBox - Воспроизведение заглавной музыки:\nЕсли игра это поддерживает, включает воспроизведение специальной музыки при выборе игры в интерфейсе. + Играть заглавную музыку:\nВключает воспроизведение специальной музыки при выборе игры в списке, если она это поддерживает. hideCursorGroupBox - Скрыть курсор:\nВыберите, когда курсор исчезнет:\nНикогда: Вы всегда будете видеть мышь.\nНеактивный: Yстановите время, через которое она исчезнет после бездействия.\nВсегда: вы никогда не увидите мышь. + Скрывать курсор:\nВыберите, когда курсор исчезнет:\nНикогда: Вы всегда будете видеть мышь.\nПри бездействии: Установите время, через которое курсор исчезнет при бездействии.\nВсегда: Вы никогда не будете видеть мышь. idleTimeoutGroupBox - Установите время, через которое курсор исчезнет после бездействия. + Установите время, через которое курсор исчезнет при бездействии. backButtonBehaviorGroupBox - Поведение кнопки «Назад»:\nУстанавливает кнопку «Назад» на контроллере для имитации нажатия в указанной позиции на сенсорной панели PS4. + Поведение кнопки «Назад»:\nНастраивает кнопку «Назад» контроллера на эмуляцию нажатия на указанную область на сенсорной панели контроллера PS4. @@ -1189,7 +1189,7 @@ Idle - Неактивный + При бездействии @@ -1199,17 +1199,17 @@ Touchpad Left - Тачпад Слева + Тачпад слева Touchpad Right - Тачпад Справа + Тачпад справа Touchpad Center - Центр Тачпада + Центр тачпада @@ -1322,7 +1322,7 @@ Play Time - Время Игры + Времени в игре @@ -1400,7 +1400,7 @@ Check for Updates at Startup - Проверка обновлений при запуске + Проверка при запуске From ddc35639a8451b20dba8267767af76caff0c4ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Fri, 18 Oct 2024 11:51:51 +0700 Subject: [PATCH 83/93] core/fs: fix file path when using separated update folder for *nix (#1403) --- src/core/file_sys/fs.cpp | 130 ++++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 64 deletions(-) diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index afa231c15..b5f1711db 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -28,16 +28,11 @@ void MntPoints::UnmountAll() { m_mnt_pairs.clear(); } -std::filesystem::path MntPoints::GetHostPath(std::string_view guest_directory, bool* is_read_only) { +std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_read_only) { // Evil games like Turok2 pass double slashes e.g /app0//game.kpf - std::string corrected_path(guest_directory); - size_t pos = corrected_path.find("//"); - while (pos != std::string::npos) { - corrected_path.replace(pos, 2, "/"); - pos = corrected_path.find("//", pos + 1); - } + const auto normalized_path = std::filesystem::path(path).lexically_normal().string(); - const MntPair* mount = GetMount(corrected_path); + const MntPair* mount = GetMount(normalized_path); if (!mount) { return ""; } @@ -47,81 +42,88 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view guest_directory, b } // Nothing to do if getting the mount itself. - if (corrected_path == mount->mount) { + if (normalized_path == mount->mount) { return mount->host_path; } // Remove device (e.g /app0) from path to retrieve relative path. - pos = mount->mount.size() + 1; - const auto rel_path = std::string_view(corrected_path).substr(pos); + const auto rel_path = std::string_view{normalized_path}.substr(mount->mount.size() + 1); std::filesystem::path host_path = mount->host_path / rel_path; - std::filesystem::path patch_path = mount->host_path; patch_path += "-UPDATE"; - if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) && - std::filesystem::exists(patch_path / rel_path)) { - host_path = patch_path / rel_path; + patch_path /= rel_path; + + if ((normalized_path.starts_with("/app0") || normalized_path.starts_with("/hostapp")) && + std::filesystem::exists(patch_path)) { + return patch_path; } if (!NeedsCaseInsensitiveSearch) { return host_path; } - // If the path does not exist attempt to verify this. - // Retrieve parent path until we find one that exists. - std::scoped_lock lk{m_mutex}; - path_parts.clear(); - auto current_path = host_path; - while (!std::filesystem::exists(current_path)) { - // We have probably cached this if it's a folder. - if (auto it = path_cache.find(current_path); it != path_cache.end()) { - current_path = it->second; - break; + const auto search = [&](const auto host_path) { + // If the path does not exist attempt to verify this. + // Retrieve parent path until we find one that exists. + std::scoped_lock lk{m_mutex}; + path_parts.clear(); + auto current_path = host_path; + while (!std::filesystem::exists(current_path)) { + // We have probably cached this if it's a folder. + if (auto it = path_cache.find(current_path); it != path_cache.end()) { + current_path = it->second; + break; + } + path_parts.emplace_back(current_path.filename()); + current_path = current_path.parent_path(); } - path_parts.emplace_back(current_path.filename()); - current_path = current_path.parent_path(); - } - - // We have found an anchor. Traverse parts we recoded and see if they - // exist in filesystem but in different case. - auto guest_path = current_path; - while (!path_parts.empty()) { - const auto part = path_parts.back(); - const auto add_match = [&](const auto& host_part) { - current_path /= host_part; - guest_path /= part; - path_cache[guest_path] = current_path; - path_parts.pop_back(); - }; - - // Can happen when the mismatch is in upper folder. - if (std::filesystem::exists(current_path / part)) { - add_match(part); - continue; - } - const auto part_low = Common::ToLower(part.string()); - bool found_match = false; - for (const auto& path : std::filesystem::directory_iterator(current_path)) { - const auto candidate = path.path().filename(); - const auto filename = Common::ToLower(candidate.string()); - // Check if a filename matches in case insensitive manner. - if (filename != part_low) { + // We have found an anchor. Traverse parts we recoded and see if they + // exist in filesystem but in different case. + auto guest_path = current_path; + while (!path_parts.empty()) { + const auto part = path_parts.back(); + const auto add_match = [&](const auto& host_part) { + current_path /= host_part; + guest_path /= part; + path_cache[guest_path] = current_path; + path_parts.pop_back(); + }; + // Can happen when the mismatch is in upper folder. + if (std::filesystem::exists(current_path / part)) { + add_match(part); continue; } - // We found a match, record the actual path in the cache. - add_match(candidate); - found_match = true; - break; - } - if (!found_match) { - // Opening the guest path will surely fail but at least gives - // a better error message than the empty path. - return host_path; + const auto part_low = Common::ToLower(part.string()); + bool found_match = false; + for (const auto& path : std::filesystem::directory_iterator(current_path)) { + const auto candidate = path.path().filename(); + const auto filename = Common::ToLower(candidate.string()); + // Check if a filename matches in case insensitive manner. + if (filename != part_low) { + continue; + } + // We found a match, record the actual path in the cache. + add_match(candidate); + found_match = true; + break; + } + if (!found_match) { + return std::optional({}); + } } + return std::optional(current_path); + }; + + if (const auto path = search(patch_path)) { + return *path; + } + if (const auto path = search(host_path)) { + return *path; } - // The path was found. - return current_path; + // Opening the guest path will surely fail but at least gives + // a better error message than the empty path. + return host_path; } int HandleTable::CreateHandle() { From 9814a1b78827ed73e1f2fe454465a6c45d3a2d72 Mon Sep 17 00:00:00 2001 From: Alexandre Bouvier Date: Fri, 18 Oct 2024 04:52:57 +0000 Subject: [PATCH 84/93] cmake: unbundle half library (#1393) --- CMakeLists.txt | 3 ++- cmake/Findhalf.cmake | 28 ++++++++++++++++++++++++++++ externals/CMakeLists.txt | 7 +++++-- 3 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 cmake/Findhalf.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 27b11aa28..9cfc0d8ef 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,6 +109,7 @@ find_package(Boost 1.84.0 CONFIG) find_package(FFmpeg 5.1.2 MODULE) find_package(fmt 10.2.0 CONFIG) find_package(glslang 14.2.0 CONFIG) +find_package(half 1.12.0 MODULE) find_package(magic_enum 0.9.6 CONFIG) find_package(RenderDoc 1.6.0 MODULE) find_package(SDL3 3.1.2 CONFIG) @@ -779,7 +780,7 @@ endif() create_target_directory_groups(shadps4) -target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half) +target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half) target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml) target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") diff --git a/cmake/Findhalf.cmake b/cmake/Findhalf.cmake new file mode 100644 index 000000000..f95a791df --- /dev/null +++ b/cmake/Findhalf.cmake @@ -0,0 +1,28 @@ +# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +find_path(half_INCLUDE_DIR NAMES half.hpp PATH_SUFFIXES half) + +if (half_INCLUDE_DIR) + file(STRINGS "${half_INCLUDE_DIR}/half.hpp" _ver_line + REGEX "^// Version [0-9.]+$" + LIMIT_COUNT 1 + ) + string(REGEX MATCH "[0-9.]+" half_VERSION "${_ver_line}") + unset(_ver_line) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(half + REQUIRED_VARS half_INCLUDE_DIR + VERSION_VAR half_VERSION +) + +if (half_FOUND AND NOT TARGET half::half) + add_library(half::half INTERFACE IMPORTED) + set_target_properties(half::half PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${half_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced(half_INCLUDE_DIR) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 9cae34381..80f49f527 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -142,8 +142,11 @@ if (WIN32) endif() # half -add_library(half INTERFACE) -target_include_directories(half INTERFACE half/include) +if (NOT TARGET half::half) + add_library(half INTERFACE) + target_include_directories(half INTERFACE half/include) + add_library(half::half ALIAS half) +endif() if (APPLE) # date From 96ea686eb6b3c4fe0c9034171647103d1dc2eb7c Mon Sep 17 00:00:00 2001 From: Herman Semenoff Date: Fri, 18 Oct 2024 08:06:11 +0000 Subject: [PATCH 85/93] Fixed return strict const iterator, replace to range-based loop C++17 and code refactor (#548) Signed-off-by: Herman Semenov Co-authored-by: georgemoralis --- .../libraries/kernel/threads/semaphore.cpp | 4 +-- src/core/memory.cpp | 6 ++-- .../frontend/control_flow_graph.cpp | 6 ++-- .../frontend/structured_control_flow.cpp | 30 +++++++++---------- src/shader_recompiler/ir/basic_block.h | 4 +-- src/video_core/buffer_cache/range_set.h | 9 +++--- .../renderer_vulkan/renderer_vulkan.h | 2 +- .../renderer_vulkan/vk_swapchain.cpp | 2 +- 8 files changed, 31 insertions(+), 32 deletions(-) diff --git a/src/core/libraries/kernel/threads/semaphore.cpp b/src/core/libraries/kernel/threads/semaphore.cpp index ff5368023..59099c1b8 100644 --- a/src/core/libraries/kernel/threads/semaphore.cpp +++ b/src/core/libraries/kernel/threads/semaphore.cpp @@ -59,7 +59,7 @@ public: for (auto it = wait_list.begin(); it != wait_list.end();) { auto* waiter = *it; if (waiter->need_count > token_count) { - it++; + ++it; continue; } it = wait_list.erase(it); @@ -148,7 +148,7 @@ public: // Find the first with priority less then us and insert right before it. auto it = wait_list.begin(); while (it != wait_list.end() && (*it)->priority > waiter->priority) { - it++; + ++it; } wait_list.insert(it, waiter); return it; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 644f9389f..471ba5856 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -98,7 +98,7 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size, return dmem_area->second.is_free && remaining_size >= size; }; while (!is_suitable() && dmem_area->second.GetEnd() <= search_end) { - dmem_area++; + ++dmem_area; } ASSERT_MSG(is_suitable(), "Unable to find free direct memory area: size = {:#x}", size); @@ -487,7 +487,7 @@ int MemoryManager::VirtualQuery(VAddr addr, int flags, auto it = FindVMA(addr); if (it->second.type == VMAType::Free && flags == 1) { - it++; + ++it; } if (it->second.type == VMAType::Free) { LOG_WARNING(Kernel_Vmm, "VirtualQuery on free memory region"); @@ -603,7 +603,7 @@ VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment) return remaining_size >= size; }; while (!is_suitable()) { - it++; + ++it; } return virtual_addr; } diff --git a/src/shader_recompiler/frontend/control_flow_graph.cpp b/src/shader_recompiler/frontend/control_flow_graph.cpp index 9d481d32c..354196d31 100644 --- a/src/shader_recompiler/frontend/control_flow_graph.cpp +++ b/src/shader_recompiler/frontend/control_flow_graph.cpp @@ -163,10 +163,10 @@ void CFG::EmitDivergenceLabels() { } void CFG::EmitBlocks() { - for (auto it = labels.begin(); it != labels.end(); it++) { + for (auto it = labels.cbegin(); it != labels.cend(); ++it) { const Label start = *it; const auto next_it = std::next(it); - const bool is_last = next_it == labels.end(); + const bool is_last = (next_it == labels.cend()); if (is_last) { // Last label is special. return; @@ -193,7 +193,7 @@ void CFG::EmitBlocks() { void CFG::LinkBlocks() { const auto get_block = [this](u32 address) { auto it = blocks.find(address, Compare{}); - ASSERT_MSG(it != blocks.end() && it->begin == address); + ASSERT_MSG(it != blocks.cend() && it->begin == address); return &*it; }; diff --git a/src/shader_recompiler/frontend/structured_control_flow.cpp b/src/shader_recompiler/frontend/structured_control_flow.cpp index bf5ba6bce..11b40d07c 100644 --- a/src/shader_recompiler/frontend/structured_control_flow.cpp +++ b/src/shader_recompiler/frontend/structured_control_flow.cpp @@ -144,32 +144,32 @@ std::string DumpExpr(const Statement* stmt) { [[maybe_unused]] std::string DumpTree(const Tree& tree, u32 indentation = 0) { std::string ret; std::string indent(indentation, ' '); - for (auto stmt = tree.begin(); stmt != tree.end(); ++stmt) { - switch (stmt->type) { + for (const auto& stmt : tree) { + switch (stmt.type) { case StatementType::Code: ret += fmt::format("{} Block {:04x} -> {:04x} (0x{:016x});\n", indent, - stmt->block->begin, stmt->block->end, - reinterpret_cast(stmt->block)); + stmt.block->begin, stmt.block->end, + reinterpret_cast(stmt.block)); break; case StatementType::Goto: - ret += fmt::format("{} if ({}) goto L{};\n", indent, DumpExpr(stmt->cond), - stmt->label->id); + ret += fmt::format("{} if ({}) goto L{};\n", indent, DumpExpr(stmt.cond), + stmt.label->id); break; case StatementType::Label: - ret += fmt::format("{}L{}:\n", indent, stmt->id); + ret += fmt::format("{}L{}:\n", indent, stmt.id); break; case StatementType::If: - ret += fmt::format("{} if ({}) {{\n", indent, DumpExpr(stmt->cond)); - ret += DumpTree(stmt->children, indentation + 4); + ret += fmt::format("{} if ({}) {{\n", indent, DumpExpr(stmt.cond)); + ret += DumpTree(stmt.children, indentation + 4); ret += fmt::format("{} }}\n", indent); break; case StatementType::Loop: ret += fmt::format("{} do {{\n", indent); - ret += DumpTree(stmt->children, indentation + 4); - ret += fmt::format("{} }} while ({});\n", indent, DumpExpr(stmt->cond)); + ret += DumpTree(stmt.children, indentation + 4); + ret += fmt::format("{} }} while ({});\n", indent, DumpExpr(stmt.cond)); break; case StatementType::Break: - ret += fmt::format("{} if ({}) break;\n", indent, DumpExpr(stmt->cond)); + ret += fmt::format("{} if ({}) break;\n", indent, DumpExpr(stmt.cond)); break; case StatementType::Return: ret += fmt::format("{} return;\n", indent); @@ -181,7 +181,7 @@ std::string DumpExpr(const Statement* stmt) { ret += fmt::format("{} unreachable;\n", indent); break; case StatementType::SetVariable: - ret += fmt::format("{} goto_L{} = {};\n", indent, stmt->id, DumpExpr(stmt->op)); + ret += fmt::format("{} goto_L{} = {};\n", indent, stmt.id, DumpExpr(stmt.op)); break; case StatementType::Function: case StatementType::Identity: @@ -625,8 +625,8 @@ private: node.data.block = current_block; }}; Tree& tree{parent.children}; - for (auto it = tree.begin(); it != tree.end(); ++it) { - Statement& stmt{*it}; + for (auto& child : tree) { + Statement& stmt{child}; switch (stmt.type) { case StatementType::Label: // Labels can be ignored diff --git a/src/shader_recompiler/ir/basic_block.h b/src/shader_recompiler/ir/basic_block.h index 11ae969bc..74a7d2c56 100644 --- a/src/shader_recompiler/ir/basic_block.h +++ b/src/shader_recompiler/ir/basic_block.h @@ -109,13 +109,13 @@ public: return instructions.begin(); } [[nodiscard]] const_iterator begin() const { - return instructions.begin(); + return instructions.cbegin(); } [[nodiscard]] iterator end() { return instructions.end(); } [[nodiscard]] const_iterator end() const { - return instructions.end(); + return instructions.cend(); } [[nodiscard]] reverse_iterator rbegin() { diff --git a/src/video_core/buffer_cache/range_set.h b/src/video_core/buffer_cache/range_set.h index fe54aff8f..2abf6e524 100644 --- a/src/video_core/buffer_cache/range_set.h +++ b/src/video_core/buffer_cache/range_set.h @@ -43,11 +43,10 @@ struct RangeSet { if (m_ranges_set.empty()) { return; } - auto it = m_ranges_set.begin(); - auto end_it = m_ranges_set.end(); - for (; it != end_it; it++) { - const VAddr inter_addr_end = it->upper(); - const VAddr inter_addr = it->lower(); + + for (const auto& set : m_ranges_set) { + const VAddr inter_addr_end = set.upper(); + const VAddr inter_addr = set.lower(); func(inter_addr, inter_addr_end); } } diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 0dd869614..a663622fc 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -70,7 +70,7 @@ public: bool IsVideoOutSurface(const AmdGpu::Liverpool::ColorBuffer& color_buffer) { return std::ranges::find_if(vo_buffers_addr, [&](VAddr vo_buffer) { return vo_buffer == color_buffer.Address(); - }) != vo_buffers_addr.end(); + }) != vo_buffers_addr.cend(); } bool ShowSplash(Frame* frame = nullptr); diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 357cd504d..86d7d5063 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -47,7 +47,7 @@ void Swapchain::Create(u32 width_, u32 height_, vk::SurfaceKHR surface_) { std::find_if(modes.begin(), modes.end(), [&requested](vk::PresentModeKHR mode) { return mode == requested; }); - return it != modes.end(); + return it != modes.cend(); }; const bool has_mailbox = find_mode(vk::PresentModeKHR::eMailbox); From 24bce59f7002795de5c86071b3d1a5a97dea7947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Fri, 18 Oct 2024 15:40:27 +0700 Subject: [PATCH 86/93] core/fs: fix file path on Windows (#1404) Revert part of commit ddc35639a8451b20dba8267767af76caff0c4ec9. --- src/core/file_sys/fs.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index b5f1711db..d86f65a2a 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -30,9 +30,13 @@ void MntPoints::UnmountAll() { std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_read_only) { // Evil games like Turok2 pass double slashes e.g /app0//game.kpf - const auto normalized_path = std::filesystem::path(path).lexically_normal().string(); + std::string corrected_path(path); + while (size_t pos = corrected_path.find("//") != std::string::npos) { + corrected_path.replace(pos, 2, "/"); + pos = corrected_path.find("//", pos + 1); + } - const MntPair* mount = GetMount(normalized_path); + const MntPair* mount = GetMount(corrected_path); if (!mount) { return ""; } @@ -42,18 +46,18 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea } // Nothing to do if getting the mount itself. - if (normalized_path == mount->mount) { + if (corrected_path == mount->mount) { return mount->host_path; } // Remove device (e.g /app0) from path to retrieve relative path. - const auto rel_path = std::string_view{normalized_path}.substr(mount->mount.size() + 1); + const auto rel_path = std::string_view{corrected_path}.substr(mount->mount.size() + 1); std::filesystem::path host_path = mount->host_path / rel_path; std::filesystem::path patch_path = mount->host_path; patch_path += "-UPDATE"; patch_path /= rel_path; - if ((normalized_path.starts_with("/app0") || normalized_path.starts_with("/hostapp")) && + if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) && std::filesystem::exists(patch_path)) { return patch_path; } From a13d1d1ab1d9aa40d16e73c9835dbbbc79993251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quang=20Ng=C3=B4?= Date: Fri, 18 Oct 2024 22:56:15 +0700 Subject: [PATCH 87/93] core/fs: fix file path (again) (#1408) Third time's the charm. --- src/core/file_sys/fs.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index d86f65a2a..769940cf0 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -31,7 +31,8 @@ void MntPoints::UnmountAll() { std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_read_only) { // Evil games like Turok2 pass double slashes e.g /app0//game.kpf std::string corrected_path(path); - while (size_t pos = corrected_path.find("//") != std::string::npos) { + size_t pos = corrected_path.find("//"); + while (pos != std::string::npos) { corrected_path.replace(pos, 2, "/"); pos = corrected_path.find("//", pos + 1); } From 47ba6c6344fefb0a0112e8a1874588e3c37ceb11 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Fri, 18 Oct 2024 12:56:31 -0300 Subject: [PATCH 88/93] Patch compatible version notice (#1407) * Patch compatible version notice * + --- src/qt_gui/cheats_patches.cpp | 91 ++++++++++++++++++++++++++++---- src/qt_gui/cheats_patches.h | 2 +- src/qt_gui/translations/ar.ts | 22 +++++++- src/qt_gui/translations/da_DK.ts | 22 +++++++- src/qt_gui/translations/de.ts | 22 +++++++- src/qt_gui/translations/el.ts | 22 +++++++- src/qt_gui/translations/en.ts | 22 +++++++- src/qt_gui/translations/es_ES.ts | 22 +++++++- src/qt_gui/translations/fa_IR.ts | 24 ++++++++- src/qt_gui/translations/fi.ts | 22 +++++++- src/qt_gui/translations/fr.ts | 22 +++++++- src/qt_gui/translations/hu_HU.ts | 22 +++++++- src/qt_gui/translations/id.ts | 22 +++++++- src/qt_gui/translations/it.ts | 22 +++++++- src/qt_gui/translations/ja_JP.ts | 22 +++++++- src/qt_gui/translations/ko_KR.ts | 22 +++++++- src/qt_gui/translations/lt_LT.ts | 22 +++++++- src/qt_gui/translations/nb.ts | 22 +++++++- src/qt_gui/translations/nl.ts | 22 +++++++- src/qt_gui/translations/pl_PL.ts | 22 +++++++- src/qt_gui/translations/pt_BR.ts | 22 +++++++- src/qt_gui/translations/ro_RO.ts | 22 +++++++- src/qt_gui/translations/ru_RU.ts | 22 +++++++- src/qt_gui/translations/sq.ts | 22 +++++++- src/qt_gui/translations/tr_TR.ts | 22 +++++++- src/qt_gui/translations/vi_VN.ts | 22 +++++++- src/qt_gui/translations/zh_CN.ts | 22 +++++++- src/qt_gui/translations/zh_TW.ts | 22 +++++++- 28 files changed, 629 insertions(+), 38 deletions(-) diff --git a/src/qt_gui/cheats_patches.cpp b/src/qt_gui/cheats_patches.cpp index c044c2c3c..6e2e4f208 100644 --- a/src/qt_gui/cheats_patches.cpp +++ b/src/qt_gui/cheats_patches.cpp @@ -32,8 +32,6 @@ #include "common/path_util.h" #include "core/module.h" -using namespace Common::FS; - CheatsPatches::CheatsPatches(const QString& gameName, const QString& gameSerial, const QString& gameVersion, const QString& gameSize, const QPixmap& gameImage, QWidget* parent) @@ -776,6 +774,7 @@ void CheatsPatches::downloadPatches(const QString repository, const bool showMes // Create the files.json file with the identification of which file to open createFilesJson(repository); populateFileListPatches(); + compatibleVersionNotice(repository); } else { if (showMessageBox) { QMessageBox::warning(this, tr("Error"), @@ -787,6 +786,84 @@ void CheatsPatches::downloadPatches(const QString repository, const bool showMes }); } +void CheatsPatches::compatibleVersionNotice(const QString repository) { + QDir patchesDir(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir)); + QDir dir = patchesDir.filePath(repository); + + QStringList xmlFiles = dir.entryList(QStringList() << "*.xml", QDir::Files); + QSet appVersionsSet; + + foreach (const QString& xmlFile, xmlFiles) { + QFile file(dir.filePath(xmlFile)); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + QMessageBox::warning(this, tr("ERROR"), + QString(tr("Failed to open file:") + "\n%1").arg(xmlFile)); + continue; + } + + QXmlStreamReader xmlReader(&file); + bool foundMatchingID = false; + + while (!xmlReader.atEnd() && !xmlReader.hasError()) { + QXmlStreamReader::TokenType token = xmlReader.readNext(); + if (token == QXmlStreamReader::StartElement) { + if (xmlReader.name() == QStringLiteral("ID")) { + QString id = xmlReader.readElementText(); + if (id == m_gameSerial) { + foundMatchingID = true; + } + } else if (xmlReader.name() == QStringLiteral("Metadata")) { + if (foundMatchingID) { + QString appVer = xmlReader.attributes().value("AppVer").toString(); + if (!appVer.isEmpty()) { + appVersionsSet.insert(appVer); + } + } + } + } + } + + if (xmlReader.hasError()) { + QMessageBox::warning(this, tr("ERROR"), + QString(tr("XML ERROR:") + "\n%1").arg(xmlReader.errorString())); + } + + if (foundMatchingID) { + QStringList incompatibleVersions; + bool hasMatchingVersion = false; + + foreach (const QString& appVer, appVersionsSet) { + if (appVer != m_gameVersion) { + incompatibleVersions.append(appVer); + } else { + hasMatchingVersion = true; + } + } + + if (!incompatibleVersions.isEmpty() || + (hasMatchingVersion && incompatibleVersions.isEmpty())) { + QString message; + + if (!incompatibleVersions.isEmpty()) { + QString versionsList = incompatibleVersions.join(", "); + message += QString(tr("The game is in version: %1")).arg(m_gameVersion) + "\n" + + QString(tr("The downloaded patch only works on version: %1")) + .arg(versionsList); + + if (hasMatchingVersion) { + message += QString(", %1").arg(m_gameVersion); + } + message += QString("\n" + tr("You may need to update your game.")); + } + + if (!message.isEmpty()) { + QMessageBox::information(this, tr("Incompatibility Notice"), message); + } + } + } + } +} + void CheatsPatches::createFilesJson(const QString& repository) { QDir dir(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir)); @@ -1126,8 +1203,8 @@ void CheatsPatches::addPatchesToLayout(const QString& filePath) { } } - // Remove the item from the list view if no patches were added (the game has patches, but not - // for the current version) + // Remove the item from the list view if no patches were added + // (the game has patches, but not for the current version) if (!patchAdded) { QStringListModel* model = qobject_cast(patchesListView->model()); if (model) { @@ -1154,12 +1231,6 @@ void CheatsPatches::updateNoteTextEdit(const QString& patchName) { QString type = lineObject["Type"].toString(); QString address = lineObject["Address"].toString(); QString patchValue = lineObject["Value"].toString(); - - // add the values ​​to be modified in instructionsTextEdit - // text.append(QString("\nType: %1\nAddress: %2\n\nValue: %3") - // .arg(type) - // .arg(address) - // .arg(patchValue)); } text.replace("\\n", "\n"); instructionsTextEdit->setText(text); diff --git a/src/qt_gui/cheats_patches.h b/src/qt_gui/cheats_patches.h index a9932886c..b07e828c2 100644 --- a/src/qt_gui/cheats_patches.h +++ b/src/qt_gui/cheats_patches.h @@ -32,11 +32,11 @@ public: const QString& gameSize, const QPixmap& gameImage, QWidget* parent = nullptr); ~CheatsPatches(); - // Public Methods void downloadCheats(const QString& source, const QString& m_gameSerial, const QString& m_gameVersion, bool showMessageBox); void downloadPatches(const QString repository, const bool showMessageBox); void createFilesJson(const QString& repository); + void compatibleVersionNotice(const QString repository); signals: void downloadFinished(); diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index f8f7f5336..457d84ef8 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - تم تنزيل التصحيحات بنجاح! تم تنزيل جميع التصحيحات لجميع الألعاب، ولا داعي لتنزيلها بشكل فردي لكل لعبة كما هو الحال مع الغش. إذا لم يظهر التحديث، قد يكون السبب أنه غير متوفر للإصدار وسيريال اللعبة المحدد. قد تحتاج إلى تحديث اللعبة. + تم تنزيل التصحيحات بنجاح! تم تنزيل جميع التصحيحات لجميع الألعاب، ولا داعي لتنزيلها بشكل فردي لكل لعبة كما هو الحال مع الغش. إذا لم يظهر التحديث، قد يكون السبب أنه غير متوفر للإصدار وسيريال اللعبة المحدد. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. .HTML فشل في استرجاع صفحة + + + The game is in version: %1 + اللعبة في الإصدار: %1 + + + + The downloaded patch only works on version: %1 + الباتش الذي تم تنزيله يعمل فقط على الإصدار: %1 + + + + You may need to update your game. + قد تحتاج إلى تحديث لعبتك. + + + + Incompatibility Notice + إشعار عدم التوافق + Failed to open file: diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 4bdc75828..e14826725 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Patcher hentet med succes! Alle patches til alle spil er blevet hentet, der er ikke behov for at hente dem individuelt for hvert spil, som det sker med snyd. Hvis opdateringen ikke vises, kan det være, at den ikke findes for den specifikke serie og version af spillet. Det kan være nødvendigt at opdatere spillet. + Patcher hentet med succes! Alle patches til alle spil er blevet hentet, der er ikke behov for at hente dem individuelt for hvert spil, som det sker med snyd. Hvis opdateringen ikke vises, kan det være, at den ikke findes for den specifikke serie og version af spillet. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. Kunne ikke hente HTML-side. + + + The game is in version: %1 + Spillet er i version: %1 + + + + The downloaded patch only works on version: %1 + Den downloadede patch fungerer kun på version: %1 + + + + You may need to update your game. + Du skal muligvis opdatere dit spil. + + + + Incompatibility Notice + Uforenelighedsmeddelelse + Failed to open file: diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index 2a436ec2c..77b6a01ac 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Patches erfolgreich heruntergeladen! Alle Patches für alle Spiele wurden heruntergeladen, es ist nicht notwendig, sie einzeln für jedes Spiel herunterzuladen, wie es bei Cheats der Fall ist. Wenn der Patch nicht angezeigt wird, könnte es sein, dass er für die spezifische Seriennummer und Version des Spiels nicht existiert. Möglicherweise müssen Sie das Spiel aktualisieren. + Patches erfolgreich heruntergeladen! Alle Patches für alle Spiele wurden heruntergeladen, es ist nicht notwendig, sie einzeln für jedes Spiel herunterzuladen, wie es bei Cheats der Fall ist. Wenn der Patch nicht angezeigt wird, könnte es sein, dass er für die spezifische Seriennummer und Version des Spiels nicht existiert. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. Fehler beim Abrufen der HTML-Seite. + + + The game is in version: %1 + Das Spiel ist in der Version: %1 + + + + The downloaded patch only works on version: %1 + Der heruntergeladene Patch funktioniert nur in der Version: %1 + + + + You may need to update your game. + Sie müssen möglicherweise Ihr Spiel aktualisieren. + + + + Incompatibility Notice + Inkompatibilitätsbenachrichtigung + Failed to open file: diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index b7d51bfb3..a738bf90a 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Τα Patches κατεβάστηκαν επιτυχώς! Όλα τα Patches για όλα τα παιχνίδια έχουν κατέβει, δεν είναι απαραίτητο να τα κατεβάσετε ένα-ένα για κάθε παιχνίδι, όπως με τα Cheats. Εάν η ενημέρωση δεν εμφανίζεται, μπορεί να μην υπάρχει για τον συγκεκριμένο σειριακό αριθμό και έκδοση του παιχνιδιού. Μπορεί να χρειαστεί να ενημερώσετε το παιχνίδι. + Τα Patches κατεβάστηκαν επιτυχώς! Όλα τα Patches για όλα τα παιχνίδια έχουν κατέβει, δεν είναι απαραίτητο να τα κατεβάσετε ένα-ένα για κάθε παιχνίδι, όπως με τα Cheats. Εάν η ενημέρωση δεν εμφανίζεται, μπορεί να μην υπάρχει για τον συγκεκριμένο σειριακό αριθμό και έκδοση του παιχνιδιού. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. Αποτυχία ανάκτησης σελίδας HTML. + + + The game is in version: %1 + Το παιχνίδι είναι στην έκδοση: %1 + + + + The downloaded patch only works on version: %1 + Η ληφθείσα ενημέρωση λειτουργεί μόνο στην έκδοση: %1 + + + + You may need to update your game. + Μπορεί να χρειαστεί να ενημερώσετε το παιχνίδι σας. + + + + Incompatibility Notice + Ειδοποίηση ασυμβατότητας + Failed to open file: diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 5b58d865f..9f25fc836 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. If the patch does not appear, it may be that it does not exist for the specific serial and version of the game. It may be necessary to update the game. + Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. If the patch does not appear, it may be that it does not exist for the specific serial and version of the game. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. Failed to retrieve HTML page. + + + The game is in version: %1 + The game is in version: %1 + + + + The downloaded patch only works on version: %1 + The downloaded patch only works on version: %1 + + + + You may need to update your game. + You may need to update your game. + + + + Incompatibility Notice + Incompatibility Notice + Failed to open file: diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 032579f91..b0a6e4335 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - ¡Parches descargados exitosamente! Todos los parches disponibles para todos los juegos han sido descargados, no es necesario descargarlos individualmente para cada juego como ocurre con los trucos. Si el parche no aparece, puede ser que no exista para el número de serie y versión específicos del juego. Puede ser necesario actualizar el juego. + ¡Parches descargados exitosamente! Todos los parches disponibles para todos los juegos han sido descargados, no es necesario descargarlos individualmente para cada juego como ocurre con los trucos. Si el parche no aparece, puede ser que no exista para el número de serie y versión específicos del juego. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. Error al recuperar la página HTML. + + + The game is in version: %1 + El juego está en la versión: %1 + + + + The downloaded patch only works on version: %1 + El parche descargado solo funciona en la versión: %1 + + + + You may need to update your game. + Puede que necesites actualizar tu juego. + + + + Incompatibility Notice + Aviso de incompatibilidad + Failed to open file: diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 8976d185c..15f5d6193 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -1021,12 +1021,12 @@ Download Complete - پچ ها با موفقیت بارگیری شدند! تمام وصله های موجود برای همه بازی ها دانلود شده اند، نیازی به دانلود جداگانه آنها برای هر بازی نیست، همانطور که در Cheats اتفاق می افتد. اگر پچ ظاهر نشد، ممکن است برای سریال و نسخه خاصی از بازی وجود نداشته باشد. ممکن است نیاز به آپدیت بازی باشد.✅ + دانلود کامل شد DownloadComplete_MSG - دانلود با موفقیت به اتمام رسید✅ + پچ ها با موفقیت بارگیری شدند! تمام وصله های موجود برای همه بازی ها دانلود شده اند، نیازی به دانلود جداگانه آنها برای هر بازی نیست، همانطور که در Cheats اتفاق می افتد. اگر پچ ظاهر نشد، ممکن است برای سریال و نسخه خاصی از بازی وجود نداشته باشد. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. HTML خطا دربازیابی صفحه + + + The game is in version: %1 + بازی در نسخه: %1 است + + + + The downloaded patch only works on version: %1 + وصله دانلود شده فقط در نسخه: %1 کار می کند + + + + You may need to update your game. + شاید لازم باشد بازی خود را به روز کنید. + + + + Incompatibility Notice + اطلاعیه عدم سازگاری + Failed to open file: diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index c2cb6c5e8..cb7426e01 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Korjaukset ladattu onnistuneesti! Kaikki saatavilla olevat korjaukset kaikille peleille on ladattu, eikä niitä tarvitse ladata yksittäin jokaiselle pelille kuten huijauksissa. Jos päivitystä ei näy, se saattaa olla, että sitä ei ole saatavilla tietylle sarjanumerolle ja peliversiolle. Saattaa olla tarpeen päivittää peli. + Korjaukset ladattu onnistuneesti! Kaikki saatavilla olevat korjaukset kaikille peleille on ladattu, eikä niitä tarvitse ladata yksittäin jokaiselle pelille kuten huijauksissa. Jos päivitystä ei näy, se saattaa olla, että sitä ei ole saatavilla tietylle sarjanumerolle ja peliversiolle. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. HTML-sivun hakeminen epäonnistui. + + + The game is in version: %1 + Peli on versiossa: %1 + + + + The downloaded patch only works on version: %1 + ladattu päivitys toimii vain versiossa: %1 + + + + You may need to update your game. + Sinun on ehkä päivitettävä peliäsi. + + + + Incompatibility Notice + Yhteensopivuusilmoitus + Failed to open file: diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index f891b7475..4c2d5cbdd 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Patchs téléchargés avec succès ! Tous les patches disponibles pour tous les jeux ont été téléchargés, il n'est pas nécessaire de les télécharger individuellement pour chaque jeu comme c'est le cas pour les Cheats. Si le correctif n'apparaît pas, il se peut qu'il n'existe pas pour le numéro de série et la version spécifiques du jeu. Il peut être nécessaire de mettre à jour le jeu. + Patchs téléchargés avec succès ! Tous les patches disponibles pour tous les jeux ont été téléchargés, il n'est pas nécessaire de les télécharger individuellement pour chaque jeu comme c'est le cas pour les Cheats. Si le correctif n'apparaît pas, il se peut qu'il n'existe pas pour le numéro de série et la version spécifiques du jeu. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. Échec de la récupération de la page HTML. + + + The game is in version: %1 + Le jeu est en version: %1 + + + + The downloaded patch only works on version: %1 + Le patch téléchargé ne fonctionne que sur la version: %1 + + + + You may need to update your game. + Vous devrez peut-être mettre à jour votre jeu. + + + + Incompatibility Notice + Avis d'incompatibilité + Failed to open file: diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 500629682..633ba9810 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Frissítések sikeresen letöltve! Minden elérhető frissítés letöltésre került, nem szükséges egyesével letölteni őket minden játékhoz, mint a csalások esetében. Ha a javítás nem jelenik meg, lehet, hogy nem létezik a játék adott sorozatszámához és verziójához. Lehet, hogy frissítenie kell a játékot. + Frissítések sikeresen letöltve! Minden elérhető frissítés letöltésre került, nem szükséges egyesével letölteni őket minden játékhoz, mint a csalások esetében. Ha a javítás nem jelenik meg, lehet, hogy nem létezik a játék adott sorozatszámához és verziójához. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. Nem sikerült HTML oldal lekérése. + + + The game is in version: %1 + A játék verziója: %1 + + + + The downloaded patch only works on version: %1 + A letöltött javítás csak a(z) %1 verzión működik + + + + You may need to update your game. + Lehet, hogy frissítened kell a játékodat. + + + + Incompatibility Notice + Inkompatibilitási értesítés + Failed to open file: diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index 8721e3a8a..f841ad3a8 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Patch Berhasil Diunduh! Semua Patch yang tersedia untuk semua game telah diunduh, tidak perlu mengunduhnya satu per satu seperti yang terjadi pada Cheat. Jika patch tidak muncul, mungkin patch tersebut tidak ada untuk nomor seri dan versi game yang spesifik. Mungkin perlu memperbarui game. + Patch Berhasil Diunduh! Semua Patch yang tersedia untuk semua game telah diunduh, tidak perlu mengunduhnya satu per satu seperti yang terjadi pada Cheat. Jika patch tidak muncul, mungkin patch tersebut tidak ada untuk nomor seri dan versi game yang spesifik. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. Gagal mengambil halaman HTML. + + + The game is in version: %1 + Permainan berada di versi: %1 + + + + The downloaded patch only works on version: %1 + Patch yang diunduh hanya berfungsi pada versi: %1 + + + + You may need to update your game. + Anda mungkin perlu memperbarui permainan Anda. + + + + Incompatibility Notice + Pemberitahuan Ketidakcocokan + Failed to open file: diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index 747148b6e..071b8e978 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Patch scaricata con successo! Vengono scaricate tutte le patch disponibili per tutti i giochi, non è necessario scaricarle singolarmente per ogni gioco come nel caso dei trucchi. Se la patch non appare, potrebbe essere che non esista per il numero di serie e la versione specifica del gioco. Potrebbe essere necessario aggiornare il gioco. + Patch scaricata con successo! Vengono scaricate tutte le patch disponibili per tutti i giochi, non è necessario scaricarle singolarmente per ogni gioco come nel caso dei trucchi. Se la patch non appare, potrebbe essere che non esista per il numero di serie e la versione specifica del gioco. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. Impossibile recuperare la pagina HTML. + + + The game is in version: %1 + Il gioco è nella versione: %1 + + + + The downloaded patch only works on version: %1 + La patch scaricata funziona solo sulla versione: %1 + + + + You may need to update your game. + Potresti aver bisogno di aggiornare il tuo gioco. + + + + Incompatibility Notice + Avviso di incompatibilità + Failed to open file: diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index a020b1687..a79b34e2a 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - パッチが正常にダウンロードされました! すべてのゲームに利用可能なパッチがダウンロードされました。チートとは異なり、各ゲームごとに個別にダウンロードする必要はありません。 パッチが表示されない場合、特定のシリアル番号とバージョンのゲームには存在しない可能性があります。ゲームを更新する必要があるかもしれません。 + パッチが正常にダウンロードされました! すべてのゲームに利用可能なパッチがダウンロードされました。チートとは異なり、各ゲームごとに個別にダウンロードする必要はありません。 パッチが表示されない場合、特定のシリアル番号とバージョンのゲームには存在しない可能性があります。 @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. HTMLページの取得に失敗しました。 + + + The game is in version: %1 + ゲームのバージョン: %1 + + + + The downloaded patch only works on version: %1 + ダウンロードしたパッチはバージョン: %1 のみ機能します + + + + You may need to update your game. + ゲームを更新する必要があるかもしれません。 + + + + Incompatibility Notice + 互換性のない通知 + Failed to open file: diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index 03ab49ad6..6ef89ea24 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. If the patch does not appear, it may be that it does not exist for the specific serial and version of the game. It may be necessary to update the game. + Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. If the patch does not appear, it may be that it does not exist for the specific serial and version of the game. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. Failed to retrieve HTML page. + + + The game is in version: %1 + The game is in version: %1 + + + + The downloaded patch only works on version: %1 + The downloaded patch only works on version: %1 + + + + You may need to update your game. + You may need to update your game. + + + + Incompatibility Notice + Incompatibility Notice + Failed to open file: diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index aecf5aec9..d7fc6e844 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Pataisos sėkmingai atsisiųstos! Visos pataisos visiems žaidimams buvo atsisiųstos, nebėra reikalo jas atsisiųsti atskirai kiekvienam žaidimui, kaip tai vyksta su sukčiavimais. Jei pleistras nepasirodo, gali būti, kad jo nėra tam tikram žaidimo serijos numeriui ir versijai. Gali prireikti atnaujinti žaidimą. + Pataisos sėkmingai atsisiųstos! Visos pataisos visiems žaidimams buvo atsisiųstos, nebėra reikalo jas atsisiųsti atskirai kiekvienam žaidimui, kaip tai vyksta su sukčiavimais. Jei pleistras nepasirodo, gali būti, kad jo nėra tam tikram žaidimo serijos numeriui ir versijai. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. Nepavyko gauti HTML puslapio. + + + The game is in version: %1 + Žaidimas yra versijoje: %1 + + + + The downloaded patch only works on version: %1 + Parsisiųstas pataisas veikia tik versijoje: %1 + + + + You may need to update your game. + Gali tekti atnaujinti savo žaidimą. + + + + Incompatibility Notice + Suderinamumo pranešimas + Failed to open file: diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index 303b5f831..cdcf4d5fb 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Oppdateringer lastet ned vellykket! Alle oppdateringer tilgjengelige for alle spill har blitt lastet ned, det er ikke nødvendig å laste dem ned individuelt for hvert spill som skjer med jukser. Hvis oppdateringen ikke vises, kan det hende at den ikke finnes for den spesifikke serienummeret og versjonen av spillet. Det kan være nødvendig å oppdatere spillet. + Oppdateringer lastet ned vellykket! Alle oppdateringer tilgjengelige for alle spill har blitt lastet ned, det er ikke nødvendig å laste dem ned individuelt for hvert spill som skjer med jukser. Hvis oppdateringen ikke vises, kan det hende at den ikke finnes for den spesifikke serienummeret og versjonen av spillet. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. Kunne ikke hente HTML-side. + + + The game is in version: %1 + Spillet er i versjon: %1 + + + + The downloaded patch only works on version: %1 + Den nedlastede patchen fungerer bare på versjon: %1 + + + + You may need to update your game. + Du må kanskje oppdatere spillet ditt. + + + + Incompatibility Notice + Inkompatibilitetsvarsel + Failed to open file: diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index c4f49b93e..380d90705 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Patches succesvol gedownload! Alle beschikbare patches voor alle spellen zijn gedownload. Het is niet nodig om ze afzonderlijk te downloaden voor elk spel dat cheats heeft. Als de patch niet verschijnt, kan het zijn dat deze niet bestaat voor het specifieke serienummer en de versie van het spel. Het kan nodig zijn om het spel bij te werken. + Patches succesvol gedownload! Alle beschikbare patches voor alle spellen zijn gedownload. Het is niet nodig om ze afzonderlijk te downloaden voor elk spel dat cheats heeft. Als de patch niet verschijnt, kan het zijn dat deze niet bestaat voor het specifieke serienummer en de versie van het spel. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. Kan HTML-pagina niet ophalen. + + + The game is in version: %1 + Het spel is in versie: %1 + + + + The downloaded patch only works on version: %1 + De gedownloade patch werkt alleen op versie: %1 + + + + You may need to update your game. + Misschien moet je je spel bijwerken. + + + + Incompatibility Notice + Incompatibiliteitsmelding + Failed to open file: diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 0f6a928b4..5d211734e 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Poprawki zostały pomyślnie pobrane! Wszystkie dostępne poprawki dla wszystkich gier zostały pobrane. Nie ma potrzeby pobierania ich osobno dla każdej gry, która ma kody. Jeśli poprawka się nie pojawia, możliwe, że nie istnieje dla konkretnego numeru seryjnego i wersji gry. Może być konieczne zaktualizowanie gry. + Poprawki zostały pomyślnie pobrane! Wszystkie dostępne poprawki dla wszystkich gier zostały pobrane. Nie ma potrzeby pobierania ich osobno dla każdej gry, która ma kody. Jeśli poprawka się nie pojawia, możliwe, że nie istnieje dla konkretnego numeru seryjnego i wersji gry. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. Nie udało się pobrać strony HTML. + + + The game is in version: %1 + Gra jest w wersji: %1 + + + + The downloaded patch only works on version: %1 + Pobrana łatka działa tylko w wersji: %1 + + + + You may need to update your game. + Możesz potrzebować zaktualizować swoją grę. + + + + Incompatibility Notice + Powiadomienie o niezgodności + Failed to open file: diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index c1c2c438b..eb79fade4 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Patches Baixados com Sucesso! Todos os patches disponíveis para todos os jogos foram baixados, não é necessário baixá-los individualmente para cada jogo como acontece com os Cheats. Se o patch não aparecer, pode ser que ele não exista para o número de série e a versão específicos do jogo. Pode ser necessário atualizar o jogo. + Patches Baixados com Sucesso! Todos os patches disponíveis para todos os jogos foram baixados, não é necessário baixá-los individualmente para cada jogo como acontece com os Cheats. Se o patch não aparecer, pode ser que ele não exista para o número de série e a versão específicos do jogo. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. Falha ao recuperar a página HTML. + + + The game is in version: %1 + O jogo está na versão: %1 + + + + The downloaded patch only works on version: %1 + O patch baixado só funciona na versão: %1 + + + + You may need to update your game. + Talvez você precise atualizar seu jogo. + + + + Incompatibility Notice + Aviso de incompatibilidade + Failed to open file: diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index ffb6b6547..603cd3a24 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Patches descărcate cu succes! Toate Patches disponibile pentru toate jocurile au fost descărcate; nu este nevoie să le descarci individual pentru fiecare joc, așa cum se întâmplă cu Cheats. Dacă patch-ul nu apare, este posibil să nu existe pentru seria și versiunea specifică a jocului. Poate fi necesar să actualizați jocul. + Patches descărcate cu succes! Toate Patches disponibile pentru toate jocurile au fost descărcate; nu este nevoie să le descarci individual pentru fiecare joc, așa cum se întâmplă cu Cheats. Dacă patch-ul nu apare, este posibil să nu existe pentru seria și versiunea specifică a jocului. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. Nu s-a reușit obținerea paginii HTML. + + + The game is in version: %1 + Jocul este în versiunea: %1 + + + + The downloaded patch only works on version: %1 + Patch-ul descărcat funcționează doar pe versiunea: %1 + + + + You may need to update your game. + Este posibil să trebuiască să actualizezi jocul tău. + + + + Incompatibility Notice + Avertizare de incompatibilitate + Failed to open file: diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 73263587d..4c58786c4 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Патчи успешно скачаны! Все доступные патчи для всех игр были скачаны, нет необходимости скачивать их по отдельности для каждой игры, как это происходит с читами. Если патч не появляется, возможно, его не существует для конкретного серийного номера и версии игры. Возможно, потребуется обновить игру. + Патчи успешно скачаны! Все доступные патчи для всех игр были скачаны, нет необходимости скачивать их по отдельности для каждой игры, как это происходит с читами. Если патч не появляется, возможно, его не существует для конкретного серийного номера и версии игры. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. Не удалось получить HTML-страницу. + + + The game is in version: %1 + Игра в версии: %1 + + + + The downloaded patch only works on version: %1 + Скачанный патч работает только на версии: %1 + + + + You may need to update your game. + Вам может понадобиться обновить игру. + + + + Incompatibility Notice + Уведомление о несовместимости + Failed to open file: diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index fd835cb81..637a754f7 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - 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. Mund të jetë e nevojshme të përditësosh lojën. + 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. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. Gjetja e faqes HTML dështoi. + + + The game is in version: %1 + Loja është në versionin: %1 + + + + The downloaded patch only works on version: %1 + Patch-i i shkarkuar funksionon vetëm në versionin: %1 + + + + You may need to update your game. + Ju mund të duhet të përditësoni lojën tuaj. + + + + Incompatibility Notice + Njoftim për papajtueshmëri + Failed to open file: diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 86930764f..6c4913603 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Yamalar başarıyla indirildi! Tüm oyunlar için mevcut tüm yamalar indirildi, her oyun için ayrı ayrı indirme yapmanız gerekmez, hilelerle olduğu gibi. Yamanın görünmemesi durumunda, belirli seri numarası ve oyun sürümü için mevcut olmayabilir. Oyunu güncellemeniz gerekebilir. + Yamalar başarıyla indirildi! Tüm oyunlar için mevcut tüm yamalar indirildi, her oyun için ayrı ayrı indirme yapmanız gerekmez, hilelerle olduğu gibi. Yamanın görünmemesi durumunda, belirli seri numarası ve oyun sürümü için mevcut olmayabilir. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. HTML sayfası alınamadı. + + + The game is in version: %1 + Oyun sürümde: %1 + + + + The downloaded patch only works on version: %1 + İndirilen yamanın sadece sürümde çalışıyor: %1 + + + + You may need to update your game. + Oyunuzu güncellemeniz gerekebilir. + + + + Incompatibility Notice + Uyumsuzluk Bildirimi + Failed to open file: diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index 8bc241ece..5fca6b6b5 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - Bản vá đã tải xuống thành công! Tất cả các bản vá có sẵn cho tất cả các trò chơi đã được tải xuống, không cần tải xuống riêng lẻ cho mỗi trò chơi như trong Cheat. Nếu bản vá không xuất hiện, có thể là nó không tồn tại cho số seri và phiên bản cụ thể của trò chơi. Có thể bạn cần phải cập nhật trò chơi. + Bản vá đã tải xuống thành công! Tất cả các bản vá có sẵn cho tất cả các trò chơi đã được tải xuống, không cần tải xuống riêng lẻ cho mỗi trò chơi như trong Cheat. Nếu bản vá không xuất hiện, có thể là nó không tồn tại cho số seri và phiên bản cụ thể của trò chơi. @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. Không thể lấy trang HTML. + + + The game is in version: %1 + Trò chơi đang ở phiên bản: %1 + + + + The downloaded patch only works on version: %1 + Patch đã tải về chỉ hoạt động trên phiên bản: %1 + + + + You may need to update your game. + Bạn có thể cần cập nhật trò chơi của mình. + + + + Incompatibility Notice + Thông báo không tương thích + Failed to open file: diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index bc943c860..89e45c7ba 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - 补丁下载成功!所有可用的补丁已下载完成,无需像作弊码那样单独下载每个游戏的补丁。如果补丁没有出现,可能是该补丁不存在于特定的序列号和游戏版本中。可能需要更新游戏。 + 补丁下载成功!所有可用的补丁已下载完成,无需像作弊码那样单独下载每个游戏的补丁。如果补丁没有出现,可能是该补丁不存在于特定的序列号和游戏版本中。 @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. 无法获取 HTML 页面。 + + + The game is in version: %1 + 游戏版本: %1 + + + + The downloaded patch only works on version: %1 + 下载的补丁仅适用于版本: %1 + + + + You may need to update your game. + 您可能需要更新您的游戏。 + + + + Incompatibility Notice + 不兼容通知 + Failed to open file: diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index c7b923f83..84b32b7a5 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -1026,7 +1026,7 @@ DownloadComplete_MSG - 修補檔下載成功!所有遊戲的修補檔已下載完成,無需像作弊碼那樣為每個遊戲單獨下載。如果補丁未顯示,可能是該補丁不適用於特定的序號和遊戲版本。可能需要更新遊戲。 + 修補檔下載成功!所有遊戲的修補檔已下載完成,無需像作弊碼那樣為每個遊戲單獨下載。如果補丁未顯示,可能是該補丁不適用於特定的序號和遊戲版本。 @@ -1038,6 +1038,26 @@ Failed to retrieve HTML page. 無法檢索 HTML 頁面。 + + + The game is in version: %1 + 遊戲版本: %1 + + + + The downloaded patch only works on version: %1 + 下載的補丁僅適用於版本: %1 + + + + You may need to update your game. + 您可能需要更新遊戲。 + + + + Incompatibility Notice + 不相容通知 + Failed to open file: From d9d53c7a38b8a768aa707a27af6ce5d7a4b326ef Mon Sep 17 00:00:00 2001 From: auser1337 Date: Fri, 18 Oct 2024 20:40:17 -0700 Subject: [PATCH 89/93] work --- externals/CMakeLists.txt | 2 +- src/core/libraries/ajm/ajm.cpp | 239 +++++++++++------- src/core/libraries/ajm/ajm.h | 11 +- src/core/libraries/ajm/ajm_at9.cpp | 46 ++-- src/core/libraries/ajm/ajm_at9.h | 32 +-- src/core/libraries/ajm/ajm_instance.h | 10 +- src/core/libraries/ajm/ajm_mp3.cpp | 13 +- src/core/libraries/ajm/ajm_mp3.h | 14 +- src/core/libraries/audio/audioout.cpp | 7 + src/video_core/page_manager.cpp | 2 +- .../renderer_vulkan/liverpool_to_vk.cpp | 2 - 11 files changed, 214 insertions(+), 164 deletions(-) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 6df5399fc..95093c5ca 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -54,7 +54,7 @@ file(GLOB LIBATRAC9_SOURCES ) add_library(LibAtrac9 STATIC ${LIBATRAC9_SOURCES}) -target_include_directories(LibAtrac9 PUBLIC LibAtrac9/C/src) +target_include_directories(LibAtrac9 INTERFACE LibAtrac9/C/src) # Zlib-Ng if (NOT TARGET zlib-ng::zlib) diff --git a/src/core/libraries/ajm/ajm.cpp b/src/core/libraries/ajm/ajm.cpp index cb3f0e265..39e25445e 100644 --- a/src/core/libraries/ajm/ajm.cpp +++ b/src/core/libraries/ajm/ajm.cpp @@ -1,12 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma clang optimize off +#include #include #include #include "ajm_at9.h" #include "common/assert.h" -#include "common/debug.h" #include "common/logging/log.h" #include "core/libraries/ajm/ajm.h" #include "core/libraries/ajm/ajm_error.h" @@ -16,7 +16,6 @@ #include "core/libraries/libs.h" extern "C" { -#include #include #include #include @@ -28,18 +27,34 @@ namespace Libraries::Ajm { static constexpr u32 AJM_INSTANCE_STATISTICS = 0x80000; +static constexpr u32 SCE_AJM_WAIT_INFINITE = -1; + static constexpr u32 MaxInstances = 0x2fff; +static constexpr u32 MaxBatches = 1000; + +struct BatchInfo { + u16 instance{}; + u16 offset_in_qwords{}; // Needed for AjmBatchError? + bool waiting{}; + bool finished{}; + std::mutex mtx; + std::condition_variable cv; + int result{}; +}; + struct AjmDevice { - u32 max_prio; - u32 min_prio; + u32 max_prio{}; + u32 min_prio{}; u32 curr_cursor{}; u32 release_cursor{MaxInstances - 1}; std::array is_registered{}; std::array free_instances{}; - std::array, MaxInstances> instances; + std::array, MaxInstances> instances; + std::vector> batches{}; + std::mutex batches_mutex; - bool IsRegistered(AjmCodecType type) const { + [[nodiscard]] bool IsRegistered(AjmCodecType type) const { return is_registered[static_cast(type)]; } @@ -86,11 +101,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobControlBufferRa(AjmSingleJob* batch_pos, u32 in batch_pos->opcode.is_statistic = instance == AJM_INSTANCE_STATISTICS; batch_pos->opcode.is_control = true; - if (instance == AJM_INSTANCE_STATISTICS) { - BREAKPOINT(); - } - - AjmInOutJob* job = nullptr; + AjmInOutJob* job; if (ret_addr == nullptr) { batch_pos->job_size = sizeof(AjmInOutJob); job = &batch_pos->job; @@ -121,45 +132,10 @@ void* PS4_SYSV_ABI sceAjmBatchJobInlineBuffer(AjmSingleJob* batch_pos, const voi return nullptr; } -auto ParseWavHeader = [](void* buf, WavHeader* header) { - if (!buf) { - // buf is passed as nullptr in some cases (i.e. GetCodecInfo) - return; - } - std::memcpy(header, buf, sizeof(WavHeader)); - - std::string riff(header->RIFF, 4); - std::string wave(header->WAVE, 4); - std::string fmt(header->fmt, 4); - std::string dataID(header->Subchunk2ID, 4); - - if (std::memcmp(header->RIFF, "RIFF", 4) != 0 || std::memcmp(header->WAVE, "WAVE", 4) != 0 || - std::memcmp(header->fmt, "fmt ", 4) != 0 || - std::memcmp(header->Subchunk2ID, "data", 4) != 0) { - LOG_ERROR(Lib_Ajm, "Invalid WAV file."); - return; - } - - LOG_INFO(Lib_Ajm, "RIFF header: {}", riff); - LOG_INFO(Lib_Ajm, "WAVE header: {}", wave); - LOG_INFO(Lib_Ajm, "FMT: {}", fmt); - LOG_INFO(Lib_Ajm, "Data size: {}", header->ChunkSize); - LOG_INFO(Lib_Ajm, "Sampling Rate: {}", header->SamplesPerSec); - LOG_INFO(Lib_Ajm, "Number of bits used: {}", header->bitsPerSample); - LOG_INFO(Lib_Ajm, "Number of channels: {}", header->NumOfChan); - LOG_INFO(Lib_Ajm, "Number of bytes per second: {}", header->bytesPerSec); - LOG_INFO(Lib_Ajm, "Data length: {}", header->Subchunk2Size); - LOG_INFO(Lib_Ajm, "Audio Format: {}", header->AudioFormat); - LOG_INFO(Lib_Ajm, "Block align: {}", header->blockAlign); - LOG_INFO(Lib_Ajm, "Data string: {}", dataID); -}; - void* PS4_SYSV_ABI sceAjmBatchJobRunBufferRa(AjmSingleJob* batch_pos, u32 instance, AjmFlags flags, - void* in_buffer, u32 in_size, u8* out_buffer, + u8* in_buffer, u32 in_size, u8* out_buffer, const u32 out_size, u8* sideband_output, const u32 sideband_output_size, const void* ret_addr) { - WavHeader header{}; - ParseWavHeader(in_buffer, &header); LOG_INFO(Lib_Ajm, "called instance = {:#x}, flags = {:#x}, cmd = {}, in_size = {:#x}, out_size = {:#x}, " "ret_addr = {}", @@ -178,7 +154,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunBufferRa(AjmSingleJob* batch_pos, u32 instan batch_pos->opcode.is_statistic = false; batch_pos->opcode.is_control = false; - AjmInOutJob* job = nullptr; + AjmInOutJob* job; if (ret_addr == nullptr) { batch_pos->job_size = sizeof(AjmInOutJob) + 16; job = &batch_pos->job; @@ -188,16 +164,17 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunBufferRa(AjmSingleJob* batch_pos, u32 instan job = &batch_pos->ret.job; } - // todo: add some missing stuff + // TODO: Check if all the fields are being set, might be missing some job->input.buf_size = in_size; - job->input.buffer = static_cast(in_buffer); + job->input.buffer = in_buffer; job->flags = u32(flags.raw); job->unk1 = (job->unk1 & 0xfc000030) + (flags.raw >> 0x1a) + 4; job->output.buf_size = out_size; job->output.buffer = out_buffer; job->output.props &= 0xffffffe0; job->output.props |= 0x12; - *reinterpret_cast(reinterpret_cast(job) + 32) = sideband_output; + // *reinterpret_cast(reinterpret_cast(job) + 32) = sideband_output; + job->sideband_output = sideband_output; return job; } @@ -213,6 +190,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 in instance, flags.raw, magic_enum::enum_name(AjmJobRunFlags(flags.command)), magic_enum::enum_name(AjmJobSidebandFlags(flags.sideband)), num_in_buffers, num_out_buffers, fmt::ptr(ret_addr)); + const u32 job_size = (num_in_buffers * 2 + 1 + num_out_buffers * 2) * 8; const bool is_debug = ret_addr != nullptr; batch_pos->opcode.instance = instance; @@ -223,7 +201,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 in batch_pos->opcode.is_statistic = false; batch_pos->opcode.is_control = false; - u32* job = nullptr; + u32* job; if (!is_debug) { batch_pos->job_size = job_size + 16; job = batch_pos->job; @@ -237,7 +215,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 in } for (s32 i = 0; i < num_in_buffers; i++) { - AjmJobBuffer* in_buf = reinterpret_cast(job); + auto* in_buf = reinterpret_cast(job); in_buf->props &= 0xffffffe0; in_buf->props |= 1; in_buf->buf_size = in_buffers[i].size; @@ -250,7 +228,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 in job += 2; for (s32 i = 0; i < num_out_buffers; i++) { - AjmJobBuffer* out_buf = reinterpret_cast(job); + auto* out_buf = reinterpret_cast(job); out_buf->props &= 0xffffffe0; out_buf->props |= 0x11; out_buf->buf_size = out_buffers[i].size; @@ -273,23 +251,23 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_ return ORBIS_AJM_ERROR_MALFORMED_BATCH; } - static constexpr u32 MaxBatches = 1000; + const auto batch_info = std::make_shared(); - struct BatchInfo { - u16 instance; - u16 offset_in_qwords; - }; - std::array batches{}; - u32 num_batches = 0; - - const u8* batch_ptr = batch; - const u8* batch_end = batch + batch_size; - while (batch_ptr < batch_end) { - if (num_batches >= MaxBatches) { + { + if (dev->batches.size() >= MaxBatches) { LOG_ERROR(Lib_Ajm, "Too many batches in job!"); return ORBIS_AJM_ERROR_OUT_OF_MEMORY; } - AjmJobHeader header; + + *out_batch_id = static_cast(dev->batches.size()); + dev->batches.push_back(batch_info); + } + + const u8* batch_ptr = batch; + const u8* batch_end = batch + batch_size; + AjmJobHeader header{}; + + while (batch_ptr < batch_end) { std::memcpy(&header, batch_ptr, sizeof(u64)); const auto& opcode = header.opcode; @@ -297,34 +275,57 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_ const u8* job_ptr = batch_ptr + sizeof(AjmJobHeader) + opcode.is_debug * 16; if (opcode.is_control) { - // ASSERT_MSG(!opcode.is_statistic, "Statistic instance is not handled"); const auto command = AjmJobControlFlags(opcode.command_flags); switch (command) { case AjmJobControlFlags::Reset: { - LOG_INFO(Lib_Ajm, "Resetting instance {}", opcode.instance); - dev->instances[opcode.instance]->Reset(); + LOG_INFO(Lib_Ajm, "Resetting instance {}", instance); + dev->instances[instance]->Reset(); break; } - case (AjmJobControlFlags::Initialize | AjmJobControlFlags::Reset): - LOG_INFO(Lib_Ajm, "Initializing instance {}", opcode.instance); + case AjmJobControlFlags::Initialize: + LOG_INFO(Lib_Ajm, "Initializing instance {}", instance); + if (dev->instances[instance]->codec_type == AjmCodecType::At9Dec) { + const auto at9_instance = + dynamic_cast(dev->instances[instance].get()); + const auto in_buffer = reinterpret_cast(job_ptr); + std::memcpy( + at9_instance->config_data, + reinterpret_cast(in_buffer->buffer) + ->config_data, + SCE_AT9_CONFIG_DATA_SIZE); + LOG_INFO( + Lib_Ajm, "Initialize params: {}, config_data: {}, reserved: {}", + fmt::ptr(reinterpret_cast( + in_buffer->buffer)), + fmt::ptr(reinterpret_cast( + in_buffer->buffer) + ->config_data), + reinterpret_cast(in_buffer->buffer) + ->reserved); + } break; case AjmJobControlFlags::Resample: - LOG_INFO(Lib_Ajm, "Set resample params of instance {}", opcode.instance); + LOG_INFO(Lib_Ajm, "Set resample params of instance {}", instance); + break; + case AjmJobControlFlags::StatisticsEngine: + ASSERT_MSG(instance == AJM_INSTANCE_STATISTICS, + "Expected AJM_INSTANCE_STATISTICS for StatisticsEngine command"); + LOG_TRACE(Lib_Ajm, "StatisticsEngine flag not implemented"); break; default: break; } // Write sideband structures. - const AjmJobBuffer* out_buffer = reinterpret_cast(job_ptr + 24); + const auto* out_buffer = reinterpret_cast(job_ptr + 24); auto* result = reinterpret_cast(out_buffer->buffer); result->result = 0; result->internal_result = 0; } else { const auto command = AjmJobRunFlags(opcode.command_flags); const auto sideband = AjmJobSidebandFlags(opcode.sideband_flags); - const AjmJobBuffer* in_buffer = reinterpret_cast(job_ptr); - const AjmJobBuffer* out_buffer = reinterpret_cast(job_ptr + 24); + const auto* in_buffer = reinterpret_cast(job_ptr); + const auto* out_buffer = reinterpret_cast(job_ptr + 24); job_ptr += 24; LOG_INFO(Lib_Ajm, "Decode job cmd = {}, sideband = {}, in_addr = {}, in_size = {}", @@ -332,7 +333,7 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_ fmt::ptr(in_buffer->buffer), in_buffer->buf_size); // Decode as much of the input bitstream as possible. - AjmAt9Decoder* decoder_instance = dev->instances[opcode.instance].get(); + AjmInstance* decoder_instance = dev->instances[instance].get(); const auto [in_remain, out_remain, num_frames] = decoder_instance->Decode( in_buffer->buffer, in_buffer->buf_size, out_buffer->buffer, out_buffer->buf_size); @@ -357,27 +358,81 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_ sideband_ptr += sizeof(AjmSidebandMFrame); } if (True(command & AjmJobRunFlags::GetCodecInfo)) { - LOG_TRACE(Lib_Ajm, "GetCodecInfo called, supplying dummy info for now"); - auto* codec_info = reinterpret_cast(sideband_ptr); - codec_info->uiFrameSamples = 0; - codec_info->uiFramesInSuperFrame = 0; - codec_info->uiNextFrameSize = 0; - codec_info->uiSuperFrameSize = 0; - sideband_ptr += sizeof(SceAjmSidebandDecAt9CodecInfo); + // TODO: Implement this for MP3 + + if (decoder_instance->codec_type == AjmCodecType::At9Dec) { + const auto at9_decoder_instance = + dynamic_cast(decoder_instance); + + Atrac9CodecInfo decoder_codec_info; + Atrac9GetCodecInfo(at9_decoder_instance->handle, &decoder_codec_info); + + auto* codec_info = + reinterpret_cast(sideband_ptr); + codec_info->uiFrameSamples = decoder_codec_info.frameSamples; + codec_info->uiFramesInSuperFrame = decoder_codec_info.framesInSuperframe; + codec_info->uiNextFrameSize = + static_cast(at9_decoder_instance->handle)->Config.FrameBytes; + codec_info->uiSuperFrameSize = decoder_codec_info.superframeSize; + sideband_ptr += sizeof(SceAjmSidebandDecAt9CodecInfo); + } } } batch_ptr += sizeof(AjmJobHeader) + header.job_size; } - static int batch_id = 0; - *out_batch_id = ++batch_id; + + batch_info->finished = true; return ORBIS_OK; } -int PS4_SYSV_ABI sceAjmBatchWait() { - LOG_ERROR(Lib_Ajm, "(STUBBED) called"); - return ORBIS_OK; +// useless for now because batch processing isn't asynchronous +int PS4_SYSV_ABI sceAjmBatchWait(const u32 context, const u32 batch_id, const u32 timeout, + AjmBatchError* const batch_error) { + LOG_INFO(Lib_Ajm, "called context = {}, batch_id = {}, timeout = {}", context, batch_id, + timeout); + + if (batch_id > 0xFF) { + return ORBIS_AJM_ERROR_INVALID_BATCH; + } + + if (batch_id >= dev->batches.size()) { + return ORBIS_AJM_ERROR_INVALID_BATCH; + } + + const std::shared_ptr batch = dev->batches[batch_id]; + + if (batch->waiting) { + return ORBIS_AJM_ERROR_BUSY; + } + + batch->waiting = true; + + if (timeout > 0 && timeout != SCE_AJM_WAIT_INFINITE) { + std::this_thread::sleep_for(std::chrono::milliseconds(timeout)); + + if (!batch->finished) { + batch->waiting = false; + return ORBIS_AJM_ERROR_IN_PROGRESS; + } + } + + if (timeout == SCE_AJM_WAIT_INFINITE) { + while (!batch->finished) { + std::this_thread::yield(); + } + } + + // timeout == 0 = non-blocking poll + if (!batch->finished) { + batch->waiting = false; + return ORBIS_AJM_ERROR_IN_PROGRESS; + } + + dev->batches.erase(dev->batches.begin() + batch_id); + + return 0; } int PS4_SYSV_ABI sceAjmDecAt9ParseConfigData() { @@ -433,16 +488,16 @@ int PS4_SYSV_ABI sceAjmInstanceCreate(u32 context, AjmCodecType codec_type, AjmI } const u32 index = dev->free_instances[dev->curr_cursor++]; dev->curr_cursor %= MaxInstances; - std::unique_ptr instance; + std::unique_ptr instance; switch (codec_type) { case AjmCodecType::Mp3Dec: - instance = std::make_unique(); + instance = std::make_unique(); break; case AjmCodecType::At9Dec: instance = std::make_unique(); break; default: - break; + UNREACHABLE_MSG("Codec #{} not implemented", u32(codec_type)); } instance->index = index; instance->codec_type = codec_type; diff --git a/src/core/libraries/ajm/ajm.h b/src/core/libraries/ajm/ajm.h index 3fbda7659..e3d474abc 100644 --- a/src/core/libraries/ajm/ajm.h +++ b/src/core/libraries/ajm/ajm.h @@ -41,12 +41,14 @@ struct AjmInOutJob { u32 unk1; u32 flags; AjmJobBuffer output; + void* sideband_output; }; enum class AjmJobControlFlags : u32 { Reset = 1 << 2, Initialize = 1 << 3, Resample = 1 << 4, + StatisticsEngine = 1U << 31, }; DECLARE_ENUM_FLAG_OPERATORS(AjmJobControlFlags) @@ -92,7 +94,7 @@ struct AjmSingleJob { }; }; -static_assert(sizeof(AjmSingleJob) == 0x40); +static_assert(sizeof(AjmSingleJob) == 0x48); struct AjmMultiJob { AjmHLEOpcode opcode; @@ -149,7 +151,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobControlBufferRa(AjmSingleJob* batch_pos, u32 in void* PS4_SYSV_ABI sceAjmBatchJobInlineBuffer(AjmSingleJob* batch_pos, const void* in_buffer, size_t in_size, const void** batch_address); void* PS4_SYSV_ABI sceAjmBatchJobRunBufferRa(AjmSingleJob* batch_pos, u32 instance, AjmFlags flags, - void * in_buffer, u32 in_size, u8* out_buffer, + u8* in_buffer, u32 in_size, u8* out_buffer, const u32 out_size, u8* sideband_output, const u32 sideband_output_size, const void* ret_addr); void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 instance, @@ -159,9 +161,10 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 in u64 num_output_buffers, void* sideband_output, u64 sideband_output_size, const void* ret_addr); int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_size, - const int priority, AjmBatchError* patch_error, + const int priority, AjmBatchError* batch_error, u32* out_batch_id); -int PS4_SYSV_ABI sceAjmBatchWait(); +int PS4_SYSV_ABI sceAjmBatchWait(const u32 context, const u32 batch_id, const u32 timeout, + AjmBatchError* const batch_error); int PS4_SYSV_ABI sceAjmDecAt9ParseConfigData(); int PS4_SYSV_ABI sceAjmDecMp3ParseFrame(const u8* stream, u32 stream_size, int parse_ofl, AjmDecMp3ParseFrame* frame); diff --git a/src/core/libraries/ajm/ajm_at9.cpp b/src/core/libraries/ajm/ajm_at9.cpp index c1a208881..ddcd8854c 100644 --- a/src/core/libraries/ajm/ajm_at9.cpp +++ b/src/core/libraries/ajm/ajm_at9.cpp @@ -4,8 +4,10 @@ #include #include "common/assert.h" #include "core/libraries/ajm/ajm_at9.h" +#include "error_codes.h" extern "C" { +#include #include } @@ -17,9 +19,14 @@ AjmAt9Decoder::AjmAt9Decoder() { AjmAt9Decoder::Reset(); } -AjmAt9Decoder::~AjmAt9Decoder() {} +AjmAt9Decoder::~AjmAt9Decoder() { + Atrac9ReleaseHandle(handle); +} void AjmAt9Decoder::Reset() { + Atrac9ReleaseHandle(handle); + handle = Atrac9GetHandle(); + ASSERT_MSG(handle, "Atrac9GetHandle failed"); decoded_samples = 0; static int filename = 0; file.close(); @@ -28,31 +35,40 @@ void AjmAt9Decoder::Reset() { std::tuple AjmAt9Decoder::Decode(const u8* in_buf, u32 in_size, u8* out_buf, u32 out_size) { - auto ParseWavHeader = [](const uint8_t* buf, WavHeader* header) { - std::memcpy(header, buf, sizeof(WavHeader)); - }; - if (in_size <= 0 || out_size <= 0) { return std::tuple(in_size, out_size, 0); } - WavHeader header{}; - ParseWavHeader(in_buf - 0x64, &header); + Atrac9InitDecoder(handle, config_data); - if (!decoder_initialized) { - Atrac9InitDecoder(handle, header.configData); - decoder_initialized = true; - } - - // todo: do something with decoded data :p + const auto decoder_handle = static_cast(handle); + int frame_count; int bytes_used; - Atrac9Decode(handle, in_buf, nullptr, &bytes_used); Atrac9CodecInfo codec_info; Atrac9GetCodecInfo(handle, &codec_info); - return std::tuple(in_size, out_size, 0); + decoded_samples = 0; + + const auto size = codec_info.channels * codec_info.frameSamples * sizeof(u16); + + for (frame_count = 0; frame_count < decoder_handle->Config.FramesPerSuperframe; frame_count++) { + short pcm_buffer[size]; + int ret = Atrac9Decode(decoder_handle, in_buf, pcm_buffer, &bytes_used); + ASSERT_MSG(ret == At9Status::ERR_SUCCESS, "Atrac9Decode failed"); + in_buf += bytes_used; + in_size -= bytes_used; + std::memcpy(out_buf, pcm_buffer, size); + file.write(reinterpret_cast(pcm_buffer), size); + out_buf += size; + out_size -= size; + decoded_samples += decoder_handle->Config.FrameSamples; + } + + LOG_TRACE(Lib_Ajm, "Decoded {} samples, frame count: {}", decoded_samples, frame_count); + + return std::tuple(in_size, out_size, frame_count); } } // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_at9.h b/src/core/libraries/ajm/ajm_at9.h index a7760829b..b96cda6a8 100644 --- a/src/core/libraries/ajm/ajm_at9.h +++ b/src/core/libraries/ajm/ajm_at9.h @@ -4,7 +4,6 @@ #pragma once #include -#include #include "common/types.h" #include "core/libraries/ajm/ajm_instance.h" @@ -16,42 +15,17 @@ namespace Libraries::Ajm { constexpr unsigned int SCE_AT9_CONFIG_DATA_SIZE = 4; -#pragma pack(push, 1) -struct WavHeader { - /* RIFF Chunk Descriptor */ - char RIFF[4]; // RIFF Header Magic header - uint32_t ChunkSize; // RIFF Chunk Size - char WAVE[4]; // WAVE Header - /* "fmt" sub-chunk */ - char fmt[4]; // FMT header - uint32_t Subchunk1Size; // Size of the fmt chunk - uint16_t AudioFormat; // Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM Mu-Law, 258=IBM A-Law, - // 259=ADPCM - uint16_t NumOfChan; // Number of channels 1=Mono 2=Sterio - uint32_t SamplesPerSec; // Sampling Frequency in Hz - uint32_t bytesPerSec; // bytes per second - uint16_t blockAlign; // 2=16-bit mono, 4=16-bit stereo - uint16_t bitsPerSample; // Number of bits per sample - u16 cbSize; // Extension size - u16 samplesPerBlock; - u32 channelMask; - GUID subFormat; - u32 versionInfo; - u8 configData[SCE_AT9_CONFIG_DATA_SIZE]; // the juicy part +struct SceAjmDecAt9InitializeParameters { + u8 config_data[SCE_AT9_CONFIG_DATA_SIZE]; u32 reserved; - /* "fact" sub-chunk */ - char Subchunk2ID[4]; // "fact" string - uint32_t Subchunk2Size; // Sampled data length }; -#pragma pack(pop) - -static_assert(sizeof(WavHeader) == 80); struct AjmAt9Decoder final : AjmInstance { void* handle; bool decoder_initialized = false; std::fstream file; int length; + u8 config_data[SCE_AT9_CONFIG_DATA_SIZE]; explicit AjmAt9Decoder(); ~AjmAt9Decoder() override; diff --git a/src/core/libraries/ajm/ajm_instance.h b/src/core/libraries/ajm/ajm_instance.h index 5fafeac80..5b5e60419 100644 --- a/src/core/libraries/ajm/ajm_instance.h +++ b/src/core/libraries/ajm/ajm_instance.h @@ -3,6 +3,7 @@ #pragma once +#include "common/enum.h" #include "common/types.h" extern "C" { @@ -19,6 +20,7 @@ enum class AjmCodecType : u32 { M4aacDec = 2, Max = 23, }; +DECLARE_ENUM_FLAG_OPERATORS(AjmCodecType); static constexpr u32 NumAjmCodecs = u32(AjmCodecType::Max); enum class AjmFormatEncoding : u32 { @@ -28,8 +30,8 @@ enum class AjmFormatEncoding : u32 { }; struct AjmSidebandResult { - s32 result; - s32 internal_result; + s32 result; + s32 internal_result; }; struct AjmSidebandMFrame { @@ -64,8 +66,8 @@ struct AjmInstance { virtual void Reset() = 0; - virtual std::tuple Decode(const u8* in_buf, u32 in_size, - u8* out_buf, u32 out_size) = 0; + virtual std::tuple Decode(const u8* in_buf, u32 in_size, u8* out_buf, + u32 out_size) = 0; }; } // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_mp3.cpp b/src/core/libraries/ajm/ajm_mp3.cpp index 6dd41de69..5bf164570 100644 --- a/src/core/libraries/ajm/ajm_mp3.cpp +++ b/src/core/libraries/ajm/ajm_mp3.cpp @@ -15,7 +15,9 @@ namespace Libraries::Ajm { // Following tables have been reversed from AJM library static constexpr std::array, 3> SamplerateTable = {{ - {0x5622, 0x5DC0, 0x3E80}, {0xAC44, 0xBB80, 0x7D00}, {0x2B11, 0x2EE0, 0x1F40}, + {0x5622, 0x5DC0, 0x3E80}, + {0xAC44, 0xBB80, 0x7D00}, + {0x2B11, 0x2EE0, 0x1F40}, }}; static constexpr std::array, 2> BitrateTable = {{ @@ -78,14 +80,13 @@ void AjmMp3Decoder::Reset() { file.open(fmt::format("inst{}_{}.raw", index, ++filename), std::ios::out | std::ios::binary); } -std::tuple AjmMp3Decoder::Decode(const u8* buf, u32 in_size, - u8* out_buf, u32 out_size) { +std::tuple AjmMp3Decoder::Decode(const u8* buf, u32 in_size, u8* out_buf, + u32 out_size) { u32 num_frames = 0; AVPacket* pkt = av_packet_alloc(); while (in_size > 0 && out_size > 0) { - int ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size, - buf, in_size, - AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); + int ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size, buf, in_size, AV_NOPTS_VALUE, + AV_NOPTS_VALUE, 0); ASSERT_MSG(ret >= 0, "Error while parsing {}", ret); buf += ret; in_size -= ret; diff --git a/src/core/libraries/ajm/ajm_mp3.h b/src/core/libraries/ajm/ajm_mp3.h index 7a81abae4..58d7e7bab 100644 --- a/src/core/libraries/ajm/ajm_mp3.h +++ b/src/core/libraries/ajm/ajm_mp3.h @@ -15,17 +15,11 @@ struct AVCodecParserContext; namespace Libraries::Ajm { -enum class AjmDecMp3OflType : u32 { - None = 0, - Lame = 1, - Vbri = 2, - Fgh = 3, - VbriAndFgh = 4 -}; +enum class AjmDecMp3OflType : u32 { None = 0, Lame = 1, Vbri = 2, Fgh = 3, VbriAndFgh = 4 }; // 11-bit syncword if MPEG 2.5 extensions are enabled static constexpr u8 SYNCWORDH = 0xff; -static constexpr u8 SYNCWORDL = 0xe0; +static constexpr u8 SYNCWORDL = 0xe0; struct AjmDecMp3ParseFrame { u64 frame_size; @@ -73,8 +67,8 @@ struct AjmMp3Decoder : public AjmInstance { void Reset() override; - std::tuple Decode(const u8* in_buf, u32 in_size, - u8* out_buf, u32 out_size) override; + std::tuple Decode(const u8* in_buf, u32 in_size, u8* out_buf, + u32 out_size) override; static int ParseMp3Header(const u8* buf, u32 stream_size, int parse_ofl, AjmDecMp3ParseFrame* frame); diff --git a/src/core/libraries/audio/audioout.cpp b/src/core/libraries/audio/audioout.cpp index 778d777c2..8057f3e11 100644 --- a/src/core/libraries/audio/audioout.cpp +++ b/src/core/libraries/audio/audioout.cpp @@ -1,7 +1,10 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include #include +#include #include #include "audio_core/sdl_audio.h" @@ -335,6 +338,10 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, const void* ptr) { int PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num) { for (u32 i = 0; i < num; i++) { + std::fstream file; + file.open(fmt::format("handle{}.raw", param[i].handle), std::ios::out | std::ios::binary); + file.write(static_cast(param[i].ptr), 1024); + file.close(); if (const auto err = sceAudioOutOutput(param[i].handle, param[i].ptr); err != 0) return err; } diff --git a/src/video_core/page_manager.cpp b/src/video_core/page_manager.cpp index a5d065ba5..fb09e70f2 100644 --- a/src/video_core/page_manager.cpp +++ b/src/video_core/page_manager.cpp @@ -10,7 +10,7 @@ #include "core/signals.h" #include "video_core/page_manager.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" -#define ENABLE_USERFAULTFD 1 + #ifndef _WIN64 #include #ifdef ENABLE_USERFAULTFD diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 48c055534..a68ec1e74 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -212,8 +212,6 @@ vk::SamplerAddressMode ClampMode(AmdGpu::ClampMode mode) { [[fallthrough]]; case AmdGpu::ClampMode::ClampBorder: return vk::SamplerAddressMode::eClampToBorder; - case AmdGpu::ClampMode::ClampHalfBorder: - return vk::SamplerAddressMode::eClampToBorder; default: UNREACHABLE(); } From 58b8864af678f7e65c517dde6d7c5d36e178b1d1 Mon Sep 17 00:00:00 2001 From: auser1337 Date: Fri, 18 Oct 2024 20:41:25 -0700 Subject: [PATCH 90/93] Remove .vscode folder --- src/.vscode/c_cpp_properties.json | 18 ---------- src/.vscode/launch.json | 24 ------------- src/.vscode/settings.json | 59 ------------------------------- 3 files changed, 101 deletions(-) delete mode 100644 src/.vscode/c_cpp_properties.json delete mode 100644 src/.vscode/launch.json delete mode 100644 src/.vscode/settings.json diff --git a/src/.vscode/c_cpp_properties.json b/src/.vscode/c_cpp_properties.json deleted file mode 100644 index c2098a2d0..000000000 --- a/src/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "configurations": [ - { - "name": "linux-gcc-x64", - "includePath": [ - "${workspaceFolder}/**" - ], - "compilerPath": "/usr/bin/gcc", - "cStandard": "${default}", - "cppStandard": "${default}", - "intelliSenseMode": "linux-gcc-x64", - "compilerArgs": [ - "" - ] - } - ], - "version": 4 -} \ No newline at end of file diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json deleted file mode 100644 index d0452262e..000000000 --- a/src/.vscode/launch.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "C/C++ Runner: Debug Session", - "type": "cppdbg", - "request": "launch", - "args": [], - "stopAtEntry": false, - "externalConsole": false, - "cwd": "/home/turtle/Desktop/shadPS4/src", - "program": "/home/turtle/Desktop/shadPS4/src/build/Debug/outDebug", - "MIMode": "gdb", - "miDebuggerPath": "gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] - } - ] -} \ No newline at end of file diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json deleted file mode 100644 index 3e5eb956e..000000000 --- a/src/.vscode/settings.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "C_Cpp_Runner.cCompilerPath": "gcc", - "C_Cpp_Runner.cppCompilerPath": "g++", - "C_Cpp_Runner.debuggerPath": "gdb", - "C_Cpp_Runner.cStandard": "", - "C_Cpp_Runner.cppStandard": "", - "C_Cpp_Runner.msvcBatchPath": "", - "C_Cpp_Runner.useMsvc": false, - "C_Cpp_Runner.warnings": [ - "-Wall", - "-Wextra", - "-Wpedantic", - "-Wshadow", - "-Wformat=2", - "-Wcast-align", - "-Wconversion", - "-Wsign-conversion", - "-Wnull-dereference" - ], - "C_Cpp_Runner.msvcWarnings": [ - "/W4", - "/permissive-", - "/w14242", - "/w14287", - "/w14296", - "/w14311", - "/w14826", - "/w44062", - "/w44242", - "/w14905", - "/w14906", - "/w14263", - "/w44265", - "/w14928" - ], - "C_Cpp_Runner.enableWarnings": true, - "C_Cpp_Runner.warningsAsError": false, - "C_Cpp_Runner.compilerArgs": [], - "C_Cpp_Runner.linkerArgs": [], - "C_Cpp_Runner.includePaths": [], - "C_Cpp_Runner.includeSearch": [ - "*", - "**/*" - ], - "C_Cpp_Runner.excludeSearch": [ - "**/build", - "**/build/**", - "**/.*", - "**/.*/**", - "**/.vscode", - "**/.vscode/**" - ], - "C_Cpp_Runner.useAddressSanitizer": false, - "C_Cpp_Runner.useUndefinedSanitizer": false, - "C_Cpp_Runner.useLeakSanitizer": false, - "C_Cpp_Runner.showCompilationTime": false, - "C_Cpp_Runner.useLinkTimeOptimization": false, - "C_Cpp_Runner.msvcSecureNoWarnings": false -} \ No newline at end of file From 87f8fea4de959f51392c06a1e39095c4aa633d86 Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Sat, 19 Oct 2024 15:30:58 +0300 Subject: [PATCH 91/93] renderer_vulkan: Commize and adjust buffer bindings (#1412) * shader_recompiler: Implement finite cmp class * shader_recompiler: Implement more opcodes * renderer_vulkan: Commonize buffer binding * liverpool: More dma data impl * fix * copy_shader: Handle additional instructions from Knack * translator: Add V_CMPX_GE_I32 --- src/core/address_space.h | 4 + src/core/memory.cpp | 11 ++ src/core/memory.h | 2 + .../frontend/copy_shader.cpp | 8 + .../frontend/translate/scalar_alu.cpp | 24 ++- .../frontend/translate/translate.h | 4 +- .../frontend/translate/vector_alu.cpp | 14 ++ .../ir/passes/constant_propagation_pass.cpp | 32 +--- .../ir/passes/resource_tracking_pass.cpp | 2 +- src/shader_recompiler/ir/reg.h | 2 + src/video_core/amdgpu/liverpool.cpp | 70 +++++++- src/video_core/amdgpu/pm4_cmds.h | 31 +++- src/video_core/buffer_cache/buffer.h | 1 + src/video_core/buffer_cache/buffer_cache.cpp | 70 ++++---- src/video_core/buffer_cache/buffer_cache.h | 23 +-- .../renderer_vulkan/vk_compute_pipeline.cpp | 159 ++++-------------- .../renderer_vulkan/vk_graphics_pipeline.cpp | 143 +++------------- .../renderer_vulkan/vk_pipeline_common.cpp | 146 +++++++++++++++- .../renderer_vulkan/vk_pipeline_common.h | 9 + .../renderer_vulkan/vk_rasterizer.cpp | 18 +- .../renderer_vulkan/vk_rasterizer.h | 2 +- src/video_core/texture_cache/sampler.cpp | 3 + .../texture_cache/texture_cache.cpp | 2 +- 23 files changed, 438 insertions(+), 342 deletions(-) diff --git a/src/core/address_space.h b/src/core/address_space.h index 3233c7588..7ccc2cd1e 100644 --- a/src/core/address_space.h +++ b/src/core/address_space.h @@ -45,6 +45,10 @@ public: explicit AddressSpace(); ~AddressSpace(); + [[nodiscard]] u8* BackingBase() const noexcept { + return backing_base; + } + [[nodiscard]] VAddr SystemManagedVirtualBase() noexcept { return reinterpret_cast(system_managed_base); } diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 471ba5856..031b7b135 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -54,6 +54,17 @@ void MemoryManager::SetupMemoryRegions(u64 flexible_size) { total_flexible_size, total_direct_size); } +bool MemoryManager::TryWriteBacking(void* address, const void* data, u32 num_bytes) { + const VAddr virtual_addr = std::bit_cast(address); + const auto& vma = FindVMA(virtual_addr)->second; + if (vma.type != VMAType::Direct) { + return false; + } + u8* backing = impl.BackingBase() + vma.phys_base + (virtual_addr - vma.base); + memcpy(backing, data, num_bytes); + return true; +} + PAddr MemoryManager::PoolExpand(PAddr search_start, PAddr search_end, size_t size, u64 alignment) { std::scoped_lock lk{mutex}; diff --git a/src/core/memory.h b/src/core/memory.h index 320aa367a..286f1c979 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -149,6 +149,8 @@ public: return impl.SystemReservedVirtualBase(); } + bool TryWriteBacking(void* address, const void* data, u32 num_bytes); + void SetupMemoryRegions(u64 flexible_size); PAddr PoolExpand(PAddr search_start, PAddr search_end, size_t size, u64 alignment); diff --git a/src/shader_recompiler/frontend/copy_shader.cpp b/src/shader_recompiler/frontend/copy_shader.cpp index b2c795667..8750e2b18 100644 --- a/src/shader_recompiler/frontend/copy_shader.cpp +++ b/src/shader_recompiler/frontend/copy_shader.cpp @@ -29,6 +29,14 @@ CopyShaderData ParseCopyShader(std::span code) { sources[inst.dst[0].code] = inst.control.sopk.simm; break; } + case Gcn::Opcode::S_MOV_B32: { + sources[inst.dst[0].code] = inst.src[0].code; + break; + } + case Gcn::Opcode::S_ADDK_I32: { + sources[inst.dst[0].code] += inst.control.sopk.simm; + break; + } case Gcn::Opcode::EXP: { const auto& exp = inst.control.exp; const IR::Attribute semantic = static_cast(exp.target); diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index 36c1ec85f..1e627d95c 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -92,8 +92,12 @@ void Translator::EmitScalarAlu(const GcnInst& inst) { break; case Opcode::S_BREV_B32: return S_BREV_B32(inst); + case Opcode::S_BCNT1_I32_B64: + return S_BCNT1_I32_B64(inst); case Opcode::S_AND_SAVEEXEC_B64: - return S_AND_SAVEEXEC_B64(inst); + return S_SAVEEXEC_B64(NegateMode::None, false, inst); + case Opcode::S_ORN2_SAVEEXEC_B64: + return S_SAVEEXEC_B64(NegateMode::Src1, true, inst); default: LogMissingOpcode(inst); } @@ -540,11 +544,17 @@ void Translator::S_BREV_B32(const GcnInst& inst) { SetDst(inst.dst[0], ir.BitReverse(GetSrc(inst.src[0]))); } -void Translator::S_AND_SAVEEXEC_B64(const GcnInst& inst) { +void Translator::S_BCNT1_I32_B64(const GcnInst& inst) { + const IR::U32 result = ir.BitCount(GetSrc(inst.src[0])); + SetDst(inst.dst[0], result); + ir.SetScc(ir.INotEqual(result, ir.Imm32(0))); +} + +void Translator::S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& inst) { // This instruction normally operates on 64-bit data (EXEC, VCC, SGPRs) // However here we flatten it to 1-bit EXEC and 1-bit VCC. For the destination // SGPR we have a special IR opcode for SPGRs that act as thread masks. - const IR::U1 exec{ir.GetExec()}; + IR::U1 exec{ir.GetExec()}; const IR::U1 src = [&] { switch (inst.src[0].field) { case OperandField::VccLo: @@ -568,7 +578,13 @@ void Translator::S_AND_SAVEEXEC_B64(const GcnInst& inst) { } // Update EXEC. - const IR::U1 result = ir.LogicalAnd(exec, src); + if (negate == NegateMode::Src1) { + exec = ir.LogicalNot(exec); + } + IR::U1 result = is_or ? ir.LogicalOr(exec, src) : ir.LogicalAnd(exec, src); + if (negate == NegateMode::Result) { + result = ir.LogicalNot(result); + } ir.SetExec(result); ir.SetScc(result); } diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index b70d4b829..79bc33f0c 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -108,8 +108,9 @@ public: void S_MOV_B64(const GcnInst& inst); void S_NOT_B64(const GcnInst& inst); void S_BREV_B32(const GcnInst& inst); + void S_BCNT1_I32_B64(const GcnInst& inst); void S_GETPC_B64(u32 pc, const GcnInst& inst); - void S_AND_SAVEEXEC_B64(const GcnInst& inst); + void S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& inst); // SOPC void S_CMP(ConditionOp cond, bool is_signed, const GcnInst& inst); @@ -225,6 +226,7 @@ public: void V_MED3_I32(const GcnInst& inst); void V_SAD(const GcnInst& inst); void V_SAD_U32(const GcnInst& inst); + void V_CVT_PK_U16_U32(const GcnInst& inst); void V_CVT_PK_U8_F32(const GcnInst& inst); void V_LSHL_B64(const GcnInst& inst); void V_MUL_F64(const GcnInst& inst); diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 279695461..433f9dce7 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -157,6 +157,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_RCP_F64(inst); case Opcode::V_RCP_IFLAG_F32: return V_RCP_F32(inst); + case Opcode::V_RCP_CLAMP_F32: + return V_RCP_F32(inst); case Opcode::V_RSQ_CLAMP_F32: return V_RSQ_F32(inst); case Opcode::V_RSQ_LEGACY_F32: @@ -268,6 +270,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_CMP_U32(ConditionOp::GT, true, true, inst); case Opcode::V_CMPX_LG_I32: return V_CMP_U32(ConditionOp::LG, true, true, inst); + case Opcode::V_CMPX_GE_I32: + return V_CMP_U32(ConditionOp::GE, true, true, inst); // V_CMP_{OP8}_U32 case Opcode::V_CMP_F_U32: @@ -355,6 +359,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_MED3_I32(inst); case Opcode::V_SAD_U32: return V_SAD_U32(inst); + case Opcode::V_CVT_PK_U16_U32: + return V_CVT_PK_U16_U32(inst); case Opcode::V_CVT_PK_U8_F32: return V_CVT_PK_U8_F32(inst); case Opcode::V_LSHL_B64: @@ -1108,6 +1114,14 @@ void Translator::V_SAD_U32(const GcnInst& inst) { SetDst(inst.dst[0], ir.IAdd(result, src2)); } +void Translator::V_CVT_PK_U16_U32(const GcnInst& inst) { + const IR::U32 src0{GetSrc(inst.src[0])}; + const IR::U32 src1{GetSrc(inst.src[1])}; + const IR::U32 lo = ir.IMin(src0, ir.Imm32(0xFFFF), false); + const IR::U32 hi = ir.IMin(src1, ir.Imm32(0xFFFF), false); + SetDst(inst.dst[0], ir.BitFieldInsert(lo, hi, ir.Imm32(16), ir.Imm32(16))); +} + void Translator::V_CVT_PK_U8_F32(const GcnInst& inst) { const IR::F32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; diff --git a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp index 775aed5b3..a03fe051c 100644 --- a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp +++ b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp @@ -6,6 +6,7 @@ #include #include "common/func_traits.h" #include "shader_recompiler/ir/basic_block.h" +#include "shader_recompiler/ir/ir_emitter.h" namespace Shader::Optimization { @@ -215,36 +216,17 @@ void FoldAdd(IR::Block& block, IR::Inst& inst) { } } -template -bool IsArgImm(const IR::Inst& inst, u32 imm) { - const IR::Value& arg = inst.Arg(idx); - return arg.IsImmediate() && arg.U32() == imm; -}; - -void FoldBooleanConvert(IR::Inst& inst) { - // Eliminate pattern - // %4 = - // %5 = SelectU32 %4, #1, #0 (uses: 2) - // %8 = INotEqual %5, #0 (uses: 1) - if (!IsArgImm<1>(inst, 0)) { - return; - } - IR::Inst* prod = inst.Arg(0).TryInstRecursive(); - if (!prod || prod->GetOpcode() != IR::Opcode::SelectU32) { - return; - } - if (IsArgImm<1>(*prod, 1) && IsArgImm<2>(*prod, 0)) { - inst.ReplaceUsesWith(prod->Arg(0)); - } -} - -void FoldCmpClass(IR::Inst& inst) { +void FoldCmpClass(IR::Block& block, IR::Inst& inst) { ASSERT_MSG(inst.Arg(1).IsImmediate(), "Unable to resolve compare operation"); const auto class_mask = static_cast(inst.Arg(1).U32()); if ((class_mask & IR::FloatClassFunc::NaN) == IR::FloatClassFunc::NaN) { inst.ReplaceOpcode(IR::Opcode::FPIsNan32); } else if ((class_mask & IR::FloatClassFunc::Infinity) == IR::FloatClassFunc::Infinity) { inst.ReplaceOpcode(IR::Opcode::FPIsInf32); + } else if ((class_mask & IR::FloatClassFunc::Finite) == IR::FloatClassFunc::Finite) { + IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; + const IR::F32 value = IR::F32{inst.Arg(0)}; + inst.ReplaceUsesWith(ir.LogicalNot(ir.LogicalOr(ir.FPIsInf(value), ir.FPIsInf(value)))); } else { UNREACHABLE(); } @@ -276,7 +258,7 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) { FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a * b; }); return; case IR::Opcode::FPCmpClass32: - FoldCmpClass(inst); + FoldCmpClass(block, inst); return; case IR::Opcode::ShiftLeftLogical32: FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return static_cast(a << b); }); diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 0d91badda..aa05d3aed 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -605,7 +605,7 @@ void PatchImageSampleInstruction(IR::Block& block, IR::Inst& inst, Info& info, : IR::F32{}; const IR::F32 lod_clamp = inst_info.has_lod_clamp ? get_addr_reg(addr_reg++) : IR::F32{}; - const auto new_inst = [&] -> IR::Value { + auto new_inst = [&] -> IR::Value { if (inst_info.is_gather) { if (inst_info.is_depth) { return ir.ImageGatherDref(handle, coords, offset, dref, inst_info); diff --git a/src/shader_recompiler/ir/reg.h b/src/shader_recompiler/ir/reg.h index d7c0b1db5..3004d2b86 100644 --- a/src/shader_recompiler/ir/reg.h +++ b/src/shader_recompiler/ir/reg.h @@ -24,6 +24,8 @@ enum class FloatClassFunc : u32 { NaN = SignalingNan | QuietNan, Infinity = PositiveInfinity | NegativeInfinity, + Finite = NegativeNormal | NegativeDenorm | NegativeZero | PositiveNormal | PositiveDenorm | + PositiveZero, }; DECLARE_ENUM_FLAG_OPERATORS(FloatClassFunc) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 7b4727088..53aab630e 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -8,6 +8,7 @@ #include "common/thread.h" #include "core/debug_state.h" #include "core/libraries/videoout/driver.h" +#include "core/memory.h" #include "video_core/amdgpu/liverpool.h" #include "video_core/amdgpu/pm4_cmds.h" #include "video_core/renderdoc.h" @@ -504,7 +505,12 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - event_eos->SignalFence(); + event_eos->SignalFence([](void* address, u64 data, u32 num_bytes) { + auto* memory = Core::Memory::Instance(); + if (!memory->TryWriteBacking(address, &data, num_bytes)) { + memcpy(address, &data, num_bytes); + } + }); if (event_eos->command == PM4CmdEventWriteEos::Command::GdsStore) { ASSERT(event_eos->size == 1); if (rasterizer) { @@ -517,13 +523,42 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - event_eop->SignalFence(); + event_eop->SignalFence([](void* address, u64 data, u32 num_bytes) { + auto* memory = Core::Memory::Instance(); + if (!memory->TryWriteBacking(address, &data, num_bytes)) { + memcpy(address, &data, num_bytes); + } + }); break; } case PM4ItOpcode::DmaData: { const auto* dma_data = reinterpret_cast(header); + if (dma_data->dst_addr_lo == 0x3022C) { + break; + } if (dma_data->src_sel == DmaDataSrc::Data && dma_data->dst_sel == DmaDataDst::Gds) { - rasterizer->InlineDataToGds(dma_data->dst_addr_lo, dma_data->data); + rasterizer->InlineData(dma_data->dst_addr_lo, &dma_data->data, sizeof(u32), + true); + } else if (dma_data->src_sel == DmaDataSrc::Memory && + dma_data->dst_sel == DmaDataDst::Gds) { + rasterizer->InlineData(dma_data->dst_addr_lo, + dma_data->SrcAddress(), + dma_data->NumBytes(), true); + } else if (dma_data->src_sel == DmaDataSrc::Data && + dma_data->dst_sel == DmaDataDst::Memory) { + rasterizer->InlineData(dma_data->DstAddress(), &dma_data->data, + sizeof(u32), false); + } else if (dma_data->src_sel == DmaDataSrc::Gds && + dma_data->dst_sel == DmaDataDst::Memory) { + LOG_WARNING(Render_Vulkan, "GDS memory read"); + } else if (dma_data->src_sel == DmaDataSrc::Memory && + dma_data->dst_sel == DmaDataDst::Memory) { + rasterizer->InlineData(dma_data->DstAddress(), + dma_data->SrcAddress(), + dma_data->NumBytes(), false); + } else { + UNREACHABLE_MSG("WriteData src_sel = {}, dst_sel = {}", + u32(dma_data->src_sel.Value()), u32(dma_data->dst_sel.Value())); } break; } @@ -631,6 +666,35 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, int vqid) { }; break; } + case PM4ItOpcode::DmaData: { + const auto* dma_data = reinterpret_cast(header); + if (dma_data->dst_addr_lo == 0x3022C) { + break; + } + if (dma_data->src_sel == DmaDataSrc::Data && dma_data->dst_sel == DmaDataDst::Gds) { + rasterizer->InlineData(dma_data->dst_addr_lo, &dma_data->data, sizeof(u32), true); + } else if (dma_data->src_sel == DmaDataSrc::Memory && + dma_data->dst_sel == DmaDataDst::Gds) { + rasterizer->InlineData(dma_data->dst_addr_lo, dma_data->SrcAddress(), + dma_data->NumBytes(), true); + } else if (dma_data->src_sel == DmaDataSrc::Data && + dma_data->dst_sel == DmaDataDst::Memory) { + rasterizer->InlineData(dma_data->DstAddress(), &dma_data->data, sizeof(u32), + false); + } else if (dma_data->src_sel == DmaDataSrc::Gds && + dma_data->dst_sel == DmaDataDst::Memory) { + LOG_WARNING(Render_Vulkan, "GDS memory read"); + } else if (dma_data->src_sel == DmaDataSrc::Memory && + dma_data->dst_sel == DmaDataDst::Memory) { + rasterizer->InlineData(dma_data->DstAddress(), + dma_data->SrcAddress(), dma_data->NumBytes(), + false); + } else { + UNREACHABLE_MSG("WriteData src_sel = {}, dst_sel = {}", + u32(dma_data->src_sel.Value()), u32(dma_data->dst_sel.Value())); + } + break; + } case PM4ItOpcode::AcquireMem: { break; } diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index a7a862ea3..a956b030d 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -313,25 +313,26 @@ struct PM4CmdEventWriteEop { return data_lo | u64(data_hi) << 32; } - void SignalFence() const { + void SignalFence(auto&& write_mem) const { + u32* address = Address(); switch (data_sel.Value()) { case DataSelect::None: { break; } case DataSelect::Data32Low: { - *Address() = DataDWord(); + write_mem(address, DataDWord(), sizeof(u32)); break; } case DataSelect::Data64: { - *Address() = DataQWord(); + write_mem(address, DataQWord(), sizeof(u64)); break; } case DataSelect::GpuClock64: { - *Address() = GetGpuClock64(); + write_mem(address, GetGpuClock64(), sizeof(u64)); break; } case DataSelect::PerfCounter: { - *Address() = Common::FencedRDTSC(); + write_mem(address, Common::FencedRDTSC(), sizeof(u64)); break; } default: { @@ -401,6 +402,20 @@ struct PM4DmaData { u32 dst_addr_lo; u32 dst_addr_hi; u32 command; + + template + T SrcAddress() const { + return std::bit_cast(src_addr_lo | u64(src_addr_hi) << 32); + } + + template + T DstAddress() const { + return std::bit_cast(dst_addr_lo | u64(dst_addr_hi) << 32); + } + + u32 NumBytes() const noexcept { + return command & 0x1fffff; + } }; struct PM4CmdWaitRegMem { @@ -432,7 +447,7 @@ struct PM4CmdWaitRegMem { template T Address() const { - return reinterpret_cast((uintptr_t(poll_addr_hi) << 32) | poll_addr_lo); + return std::bit_cast((uintptr_t(poll_addr_hi) << 32) | poll_addr_lo); } bool Test() const { @@ -534,11 +549,11 @@ struct PM4CmdEventWriteEos { return this->data; } - void SignalFence() const { + void SignalFence(auto&& write_mem) const { const auto cmd = command.Value(); switch (cmd) { case Command::SignalFence: { - *Address() = DataDWord(); + write_mem(Address(), DataDWord(), sizeof(u32)); break; } case Command::GdsStore: { diff --git a/src/video_core/buffer_cache/buffer.h b/src/video_core/buffer_cache/buffer.h index 403d4ed85..f67278f64 100644 --- a/src/video_core/buffer_cache/buffer.h +++ b/src/video_core/buffer_cache/buffer.h @@ -142,6 +142,7 @@ public: VAddr cpu_addr = 0; bool is_picked{}; bool is_coherent{}; + bool is_deleted{}; int stream_score = 0; size_t size_bytes = 0; std::span mapped_data; diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index c2993f3d1..f665ba512 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -20,7 +20,7 @@ static constexpr size_t StagingBufferSize = 1_GB; static constexpr size_t UboStreamBufferSize = 64_MB; BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, - const AmdGpu::Liverpool* liverpool_, TextureCache& texture_cache_, + AmdGpu::Liverpool* liverpool_, TextureCache& texture_cache_, PageManager& tracker_) : instance{instance_}, scheduler{scheduler_}, liverpool{liverpool_}, texture_cache{texture_cache_}, tracker{tracker_}, @@ -70,11 +70,10 @@ void BufferCache::InvalidateMemory(VAddr device_addr, u64 size) { void BufferCache::DownloadBufferMemory(Buffer& buffer, VAddr device_addr, u64 size) { boost::container::small_vector copies; u64 total_size_bytes = 0; - u64 largest_copy = 0; memory_tracker.ForEachDownloadRange( device_addr, size, [&](u64 device_addr_out, u64 range_size) { const VAddr buffer_addr = buffer.CpuAddr(); - const auto add_download = [&](VAddr start, VAddr end, u64) { + const auto add_download = [&](VAddr start, VAddr end) { const u64 new_offset = start - buffer_addr; const u64 new_size = end - start; copies.push_back(vk::BufferCopy{ @@ -82,12 +81,10 @@ void BufferCache::DownloadBufferMemory(Buffer& buffer, VAddr device_addr, u64 si .dstOffset = total_size_bytes, .size = new_size, }); - // Align up to avoid cache conflicts - constexpr u64 align = 64ULL; - constexpr u64 mask = ~(align - 1ULL); - total_size_bytes += (new_size + align - 1) & mask; - largest_copy = std::max(largest_copy, new_size); + total_size_bytes += new_size; }; + gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, add_download); + gpu_modified_ranges.Subtract(device_addr_out, range_size); }); if (total_size_bytes == 0) { return; @@ -181,6 +178,9 @@ bool BufferCache::BindVertexBuffers(const Shader::Info& vs_info) { .divisor = 1, }); } + if (ranges.empty()) { + return false; + } std::ranges::sort(ranges, [](const BufferRange& lhv, const BufferRange& rhv) { return lhv.base_address < rhv.base_address; @@ -269,48 +269,62 @@ u32 BufferCache::BindIndexBuffer(bool& is_indexed, u32 index_offset) { return regs.num_indices; } -void BufferCache::InlineDataToGds(u32 gds_offset, u32 value) { - ASSERT_MSG(gds_offset % 4 == 0, "GDS offset must be dword aligned"); +void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds) { + ASSERT_MSG(address % 4 == 0, "GDS offset must be dword aligned"); + if (!is_gds && !IsRegionRegistered(address, num_bytes)) { + memcpy(std::bit_cast(address), value, num_bytes); + return; + } scheduler.EndRendering(); const auto cmdbuf = scheduler.CommandBuffer(); + const Buffer* buffer = [&] { + if (is_gds) { + return &gds_buffer; + } + const BufferId buffer_id = FindBuffer(address, num_bytes); + return &slot_buffers[buffer_id]; + }(); const vk::BufferMemoryBarrier2 buf_barrier = { .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, .dstAccessMask = vk::AccessFlagBits2::eMemoryRead, - .buffer = gds_buffer.Handle(), - .offset = gds_offset, - .size = sizeof(u32), + .buffer = buffer->Handle(), + .offset = buffer->Offset(address), + .size = num_bytes, }; cmdbuf.pipelineBarrier2(vk::DependencyInfo{ .dependencyFlags = vk::DependencyFlagBits::eByRegion, .bufferMemoryBarrierCount = 1, .pBufferMemoryBarriers = &buf_barrier, }); - cmdbuf.updateBuffer(gds_buffer.Handle(), gds_offset, sizeof(u32), &value); + cmdbuf.updateBuffer(buffer->Handle(), buf_barrier.offset, num_bytes, value); } std::pair BufferCache::ObtainBuffer(VAddr device_addr, u32 size, bool is_written, - bool is_texel_buffer) { + bool is_texel_buffer, BufferId buffer_id) { + // For small uniform buffers that have not been modified by gpu + // use device local stream buffer to reduce renderpass breaks. static constexpr u64 StreamThreshold = CACHING_PAGESIZE; const bool is_gpu_dirty = memory_tracker.IsRegionGpuModified(device_addr, size); if (!is_written && size <= StreamThreshold && !is_gpu_dirty) { - // For small uniform buffers that have not been modified by gpu - // use device local stream buffer to reduce renderpass breaks. const u64 offset = stream_buffer.Copy(device_addr, size, instance.UniformMinAlignment()); return {&stream_buffer, offset}; } - const BufferId buffer_id = FindBuffer(device_addr, size); + if (!buffer_id || slot_buffers[buffer_id].is_deleted) { + buffer_id = FindBuffer(device_addr, size); + } Buffer& buffer = slot_buffers[buffer_id]; SynchronizeBuffer(buffer, device_addr, size, is_texel_buffer); if (is_written) { memory_tracker.MarkRegionAsGpuModified(device_addr, size); + gpu_modified_ranges.Add(device_addr, size); } return {&buffer, buffer.Offset(device_addr)}; } -std::pair BufferCache::ObtainTempBuffer(VAddr gpu_addr, u32 size) { +std::pair BufferCache::ObtainViewBuffer(VAddr gpu_addr, u32 size) { const u64 page = gpu_addr >> CACHING_PAGEBITS; const BufferId buffer_id = page_table[page]; if (buffer_id) { @@ -474,7 +488,7 @@ void BufferCache::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eAllCommands, vk::DependencyFlagBits::eByRegion, WRITE_BARRIER, {}, {}); - DeleteBuffer(overlap_id, true); + DeleteBuffer(overlap_id); } BufferId BufferCache::CreateBuffer(VAddr device_addr, u32 wanted_size) { @@ -529,7 +543,7 @@ void BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size, u64 total_size_bytes = 0; u64 largest_copy = 0; VAddr buffer_start = buffer.CpuAddr(); - const auto add_copy = [&](VAddr device_addr_out, u64 range_size) { + memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) { copies.push_back(vk::BufferCopy{ .srcOffset = total_size_bytes, .dstOffset = device_addr_out - buffer_start, @@ -537,11 +551,6 @@ void BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size, }); total_size_bytes += range_size; largest_copy = std::max(largest_copy, range_size); - }; - memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) { - add_copy(device_addr_out, range_size); - // Prevent uploading to gpu modified regions. - // gpu_modified_ranges.ForEachNotInRange(device_addr_out, range_size, add_copy); }); SCOPE_EXIT { if (is_texel_buffer) { @@ -654,14 +663,11 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, return true; } -void BufferCache::DeleteBuffer(BufferId buffer_id, bool do_not_mark) { - // Mark the whole buffer as CPU written to stop tracking CPU writes - if (!do_not_mark) { - Buffer& buffer = slot_buffers[buffer_id]; - memory_tracker.MarkRegionAsCpuModified(buffer.CpuAddr(), buffer.SizeBytes()); - } +void BufferCache::DeleteBuffer(BufferId buffer_id) { + Buffer& buffer = slot_buffers[buffer_id]; Unregister(buffer_id); scheduler.DeferOperation([this, buffer_id] { slot_buffers.erase(buffer_id); }); + buffer.is_deleted = true; } } // namespace VideoCore diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 76309363a..6710c8615 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -12,6 +12,7 @@ #include "common/types.h" #include "video_core/buffer_cache/buffer.h" #include "video_core/buffer_cache/memory_tracker_base.h" +#include "video_core/buffer_cache/range_set.h" #include "video_core/multi_level_page_table.h" namespace AmdGpu { @@ -53,7 +54,7 @@ public: public: explicit BufferCache(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler, - const AmdGpu::Liverpool* liverpool, TextureCache& texture_cache, + AmdGpu::Liverpool* liverpool, TextureCache& texture_cache, PageManager& tracker); ~BufferCache(); @@ -80,15 +81,16 @@ public: /// Bind host index buffer for the current draw. u32 BindIndexBuffer(bool& is_indexed, u32 index_offset); - /// Writes a value to GDS buffer. - void InlineDataToGds(u32 gds_offset, u32 value); + /// Writes a value to GPU buffer. + void InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds); /// Obtains a buffer for the specified region. [[nodiscard]] std::pair ObtainBuffer(VAddr gpu_addr, u32 size, bool is_written, - bool is_texel_buffer = false); + bool is_texel_buffer = false, + BufferId buffer_id = {}); - /// Obtains a temporary buffer for usage in texture cache. - [[nodiscard]] std::pair ObtainTempBuffer(VAddr gpu_addr, u32 size); + /// Attempts to obtain a buffer without modifying the cache contents. + [[nodiscard]] std::pair ObtainViewBuffer(VAddr gpu_addr, u32 size); /// Return true when a region is registered on the cache [[nodiscard]] bool IsRegionRegistered(VAddr addr, size_t size); @@ -99,6 +101,8 @@ public: /// Return true when a CPU region is modified from the GPU [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size); + [[nodiscard]] BufferId FindBuffer(VAddr device_addr, u32 size); + private: template void ForEachBufferInRange(VAddr device_addr, u64 size, Func&& func) { @@ -119,8 +123,6 @@ private: void DownloadBufferMemory(Buffer& buffer, VAddr device_addr, u64 size); - [[nodiscard]] BufferId FindBuffer(VAddr device_addr, u32 size); - [[nodiscard]] OverlapResult ResolveOverlaps(VAddr device_addr, u32 wanted_size); void JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, bool accumulate_stream_score); @@ -138,11 +140,11 @@ private: bool SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, u32 size); - void DeleteBuffer(BufferId buffer_id, bool do_not_mark = false); + void DeleteBuffer(BufferId buffer_id); const Vulkan::Instance& instance; Vulkan::Scheduler& scheduler; - const AmdGpu::Liverpool* liverpool; + AmdGpu::Liverpool* liverpool; TextureCache& texture_cache; PageManager& tracker; StreamBuffer staging_buffer; @@ -150,6 +152,7 @@ private: Buffer gds_buffer; std::mutex mutex; Common::SlotVector slot_buffers; + RangeSet gpu_modified_ranges; vk::BufferView null_buffer_view; MemoryTracker memory_tracker; PageTable page_table; diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 37a44ddac..7122ca134 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -3,7 +3,6 @@ #include -#include "common/alignment.h" #include "video_core/buffer_cache/buffer_cache.h" #include "video_core/renderer_vulkan/vk_compute_pipeline.h" #include "video_core/renderer_vulkan/vk_instance.h" @@ -113,140 +112,45 @@ ComputePipeline::~ComputePipeline() = default; bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache, VideoCore::TextureCache& texture_cache) const { // Bind resource buffers and textures. - boost::container::static_vector buffer_views; - boost::container::static_vector buffer_infos; boost::container::small_vector set_writes; - boost::container::small_vector buffer_barriers; + BufferBarriers buffer_barriers; Shader::PushData push_data{}; Shader::Backend::Bindings binding{}; + info->PushUd(binding, push_data); + + buffer_infos.clear(); + buffer_views.clear(); image_infos.clear(); - info->PushUd(binding, push_data); - for (const auto& desc : info->buffers) { - bool is_storage = true; - if (desc.is_gds_buffer) { - auto* vk_buffer = buffer_cache.GetGdsBuffer(); - buffer_infos.emplace_back(vk_buffer->Handle(), 0, vk_buffer->SizeBytes()); - } else { - const auto vsharp = desc.GetSharp(*info); - is_storage = desc.IsStorage(vsharp); - const VAddr address = vsharp.base_address; - // Most of the time when a metadata is updated with a shader it gets cleared. It means - // 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 - // will need its full emulation anyways. For cases of metadata read a warning will be - // logged. - if (desc.is_written) { - if (texture_cache.TouchMeta(address, true)) { - LOG_TRACE(Render_Vulkan, "Metadata update skipped"); - return false; - } - } else { - if (texture_cache.IsMeta(address)) { - LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a CS shader (buffer)"); - } + // Most of the time when a metadata is updated with a shader it gets cleared. It means + // 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 + // will need its full emulation anyways. For cases of metadata read a warning will be logged. + for (const auto& desc : info->texture_buffers) { + const VAddr address = desc.GetSharp(*info).base_address; + if (desc.is_written) { + if (texture_cache.TouchMeta(address, true)) { + LOG_TRACE(Render_Vulkan, "Metadata update skipped"); + return false; + } + } else { + if (texture_cache.IsMeta(address)) { + LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a CS shader (buffer)"); } - const u32 size = vsharp.GetSize(); - const u32 alignment = - is_storage ? instance.StorageMinAlignment() : instance.UniformMinAlignment(); - const auto [vk_buffer, offset] = - buffer_cache.ObtainBuffer(address, size, desc.is_written); - const u32 offset_aligned = Common::AlignDown(offset, alignment); - const u32 adjust = offset - offset_aligned; - ASSERT(adjust % 4 == 0); - push_data.AddOffset(binding.buffer, adjust); - buffer_infos.emplace_back(vk_buffer->Handle(), offset_aligned, size + adjust); } - set_writes.push_back({ - .dstSet = VK_NULL_HANDLE, - .dstBinding = binding.unified++, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = is_storage ? vk::DescriptorType::eStorageBuffer - : vk::DescriptorType::eUniformBuffer, - .pBufferInfo = &buffer_infos.back(), - }); - ++binding.buffer; } - const auto null_buffer_view = - instance.IsNullDescriptorSupported() ? VK_NULL_HANDLE : buffer_cache.NullBufferView(); - for (const auto& desc : info->texture_buffers) { - const auto vsharp = desc.GetSharp(*info); - vk::BufferView& buffer_view = buffer_views.emplace_back(null_buffer_view); - const u32 size = vsharp.GetSize(); - if (vsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid && size != 0) { - const VAddr address = vsharp.base_address; - if (desc.is_written) { - if (texture_cache.TouchMeta(address, true)) { - LOG_TRACE(Render_Vulkan, "Metadata update skipped"); - return false; - } - } else { - if (texture_cache.IsMeta(address)) { - LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a CS shader (buffer)"); - } - } - const u32 alignment = instance.TexelBufferMinAlignment(); - const auto [vk_buffer, offset] = - buffer_cache.ObtainBuffer(address, size, desc.is_written, true); - const u32 fmt_stride = AmdGpu::NumBits(vsharp.GetDataFmt()) >> 3; - ASSERT_MSG(fmt_stride == vsharp.GetStride(), - "Texel buffer stride must match format stride"); - const u32 offset_aligned = Common::AlignDown(offset, alignment); - const u32 adjust = offset - offset_aligned; - ASSERT(adjust % fmt_stride == 0); - push_data.AddOffset(binding.buffer, adjust / fmt_stride); - buffer_view = vk_buffer->View(offset_aligned, size + adjust, desc.is_written, - vsharp.GetDataFmt(), vsharp.GetNumberFmt()); - if (auto barrier = - vk_buffer->GetBarrier(desc.is_written ? vk::AccessFlagBits2::eShaderWrite - : vk::AccessFlagBits2::eShaderRead, - vk::PipelineStageFlagBits2::eComputeShader)) { - buffer_barriers.emplace_back(*barrier); - } - if (desc.is_written) { - texture_cache.InvalidateMemoryFromGPU(address, size); - } - } - set_writes.push_back({ - .dstSet = VK_NULL_HANDLE, - .dstBinding = binding.unified++, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = desc.is_written ? vk::DescriptorType::eStorageTexelBuffer - : vk::DescriptorType::eUniformTexelBuffer, - .pTexelBufferView = &buffer_view, - }); - ++binding.buffer; - } + BindBuffers(buffer_cache, texture_cache, *info, binding, push_data, set_writes, + buffer_barriers); BindTextures(texture_cache, *info, binding, set_writes); - for (const auto& sampler : info->samplers) { - const auto ssharp = sampler.GetSharp(*info); - if (ssharp.force_degamma) { - LOG_WARNING(Render_Vulkan, "Texture requires gamma correction"); - } - const auto vk_sampler = texture_cache.GetSampler(ssharp); - image_infos.emplace_back(vk_sampler, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); - set_writes.push_back({ - .dstSet = VK_NULL_HANDLE, - .dstBinding = binding.unified++, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eSampler, - .pImageInfo = &image_infos.back(), - }); - } - if (set_writes.empty()) { return false; } const auto cmdbuf = scheduler.CommandBuffer(); - if (!buffer_barriers.empty()) { const auto dependencies = vk::DependencyInfo{ .dependencyFlags = vk::DependencyFlagBits::eByRegion, @@ -257,21 +161,22 @@ bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache, cmdbuf.pipelineBarrier2(dependencies); } + cmdbuf.pushConstants(*pipeline_layout, vk::ShaderStageFlagBits::eCompute, 0u, sizeof(push_data), + &push_data); + + // Bind descriptor set. if (uses_push_descriptors) { cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute, *pipeline_layout, 0, set_writes); - } else { - const auto desc_set = desc_heap.Commit(*desc_layout); - for (auto& set_write : set_writes) { - set_write.dstSet = desc_set; - } - instance.GetDevice().updateDescriptorSets(set_writes, {}); - cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eCompute, *pipeline_layout, 0, desc_set, - {}); + return true; } + const auto desc_set = desc_heap.Commit(*desc_layout); + for (auto& set_write : set_writes) { + set_write.dstSet = desc_set; + } + instance.GetDevice().updateDescriptorSets(set_writes, {}); + cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eCompute, *pipeline_layout, 0, desc_set, {}); - cmdbuf.pushConstants(*pipeline_layout, vk::ShaderStageFlagBits::eCompute, 0u, sizeof(push_data), - &push_data); return true; } diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index cbc0fc5ec..f6d0b49b6 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -5,8 +5,8 @@ #include #include -#include "common/alignment.h" #include "common/assert.h" +#include "common/scope_exit.h" #include "video_core/amdgpu/resource.h" #include "video_core/buffer_cache/buffer_cache.h" #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" @@ -384,13 +384,13 @@ void GraphicsPipeline::BindResources(const Liverpool::Regs& regs, VideoCore::BufferCache& buffer_cache, VideoCore::TextureCache& texture_cache) const { // Bind resource buffers and textures. - boost::container::static_vector buffer_views; - boost::container::static_vector buffer_infos; boost::container::small_vector set_writes; - boost::container::small_vector buffer_barriers; + BufferBarriers buffer_barriers; Shader::PushData push_data{}; Shader::Backend::Bindings binding{}; + buffer_infos.clear(); + buffer_views.clear(); image_infos.clear(); for (const auto* stage : stages) { @@ -402,111 +402,22 @@ void GraphicsPipeline::BindResources(const Liverpool::Regs& regs, push_data.step1 = regs.vgt_instance_step_rate_1; } stage->PushUd(binding, push_data); - for (const auto& buffer : stage->buffers) { - const auto vsharp = buffer.GetSharp(*stage); - const bool is_storage = buffer.IsStorage(vsharp); - if (vsharp && vsharp.GetSize() > 0) { - const VAddr address = vsharp.base_address; - if (texture_cache.IsMeta(address)) { - LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a PS shader (buffer)"); - } - const u32 size = vsharp.GetSize(); - const u32 alignment = - is_storage ? instance.StorageMinAlignment() : instance.UniformMinAlignment(); - const auto [vk_buffer, offset] = - buffer_cache.ObtainBuffer(address, size, buffer.is_written); - const u32 offset_aligned = Common::AlignDown(offset, alignment); - const u32 adjust = offset - offset_aligned; - ASSERT(adjust % 4 == 0); - push_data.AddOffset(binding.buffer, adjust); - buffer_infos.emplace_back(vk_buffer->Handle(), offset_aligned, size + adjust); - } else if (instance.IsNullDescriptorSupported()) { - buffer_infos.emplace_back(VK_NULL_HANDLE, 0, VK_WHOLE_SIZE); - } else { - auto& null_buffer = buffer_cache.GetBuffer(VideoCore::NULL_BUFFER_ID); - buffer_infos.emplace_back(null_buffer.Handle(), 0, VK_WHOLE_SIZE); - } - set_writes.push_back({ - .dstSet = VK_NULL_HANDLE, - .dstBinding = binding.unified++, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = is_storage ? vk::DescriptorType::eStorageBuffer - : vk::DescriptorType::eUniformBuffer, - .pBufferInfo = &buffer_infos.back(), - }); - ++binding.buffer; - } - const auto null_buffer_view = - instance.IsNullDescriptorSupported() ? VK_NULL_HANDLE : buffer_cache.NullBufferView(); - for (const auto& desc : stage->texture_buffers) { - const auto vsharp = desc.GetSharp(*stage); - vk::BufferView& buffer_view = buffer_views.emplace_back(null_buffer_view); - const u32 size = vsharp.GetSize(); - if (vsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid && size != 0) { - const VAddr address = vsharp.base_address; - const u32 alignment = instance.TexelBufferMinAlignment(); - const auto [vk_buffer, offset] = - buffer_cache.ObtainBuffer(address, size, desc.is_written, true); - const u32 fmt_stride = AmdGpu::NumBits(vsharp.GetDataFmt()) >> 3; - ASSERT_MSG(fmt_stride == vsharp.GetStride(), - "Texel buffer stride must match format stride"); - const u32 offset_aligned = Common::AlignDown(offset, alignment); - const u32 adjust = offset - offset_aligned; - ASSERT(adjust % fmt_stride == 0); - push_data.AddOffset(binding.buffer, adjust / fmt_stride); - buffer_view = vk_buffer->View(offset_aligned, size + adjust, desc.is_written, - vsharp.GetDataFmt(), vsharp.GetNumberFmt()); - const auto dst_access = desc.is_written ? vk::AccessFlagBits2::eShaderWrite - : vk::AccessFlagBits2::eShaderRead; - if (auto barrier = vk_buffer->GetBarrier( - dst_access, vk::PipelineStageFlagBits2::eVertexShader)) { - buffer_barriers.emplace_back(*barrier); - } - if (desc.is_written) { - texture_cache.InvalidateMemoryFromGPU(address, size); - } - } - set_writes.push_back({ - .dstSet = VK_NULL_HANDLE, - .dstBinding = binding.unified++, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = desc.is_written ? vk::DescriptorType::eStorageTexelBuffer - : vk::DescriptorType::eUniformTexelBuffer, - .pTexelBufferView = &buffer_view, - }); - ++binding.buffer; - } + BindBuffers(buffer_cache, texture_cache, *stage, binding, push_data, set_writes, + buffer_barriers); BindTextures(texture_cache, *stage, binding, set_writes); - - for (const auto& sampler : stage->samplers) { - auto ssharp = sampler.GetSharp(*stage); - if (ssharp.force_degamma) { - LOG_WARNING(Render_Vulkan, "Texture requires gamma correction"); - } - if (sampler.disable_aniso) { - const auto& tsharp = stage->images[sampler.associated_image].GetSharp(*stage); - if (tsharp.base_level == 0 && tsharp.last_level == 0) { - ssharp.max_aniso.Assign(AmdGpu::AnisoRatio::One); - } - } - const auto vk_sampler = texture_cache.GetSampler(ssharp); - image_infos.emplace_back(vk_sampler, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); - set_writes.push_back({ - .dstSet = VK_NULL_HANDLE, - .dstBinding = binding.unified++, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eSampler, - .pImageInfo = &image_infos.back(), - }); - } } const auto cmdbuf = scheduler.CommandBuffer(); + SCOPE_EXIT { + cmdbuf.pushConstants(*pipeline_layout, gp_stage_flags, 0U, sizeof(push_data), &push_data); + cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, Handle()); + }; + + if (set_writes.empty()) { + return; + } if (!buffer_barriers.empty()) { const auto dependencies = vk::DependencyInfo{ @@ -518,22 +429,18 @@ void GraphicsPipeline::BindResources(const Liverpool::Regs& regs, cmdbuf.pipelineBarrier2(dependencies); } - if (!set_writes.empty()) { - if (uses_push_descriptors) { - cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eGraphics, *pipeline_layout, 0, - set_writes); - } else { - const auto desc_set = desc_heap.Commit(*desc_layout); - for (auto& set_write : set_writes) { - set_write.dstSet = desc_set; - } - instance.GetDevice().updateDescriptorSets(set_writes, {}); - cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, *pipeline_layout, 0, - desc_set, {}); - } + // Bind descriptor set. + if (uses_push_descriptors) { + cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eGraphics, *pipeline_layout, 0, + set_writes); + return; } - cmdbuf.pushConstants(*pipeline_layout, gp_stage_flags, 0U, sizeof(push_data), &push_data); - cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, Handle()); + const auto desc_set = desc_heap.Commit(*desc_layout); + for (auto& set_write : set_writes) { + set_write.dstSet = desc_set; + } + instance.GetDevice().updateDescriptorSets(set_writes, {}); + cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, *pipeline_layout, 0, desc_set, {}); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_pipeline_common.cpp b/src/video_core/renderer_vulkan/vk_pipeline_common.cpp index 61e564150..efe2838e4 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_common.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_common.cpp @@ -4,6 +4,7 @@ #include #include "shader_recompiler/info.h" +#include "video_core/buffer_cache/buffer_cache.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_pipeline_common.h" #include "video_core/renderer_vulkan/vk_scheduler.h" @@ -12,6 +13,8 @@ namespace Vulkan { boost::container::static_vector Pipeline::image_infos; +boost::container::static_vector Pipeline::buffer_views; +boost::container::static_vector Pipeline::buffer_infos; Pipeline::Pipeline(const Instance& instance_, Scheduler& scheduler_, DescriptorHeap& desc_heap_, vk::PipelineCache pipeline_cache) @@ -19,12 +22,133 @@ Pipeline::Pipeline(const Instance& instance_, Scheduler& scheduler_, DescriptorH Pipeline::~Pipeline() = default; +void Pipeline::BindBuffers(VideoCore::BufferCache& buffer_cache, + VideoCore::TextureCache& texture_cache, const Shader::Info& stage, + Shader::Backend::Bindings& binding, Shader::PushData& push_data, + DescriptorWrites& set_writes, BufferBarriers& buffer_barriers) const { + using BufferBindingInfo = std::pair; + static boost::container::static_vector buffer_bindings; + + buffer_bindings.clear(); + + for (const auto& desc : stage.buffers) { + const auto vsharp = desc.GetSharp(stage); + if (!desc.is_gds_buffer && vsharp.base_address != 0 && vsharp.GetSize() > 0) { + const auto buffer_id = buffer_cache.FindBuffer(vsharp.base_address, vsharp.GetSize()); + buffer_bindings.emplace_back(buffer_id, vsharp); + } else { + buffer_bindings.emplace_back(VideoCore::BufferId{}, vsharp); + } + } + + using TexBufferBindingInfo = std::pair; + static boost::container::static_vector texbuffer_bindings; + + texbuffer_bindings.clear(); + + for (const auto& desc : stage.texture_buffers) { + const auto vsharp = desc.GetSharp(stage); + if (vsharp.base_address != 0 && vsharp.GetSize() > 0 && + vsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid) { + const auto buffer_id = buffer_cache.FindBuffer(vsharp.base_address, vsharp.GetSize()); + texbuffer_bindings.emplace_back(buffer_id, vsharp); + } else { + texbuffer_bindings.emplace_back(VideoCore::BufferId{}, vsharp); + } + } + + // Second pass to re-bind buffers that were updated after binding + for (u32 i = 0; i < buffer_bindings.size(); i++) { + const auto& [buffer_id, vsharp] = buffer_bindings[i]; + const auto& desc = stage.buffers[i]; + const bool is_storage = desc.IsStorage(vsharp); + if (!buffer_id) { + if (desc.is_gds_buffer) { + const auto* gds_buf = buffer_cache.GetGdsBuffer(); + buffer_infos.emplace_back(gds_buf->Handle(), 0, gds_buf->SizeBytes()); + } else if (instance.IsNullDescriptorSupported()) { + buffer_infos.emplace_back(VK_NULL_HANDLE, 0, VK_WHOLE_SIZE); + } else { + auto& null_buffer = buffer_cache.GetBuffer(VideoCore::NULL_BUFFER_ID); + buffer_infos.emplace_back(null_buffer.Handle(), 0, VK_WHOLE_SIZE); + } + } else { + const auto [vk_buffer, offset] = buffer_cache.ObtainBuffer( + vsharp.base_address, vsharp.GetSize(), desc.is_written, false, buffer_id); + const u32 alignment = + is_storage ? instance.StorageMinAlignment() : instance.UniformMinAlignment(); + const u32 offset_aligned = Common::AlignDown(offset, alignment); + const u32 adjust = offset - offset_aligned; + ASSERT(adjust % 4 == 0); + push_data.AddOffset(binding.buffer, adjust); + buffer_infos.emplace_back(vk_buffer->Handle(), offset_aligned, + vsharp.GetSize() + adjust); + } + + set_writes.push_back({ + .dstSet = VK_NULL_HANDLE, + .dstBinding = binding.unified++, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = is_storage ? vk::DescriptorType::eStorageBuffer + : vk::DescriptorType::eUniformBuffer, + .pBufferInfo = &buffer_infos.back(), + }); + ++binding.buffer; + } + + const auto null_buffer_view = + instance.IsNullDescriptorSupported() ? VK_NULL_HANDLE : buffer_cache.NullBufferView(); + for (u32 i = 0; i < texbuffer_bindings.size(); i++) { + const auto& [buffer_id, vsharp] = texbuffer_bindings[i]; + const auto& desc = stage.texture_buffers[i]; + vk::BufferView& buffer_view = buffer_views.emplace_back(null_buffer_view); + if (buffer_id) { + const u32 alignment = instance.TexelBufferMinAlignment(); + const auto [vk_buffer, offset] = buffer_cache.ObtainBuffer( + vsharp.base_address, vsharp.GetSize(), desc.is_written, true, buffer_id); + const u32 fmt_stride = AmdGpu::NumBits(vsharp.GetDataFmt()) >> 3; + ASSERT_MSG(fmt_stride == vsharp.GetStride(), + "Texel buffer stride must match format stride"); + const u32 offset_aligned = Common::AlignDown(offset, alignment); + const u32 adjust = offset - offset_aligned; + ASSERT(adjust % fmt_stride == 0); + push_data.AddOffset(binding.buffer, adjust / fmt_stride); + buffer_view = + vk_buffer->View(offset_aligned, vsharp.GetSize() + adjust, desc.is_written, + vsharp.GetDataFmt(), vsharp.GetNumberFmt()); + if (auto barrier = + vk_buffer->GetBarrier(desc.is_written ? vk::AccessFlagBits2::eShaderWrite + : vk::AccessFlagBits2::eShaderRead, + vk::PipelineStageFlagBits2::eComputeShader)) { + buffer_barriers.emplace_back(*barrier); + } + if (desc.is_written) { + texture_cache.InvalidateMemoryFromGPU(vsharp.base_address, vsharp.GetSize()); + } + } + + set_writes.push_back({ + .dstSet = VK_NULL_HANDLE, + .dstBinding = binding.unified++, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = desc.is_written ? vk::DescriptorType::eStorageTexelBuffer + : vk::DescriptorType::eUniformTexelBuffer, + .pTexelBufferView = &buffer_view, + }); + ++binding.buffer; + } +} + void Pipeline::BindTextures(VideoCore::TextureCache& texture_cache, const Shader::Info& stage, Shader::Backend::Bindings& binding, DescriptorWrites& set_writes) const { using ImageBindingInfo = std::tuple; - boost::container::static_vector image_bindings; + static boost::container::static_vector image_bindings; + + image_bindings.clear(); for (const auto& image_desc : stage.images) { const auto tsharp = image_desc.GetSharp(stage); @@ -76,6 +200,26 @@ void Pipeline::BindTextures(VideoCore::TextureCache& texture_cache, const Shader .pImageInfo = &image_infos.back(), }); } + + for (const auto& sampler : stage.samplers) { + auto ssharp = sampler.GetSharp(stage); + if (sampler.disable_aniso) { + const auto& tsharp = stage.images[sampler.associated_image].GetSharp(stage); + if (tsharp.base_level == 0 && tsharp.last_level == 0) { + ssharp.max_aniso.Assign(AmdGpu::AnisoRatio::One); + } + } + const auto vk_sampler = texture_cache.GetSampler(ssharp); + image_infos.emplace_back(vk_sampler, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); + set_writes.push_back({ + .dstSet = VK_NULL_HANDLE, + .dstBinding = binding.unified++, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eSampler, + .pImageInfo = &image_infos.back(), + }); + } } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_pipeline_common.h b/src/video_core/renderer_vulkan/vk_pipeline_common.h index ab99e7b33..75764bfa6 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_common.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_common.h @@ -33,6 +33,13 @@ public: } using DescriptorWrites = boost::container::small_vector; + using BufferBarriers = boost::container::small_vector; + + void BindBuffers(VideoCore::BufferCache& buffer_cache, VideoCore::TextureCache& texture_cache, + const Shader::Info& stage, Shader::Backend::Bindings& binding, + Shader::PushData& push_data, DescriptorWrites& set_writes, + BufferBarriers& buffer_barriers) const; + void BindTextures(VideoCore::TextureCache& texture_cache, const Shader::Info& stage, Shader::Backend::Bindings& binding, DescriptorWrites& set_writes) const; @@ -44,6 +51,8 @@ protected: vk::UniquePipelineLayout pipeline_layout; vk::UniqueDescriptorSetLayout desc_layout; static boost::container::static_vector image_infos; + static boost::container::static_vector buffer_views; + static boost::container::static_vector buffer_infos; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index b3c42fcb6..14a73261d 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -98,10 +98,9 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr address, u32 offset, u32 si const auto& vs_info = pipeline->GetStage(Shader::Stage::Vertex); buffer_cache.BindVertexBuffers(vs_info); - const u32 num_indices = buffer_cache.BindIndexBuffer(is_indexed, 0); + buffer_cache.BindIndexBuffer(is_indexed, 0); - const auto [buffer, base] = buffer_cache.ObtainBuffer(address, size, true); - const auto total_offset = base + offset; + const auto [buffer, base] = buffer_cache.ObtainBuffer(address + offset, size, false); BeginRendering(*pipeline); UpdateDynamicState(*pipeline); @@ -110,9 +109,9 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr address, u32 offset, u32 si // instance offsets will be automatically applied by Vulkan from indirect args buffer. if (is_indexed) { - cmdbuf.drawIndexedIndirect(buffer->Handle(), total_offset, 1, 0); + cmdbuf.drawIndexedIndirect(buffer->Handle(), base, 1, 0); } else { - cmdbuf.drawIndirect(buffer->Handle(), total_offset, 1, 0); + cmdbuf.drawIndirect(buffer->Handle(), base, 1, 0); } } @@ -161,9 +160,8 @@ void Rasterizer::DispatchIndirect(VAddr address, u32 offset, u32 size) { scheduler.EndRendering(); cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline->Handle()); - const auto [buffer, base] = buffer_cache.ObtainBuffer(address, size, true); - const auto total_offset = base + offset; - cmdbuf.dispatchIndirect(buffer->Handle(), total_offset); + const auto [buffer, base] = buffer_cache.ObtainBuffer(address + offset, size, false); + cmdbuf.dispatchIndirect(buffer->Handle(), base); } u64 Rasterizer::Flush() { @@ -260,8 +258,8 @@ void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline) { scheduler.BeginRendering(state); } -void Rasterizer::InlineDataToGds(u32 gds_offset, u32 value) { - buffer_cache.InlineDataToGds(gds_offset, value); +void Rasterizer::InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds) { + buffer_cache.InlineData(address, value, num_bytes, is_gds); } u32 Rasterizer::ReadDataFromGds(u32 gds_offset) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index bc14f39a4..d5cfbfd66 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -42,7 +42,7 @@ public: void ScopedMarkerInsert(const std::string_view& str); void ScopedMarkerInsertColor(const std::string_view& str, const u32 color); - void InlineDataToGds(u32 gds_offset, u32 value); + void InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds); u32 ReadDataFromGds(u32 gsd_offset); void InvalidateMemory(VAddr addr, u64 size); void MapMemory(VAddr addr, u64 size); diff --git a/src/video_core/texture_cache/sampler.cpp b/src/video_core/texture_cache/sampler.cpp index 179dd6646..e47f53abf 100644 --- a/src/video_core/texture_cache/sampler.cpp +++ b/src/video_core/texture_cache/sampler.cpp @@ -8,6 +8,9 @@ namespace VideoCore { Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler) { + if (sampler.force_degamma) { + LOG_WARNING(Render_Vulkan, "Texture requires gamma correction"); + } using namespace Vulkan; const vk::SamplerCreateInfo sampler_ci = { .magFilter = LiverpoolToVK::Filter(sampler.xy_mag_filter), diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 00e6bea82..279e0d82b 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -427,7 +427,7 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule const VAddr image_addr = image.info.guest_address; const size_t image_size = image.info.guest_size_bytes; - const auto [vk_buffer, buf_offset] = buffer_cache.ObtainTempBuffer(image_addr, image_size); + const auto [vk_buffer, buf_offset] = buffer_cache.ObtainViewBuffer(image_addr, image_size); // The obtained buffer may be written by a shader so we need to emit a barrier to prevent RAW // hazard if (auto barrier = vk_buffer->GetBarrier(vk::AccessFlagBits2::eTransferRead, From c38e3f77f9cd5741198f79bff8178744baebac5a Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Sat, 19 Oct 2024 15:57:01 +0300 Subject: [PATCH 92/93] Add poll interval to libScePad (#1415) --- src/emulator.cpp | 1 + src/input/controller.cpp | 26 ++++++++++++++++++++++++-- src/input/controller.h | 1 + src/sdl_window.cpp | 10 ++++++++++ src/sdl_window.h | 2 ++ 5 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/emulator.cpp b/src/emulator.cpp index a3019c9ca..83ea2c1d8 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -257,6 +257,7 @@ void Emulator::Run(const std::filesystem::path& file) { std::jthread mainthread = std::jthread([this](std::stop_token stop_token) { linker->Execute(); }); + window->initTimers(); while (window->isOpen()) { window->waitEvent(); } diff --git a/src/input/controller.cpp b/src/input/controller.cpp index dcd8ed946..2187608e5 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -1,10 +1,13 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include +#include "controller.h" + +#include "common/assert.h" #include "core/libraries/kernel/time_management.h" #include "core/libraries/pad/pad.h" -#include "input/controller.h" + +#include namespace Input { @@ -157,4 +160,23 @@ void GameController::TryOpenSDLController() { } } +u32 GameController::Poll() { + if (m_connected) { + auto time = Libraries::Kernel::sceKernelGetProcessTime(); + if (m_states_num == 0) { + auto diff = (time - m_last_state.time) / 1000; + if (diff >= 100) { + AddState(GetLastState()); + } + } else { + auto index = (m_first_state - 1 + m_states_num) % MAX_STATES; + auto diff = (time - m_states[index].time) / 1000; + if (m_private[index].obtained && diff >= 100) { + AddState(GetLastState()); + } + } + } + return 100; +} + } // namespace Input diff --git a/src/input/controller.h b/src/input/controller.h index 0a0d663a1..01ea21c0c 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -56,6 +56,7 @@ public: bool SetVibration(u8 smallMotor, u8 largeMotor); void SetTouchpadState(int touchIndex, bool touchDown, float x, float y); void TryOpenSDLController(); + u32 Poll(); private: struct StateInternal { diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index bf29b37f6..ad7d1b4a6 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "common/assert.h" #include "common/config.h" @@ -20,6 +21,11 @@ namespace Frontend { +static Uint32 SDLCALL PollController(void* userdata, SDL_TimerID timer_id, Uint32 interval) { + auto* controller = reinterpret_cast(userdata); + return controller->Poll(); +} + WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_, std::string_view window_title) : width{width_}, height{height_}, controller{controller_} { @@ -119,6 +125,10 @@ void WindowSDL::waitEvent() { } } +void WindowSDL::initTimers() { + SDL_AddTimer(100, &PollController, controller); +} + void WindowSDL::onResize() { SDL_GetWindowSizeInPixels(window, &width, &height); ImGui::Core::OnResize(); diff --git a/src/sdl_window.h b/src/sdl_window.h index 2a5aeb38c..ec8de354b 100644 --- a/src/sdl_window.h +++ b/src/sdl_window.h @@ -68,6 +68,8 @@ public: void waitEvent(); + void initTimers(); + private: void onResize(); void onKeyPress(const SDL_Event* event); From 0ecb6695de271a354eb5950ea13a25d03edbb34b Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Sat, 19 Oct 2024 16:09:36 +0300 Subject: [PATCH 93/93] cmake: Allow disabling discord rpc (#1416) --- CMakeLists.txt | 11 ++++++++--- externals/CMakeLists.txt | 8 +++++--- src/emulator.cpp | 4 ++++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9cfc0d8ef..46d3249cd 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ if(UNIX AND NOT APPLE) endif() option(ENABLE_QT_GUI "Enable the Qt GUI. If not selected then the emulator uses a minimal SDL-based UI instead" OFF) +option(ENABLE_DISCORD_RPC "Enable the Discord RPC integration" ON) # First, determine whether to use CMAKE_OSX_ARCHITECTURES or CMAKE_SYSTEM_PROCESSOR. if (APPLE AND CMAKE_OSX_ARCHITECTURES) @@ -405,8 +406,6 @@ set(COMMON src/common/logging/backend.cpp src/common/debug.h src/common/decoder.cpp src/common/decoder.h - src/common/discord_rpc_handler.cpp - src/common/discord_rpc_handler.h src/common/elf_info.h src/common/endian.h src/common/enum.h @@ -447,6 +446,10 @@ set(COMMON src/common/logging/backend.cpp src/common/scm_rev.h ) +if (ENABLE_DISCORD_RPC) + list(APPEND COMMON src/common/discord_rpc_handler.cpp src/common/discord_rpc_handler.h) +endif() + set(CORE src/core/aerolib/stubs.cpp src/core/aerolib/stubs.h src/core/aerolib/aerolib.cpp @@ -895,7 +898,9 @@ if (UNIX AND NOT APPLE) endif() # Discord RPC -target_link_libraries(shadps4 PRIVATE discord-rpc) +if (ENABLE_DISCORD_RPC) + target_link_libraries(shadps4 PRIVATE discord-rpc) +endif() # Install rules install(TARGETS shadps4 BUNDLE DESTINATION .) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 80f49f527..8fefee0f8 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -188,9 +188,11 @@ if (NOT TARGET pugixml::pugixml) endif() # Discord RPC -set(BUILD_EXAMPLES OFF) -add_subdirectory(discord-rpc/) -target_include_directories(discord-rpc INTERFACE discord-rpc/include) +if (ENABLE_DISCORD_RPC) + set(BUILD_EXAMPLES OFF) + add_subdirectory(discord-rpc/) + target_include_directories(discord-rpc INTERFACE discord-rpc/include) +endif() # GCN Headers add_subdirectory(gcn) diff --git a/src/emulator.cpp b/src/emulator.cpp index 83ea2c1d8..939a18076 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -12,7 +12,9 @@ #include "common/memory_patcher.h" #endif #include "common/assert.h" +#ifdef ENABLE_DISCORD_RPC #include "common/discord_rpc_handler.h" +#endif #include "common/elf_info.h" #include "common/ntapi.h" #include "common/path_util.h" @@ -244,6 +246,7 @@ void Emulator::Run(const std::filesystem::path& file) { } } +#ifdef ENABLE_DISCORD_RPC // Discord RPC if (Config::getEnableDiscordRPC()) { auto* rpc = Common::Singleton::Instance(); @@ -252,6 +255,7 @@ void Emulator::Run(const std::filesystem::path& file) { } rpc->setStatusPlaying(game_info.title, id); } +#endif // start execution std::jthread mainthread =