diff --git a/CMakeLists.txt b/CMakeLists.txt index 88d874af0..b8d82a11c 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -917,6 +917,9 @@ set(QT_GUI src/qt_gui/about_dialog.cpp src/qt_gui/control_settings.cpp src/qt_gui/control_settings.h src/qt_gui/control_settings.ui + src/qt_gui/kbm_gui.cpp + src/qt_gui/kbm_gui.h + src/qt_gui/kbm_gui.ui src/qt_gui/main_window_ui.h src/qt_gui/main_window.cpp src/qt_gui/main_window.h diff --git a/REUSE.toml b/REUSE.toml index d8f31c2f2..5cf7b01bf 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -41,6 +41,7 @@ path = [ "src/images/grid_icon.png", "src/images/keyboard_icon.png", "src/images/iconsize_icon.png", + "src/images/KBM.png", "src/images/ko-fi.png", "src/images/list_icon.png", "src/images/list_mode_icon.png", diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index 6bc73ee43..702d0fabc 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -17,6 +17,8 @@ #ifdef _WIN32 // This is the maximum number of UTF-16 code units permissible in Windows file paths #define MAX_PATH 260 +#include +#include #else // This is the maximum number of UTF-8 code units permissible in all other OSes' file paths #define MAX_PATH 1024 @@ -106,6 +108,10 @@ static auto UserPaths = [] { } else { user_dir = std::filesystem::path(getenv("HOME")) / ".local" / "share" / "shadPS4"; } +#elif _WIN32 + TCHAR appdata[MAX_PATH] = {0}; + SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, appdata); + user_dir = std::filesystem::path(appdata) / "shadPS4"; #endif } diff --git a/src/core/cpu_patches.cpp b/src/core/cpu_patches.cpp index 21acf1a7b..a9f6c67a8 100644 --- a/src/core/cpu_patches.cpp +++ b/src/core/cpu_patches.cpp @@ -180,28 +180,44 @@ static void RestoreStack(Xbyak::CodeGenerator& c) { c.mov(rsp, qword[reinterpret_cast(stack_pointer_slot * sizeof(void*))]); } +/// Validates that the dst register is supported given the SaveStack/RestoreStack implementation. +static void ValidateDst(const Xbyak::Reg& dst) { + // No restrictions. +} + #else -// These utilities are not implemented as we can't save anything to thread local storage without -// temporary registers. void InitializeThreadPatchStack() { // No-op } +// NOTE: Since stack pointer here is subtracted through safe zone and not saved anywhere, +// it must not be modified during the instruction. Otherwise, we will not be able to find +// and load registers back from where they were saved. Thus, a limitation is placed on +// instructions, that they must not use the stack pointer register as a destination. + /// Saves the stack pointer to thread local storage and loads the patch stack. static void SaveStack(Xbyak::CodeGenerator& c) { - UNIMPLEMENTED(); + c.lea(rsp, ptr[rsp - 128]); // red zone } /// Restores the stack pointer from thread local storage. static void RestoreStack(Xbyak::CodeGenerator& c) { - UNIMPLEMENTED(); + c.lea(rsp, ptr[rsp + 128]); // red zone +} + +/// Validates that the dst register is supported given the SaveStack/RestoreStack implementation. +static void ValidateDst(const Xbyak::Reg& dst) { + // Stack pointer is not preserved, so it can't be used as a dst. + ASSERT_MSG(dst.getIdx() != rsp.getIdx(), "Stack pointer not supported as destination."); } #endif /// Switches to the patch stack, saves registers, and restores the original stack. static void SaveRegisters(Xbyak::CodeGenerator& c, const std::initializer_list regs) { + // Uses a more robust solution for saving registers on MacOS to avoid potential stack corruption + // if games decide to not follow the ABI and use the red zone. SaveStack(c); for (const auto& reg : regs) { c.push(reg.cvt64()); @@ -257,12 +273,11 @@ static void RestoreContext(Xbyak::CodeGenerator& c, const Xbyak::Operand& dst, RestoreStack(c); } -#ifdef __APPLE__ - static void GenerateANDN(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { const auto dst = ZydisToXbyakRegisterOperand(operands[0]); const auto src1 = ZydisToXbyakRegisterOperand(operands[1]); const auto src2 = ZydisToXbyakOperand(operands[2]); + ValidateDst(dst); // Check if src2 is a memory operand or a register different to dst. // In those cases, we don't need to use a temporary register and are free to modify dst. @@ -301,6 +316,7 @@ static void GenerateBEXTR(const ZydisDecodedOperand* operands, Xbyak::CodeGenera const auto dst = ZydisToXbyakRegisterOperand(operands[0]); const auto src = ZydisToXbyakOperand(operands[1]); const auto start_len = ZydisToXbyakRegisterOperand(operands[2]); + ValidateDst(dst); const Xbyak::Reg32e shift(Xbyak::Operand::RCX, static_cast(start_len.getBit())); const auto scratch1 = @@ -338,6 +354,7 @@ static void GenerateBEXTR(const ZydisDecodedOperand* operands, Xbyak::CodeGenera static void GenerateBLSI(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { const auto dst = ZydisToXbyakRegisterOperand(operands[0]); const auto src = ZydisToXbyakOperand(operands[1]); + ValidateDst(dst); const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit()); @@ -367,6 +384,7 @@ static void GenerateBLSI(const ZydisDecodedOperand* operands, Xbyak::CodeGenerat static void GenerateBLSMSK(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { const auto dst = ZydisToXbyakRegisterOperand(operands[0]); const auto src = ZydisToXbyakOperand(operands[1]); + ValidateDst(dst); const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit()); @@ -395,9 +413,37 @@ static void GenerateBLSMSK(const ZydisDecodedOperand* operands, Xbyak::CodeGener RestoreRegisters(c, {scratch}); } +static void GenerateTZCNT(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { + const auto dst = ZydisToXbyakRegisterOperand(operands[0]); + const auto src = ZydisToXbyakOperand(operands[1]); + ValidateDst(dst); + + Xbyak::Label src_zero, end; + + c.cmp(*src, 0); + c.je(src_zero); + + // If src is not zero, functions like a BSF, but also clears the CF + c.bsf(dst, *src); + c.clc(); + c.jmp(end); + + c.L(src_zero); + c.mov(dst, operands[0].size); + // Since dst is not zero, also set ZF to zero. Testing dst with itself when we know + // it isn't zero is a good way to do this. + // Use cvt32 to avoid REX/Operand size prefixes. + c.test(dst.cvt32(), dst.cvt32()); + // When source is zero, TZCNT also sets CF. + c.stc(); + + c.L(end); +} + static void GenerateBLSR(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { const auto dst = ZydisToXbyakRegisterOperand(operands[0]); const auto src = ZydisToXbyakOperand(operands[1]); + ValidateDst(dst); const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit()); @@ -426,6 +472,8 @@ static void GenerateBLSR(const ZydisDecodedOperand* operands, Xbyak::CodeGenerat RestoreRegisters(c, {scratch}); } +#ifdef __APPLE__ + static __attribute__((sysv_abi)) void PerformVCVTPH2PS(float* out, const half_float::half* in, const u32 count) { for (u32 i = 0; i < count; i++) { @@ -616,6 +664,11 @@ static bool FilterNoSSE4a(const ZydisDecodedOperand*) { return !cpu.has(Cpu::tSSE4a); } +static bool FilterNoBMI1(const ZydisDecodedOperand*) { + Cpu cpu; + return !cpu.has(Cpu::tBMI1); +} + static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { bool immediateForm = operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE && operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; @@ -897,14 +950,16 @@ static const std::unordered_map Patches = { {ZYDIS_MNEMONIC_EXTRQ, {FilterNoSSE4a, GenerateEXTRQ, true}}, {ZYDIS_MNEMONIC_INSERTQ, {FilterNoSSE4a, GenerateINSERTQ, true}}, + // BMI1 + {ZYDIS_MNEMONIC_ANDN, {FilterNoBMI1, GenerateANDN, true}}, + {ZYDIS_MNEMONIC_BEXTR, {FilterNoBMI1, GenerateBEXTR, true}}, + {ZYDIS_MNEMONIC_BLSI, {FilterNoBMI1, GenerateBLSI, true}}, + {ZYDIS_MNEMONIC_BLSMSK, {FilterNoBMI1, GenerateBLSMSK, true}}, + {ZYDIS_MNEMONIC_BLSR, {FilterNoBMI1, GenerateBLSR, true}}, + {ZYDIS_MNEMONIC_TZCNT, {FilterNoBMI1, GenerateTZCNT, true}}, + #ifdef __APPLE__ // Patches for instruction sets not supported by Rosetta 2. - // BMI1 - {ZYDIS_MNEMONIC_ANDN, {FilterRosetta2Only, GenerateANDN, true}}, - {ZYDIS_MNEMONIC_BEXTR, {FilterRosetta2Only, GenerateBEXTR, true}}, - {ZYDIS_MNEMONIC_BLSI, {FilterRosetta2Only, GenerateBLSI, true}}, - {ZYDIS_MNEMONIC_BLSMSK, {FilterRosetta2Only, GenerateBLSMSK, true}}, - {ZYDIS_MNEMONIC_BLSR, {FilterRosetta2Only, GenerateBLSR, true}}, // F16C {ZYDIS_MNEMONIC_VCVTPH2PS, {FilterRosetta2Only, GenerateVCVTPH2PS, true}}, {ZYDIS_MNEMONIC_VCVTPS2PH, {FilterRosetta2Only, GenerateVCVTPS2PH, true}}, diff --git a/src/images/KBM.png b/src/images/KBM.png new file mode 100644 index 000000000..37f52d549 Binary files /dev/null and b/src/images/KBM.png differ diff --git a/src/qt_gui/kbm_gui.cpp b/src/qt_gui/kbm_gui.cpp new file mode 100644 index 000000000..e43eb282f --- /dev/null +++ b/src/qt_gui/kbm_gui.cpp @@ -0,0 +1,1047 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include + +#include "common/path_util.h" +#include "kbm_config_dialog.h" +#include "kbm_gui.h" +#include "kbm_help_dialog.h" +#include "ui_kbm_gui.h" + +HelpDialog* HelpWindow; + +KBMSettings::KBMSettings(std::shared_ptr game_info_get, QWidget* parent) + : QDialog(parent), m_game_info(game_info_get), ui(new Ui::KBMSettings) { + + ui->setupUi(this); + ui->PerGameCheckBox->setChecked(!Config::GetUseUnifiedInputConfig()); + ui->TextEditorButton->setFocus(); + this->setFocusPolicy(Qt::StrongFocus); + + ui->MouseJoystickBox->addItem("none"); + ui->MouseJoystickBox->addItem("right"); + ui->MouseJoystickBox->addItem("left"); + + ui->ProfileComboBox->addItem("Common Config"); + for (int i = 0; i < m_game_info->m_games.size(); i++) { + ui->ProfileComboBox->addItem(QString::fromStdString(m_game_info->m_games[i].serial)); + } + + ButtonsList = { + ui->CrossButton, ui->CircleButton, ui->TriangleButton, ui->SquareButton, + ui->L1Button, ui->R1Button, ui->L2Button, ui->R2Button, + ui->L3Button, ui->R3Button, ui->TouchpadButton, ui->OptionsButton, + ui->TouchpadButton, ui->DpadUpButton, ui->DpadDownButton, ui->DpadLeftButton, + ui->DpadRightButton, ui->LStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, + ui->LStickRightButton, ui->RStickUpButton, ui->RStickDownButton, ui->RStickLeftButton, + ui->RStickRightButton, ui->LHalfButton, ui->RHalfButton}; + + ButtonConnects(); + SetUIValuestoMappings("default"); + installEventFilter(this); + + ui->ProfileComboBox->setCurrentText("Common Config"); + ui->TitleLabel->setText("Common Config"); + + connect(ui->buttonBox, &QDialogButtonBox::clicked, this, [this](QAbstractButton* button) { + if (button == ui->buttonBox->button(QDialogButtonBox::Save)) { + if (HelpWindowOpen) { + HelpWindow->close(); + HelpWindowOpen = false; + } + SaveKBMConfig(true); + } else if (button == ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)) { + SetDefault(); + } else if (button == ui->buttonBox->button(QDialogButtonBox::Apply)) { + SaveKBMConfig(false); + } + }); + + connect(ui->HelpButton, &QPushButton::clicked, this, &KBMSettings::onHelpClicked); + connect(ui->TextEditorButton, &QPushButton::clicked, this, [this]() { + auto kbmWindow = new EditorDialog(this); + kbmWindow->exec(); + }); + + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] { + QWidget::close(); + if (HelpWindowOpen) { + HelpWindow->close(); + HelpWindowOpen = false; + } + }); + + connect(ui->ProfileComboBox, &QComboBox::currentTextChanged, this, [this] { + GetGameTitle(); + std::string config_id = (ui->ProfileComboBox->currentText() == "Common Config") + ? "default" + : ui->ProfileComboBox->currentText().toStdString(); + SetUIValuestoMappings(config_id); + }); + + connect(ui->CopyCommonButton, &QPushButton::clicked, this, [this] { + if (ui->ProfileComboBox->currentText() == "Common Config") { + QMessageBox::information(this, "Common Config Selected", + "This button copies mappings from the Common Config to the " + "currently selected profile, and cannot be used when the " + "currently selected profile is the Common Config."); + } else { + QMessageBox::StandardButton reply = + QMessageBox::question(this, "Copy values from Common Config", + "Do you want to overwrite existing mappings with the " + "mappings from the Common Config?", + QMessageBox::Yes | QMessageBox::No); + if (reply == QMessageBox::Yes) { + SetUIValuestoMappings("default"); + } + } + }); + + connect(ui->DeadzoneOffsetSlider, &QSlider::valueChanged, this, [this](int value) { + QString DOSValue = QString::number(value / 100.0, 'f', 2); + QString DOSString = tr("Deadzone Offset (def 0.50): ") + DOSValue; + ui->DeadzoneOffsetLabel->setText(DOSString); + }); + + connect(ui->SpeedMultiplierSlider, &QSlider::valueChanged, this, [this](int value) { + QString SMSValue = QString::number(value / 10.0, 'f', 1); + QString SMSString = tr("Speed Multiplier (def 1.0): ") + SMSValue; + ui->SpeedMultiplierLabel->setText(SMSString); + }); + + connect(ui->SpeedOffsetSlider, &QSlider::valueChanged, this, [this](int value) { + QString SOSValue = QString::number(value / 1000.0, 'f', 3); + QString SOSString = tr("Speed Offset (def 0.125):") + " " + SOSValue; + ui->SpeedOffsetLabel->setText(SOSString); + }); +} + +void KBMSettings::ButtonConnects() { + connect(ui->CrossButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->CrossButton); }); + connect(ui->CircleButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->CircleButton); }); + connect(ui->TriangleButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->TriangleButton); }); + connect(ui->SquareButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->SquareButton); }); + + connect(ui->L1Button, &QPushButton::clicked, this, [this]() { StartTimer(ui->L1Button); }); + connect(ui->L2Button, &QPushButton::clicked, this, [this]() { StartTimer(ui->L2Button); }); + connect(ui->L3Button, &QPushButton::clicked, this, [this]() { StartTimer(ui->L3Button); }); + connect(ui->R1Button, &QPushButton::clicked, this, [this]() { StartTimer(ui->R1Button); }); + connect(ui->R2Button, &QPushButton::clicked, this, [this]() { StartTimer(ui->R2Button); }); + connect(ui->R3Button, &QPushButton::clicked, this, [this]() { StartTimer(ui->R3Button); }); + + connect(ui->TouchpadButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->TouchpadButton); }); + connect(ui->OptionsButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->OptionsButton); }); + + connect(ui->DpadUpButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->DpadUpButton); }); + connect(ui->DpadDownButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->DpadDownButton); }); + connect(ui->DpadLeftButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->DpadLeftButton); }); + connect(ui->DpadRightButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->DpadRightButton); }); + + connect(ui->LStickUpButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->LStickUpButton); }); + connect(ui->LStickDownButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->LStickDownButton); }); + connect(ui->LStickLeftButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->LStickLeftButton); }); + connect(ui->LStickRightButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->LStickRightButton); }); + + connect(ui->RStickUpButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->RStickUpButton); }); + connect(ui->RStickDownButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->RStickDownButton); }); + connect(ui->RStickLeftButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->RStickLeftButton); }); + connect(ui->RStickRightButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->RStickRightButton); }); + + connect(ui->LHalfButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->LHalfButton); }); + connect(ui->RHalfButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->RHalfButton); }); +} + +void KBMSettings::DisableMappingButtons() { + for (const auto& i : ButtonsList) { + i->setEnabled(false); + } +} + +void KBMSettings::EnableMappingButtons() { + for (const auto& i : ButtonsList) { + i->setEnabled(true); + } +} + +void KBMSettings::SaveKBMConfig(bool CloseOnSave) { + std::string output_string = "", input_string = ""; + std::vector lines, inputs; + + lines.push_back("#Feeling lost? Check out the Help section!"); + lines.push_back(""); + lines.push_back("#Keyboard bindings"); + lines.push_back(""); + + input_string = ui->CrossButton->text().toStdString(); + output_string = "cross"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->CircleButton->text().toStdString(); + output_string = "circle"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->TriangleButton->text().toStdString(); + output_string = "triangle"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->SquareButton->text().toStdString(); + output_string = "square"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + lines.push_back(""); + + input_string = ui->DpadUpButton->text().toStdString(); + output_string = "pad_up"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->DpadDownButton->text().toStdString(); + output_string = "pad_down"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->DpadLeftButton->text().toStdString(); + output_string = "pad_left"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->DpadRightButton->text().toStdString(); + output_string = "pad_right"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + lines.push_back(""); + + input_string = ui->L1Button->text().toStdString(); + output_string = "l1"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->R1Button->text().toStdString(); + output_string = "r1"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->L2Button->text().toStdString(); + output_string = "l2"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->R2Button->text().toStdString(); + output_string = "r2"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->L3Button->text().toStdString(); + output_string = "l3"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->R3Button->text().toStdString(); + output_string = "r3"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + lines.push_back(""); + + input_string = ui->OptionsButton->text().toStdString(); + output_string = "options"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->TouchpadButton->text().toStdString(); + output_string = "touchpad"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + lines.push_back(""); + + input_string = ui->LStickUpButton->text().toStdString(); + output_string = "axis_left_y_minus"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->LStickDownButton->text().toStdString(); + output_string = "axis_left_y_plus"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->LStickLeftButton->text().toStdString(); + output_string = "axis_left_x_minus"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->LStickRightButton->text().toStdString(); + output_string = "axis_left_x_plus"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + lines.push_back(""); + + input_string = ui->RStickUpButton->text().toStdString(); + output_string = "axis_right_y_minus"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->RStickDownButton->text().toStdString(); + output_string = "axis_right_y_plus"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->RStickLeftButton->text().toStdString(); + output_string = "axis_right_x_minus"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->RStickRightButton->text().toStdString(); + output_string = "axis_right_x_plus"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + lines.push_back(""); + + input_string = ui->MouseJoystickBox->currentText().toStdString(); + output_string = "mouse_to_joystick"; + if (input_string != "unmapped") + lines.push_back(output_string + " = " + input_string); + + input_string = ui->LHalfButton->text().toStdString(); + output_string = "leftjoystick_halfmode"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->RHalfButton->text().toStdString(); + output_string = "rightjoystick_halfmode"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + std::string DOString = std::format("{:.2f}", (ui->DeadzoneOffsetSlider->value() / 100.f)); + std::string SMString = std::format("{:.1f}", (ui->SpeedMultiplierSlider->value() / 10.f)); + std::string SOString = std::format("{:.3f}", (ui->SpeedOffsetSlider->value() / 1000.f)); + input_string = DOString + ", " + SMString + ", " + SOString; + output_string = "mouse_movement_params"; + lines.push_back(output_string + " = " + input_string); + + lines.push_back(""); + + std::string config_id = (ui->ProfileComboBox->currentText() == "Common Config") + ? "default" + : ui->ProfileComboBox->currentText().toStdString(); + const auto config_file = Config::GetFoolproofKbmConfigFile(config_id); + std::fstream file(config_file); + int lineCount = 0; + std::string line; + while (std::getline(file, line)) { + lineCount++; + + if (line.empty()) { + lines.push_back(line); + continue; + } + + std::size_t comment_pos = line.find('#'); + if (comment_pos != std::string::npos) { + if (!line.contains("Keyboard bindings") && !line.contains("Feeling lost") && + !line.contains("Alternatives for users")) + lines.push_back(line); + continue; + } + + std::size_t equal_pos = line.find('='); + if (equal_pos == std::string::npos) { + lines.push_back(line); + continue; + } + + output_string = line.substr(0, equal_pos - 1); + input_string = line.substr(equal_pos + 2); + + if (std::find(ControllerInputs.begin(), ControllerInputs.end(), input_string) != + ControllerInputs.end() || + output_string == "analog_deadzone" || output_string == "override_controller_color") { + lines.push_back(line); + } + } + file.close(); + + // Prevent duplicate inputs for KBM as this breaks the engine + for (auto it = inputs.begin(); it != inputs.end(); ++it) { + if (std::find(it + 1, inputs.end(), *it) != inputs.end()) { + QMessageBox::information(this, "Unable to Save", + "Cannot bind any unique input more than once"); + return; + } + } + + std::vector save; + bool CurrentLineEmpty = false, LastLineEmpty = false; + for (auto const& line : lines) { + LastLineEmpty = CurrentLineEmpty ? true : false; + CurrentLineEmpty = line.empty() ? true : false; + if (!CurrentLineEmpty || !LastLineEmpty) + save.push_back(line); + } + + std::ofstream output_file(config_file); + for (auto const& line : save) { + output_file << line << '\n'; + } + output_file.close(); + + Config::SetUseUnifiedInputConfig(!ui->PerGameCheckBox->isChecked()); + Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml"); + + if (CloseOnSave) + QWidget::close(); +} + +void KBMSettings::SetDefault() { + ui->CrossButton->setText("kp2"); + ui->CircleButton->setText("kp6"); + ui->TriangleButton->setText("kp8"); + ui->SquareButton->setText("kp4"); + + ui->L1Button->setText("q"); + ui->L2Button->setText("e"); + ui->L3Button->setText("x"); + ui->R1Button->setText("u"); + ui->R2Button->setText("o"); + ui->R3Button->setText("m"); + + ui->TouchpadButton->setText("space"); + ui->OptionsButton->setText("enter"); + + ui->DpadUpButton->setText("up"); + ui->DpadDownButton->setText("down"); + ui->DpadLeftButton->setText("left"); + ui->DpadRightButton->setText("right"); + + ui->LStickUpButton->setText("w"); + ui->LStickDownButton->setText("s"); + ui->LStickLeftButton->setText("a"); + ui->LStickRightButton->setText("d"); + + ui->RStickUpButton->setText("i"); + ui->RStickDownButton->setText("k"); + ui->RStickLeftButton->setText("j"); + ui->RStickRightButton->setText("l"); + + ui->LHalfButton->setText("unmapped"); + ui->RHalfButton->setText("unmapped"); + + ui->MouseJoystickBox->setCurrentText("none"); + ui->DeadzoneOffsetSlider->setValue(50); + ui->SpeedMultiplierSlider->setValue(10); + ui->SpeedOffsetSlider->setValue(125); +} + +void KBMSettings::SetUIValuestoMappings(std::string config_id) { + const auto config_file = Config::GetFoolproofKbmConfigFile(config_id); + std::ifstream file(config_file); + + int lineCount = 0; + std::string line = ""; + while (std::getline(file, line)) { + lineCount++; + + std::size_t comment_pos = line.find('#'); + if (comment_pos != std::string::npos) + line = line.substr(0, comment_pos); + + std::size_t equal_pos = line.find('='); + if (equal_pos == std::string::npos) + continue; + + std::string output_string = line.substr(0, equal_pos - 1); + std::string input_string = line.substr(equal_pos + 2); + + if (std::find(ControllerInputs.begin(), ControllerInputs.end(), input_string) == + ControllerInputs.end()) { + + if (output_string == "cross") { + ui->CrossButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "circle") { + ui->CircleButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "square") { + ui->SquareButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "triangle") { + ui->TriangleButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "l1") { + ui->L1Button->setText(QString::fromStdString(input_string)); + } else if (output_string == "l2") { + ui->L2Button->setText(QString::fromStdString(input_string)); + } else if (output_string == "r1") { + ui->R1Button->setText(QString::fromStdString(input_string)); + } else if (output_string == "r2") { + ui->R2Button->setText(QString::fromStdString(input_string)); + } else if (output_string == "l3") { + ui->L3Button->setText(QString::fromStdString(input_string)); + } else if (output_string == "r3") { + ui->R3Button->setText(QString::fromStdString(input_string)); + } else if (output_string == "pad_up") { + ui->DpadUpButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "pad_down") { + ui->DpadDownButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "pad_left") { + ui->DpadLeftButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "pad_right") { + ui->DpadRightButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "options") { + ui->OptionsButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "touchpad") { + ui->TouchpadButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "axis_left_x_minus") { + ui->LStickLeftButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "axis_left_x_plus") { + ui->LStickRightButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "axis_left_y_minus") { + ui->LStickUpButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "axis_left_y_plus") { + ui->LStickDownButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "axis_right_x_minus") { + ui->RStickLeftButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "axis_right_x_plus") { + ui->RStickRightButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "axis_right_y_minus") { + ui->RStickUpButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "axis_right_y_plus") { + ui->RStickDownButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "mouse_to_joystick") { + ui->MouseJoystickBox->setCurrentText(QString::fromStdString(input_string)); + } else if (output_string == "leftjoystick_halfmode") { + ui->LHalfButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "rightjoystick_halfmode") { + ui->RHalfButton->setText(QString::fromStdString(input_string)); + } else if (output_string.contains("mouse_movement_params")) { + std::size_t comma_pos = line.find(','); + if (comma_pos != std::string::npos) { + std::string DOstring = line.substr(equal_pos + 1, comma_pos); + float DOffsetValue = std::stof(DOstring) * 100.0; + int DOffsetInt = int(DOffsetValue); + ui->DeadzoneOffsetSlider->setValue(DOffsetInt); + QString LabelValue = QString::number(DOffsetInt / 100.0, 'f', 2); + QString LabelString = tr("Deadzone Offset (def 0.50): ") + LabelValue; + ui->DeadzoneOffsetLabel->setText(LabelString); + + std::string SMSOstring = line.substr(comma_pos + 1); + std::size_t comma_pos2 = SMSOstring.find(','); + if (comma_pos2 != std::string::npos) { + std::string SMstring = SMSOstring.substr(0, comma_pos2); + float SpeedMultValue = std::stof(SMstring) * 10.0; + int SpeedMultInt = int(SpeedMultValue); + if (SpeedMultInt < 1) + SpeedMultInt = 1; + if (SpeedMultInt > 50) + SpeedMultInt = 50; + ui->SpeedMultiplierSlider->setValue(SpeedMultInt); + LabelValue = QString::number(SpeedMultInt / 10.0, 'f', 1); + LabelString = tr("Speed Multiplier (def 1.0): ") + LabelValue; + ui->SpeedMultiplierLabel->setText(LabelString); + + std::string SOstring = SMSOstring.substr(comma_pos2 + 1); + float SOffsetValue = std::stof(SOstring) * 1000.0; + int SOffsetInt = int(SOffsetValue); + ui->SpeedOffsetSlider->setValue(SOffsetInt); + LabelValue = QString::number(SOffsetInt / 1000.0, 'f', 3); + LabelString = tr("Speed Offset (def 0.125): ") + LabelValue; + ui->SpeedOffsetLabel->setText(LabelString); + } + } + } + } + } + file.close(); +} + +void KBMSettings::GetGameTitle() { + if (ui->ProfileComboBox->currentText() == "Common Config") { + ui->TitleLabel->setText("Common Config"); + } else { + for (int i = 0; i < m_game_info->m_games.size(); i++) { + if (m_game_info->m_games[i].serial == + ui->ProfileComboBox->currentText().toStdString()) { + ui->TitleLabel->setText(QString::fromStdString(m_game_info->m_games[i].name)); + } + } + } +} + +void KBMSettings::onHelpClicked() { + if (!HelpWindowOpen) { + HelpWindow = new HelpDialog(&HelpWindowOpen, this); + HelpWindow->setWindowTitle("Help"); + HelpWindow->setAttribute(Qt::WA_DeleteOnClose); // Clean up on close + HelpWindow->show(); + HelpWindowOpen = true; + } else { + HelpWindow->close(); + HelpWindowOpen = false; + } +} + +void KBMSettings::StartTimer(QPushButton*& button) { + MappingTimer = 3; + EnableMapping = true; + MappingCompleted = false; + modifier = ""; + mapping = button->text(); + + DisableMappingButtons(); + button->setText("Press a key [" + QString::number(MappingTimer) + "]"); + timer = new QTimer(this); + MappingButton = button; + connect(timer, &QTimer::timeout, this, [this]() { CheckMapping(MappingButton); }); + timer->start(1000); +} + +void KBMSettings::CheckMapping(QPushButton*& button) { + MappingTimer -= 1; + button->setText("Press a key [" + QString::number(MappingTimer) + "]"); + + if (MappingCompleted) { + EnableMapping = false; + EnableMappingButtons(); + timer->stop(); + + if (mapping == "lshift" || mapping == "lalt" || mapping == "lctrl" || mapping == "lmeta" || + mapping == "lwin") { + modifier = ""; + } + + if (modifier != "") { + button->setText(modifier + ", " + mapping); + } else { + button->setText(mapping); + } + } + + if (MappingTimer <= 0) { + button->setText(mapping); + EnableMapping = false; + EnableMappingButtons(); + timer->stop(); + } +} + +void KBMSettings::SetMapping(QString input) { + mapping = input; + MappingCompleted = true; +} + +bool KBMSettings::eventFilter(QObject* obj, QEvent* event) { + if (event->type() == QEvent::Close) { + if (HelpWindowOpen) { + HelpWindow->close(); + HelpWindowOpen = false; + } + } + + if (EnableMapping) { + if (Qt::ShiftModifier & QApplication::keyboardModifiers()) { + modifier = "lshift"; + } else if (Qt::AltModifier & QApplication::keyboardModifiers()) { + modifier = "lalt"; + } else if (Qt::ControlModifier & QApplication::keyboardModifiers()) { + modifier = "lctrl"; + } else if (Qt::MetaModifier & QApplication::keyboardModifiers()) { +#ifdef _WIN32 + modifier = "lwin"; +#else + modifier = "lmeta"; +#endif + } + + if (event->type() == QEvent::KeyPress) { + QKeyEvent* keyEvent = static_cast(event); + + switch (keyEvent->key()) { + case Qt::Key_Space: + SetMapping("space"); + break; + case Qt::Key_Comma: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kpcomma"); + } else { + SetMapping("comma"); + } + break; + case Qt::Key_Period: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kpperiod"); + } else { + SetMapping("period"); + } + break; + case Qt::Key_Slash: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) + SetMapping("kpdivide"); + break; + case Qt::Key_Asterisk: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) + SetMapping("kpmultiply"); + break; + case Qt::Key_Question: + SetMapping("question"); + break; + case Qt::Key_Semicolon: + SetMapping("semicolon"); + break; + case Qt::Key_Minus: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kpminus"); + } else { + SetMapping("minus"); + } + break; + case Qt::Key_Plus: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kpplus"); + } else { + SetMapping("plus"); + } + break; + case Qt::Key_ParenLeft: + SetMapping("lparenthesis"); + break; + case Qt::Key_ParenRight: + SetMapping("rparenthesis"); + break; + case Qt::Key_BracketLeft: + SetMapping("lbracket"); + break; + case Qt::Key_BracketRight: + SetMapping("rbracket"); + break; + case Qt::Key_BraceLeft: + SetMapping("lbrace"); + break; + case Qt::Key_BraceRight: + SetMapping("rbrace"); + break; + case Qt::Key_Backslash: + SetMapping("backslash"); + break; + case Qt::Key_Tab: + SetMapping("tab"); + break; + case Qt::Key_Backspace: + SetMapping("backspace"); + break; + case Qt::Key_Return: + SetMapping("enter"); + break; + case Qt::Key_Enter: + SetMapping("kpenter"); + break; + case Qt::Key_Escape: + SetMapping("unmapped"); + break; + case Qt::Key_Shift: + SetMapping("lshift"); + break; + case Qt::Key_Alt: + SetMapping("lalt"); + break; + case Qt::Key_Control: + SetMapping("lctrl"); + break; + case Qt::Key_Meta: + activateWindow(); +#ifdef _WIN32 + SetMapping("lwin"); +#else + SetMapping("lmeta"); +#endif + case Qt::Key_1: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp1"); + } else { + SetMapping("1"); + } + break; + case Qt::Key_2: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp2"); + } else { + SetMapping("2"); + } + break; + case Qt::Key_3: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp3"); + } else { + SetMapping("3"); + } + break; + case Qt::Key_4: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp4"); + } else { + SetMapping("4"); + } + break; + case Qt::Key_5: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp5"); + } else { + SetMapping("5"); + } + break; + case Qt::Key_6: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp6"); + } else { + SetMapping("6"); + } + break; + case Qt::Key_7: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp7"); + } else { + SetMapping("7"); + } + break; + case Qt::Key_8: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp8"); + } else { + SetMapping("8"); + } + break; + case Qt::Key_9: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp9"); + } else { + SetMapping("9"); + } + break; + case Qt::Key_0: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp0"); + } else { + SetMapping("0"); + } + break; + case Qt::Key_Up: + activateWindow(); + SetMapping("up"); + break; + case Qt::Key_Down: + SetMapping("down"); + break; + case Qt::Key_Left: + SetMapping("left"); + break; + case Qt::Key_Right: + SetMapping("right"); + break; + case Qt::Key_A: + SetMapping("a"); + break; + case Qt::Key_B: + SetMapping("b"); + break; + case Qt::Key_C: + SetMapping("c"); + break; + case Qt::Key_D: + SetMapping("d"); + break; + case Qt::Key_E: + SetMapping("e"); + break; + case Qt::Key_F: + SetMapping("f"); + break; + case Qt::Key_G: + SetMapping("g"); + break; + case Qt::Key_H: + SetMapping("h"); + break; + case Qt::Key_I: + SetMapping("i"); + break; + case Qt::Key_J: + SetMapping("j"); + break; + case Qt::Key_K: + SetMapping("k"); + break; + case Qt::Key_L: + SetMapping("l"); + break; + case Qt::Key_M: + SetMapping("m"); + break; + case Qt::Key_N: + SetMapping("n"); + break; + case Qt::Key_O: + SetMapping("o"); + break; + case Qt::Key_P: + SetMapping("p"); + break; + case Qt::Key_Q: + SetMapping("q"); + break; + case Qt::Key_R: + SetMapping("r"); + break; + case Qt::Key_S: + SetMapping("s"); + break; + case Qt::Key_T: + SetMapping("t"); + break; + case Qt::Key_U: + SetMapping("u"); + break; + case Qt::Key_V: + SetMapping("v"); + break; + case Qt::Key_W: + SetMapping("w"); + break; + case Qt::Key_X: + SetMapping("x"); + break; + case Qt::Key_Y: + SetMapping("Y"); + break; + case Qt::Key_Z: + SetMapping("z"); + break; + default: + break; + } + return true; + } + + if (event->type() == QEvent::MouseButtonPress) { + QMouseEvent* mouseEvent = static_cast(event); + switch (mouseEvent->button()) { + case Qt::LeftButton: + SetMapping("leftbutton"); + break; + case Qt::RightButton: + SetMapping("rightbutton"); + break; + case Qt::MiddleButton: + SetMapping("middlebutton"); + break; + default: + break; + } + return true; + } + + const QList AxisList = { + ui->LStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, ui->LStickRightButton, + ui->RStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, ui->RStickRightButton}; + + if (event->type() == QEvent::Wheel) { + QWheelEvent* wheelEvent = static_cast(event); + if (wheelEvent->angleDelta().y() > 5) { + if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) { + SetMapping("mousewheelup"); + } else { + QMessageBox::information(this, "Cannot set mapping", + "Mousewheel cannot be mapped to stick outputs"); + } + } else if (wheelEvent->angleDelta().y() < -5) { + if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) { + SetMapping("mousewheeldown"); + } else { + QMessageBox::information(this, "Cannot set mapping", + "Mousewheel cannot be mapped to stick outputs"); + } + } + if (wheelEvent->angleDelta().x() > 5) { + if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) { + // QT changes scrolling to horizontal for all widgets with the alt modifier + if (Qt::AltModifier & QApplication::keyboardModifiers()) { + SetMapping("mousewheelup"); + } else { + SetMapping("mousewheelright"); + } + } else { + QMessageBox::information(this, "Cannot set mapping", + "Mousewheel cannot be mapped to stick outputs"); + } + } else if (wheelEvent->angleDelta().x() < -5) { + if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) { + if (Qt::AltModifier & QApplication::keyboardModifiers()) { + SetMapping("mousewheeldown"); + } else { + SetMapping("mousewheelleft"); + } + } else { + QMessageBox::information(this, "Cannot set mapping", + "Mousewheel cannot be mapped to stick outputs"); + } + } + return true; + } + } + return QDialog::eventFilter(obj, event); +} + +KBMSettings::~KBMSettings() {} diff --git a/src/qt_gui/kbm_gui.h b/src/qt_gui/kbm_gui.h new file mode 100644 index 000000000..e63d7bd43 --- /dev/null +++ b/src/qt_gui/kbm_gui.h @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "game_info.h" + +namespace Ui { +class KBMSettings; +} + +class KBMSettings : public QDialog { + Q_OBJECT +public: + explicit KBMSettings(std::shared_ptr game_info_get, QWidget* parent = nullptr); + ~KBMSettings(); + +private Q_SLOTS: + void SaveKBMConfig(bool CloseOnSave); + void SetDefault(); + void CheckMapping(QPushButton*& button); + void StartTimer(QPushButton*& button); + void onHelpClicked(); + +private: + std::unique_ptr ui; + std::shared_ptr m_game_info; + + bool eventFilter(QObject* obj, QEvent* event) override; + void ButtonConnects(); + void SetUIValuestoMappings(std::string config_id); + void GetGameTitle(); + void DisableMappingButtons(); + void EnableMappingButtons(); + void SetMapping(QString input); + + bool EnableMapping = false; + bool MappingCompleted = false; + bool HelpWindowOpen = false; + QString mapping; + QString modifier; + int MappingTimer; + QTimer* timer; + QPushButton* MappingButton; + QList ButtonsList; + + const std::vector ControllerInputs = { + "cross", "circle", "square", "triangle", "l1", + "r1", "l2", "r2", "l3", + + "r3", "options", "pad_up", + + "pad_down", + + "pad_left", "pad_right", "axis_left_x", "axis_left_y", "axis_right_x", + "axis_right_y", "back"}; +}; diff --git a/src/qt_gui/kbm_gui.ui b/src/qt_gui/kbm_gui.ui new file mode 100644 index 000000000..fb8e4882b --- /dev/null +++ b/src/qt_gui/kbm_gui.ui @@ -0,0 +1,1708 @@ + + + + KBMSettings + + + Qt::WindowModality::WindowModal + + + + 0 + 0 + 1193 + 754 + + + + Qt::FocusPolicy::StrongFocus + + + Configure Controls + + + true + + + + + + Qt::FocusPolicy::NoFocus + + + true + + + + + 0 + 0 + 1173 + 704 + + + + + + 0 + 0 + 1171 + 703 + + + + + + + 5 + + + + + true + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + D-Pad + + + + 6 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 160 + 0 + + + + + 0 + 16777215 + + + + Up + + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + Left + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + Right + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 160 + 0 + + + + + 124 + 16777215 + + + + Down + + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Maximum + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + Left Analog Halfmode + + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + true + + + + hold to move left stick at half-speed + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Left Stick + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 16777215 + 2121 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 160 + 0 + + + + + 124 + 16777215 + + + + Up + + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + Left + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + 179 + 16777215 + + + + Right + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 160 + 0 + + + + + 124 + 21212 + + + + Down + + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + + + + 0 + + + + + + 0 + 0 + + + + + 12 + true + + + + Config Selection + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + + 9 + false + + + + Qt::FocusPolicy::NoFocus + + + + + + -1 + + + Common Config + + + + + + + + 10 + true + + + + Common Config + + + Qt::AlignmentFlag::AlignCenter + + + true + + + + + + + + + + + + 0 + 0 + + + + + 9 + false + + + + Qt::FocusPolicy::NoFocus + + + Use per-game configs + + + + + + + + 9 + false + + + + Qt::FocusPolicy::NoFocus + + + Copy from Common Config + + + + + + + + + + + + 0 + + + + + + + L1 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + 160 + 0 + + + + L2 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + true + + + + + + + + + + Qt::FocusPolicy::NoFocus + + + Text Editor + + + + + + + Qt::FocusPolicy::NoFocus + + + Help + + + + + + + + + + + + + + + 160 + 0 + + + + R1 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + R2 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + 0 + 200 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 420 + 200 + + + + :/images/KBM.png + + + true + + + Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignHCenter + + + + + + + + + + 0 + + + QLayout::SizeConstraint::SetDefaultConstraint + + + + + + + + 160 + 0 + + + + L3 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + Touchpad Click + + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + Mouse to Joystick + + + + + + Qt::FocusPolicy::NoFocus + + + + + + + + true + + + + *press F7 ingame to activate + + + true + + + + + + + + + + + + + + + 160 + 0 + + + + R3 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + 145 + 0 + + + + Options + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + + + + + false + + + + Mouse Movement Parameters + + + + + + + + + false + + + + Deadzone Offset (def 0.50): 0.50 + + + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + 100 + + + 50 + + + Qt::Orientation::Horizontal + + + + + + + + + + + + false + + + + Speed Multiplier (def 1.0): 1.0 + + + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + 1 + + + 50 + + + 5 + + + 10 + + + Qt::Orientation::Horizontal + + + + + + + + + + + + false + + + + Speed Offset (def 0.125): 0.125 + + + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + 1000 + + + 100 + + + 125 + + + Qt::Orientation::Horizontal + + + + + + + + + + + + + + + + + true + false + + + + note: click Help Button/Special Keybindings for more information + + + + + + + + + 5 + + + + + + 0 + 0 + + + + Face Buttons + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 160 + 0 + + + + + 0 + 16777215 + + + + Triangle + + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + Square + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + Circle + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 160 + 0 + + + + + 124 + 16777215 + + + + Cross + + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Maximum + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + Right Analog Halfmode + + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + true + + + + hold to move right stick at half-speed + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Right Stick + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 160 + 0 + + + + + 124 + 1231321 + + + + Up + + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + Left + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + Right + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 160 + 0 + + + + + 124 + 2121 + + + + Down + + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + + + + + + + + + QDialogButtonBox::StandardButton::Apply|QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::RestoreDefaults|QDialogButtonBox::StandardButton::Save + + + false + + + + + + + + + + diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index d9fb45fac..bde32a52d 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -21,11 +21,10 @@ #include "core/loader.h" #include "game_install_dialog.h" #include "install_dir_select.h" +#include "kbm_gui.h" #include "main_window.h" #include "settings_dialog.h" -#include "kbm_config_dialog.h" - #include "video_core/renderer_vulkan/vk_instance.h" #ifdef ENABLE_DISCORD_RPC #include "common/discord_rpc_handler.h" @@ -348,14 +347,13 @@ void MainWindow::CreateConnects() { settingsDialog->exec(); }); - // this is the editor for kbm keybinds connect(ui->controllerButton, &QPushButton::clicked, this, [this]() { auto configWindow = new ControlSettings(m_game_info, this); configWindow->exec(); }); connect(ui->keyboardButton, &QPushButton::clicked, this, [this]() { - auto kbmWindow = new EditorDialog(this); + auto kbmWindow = new KBMSettings(m_game_info, this); kbmWindow->exec(); }); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 8bd72a237..69f5d3c8a 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "common/config.h" @@ -234,6 +235,21 @@ SettingsDialog::SettingsDialog(std::span physical_devices, Common::FS::GetUserPath(Common::FS::PathType::CustomTrophy)); QDesktopServices::openUrl(QUrl::fromLocalFile(userPath)); }); + + connect(ui->PortableUserButton, &QPushButton::clicked, this, []() { + QString userDir; + Common::FS::PathToQString(userDir, std::filesystem::current_path() / "user"); + if (std::filesystem::exists(std::filesystem::current_path() / "user")) { + QMessageBox::information(NULL, "Cannot create portable user folder", + userDir + " already exists"); + } else { + std::filesystem::copy(Common::FS::GetUserPath(Common::FS::PathType::UserDir), + std::filesystem::current_path() / "user", + std::filesystem::copy_options::recursive); + QMessageBox::information(NULL, "Portable user folder created", + userDir + " successfully created"); + } + }); } // Input TAB @@ -344,6 +360,7 @@ SettingsDialog::SettingsDialog(std::span physical_devices, ui->saveDataGroupBox->installEventFilter(this); ui->currentSaveDataPath->installEventFilter(this); ui->browseButton->installEventFilter(this); + ui->PortableUserFolderGroupBox->installEventFilter(this); // Debug ui->debugDump->installEventFilter(this); @@ -650,6 +667,8 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("Add:\\nAdd a folder to the list."); } else if (elementName == "removeFolderButton") { text = tr("Remove:\\nRemove a folder from the list."); + } else if (elementName == "PortableUserFolderGroupBox") { + text = tr("Portable user folder:\\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it."); } // Save Data diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 7db0afa59..5600a0db7 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -31,7 +31,7 @@ Settings - + :/images/shadps4.ico:/images/shadps4.ico @@ -59,7 +59,7 @@ - 0 + 5 @@ -74,7 +74,7 @@ 0 0 946 - 545 + 536 @@ -130,9 +130,6 @@ 9 - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - @@ -455,7 +452,7 @@ 0 0 946 - 545 + 536 @@ -904,7 +901,7 @@ 0 0 946 - 545 + 536 @@ -1199,7 +1196,7 @@ 0 0 946 - 545 + 536 @@ -1306,14 +1303,14 @@ Top - + Bottom - + @@ -1335,8 +1332,7 @@ - - + @@ -1442,7 +1438,7 @@ 0 0 946 - 545 + 536 @@ -1726,7 +1722,7 @@ 0 0 946 - 545 + 536 @@ -1800,6 +1796,58 @@ + + + + Portable User Folder + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Create Portable User Folder from Common User Folder + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + @@ -1816,7 +1864,7 @@ 0 0 946 - 545 + 536 @@ -2068,6 +2116,8 @@ - + + + diff --git a/src/qt_gui/translations/ar_SA.ts b/src/qt_gui/translations/ar_SA.ts index 493a33f82..44d68dcb4 100644 --- a/src/qt_gui/translations/ar_SA.ts +++ b/src/qt_gui/translations/ar_SA.ts @@ -1632,8 +1632,8 @@ Update Compatibility Database:\nImmediately update the compatibility database. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Right + + Top + Top + + + Bottom + Bottom + Notification Duration Notification Duration diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 6e8c8a6ff..a9871f21d 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -1632,8 +1632,8 @@ Update Compatibility Database:\nImmediately update the compatibility database. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Right + + Top + Top + + + Bottom + Bottom + Notification Duration Notification Duration diff --git a/src/qt_gui/translations/de_DE.ts b/src/qt_gui/translations/de_DE.ts index 64b28c179..97eb99d0d 100644 --- a/src/qt_gui/translations/de_DE.ts +++ b/src/qt_gui/translations/de_DE.ts @@ -1632,8 +1632,8 @@ Aktualisiere Kompatibilitätsdatenbank:\nAktualisiere sofort die Kompatibilitätsdatenbank. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Right + + Top + Top + + + Bottom + Bottom + Notification Duration Notification Duration diff --git a/src/qt_gui/translations/el_GR.ts b/src/qt_gui/translations/el_GR.ts index 3d0b89bb8..c409d558d 100644 --- a/src/qt_gui/translations/el_GR.ts +++ b/src/qt_gui/translations/el_GR.ts @@ -1632,8 +1632,8 @@ Update Compatibility Database:\nImmediately update the compatibility database. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Right + + Top + Top + + + Bottom + Bottom + Notification Duration Notification Duration diff --git a/src/qt_gui/translations/en_US.ts b/src/qt_gui/translations/en_US.ts index df4abdbf0..2aa65e9d1 100644 --- a/src/qt_gui/translations/en_US.ts +++ b/src/qt_gui/translations/en_US.ts @@ -903,6 +903,169 @@ + + KBMSettings + + Configure Controls + + + + D-Pad + + + + Up + + + + unmapped + + + + Left + Left + + + Right + Right + + + Down + + + + Left Analog Halfmode + + + + hold to move left stick at half-speed + + + + Left Stick + + + + Config Selection + + + + Common Config + + + + Use per-game configs + + + + Copy from Common Config + + + + L1 + + + + L2 + + + + Text Editor + + + + Help + Help + + + R1 + + + + R2 + + + + L3 + + + + Touchpad Click + + + + Mouse to Joystick + + + + *press F7 ingame to activate + + + + R3 + + + + Options + + + + Mouse Movement Parameters + + + + note: click Help Button/Special Keybindings for more information + + + + Face Buttons + + + + Triangle + + + + Square + + + + Circle + + + + Cross + + + + Right Analog Halfmode + + + + hold to move right stick at half-speed + + + + Right Stick + + + + Deadzone Offset (def 0.50): + + + + Speed Multiplier (def 1.0): + + + + Speed Offset (def 0.125): + + + + Speed Offset (def 0.125): + + + MainWindow @@ -1851,6 +2014,18 @@ Notification Duration Notification Duration + + Portable User Folder + + + + Create Portable User Folder from Common User Folder + + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + + TrophyViewer diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index a9db8860e..c6caed584 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -1632,8 +1632,8 @@ Update Compatibility Database:\nImmediately update the compatibility database. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Right + + Top + Top + + + Bottom + Bottom + Notification Duration Notification Duration diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index da9424a3a..17a1ecfad 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -1632,8 +1632,8 @@ به‌روزرسانی پایگاه داده سازگاری:\nپایگاه داده سازگاری را بلافاصله به‌روزرسانی می‌کند. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Right + + Top + Top + + + Bottom + Bottom + Notification Duration Notification Duration diff --git a/src/qt_gui/translations/fi_FI.ts b/src/qt_gui/translations/fi_FI.ts index f20461015..74d9bb518 100644 --- a/src/qt_gui/translations/fi_FI.ts +++ b/src/qt_gui/translations/fi_FI.ts @@ -1632,8 +1632,8 @@ Päivitä Yhteensopivuustietokanta:\nPäivitää yhteensopivuustietokannan heti. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Right + + Top + Top + + + Bottom + Bottom + Notification Duration Notification Duration diff --git a/src/qt_gui/translations/fr_FR.ts b/src/qt_gui/translations/fr_FR.ts index 41a588aee..ecbe88b1e 100644 --- a/src/qt_gui/translations/fr_FR.ts +++ b/src/qt_gui/translations/fr_FR.ts @@ -1632,8 +1632,8 @@ Mettre à jour la compatibilité au démarrage:\nMet à jour immédiatement la base de données de compatibilité. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Right + + Top + Top + + + Bottom + Bottom + Notification Duration Notification Duration diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 35ad71d59..aa123ccf2 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -1632,8 +1632,8 @@ Update Compatibility Database:\nImmediately update the compatibility database. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Right + + Top + Top + + + Bottom + Bottom + Notification Duration Notification Duration diff --git a/src/qt_gui/translations/id_ID.ts b/src/qt_gui/translations/id_ID.ts index 25835f925..bd8f6b11d 100644 --- a/src/qt_gui/translations/id_ID.ts +++ b/src/qt_gui/translations/id_ID.ts @@ -1632,8 +1632,8 @@ Update Compatibility Database:\nImmediately update the compatibility database. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Right + + Top + Top + + + Bottom + Bottom + Notification Duration Notification Duration diff --git a/src/qt_gui/translations/it_IT.ts b/src/qt_gui/translations/it_IT.ts index a3f06591d..032234300 100644 --- a/src/qt_gui/translations/it_IT.ts +++ b/src/qt_gui/translations/it_IT.ts @@ -1632,8 +1632,8 @@ Aggiorna Database Compatibilità:\nAggiorna immediatamente il database di compatibilità. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Apri la cartella personalizzata delle immagini/suoni dei trofei:\nPuoi aggiungere immagini e audio personalizzato ai trofei.\nAggiungi i file in custom_trophy con i seguenti nomi:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Destra + + Top + In alto + + + Bottom + In basso + Notification Duration Durata Notifica diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index f9f4d6370..7350057dd 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -1632,8 +1632,8 @@ 互換性データベースを更新する:\n今すぐ互換性データベースを更新します。 - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Right + + Top + Top + + + Bottom + Bottom + Notification Duration Notification Duration diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index 3e6c26dde..6bc8ddfb1 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -1632,8 +1632,8 @@ Update Compatibility Database:\nImmediately update the compatibility database. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Right + + Top + Top + + + Bottom + Bottom + Notification Duration Notification Duration diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index c9be4c048..c384da08b 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -1632,8 +1632,8 @@ Update Compatibility Database:\nImmediately update the compatibility database. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Right + + Top + Top + + + Bottom + Bottom + Notification Duration Notification Duration diff --git a/src/qt_gui/translations/nb_NO.ts b/src/qt_gui/translations/nb_NO.ts index d163396ee..89400aaa4 100644 --- a/src/qt_gui/translations/nb_NO.ts +++ b/src/qt_gui/translations/nb_NO.ts @@ -1632,8 +1632,8 @@ Oppdater kompatibilitets-database:\nOppdater kompatibilitets-databasen nå. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Åpne mappa med tilpassede bilder og lyder for trofé:\nDu kan legge til tilpassede bilder til trofeene og en lyd.\nLegg filene til custom_trophy med følgende navn:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Åpne mappa med tilpassede bilder og lyder for trofé:\nDu kan legge til tilpassede bilder til trofeer med en lyd.\nLegg filene til custom_trophy med følgende navn:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nMerk: Avspilling av lyden vil bare fungere med Qt versjonen. Never @@ -1839,6 +1839,14 @@ Right Høyre + + Top + Øverst + + + Bottom + Nederst + Notification Duration Varslingsvarighet diff --git a/src/qt_gui/translations/nl_NL.ts b/src/qt_gui/translations/nl_NL.ts index a61ca1adf..924f59ef7 100644 --- a/src/qt_gui/translations/nl_NL.ts +++ b/src/qt_gui/translations/nl_NL.ts @@ -1632,8 +1632,8 @@ Update Compatibility Database:\nImmediately update the compatibility database. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Right + + Top + Top + + + Bottom + Bottom + Notification Duration Notification Duration diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 64134d055..8a8ac83f7 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -1632,8 +1632,8 @@ Zaktualizuj bazę danych zgodności:\nNatychmiast zaktualizuj bazę danych zgodności. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Otwórz niestandardowy folder obrazów/dźwięków:\nMożesz dodać własne obrazy do trofeów i dźwięku.\nDodaj pliki do custom_trophy o następujących nazwach:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Z prawej + + Top + Z góry + + + Bottom + Z dołu + Notification Duration Czas trwania powiadomienia diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index 103f0b66e..5fe1774ba 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -733,7 +733,7 @@ Open Log Folder - Abrir Pasta de Registros + Abrir Pasta de Log Copy info... @@ -1517,7 +1517,7 @@ Update Compatibility Database On Startup - Atualizar Base de Dados de Compatibilidade ao Inicializar + Atualizar Banco de Dados de Compatibilidade ao Inicializar Game Compatibility @@ -1589,7 +1589,7 @@ 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. - Filtro de Registro:\nFiltra o registro para exibir apenas informações específicas.\nExemplos: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nNíveis: Trace, Debug, Info, Warning, Error, Critical - nesta ordem, um nível específico silencia todos os níveis anteriores na lista e registra todos os níveis após ele. + Filtro do Registro:\nFiltra o registro para exibir apenas informações específicas.\nExemplos: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nNíveis: Trace, Debug, Info, Warning, Error, Critical - nesta ordem, um nível específico silencia todos os níveis anteriores na lista e registra todos os níveis após este. Update:\nRelease: Official versions released every month that may be very outdated, but are more reliable and tested.\nNightly: Development versions that have all the latest features and fixes, but may contain bugs and are less stable. @@ -1632,8 +1632,8 @@ Atualizar Lista de Compatibilidade:\nAtualiza imediatamente o banco de dados de compatibilidade. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Abrir a pasta de imagens/sons de troféus personalizados:\nVocê pode adicionar imagens e sons personalizados aos troféus.\nAdicione os arquivos em custom_trophy com os seguintes nomes:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Abrir a pasta de imagens/sons de troféus personalizados:\nVocê pode adicionar imagens personalizadas aos troféus e um áudio.\nAdicione os arquivos na pasta custom_trophy com os seguintes nomes:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nObservação: O som funcionará apenas em versões Qt. Never @@ -1839,6 +1839,14 @@ Right Direita + + Top + Acima + + + Bottom + Abaixo + Notification Duration Duração da Notificação diff --git a/src/qt_gui/translations/pt_PT.ts b/src/qt_gui/translations/pt_PT.ts index 8db33ca5b..b279d1c5b 100644 --- a/src/qt_gui/translations/pt_PT.ts +++ b/src/qt_gui/translations/pt_PT.ts @@ -777,7 +777,7 @@ Delete Trophy - Delete Trophy + Eliminar Troféu Compatibility... @@ -857,7 +857,7 @@ No log file found for this game! - No log file found for this game! + Não foi encontrado nenhum ficheiro de registo para este jogo! Failed to convert icon. @@ -869,7 +869,7 @@ This game has no saved trophies to delete! - This game has no saved trophies to delete! + Este jogo não tem troféus guardados para eliminar! Save Data @@ -877,7 +877,7 @@ Trophy - Trophy + Troféus SFO Viewer for @@ -1329,7 +1329,7 @@ Open the custom trophy images/sounds folder - Open the custom trophy images/sounds folder + Abrir a pasta de imagens/sons de troféus personalizados Logger @@ -1497,7 +1497,7 @@ Disable Trophy Notification - Disable Trophy Notification + Desativar Notificações de Troféus Background Image @@ -1632,8 +1632,8 @@ Atualizar Base de Dados de Compatibilidade:\nAtualiza imediatamente a base de dados de compatibilidade. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Abrir a pasta de imagens/sons de troféus personalizados:\nPoderá adicionar imagens personalizadas aos troféus e um áudio.\nAdicione os ficheiros na pasta custom_trophy com os seguintes nomes:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nObservação: O som funcionará apenas nas versões Qt. Never @@ -1829,19 +1829,27 @@ Trophy Notification Position - Trophy Notification Position + Posição da Notificação do Troféu Left - Left + Esquerda Right - Right + Direita + + + Top + Acima + + + Bottom + Abaixo Notification Duration - Notification Duration + Duração da Notificação @@ -1852,19 +1860,19 @@ Progress - Progress + Progresso Show Earned Trophies - Show Earned Trophies + Mostrar Troféus Conquistados Show Not Earned Trophies - Show Not Earned Trophies + Mostrar Troféus Não Conquistados Show Hidden Trophies - Show Hidden Trophies + Mostrar Troféus Ocultos diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index be1613c57..e57b23150 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -1632,8 +1632,8 @@ Update Compatibility Database:\nImmediately update the compatibility database. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Right + + Top + Top + + + Bottom + Bottom + Notification Duration Notification Duration diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 313233c92..c454ead29 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -1632,8 +1632,8 @@ Обновить базу совместимости:\nНемедленно обновить базу данных совместимости. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Открыть пользовательскую папку с трофеями изображений/звуков:\nВы можете добавить пользовательские изображения к трофеям и аудио.\nДобавьте файлы в custom_trophy со следующими именами:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Открыть папку с пользовательскими изображениями/звуками трофеев:\nВы можете добавить пользовательские изображения к трофеям и аудио.\nДобавьте файлы в custom_trophy со следующими именами:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nПримечание: звук будет работать только в QT-версии. Never @@ -1839,6 +1839,14 @@ Right Вправо + + Top + Сверху + + + Bottom + Снизу + Notification Duration Продолжительность уведомления diff --git a/src/qt_gui/translations/sq_AL.ts b/src/qt_gui/translations/sq_AL.ts index 0783fb1a7..0b75e767d 100644 --- a/src/qt_gui/translations/sq_AL.ts +++ b/src/qt_gui/translations/sq_AL.ts @@ -1632,8 +1632,8 @@ Përditëso bazën e të dhënave të përputhshmërisë:\nPërditëso menjëherë bazën e të dhënave të përputhshmërisë. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Hap dosjen e imazheve/tingujve të trofeve të personalizuar:\nMund të shtosh imazhe të personalizuara për trofetë dhe një skedar audio.\nShto skedarët në dosjen custom_trophy me emrat që vijojnë:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Hap dosjen e imazheve/tingujve të trofeve të personalizuar:\nMund të shtosh imazhe të personalizuara për trofetë dhe një audio.\nShto skedarët në dosjen custom_trophy me emrat që vijojnë:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nShënim: Tingulli do të punojë vetëm në versionet QT. Never @@ -1839,6 +1839,14 @@ Right Djathtas + + Top + Sipër + + + Bottom + Poshtë + Notification Duration Kohëzgjatja e Njoftimit diff --git a/src/qt_gui/translations/sv_SE.ts b/src/qt_gui/translations/sv_SE.ts index 6cbd33f17..f317b1974 100644 --- a/src/qt_gui/translations/sv_SE.ts +++ b/src/qt_gui/translations/sv_SE.ts @@ -1329,7 +1329,7 @@ Open the custom trophy images/sounds folder - Öppna mapp för anpassade trofébilder/ljud + Öppna mappen för anpassade trofébilder/ljud Logger @@ -1632,8 +1632,8 @@ Uppdatera kompatibilitetsdatabasen:\nUppdaterar kompatibilitetsdatabasen direkt - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Öppna mappen med anpassade trofébilder/ljud:\nDu kan lägga till anpassade bilder till troféerna och ett ljud.\nLägg till filerna i custom_trophy med följande namn:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Öppna mappen för anpassade trofébilder/ljud:\nDu kan lägga till anpassade bilder till troféerna och ett ljud.\nLägg till filerna i custom_trophy med följande namn:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nObservera: Ljudet fungerar endast i QT-versioner. Never @@ -1839,6 +1839,14 @@ Right Höger + + Top + Överst + + + Bottom + Nederst + Notification Duration Varaktighet för avisering diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index cd4f1cadd..7b979e653 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -1632,8 +1632,8 @@ Uyumluluk Veritabanını Güncelle:\nUyumluluk veri tabanını hemen güncelleyin. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Sağ + + Top + Top + + + Bottom + Bottom + Notification Duration Bildirim Süresi diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 63f8b012f..222cb9e1a 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -1632,8 +1632,8 @@ Оновити данні ігрової сумістності:\nНегайно оновить базу даних ігрової сумісності. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Right + + Top + Top + + + Bottom + Bottom + Notification Duration Notification Duration diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index fe9ad915f..5ab469b92 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -1632,8 +1632,8 @@ Update Compatibility Database:\nImmediately update the compatibility database. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Right + + Top + Top + + + Bottom + Bottom + Notification Duration Notification Duration diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index a9cf7ca19..dbfe61c16 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -1632,8 +1632,8 @@ 更新兼容性数据库:\n立即更新兼容性数据库。 - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - 打开自定义奖杯图像/声音文件夹:\n您可以自定义奖杯图像和声音。\n将文件添加到 custom_trophy 文件夹中,文件名如下:\nthophy.mp3、bronze.png、gold.png、platinum.png、silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right 右边 + + Top + Top + + + Bottom + Bottom + Notification Duration 通知显示持续时间 diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index bd3ef8f0b..cb78b7a64 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -1632,8 +1632,8 @@ Update Compatibility Database:\nImmediately update the compatibility database. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. Never @@ -1839,6 +1839,14 @@ Right Right + + Top + Top + + + Bottom + Bottom + Notification Duration Notification Duration diff --git a/src/shadps4.qrc b/src/shadps4.qrc index 14b50f7a5..a1ff680ed 100644 --- a/src/shadps4.qrc +++ b/src/shadps4.qrc @@ -32,5 +32,6 @@ images/website.png images/ps4_controller.png images/keyboard_icon.png + images/KBM.png