mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-07-14 13:45:59 +00:00
Merge branch 'shadps4-emu:main' into allocate-fixes
This commit is contained in:
commit
e3f1e4cf1d
43 changed files with 4178 additions and 1539 deletions
|
@ -115,9 +115,20 @@ execute_process(
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
)
|
)
|
||||||
|
|
||||||
# Default to origin if there's no upstream set or if the command failed
|
# If there's no upstream set or the command failed, check remote.pushDefault
|
||||||
if (GIT_BRANCH_RESULT OR GIT_REMOTE_NAME STREQUAL "")
|
if (GIT_BRANCH_RESULT OR GIT_REMOTE_NAME STREQUAL "")
|
||||||
set(GIT_REMOTE_NAME "origin")
|
execute_process(
|
||||||
|
COMMAND git config --get remote.pushDefault
|
||||||
|
OUTPUT_VARIABLE GIT_REMOTE_NAME
|
||||||
|
RESULT_VARIABLE GIT_PUSH_DEFAULT_RESULT
|
||||||
|
ERROR_QUIET
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
|
||||||
|
# If remote.pushDefault is not set or fails, default to origin
|
||||||
|
if (GIT_PUSH_DEFAULT_RESULT OR GIT_REMOTE_NAME STREQUAL "")
|
||||||
|
set(GIT_REMOTE_NAME "origin")
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
# Extract remote name if the output contains a remote/branch format
|
# Extract remote name if the output contains a remote/branch format
|
||||||
string(FIND "${GIT_REMOTE_NAME}" "/" INDEX)
|
string(FIND "${GIT_REMOTE_NAME}" "/" INDEX)
|
||||||
|
@ -886,6 +897,9 @@ set(QT_GUI src/qt_gui/about_dialog.cpp
|
||||||
src/qt_gui/cheats_patches.h
|
src/qt_gui/cheats_patches.h
|
||||||
src/qt_gui/compatibility_info.cpp
|
src/qt_gui/compatibility_info.cpp
|
||||||
src/qt_gui/compatibility_info.h
|
src/qt_gui/compatibility_info.h
|
||||||
|
src/qt_gui/control_settings.cpp
|
||||||
|
src/qt_gui/control_settings.h
|
||||||
|
src/qt_gui/control_settings.ui
|
||||||
src/qt_gui/main_window_ui.h
|
src/qt_gui/main_window_ui.h
|
||||||
src/qt_gui/main_window.cpp
|
src/qt_gui/main_window.cpp
|
||||||
src/qt_gui/main_window.h
|
src/qt_gui/main_window.h
|
||||||
|
|
|
@ -38,6 +38,7 @@ path = [
|
||||||
"src/images/list_mode_icon.png",
|
"src/images/list_mode_icon.png",
|
||||||
"src/images/pause_icon.png",
|
"src/images/pause_icon.png",
|
||||||
"src/images/play_icon.png",
|
"src/images/play_icon.png",
|
||||||
|
"src/images/ps4_controller.png",
|
||||||
"src/images/refresh_icon.png",
|
"src/images/refresh_icon.png",
|
||||||
"src/images/settings_icon.png",
|
"src/images/settings_icon.png",
|
||||||
"src/images/stop_icon.png",
|
"src/images/stop_icon.png",
|
||||||
|
|
2
dist/net.shadps4.shadPS4.desktop
vendored
2
dist/net.shadps4.shadPS4.desktop
vendored
|
@ -5,5 +5,5 @@ Terminal=false
|
||||||
Type=Application
|
Type=Application
|
||||||
Icon=net.shadps4.shadPS4
|
Icon=net.shadps4.shadPS4
|
||||||
Comment=PlayStation 4 emulator
|
Comment=PlayStation 4 emulator
|
||||||
Categories=Game;
|
Categories=Game;Emulator;
|
||||||
StartupWMClass=shadps4;
|
StartupWMClass=shadps4;
|
||||||
|
|
|
@ -95,6 +95,8 @@ std::vector<std::string> m_pkg_viewer;
|
||||||
std::vector<std::string> m_elf_viewer;
|
std::vector<std::string> m_elf_viewer;
|
||||||
std::vector<std::string> m_recent_files;
|
std::vector<std::string> m_recent_files;
|
||||||
std::string emulator_language = "en";
|
std::string emulator_language = "en";
|
||||||
|
static int backgroundImageOpacity = 50;
|
||||||
|
static bool showBackgroundImage = true;
|
||||||
|
|
||||||
// Language
|
// Language
|
||||||
u32 m_language = 1; // english
|
u32 m_language = 1; // english
|
||||||
|
@ -611,6 +613,22 @@ u32 GetLanguage() {
|
||||||
return m_language;
|
return m_language;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getBackgroundImageOpacity() {
|
||||||
|
return backgroundImageOpacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBackgroundImageOpacity(int opacity) {
|
||||||
|
backgroundImageOpacity = std::clamp(opacity, 0, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getShowBackgroundImage() {
|
||||||
|
return showBackgroundImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setShowBackgroundImage(bool show) {
|
||||||
|
showBackgroundImage = show;
|
||||||
|
}
|
||||||
|
|
||||||
void load(const std::filesystem::path& path) {
|
void load(const std::filesystem::path& path) {
|
||||||
// If the configuration file does not exist, create it and return
|
// If the configuration file does not exist, create it and return
|
||||||
std::error_code error;
|
std::error_code error;
|
||||||
|
@ -731,6 +749,8 @@ void load(const std::filesystem::path& path) {
|
||||||
m_recent_files = toml::find_or<std::vector<std::string>>(gui, "recentFiles", {});
|
m_recent_files = toml::find_or<std::vector<std::string>>(gui, "recentFiles", {});
|
||||||
m_table_mode = toml::find_or<int>(gui, "gameTableMode", 0);
|
m_table_mode = toml::find_or<int>(gui, "gameTableMode", 0);
|
||||||
emulator_language = toml::find_or<std::string>(gui, "emulatorLanguage", "en");
|
emulator_language = toml::find_or<std::string>(gui, "emulatorLanguage", "en");
|
||||||
|
backgroundImageOpacity = toml::find_or<int>(gui, "backgroundImageOpacity", 50);
|
||||||
|
showBackgroundImage = toml::find_or<bool>(gui, "showBackgroundImage", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.contains("Settings")) {
|
if (data.contains("Settings")) {
|
||||||
|
@ -821,6 +841,8 @@ void save(const std::filesystem::path& path) {
|
||||||
data["GUI"]["addonInstallDir"] =
|
data["GUI"]["addonInstallDir"] =
|
||||||
std::string{fmt::UTF(settings_addon_install_dir.u8string()).data};
|
std::string{fmt::UTF(settings_addon_install_dir.u8string()).data};
|
||||||
data["GUI"]["emulatorLanguage"] = emulator_language;
|
data["GUI"]["emulatorLanguage"] = emulator_language;
|
||||||
|
data["GUI"]["backgroundImageOpacity"] = backgroundImageOpacity;
|
||||||
|
data["GUI"]["showBackgroundImage"] = showBackgroundImage;
|
||||||
data["Settings"]["consoleLanguage"] = m_language;
|
data["Settings"]["consoleLanguage"] = m_language;
|
||||||
|
|
||||||
std::ofstream file(path, std::ios::binary);
|
std::ofstream file(path, std::ios::binary);
|
||||||
|
@ -914,6 +936,8 @@ void setDefaultValues() {
|
||||||
separateupdatefolder = false;
|
separateupdatefolder = false;
|
||||||
compatibilityData = false;
|
compatibilityData = false;
|
||||||
checkCompatibilityOnStartup = false;
|
checkCompatibilityOnStartup = false;
|
||||||
|
backgroundImageOpacity = 50;
|
||||||
|
showBackgroundImage = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::string_view GetDefaultKeyboardConfig() {
|
constexpr std::string_view GetDefaultKeyboardConfig() {
|
||||||
|
|
|
@ -30,6 +30,8 @@ bool getEnableDiscordRPC();
|
||||||
bool getSeparateUpdateEnabled();
|
bool getSeparateUpdateEnabled();
|
||||||
bool getCompatibilityEnabled();
|
bool getCompatibilityEnabled();
|
||||||
bool getCheckCompatibilityOnStartup();
|
bool getCheckCompatibilityOnStartup();
|
||||||
|
int getBackgroundImageOpacity();
|
||||||
|
bool getShowBackgroundImage();
|
||||||
|
|
||||||
std::string getLogFilter();
|
std::string getLogFilter();
|
||||||
std::string getLogType();
|
std::string getLogType();
|
||||||
|
@ -88,6 +90,8 @@ void setGameInstallDirs(const std::vector<std::filesystem::path>& settings_insta
|
||||||
void setSaveDataPath(const std::filesystem::path& path);
|
void setSaveDataPath(const std::filesystem::path& path);
|
||||||
void setCompatibilityEnabled(bool use);
|
void setCompatibilityEnabled(bool use);
|
||||||
void setCheckCompatibilityOnStartup(bool use);
|
void setCheckCompatibilityOnStartup(bool use);
|
||||||
|
void setBackgroundImageOpacity(int opacity);
|
||||||
|
void setShowBackgroundImage(bool show);
|
||||||
|
|
||||||
void setCursorState(s16 cursorState);
|
void setCursorState(s16 cursorState);
|
||||||
void setCursorHideTimeout(int newcursorHideTimeout);
|
void setCursorHideTimeout(int newcursorHideTimeout);
|
||||||
|
|
|
@ -23,8 +23,8 @@ std::string FormatLogMessage(const Entry& entry) {
|
||||||
const char* class_name = GetLogClassName(entry.log_class);
|
const char* class_name = GetLogClassName(entry.log_class);
|
||||||
const char* level_name = GetLevelName(entry.log_level);
|
const char* level_name = GetLevelName(entry.log_level);
|
||||||
|
|
||||||
return fmt::format("[{}] <{}> {}:{}:{}: {}", class_name, level_name, entry.filename,
|
return fmt::format("[{}] <{}> {}:{} {}: {}", class_name, level_name, entry.filename,
|
||||||
entry.function, entry.line_num, entry.message);
|
entry.line_num, entry.function, entry.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintMessage(const Entry& entry) {
|
void PrintMessage(const Entry& entry) {
|
||||||
|
|
|
@ -174,7 +174,7 @@ void OnGameLoaded() {
|
||||||
patchMask = MemoryPatcher::PatchMask::Mask_Jump32;
|
patchMask = MemoryPatcher::PatchMask::Mask_Jump32;
|
||||||
|
|
||||||
MemoryPatcher::PatchMemory(currentPatchName, address, patchValue, false,
|
MemoryPatcher::PatchMemory(currentPatchName, address, patchValue, false,
|
||||||
littleEndian, patchMask);
|
littleEndian, patchMask, maskOffsetValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,6 +278,7 @@ void OnGameLoaded() {
|
||||||
lineObject["Type"] = attributes.value("Type").toString();
|
lineObject["Type"] = attributes.value("Type").toString();
|
||||||
lineObject["Address"] = attributes.value("Address").toString();
|
lineObject["Address"] = attributes.value("Address").toString();
|
||||||
lineObject["Value"] = attributes.value("Value").toString();
|
lineObject["Value"] = attributes.value("Value").toString();
|
||||||
|
lineObject["Offset"] = attributes.value("Offset").toString();
|
||||||
linesArray.append(lineObject);
|
linesArray.append(lineObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -321,7 +322,7 @@ void OnGameLoaded() {
|
||||||
|
|
||||||
MemoryPatcher::PatchMemory(currentPatchName, address.toStdString(),
|
MemoryPatcher::PatchMemory(currentPatchName, address.toStdString(),
|
||||||
patchValue.toStdString(), false,
|
patchValue.toStdString(), false,
|
||||||
littleEndian, patchMask);
|
littleEndian, patchMask, maskOffsetValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -447,4 +448,4 @@ uintptr_t PatternScan(const std::string& signature) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace MemoryPatcher
|
} // namespace MemoryPatcher
|
||||||
|
|
|
@ -84,7 +84,11 @@ bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) {
|
||||||
std::scoped_lock lock{m_mutex};
|
std::scoped_lock lock{m_mutex};
|
||||||
for (auto& event : m_events) {
|
for (auto& event : m_events) {
|
||||||
if (event.event.ident == ident && event.event.filter == filter) {
|
if (event.event.ident == ident && event.event.filter == filter) {
|
||||||
event.Trigger(trigger_data);
|
if (filter == SceKernelEvent::Filter::VideoOut) {
|
||||||
|
event.TriggerDisplay(trigger_data);
|
||||||
|
} else {
|
||||||
|
event.Trigger(trigger_data);
|
||||||
|
}
|
||||||
has_found = true;
|
has_found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <boost/asio/steady_timer.hpp>
|
#include <boost/asio/steady_timer.hpp>
|
||||||
|
|
||||||
|
#include "common/rdtsc.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
|
||||||
namespace Core::Loader {
|
namespace Core::Loader {
|
||||||
|
@ -81,6 +82,25 @@ struct EqueueEvent {
|
||||||
event.data = reinterpret_cast<uintptr_t>(data);
|
event.data = reinterpret_cast<uintptr_t>(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TriggerDisplay(void* data) {
|
||||||
|
is_triggered = true;
|
||||||
|
auto hint = reinterpret_cast<u64>(data);
|
||||||
|
if (hint != 0) {
|
||||||
|
auto hint_h = static_cast<u32>(hint >> 8) & 0xFFFFFF;
|
||||||
|
auto ident_h = static_cast<u32>(event.ident >> 40);
|
||||||
|
if ((static_cast<u32>(hint) & 0xFF) == event.ident && event.ident != 0xFE &&
|
||||||
|
((hint_h ^ ident_h) & 0xFF) == 0) {
|
||||||
|
auto time = Common::FencedRDTSC();
|
||||||
|
auto mask = 0xF000;
|
||||||
|
if ((static_cast<u32>(event.data) & 0xF000) != 0xF000) {
|
||||||
|
mask = (static_cast<u32>(event.data) + 0x1000) & 0xF000;
|
||||||
|
}
|
||||||
|
event.data = (mask | static_cast<u64>(static_cast<u32>(time) & 0xFFF) |
|
||||||
|
(hint & 0xFFFFFFFFFFFF0000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool IsTriggered() const {
|
bool IsTriggered() const {
|
||||||
return is_triggered;
|
return is_triggered;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
namespace Libraries::Kernel {
|
namespace Libraries::Kernel {
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelIsNeoMode() {
|
int PS4_SYSV_ABI sceKernelIsNeoMode() {
|
||||||
LOG_DEBUG(Kernel_Sce, "called");
|
|
||||||
return Config::isNeoModeConsole() &&
|
return Config::isNeoModeConsole() &&
|
||||||
Common::ElfInfo::Instance().GetPSFAttributes().support_neo_mode;
|
Common::ElfInfo::Instance().GetPSFAttributes().support_neo_mode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -185,9 +185,11 @@ void VideoOutDriver::Flip(const Request& req) {
|
||||||
// Trigger flip events for the port.
|
// Trigger flip events for the port.
|
||||||
for (auto& event : port->flip_events) {
|
for (auto& event : port->flip_events) {
|
||||||
if (event != nullptr) {
|
if (event != nullptr) {
|
||||||
event->TriggerEvent(u64(OrbisVideoOutEventId::Flip),
|
event->TriggerEvent(
|
||||||
Kernel::SceKernelEvent::Filter::VideoOut,
|
static_cast<u64>(OrbisVideoOutInternalEventId::Flip),
|
||||||
reinterpret_cast<void*>(req.flip_arg));
|
Kernel::SceKernelEvent::Filter::VideoOut,
|
||||||
|
reinterpret_cast<void*>(static_cast<u64>(OrbisVideoOutInternalEventId::Flip) |
|
||||||
|
(req.flip_arg << 16)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,7 +325,7 @@ void VideoOutDriver::PresentThread(std::stop_token token) {
|
||||||
// Trigger flip events for the port.
|
// Trigger flip events for the port.
|
||||||
for (auto& event : main_port.vblank_events) {
|
for (auto& event : main_port.vblank_events) {
|
||||||
if (event != nullptr) {
|
if (event != nullptr) {
|
||||||
event->TriggerEvent(u64(OrbisVideoOutEventId::Vblank),
|
event->TriggerEvent(static_cast<u64>(OrbisVideoOutInternalEventId::Vblank),
|
||||||
Kernel::SceKernelEvent::Filter::VideoOut, nullptr);
|
Kernel::SceKernelEvent::Filter::VideoOut, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::SceKernelEqueue eq, s32 handle,
|
||||||
}
|
}
|
||||||
|
|
||||||
Kernel::EqueueEvent event{};
|
Kernel::EqueueEvent event{};
|
||||||
event.event.ident = u64(OrbisVideoOutEventId::Flip);
|
event.event.ident = static_cast<u64>(OrbisVideoOutInternalEventId::Flip);
|
||||||
event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut;
|
event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut;
|
||||||
event.event.flags = Kernel::SceKernelEvent::Flags::Add;
|
event.event.flags = Kernel::SceKernelEvent::Flags::Add;
|
||||||
event.event.udata = udata;
|
event.event.udata = udata;
|
||||||
|
@ -63,6 +63,20 @@ s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::SceKernelEqueue eq, s32 handle,
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceVideoOutDeleteFlipEvent(Kernel::SceKernelEqueue eq, s32 handle) {
|
||||||
|
auto* port = driver->GetPort(handle);
|
||||||
|
if (port == nullptr) {
|
||||||
|
return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eq == nullptr) {
|
||||||
|
return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
|
||||||
|
}
|
||||||
|
eq->RemoveEvent(handle, Kernel::SceKernelEvent::Filter::VideoOut);
|
||||||
|
port->flip_events.erase(find(port->flip_events.begin(), port->flip_events.end(), eq));
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handle, void* udata) {
|
s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handle, void* udata) {
|
||||||
LOG_INFO(Lib_VideoOut, "handle = {}", handle);
|
LOG_INFO(Lib_VideoOut, "handle = {}", handle);
|
||||||
|
|
||||||
|
@ -76,7 +90,7 @@ s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handl
|
||||||
}
|
}
|
||||||
|
|
||||||
Kernel::EqueueEvent event{};
|
Kernel::EqueueEvent event{};
|
||||||
event.event.ident = u64(OrbisVideoOutEventId::Vblank);
|
event.event.ident = static_cast<u64>(OrbisVideoOutInternalEventId::Vblank);
|
||||||
event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut;
|
event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut;
|
||||||
event.event.flags = Kernel::SceKernelEvent::Flags::Add;
|
event.event.flags = Kernel::SceKernelEvent::Flags::Add;
|
||||||
event.event.udata = udata;
|
event.event.udata = udata;
|
||||||
|
@ -156,9 +170,27 @@ int PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev) {
|
||||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS;
|
return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS;
|
||||||
}
|
}
|
||||||
if (ev->filter != Kernel::SceKernelEvent::Filter::VideoOut) {
|
if (ev->filter != Kernel::SceKernelEvent::Filter::VideoOut) {
|
||||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
|
return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
OrbisVideoOutInternalEventId internal_event_id =
|
||||||
|
static_cast<OrbisVideoOutInternalEventId>(ev->ident);
|
||||||
|
switch (internal_event_id) {
|
||||||
|
case OrbisVideoOutInternalEventId::Flip:
|
||||||
|
return static_cast<s32>(OrbisVideoOutEventId::Flip);
|
||||||
|
case OrbisVideoOutInternalEventId::Vblank:
|
||||||
|
case OrbisVideoOutInternalEventId::SysVblank:
|
||||||
|
return static_cast<s32>(OrbisVideoOutEventId::Vblank);
|
||||||
|
case OrbisVideoOutInternalEventId::PreVblankStart:
|
||||||
|
return static_cast<s32>(OrbisVideoOutEventId::PreVblankStart);
|
||||||
|
case OrbisVideoOutInternalEventId::SetMode:
|
||||||
|
return static_cast<s32>(OrbisVideoOutEventId::SetMode);
|
||||||
|
case OrbisVideoOutInternalEventId::Position:
|
||||||
|
return static_cast<s32>(OrbisVideoOutEventId::Position);
|
||||||
|
default: {
|
||||||
|
return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ev->ident;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, int64_t* data) {
|
int PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, int64_t* data) {
|
||||||
|
@ -356,6 +388,8 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||||
sceVideoOutColorSettingsSetGamma);
|
sceVideoOutColorSettingsSetGamma);
|
||||||
LIB_FUNCTION("pv9CI5VC+R0", "libSceVideoOut", 1, "libSceVideoOut", 0, 0,
|
LIB_FUNCTION("pv9CI5VC+R0", "libSceVideoOut", 1, "libSceVideoOut", 0, 0,
|
||||||
sceVideoOutAdjustColor);
|
sceVideoOutAdjustColor);
|
||||||
|
LIB_FUNCTION("-Ozn0F1AFRg", "libSceVideoOut", 1, "libSceVideoOut", 0, 0,
|
||||||
|
sceVideoOutDeleteFlipEvent);
|
||||||
|
|
||||||
// openOrbis appears to have libSceVideoOut_v1 module libSceVideoOut_v1.1
|
// openOrbis appears to have libSceVideoOut_v1 module libSceVideoOut_v1.1
|
||||||
LIB_FUNCTION("Up36PTk687E", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutOpen);
|
LIB_FUNCTION("Up36PTk687E", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutOpen);
|
||||||
|
|
|
@ -40,7 +40,22 @@ constexpr int SCE_VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_NONE = 0;
|
||||||
constexpr int SCE_VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_VR = 7;
|
constexpr int SCE_VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_VR = 7;
|
||||||
constexpr int SCE_VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_STRICT_COLORIMETRY = 8;
|
constexpr int SCE_VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_STRICT_COLORIMETRY = 8;
|
||||||
|
|
||||||
enum class OrbisVideoOutEventId : s16 { Flip = 0, Vblank = 1, PreVblankStart = 2 };
|
enum class OrbisVideoOutEventId : s16 {
|
||||||
|
Flip = 0,
|
||||||
|
Vblank = 1,
|
||||||
|
PreVblankStart = 2,
|
||||||
|
SetMode = 8,
|
||||||
|
Position = 12,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class OrbisVideoOutInternalEventId : s16 {
|
||||||
|
Flip = 0x6,
|
||||||
|
Vblank = 0x7,
|
||||||
|
SetMode = 0x51,
|
||||||
|
Position = 0x58,
|
||||||
|
PreVblankStart = 0x59,
|
||||||
|
SysVblank = 0x63,
|
||||||
|
};
|
||||||
|
|
||||||
enum class AspectRatioMode : s32 {
|
enum class AspectRatioMode : s32 {
|
||||||
Ratio16_9 = 0,
|
Ratio16_9 = 0,
|
||||||
|
|
|
@ -201,12 +201,16 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector<std::str
|
||||||
window_title = fmt::format("shadPS4 v{} | {}", Common::VERSION, game_title);
|
window_title = fmt::format("shadPS4 v{} | {}", Common::VERSION, game_title);
|
||||||
} else {
|
} else {
|
||||||
std::string remote_url(Common::g_scm_remote_url);
|
std::string remote_url(Common::g_scm_remote_url);
|
||||||
if (remote_url == "https://github.com/shadps4-emu/shadPS4.git" ||
|
std::string remote_host;
|
||||||
remote_url.length() == 0) {
|
try {
|
||||||
|
remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
|
||||||
|
} catch (...) {
|
||||||
|
remote_host = "unknown";
|
||||||
|
}
|
||||||
|
if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
|
||||||
window_title = fmt::format("shadPS4 v{} {} {} | {}", Common::VERSION,
|
window_title = fmt::format("shadPS4 v{} {} {} | {}", Common::VERSION,
|
||||||
Common::g_scm_branch, Common::g_scm_desc, game_title);
|
Common::g_scm_branch, Common::g_scm_desc, game_title);
|
||||||
} else {
|
} else {
|
||||||
std::string remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
|
|
||||||
window_title = fmt::format("shadPS4 v{} {}/{} {} | {}", Common::VERSION, remote_host,
|
window_title = fmt::format("shadPS4 v{} {}/{} {} | {}", Common::VERSION, remote_host,
|
||||||
Common::g_scm_branch, Common::g_scm_desc, game_title);
|
Common::g_scm_branch, Common::g_scm_desc, game_title);
|
||||||
}
|
}
|
||||||
|
|
BIN
src/images/ps4_controller.png
Normal file
BIN
src/images/ps4_controller.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 92 KiB |
498
src/qt_gui/control_settings.cpp
Normal file
498
src/qt_gui/control_settings.cpp
Normal file
|
@ -0,0 +1,498 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include "common/path_util.h"
|
||||||
|
#include "control_settings.h"
|
||||||
|
#include "kbm_config_dialog.h"
|
||||||
|
#include "ui_control_settings.h"
|
||||||
|
|
||||||
|
ControlSettings::ControlSettings(std::shared_ptr<GameInfoClass> game_info_get, QWidget* parent)
|
||||||
|
: QDialog(parent), m_game_info(game_info_get), ui(new Ui::ControlSettings) {
|
||||||
|
|
||||||
|
ui->setupUi(this);
|
||||||
|
ui->PerGameCheckBox->setChecked(!Config::GetUseUnifiedInputConfig());
|
||||||
|
|
||||||
|
AddBoxItems();
|
||||||
|
SetUIValuestoMappings();
|
||||||
|
|
||||||
|
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, [this](QAbstractButton* button) {
|
||||||
|
if (button == ui->buttonBox->button(QDialogButtonBox::Save)) {
|
||||||
|
SaveControllerConfig(true);
|
||||||
|
} else if (button == ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)) {
|
||||||
|
SetDefault();
|
||||||
|
} else if (button == ui->buttonBox->button(QDialogButtonBox::Apply)) {
|
||||||
|
SaveControllerConfig(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close);
|
||||||
|
connect(ui->KBMButton, &QPushButton::clicked, this, [this] {
|
||||||
|
auto KBMWindow = new EditorDialog(this);
|
||||||
|
KBMWindow->exec();
|
||||||
|
});
|
||||||
|
connect(ui->ProfileComboBox, &QComboBox::currentTextChanged, this, [this] {
|
||||||
|
GetGameTitle();
|
||||||
|
SetUIValuestoMappings();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->LeftDeadzoneSlider, &QSlider::valueChanged, this,
|
||||||
|
[this](int value) { ui->LeftDeadzoneValue->setText(QString::number(value)); });
|
||||||
|
connect(ui->RightDeadzoneSlider, &QSlider::valueChanged, this,
|
||||||
|
[this](int value) { ui->RightDeadzoneValue->setText(QString::number(value)); });
|
||||||
|
|
||||||
|
connect(ui->LStickUpBox, &QComboBox::currentIndexChanged, this,
|
||||||
|
[this](int value) { ui->LStickDownBox->setCurrentIndex(value); });
|
||||||
|
connect(ui->LStickDownBox, &QComboBox::currentIndexChanged, this,
|
||||||
|
[this](int value) { ui->LStickUpBox->setCurrentIndex(value); });
|
||||||
|
connect(ui->LStickRightBox, &QComboBox::currentIndexChanged, this,
|
||||||
|
[this](int value) { ui->LStickLeftBox->setCurrentIndex(value); });
|
||||||
|
connect(ui->LStickLeftBox, &QComboBox::currentIndexChanged, this,
|
||||||
|
[this](int value) { ui->LStickRightBox->setCurrentIndex(value); });
|
||||||
|
|
||||||
|
connect(ui->RStickUpBox, &QComboBox::currentIndexChanged, this,
|
||||||
|
[this](int value) { ui->RStickDownBox->setCurrentIndex(value); });
|
||||||
|
connect(ui->RStickDownBox, &QComboBox::currentIndexChanged, this,
|
||||||
|
[this](int value) { ui->RStickUpBox->setCurrentIndex(value); });
|
||||||
|
connect(ui->RStickRightBox, &QComboBox::currentIndexChanged, this,
|
||||||
|
[this](int value) { ui->RStickLeftBox->setCurrentIndex(value); });
|
||||||
|
connect(ui->RStickLeftBox, &QComboBox::currentIndexChanged, this,
|
||||||
|
[this](int value) { ui->RStickRightBox->setCurrentIndex(value); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlSettings::SaveControllerConfig(bool CloseOnSave) {
|
||||||
|
QList<QComboBox*> list;
|
||||||
|
list << ui->RStickUpBox << ui->RStickRightBox << ui->LStickUpBox << ui->LStickRightBox;
|
||||||
|
int count_axis_left_x = 0, count_axis_left_y = 0, count_axis_right_x = 0,
|
||||||
|
count_axis_right_y = 0;
|
||||||
|
for (const auto& i : list) {
|
||||||
|
if (i->currentText() == "axis_left_x") {
|
||||||
|
count_axis_left_x = count_axis_left_x + 1;
|
||||||
|
} else if (i->currentText() == "axis_left_y") {
|
||||||
|
count_axis_left_y = count_axis_left_y + 1;
|
||||||
|
} else if (i->currentText() == "axis_right_x") {
|
||||||
|
count_axis_right_x = count_axis_right_x + 1;
|
||||||
|
} else if (i->currentText() == "axis_right_y") {
|
||||||
|
count_axis_right_y = count_axis_right_y + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count_axis_left_x > 1 | count_axis_left_y > 1 | count_axis_right_x > 1 |
|
||||||
|
count_axis_right_y > 1) {
|
||||||
|
QMessageBox::StandardButton nosave;
|
||||||
|
nosave = QMessageBox::information(this, "Unable to Save",
|
||||||
|
"Cannot bind axis values more than once");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string config_id;
|
||||||
|
config_id = (ui->ProfileComboBox->currentText() == "Common Config")
|
||||||
|
? "default"
|
||||||
|
: ui->ProfileComboBox->currentText().toStdString();
|
||||||
|
const auto config_file = Config::GetFoolproofKbmConfigFile(config_id);
|
||||||
|
|
||||||
|
int lineCount = 0;
|
||||||
|
std::string line;
|
||||||
|
std::vector<std::string> lines;
|
||||||
|
std::string output_string = "", input_string = "";
|
||||||
|
std::fstream file(config_file);
|
||||||
|
|
||||||
|
while (std::getline(file, line)) {
|
||||||
|
lineCount++;
|
||||||
|
|
||||||
|
std::size_t comment_pos = line.find('#');
|
||||||
|
if (comment_pos != std::string::npos) {
|
||||||
|
if (!line.contains("Range of deadzones"))
|
||||||
|
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") {
|
||||||
|
line.erase();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
lines.push_back(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
input_string = "cross";
|
||||||
|
output_string = ui->ABox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
input_string = "circle";
|
||||||
|
output_string = ui->BBox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
input_string = "square";
|
||||||
|
output_string = ui->XBox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
input_string = "triangle";
|
||||||
|
output_string = ui->YBox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
lines.push_back("");
|
||||||
|
|
||||||
|
input_string = "l1";
|
||||||
|
output_string = ui->LBBox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
input_string = "r1";
|
||||||
|
output_string = ui->RBBox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
input_string = "l2";
|
||||||
|
output_string = ui->LTBox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
input_string = "r2";
|
||||||
|
output_string = ui->RTBox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
input_string = "l3";
|
||||||
|
output_string = ui->LClickBox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
input_string = "r3";
|
||||||
|
output_string = ui->RClickBox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
lines.push_back("");
|
||||||
|
|
||||||
|
input_string = "back";
|
||||||
|
output_string = ui->BackBox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
input_string = "options";
|
||||||
|
output_string = ui->StartBox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
lines.push_back("");
|
||||||
|
|
||||||
|
input_string = "pad_up";
|
||||||
|
output_string = ui->DpadUpBox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
input_string = "pad_down";
|
||||||
|
output_string = ui->DpadDownBox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
input_string = "pad_left";
|
||||||
|
output_string = ui->DpadLeftBox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
input_string = "pad_right";
|
||||||
|
output_string = ui->DpadRightBox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
lines.push_back("");
|
||||||
|
|
||||||
|
input_string = "axis_left_x";
|
||||||
|
output_string = ui->LStickRightBox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
input_string = "axis_left_y";
|
||||||
|
output_string = ui->LStickUpBox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
input_string = "axis_right_x";
|
||||||
|
output_string = ui->RStickRightBox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
input_string = "axis_right_y";
|
||||||
|
output_string = ui->RStickUpBox->currentText().toStdString();
|
||||||
|
lines.push_back(output_string + " = " + input_string);
|
||||||
|
|
||||||
|
lines.push_back("");
|
||||||
|
lines.push_back("# Range of deadzones: 1 (almost none) to 127 (max)");
|
||||||
|
|
||||||
|
std::string deadzonevalue = std::to_string(ui->LeftDeadzoneSlider->value());
|
||||||
|
lines.push_back("analog_deadzone = leftjoystick, " + deadzonevalue);
|
||||||
|
|
||||||
|
deadzonevalue = std::to_string(ui->RightDeadzoneSlider->value());
|
||||||
|
lines.push_back("analog_deadzone = rightjoystick, " + deadzonevalue);
|
||||||
|
|
||||||
|
std::vector<std::string> 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 ControlSettings::SetDefault() {
|
||||||
|
ui->ABox->setCurrentIndex(0);
|
||||||
|
ui->BBox->setCurrentIndex(1);
|
||||||
|
ui->XBox->setCurrentIndex(2);
|
||||||
|
ui->YBox->setCurrentIndex(3);
|
||||||
|
ui->DpadUpBox->setCurrentIndex(11);
|
||||||
|
ui->DpadDownBox->setCurrentIndex(12);
|
||||||
|
ui->DpadLeftBox->setCurrentIndex(13);
|
||||||
|
ui->DpadRightBox->setCurrentIndex(14);
|
||||||
|
ui->LClickBox->setCurrentIndex(8);
|
||||||
|
ui->RClickBox->setCurrentIndex(9);
|
||||||
|
ui->LBBox->setCurrentIndex(4);
|
||||||
|
ui->RBBox->setCurrentIndex(5);
|
||||||
|
ui->LTBox->setCurrentIndex(6);
|
||||||
|
ui->RTBox->setCurrentIndex(7);
|
||||||
|
ui->StartBox->setCurrentIndex(10);
|
||||||
|
ui->BackBox->setCurrentIndex(15);
|
||||||
|
|
||||||
|
ui->LStickUpBox->setCurrentIndex(1);
|
||||||
|
ui->LStickDownBox->setCurrentIndex(1);
|
||||||
|
ui->LStickLeftBox->setCurrentIndex(0);
|
||||||
|
ui->LStickRightBox->setCurrentIndex(0);
|
||||||
|
ui->RStickUpBox->setCurrentIndex(3);
|
||||||
|
ui->RStickDownBox->setCurrentIndex(3);
|
||||||
|
ui->RStickLeftBox->setCurrentIndex(2);
|
||||||
|
ui->RStickRightBox->setCurrentIndex(2);
|
||||||
|
|
||||||
|
ui->LeftDeadzoneSlider->setValue(2);
|
||||||
|
ui->RightDeadzoneSlider->setValue(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlSettings::AddBoxItems() {
|
||||||
|
ui->DpadUpBox->addItems(ButtonOutputs);
|
||||||
|
ui->DpadDownBox->addItems(ButtonOutputs);
|
||||||
|
ui->DpadLeftBox->addItems(ButtonOutputs);
|
||||||
|
ui->DpadRightBox->addItems(ButtonOutputs);
|
||||||
|
ui->LBBox->addItems(ButtonOutputs);
|
||||||
|
ui->RBBox->addItems(ButtonOutputs);
|
||||||
|
ui->LTBox->addItems(ButtonOutputs);
|
||||||
|
ui->RTBox->addItems(ButtonOutputs);
|
||||||
|
ui->RClickBox->addItems(ButtonOutputs);
|
||||||
|
ui->LClickBox->addItems(ButtonOutputs);
|
||||||
|
ui->StartBox->addItems(ButtonOutputs);
|
||||||
|
ui->ABox->addItems(ButtonOutputs);
|
||||||
|
ui->BBox->addItems(ButtonOutputs);
|
||||||
|
ui->XBox->addItems(ButtonOutputs);
|
||||||
|
ui->YBox->addItems(ButtonOutputs);
|
||||||
|
ui->BackBox->addItems(ButtonOutputs);
|
||||||
|
|
||||||
|
ui->LStickUpBox->addItems(StickOutputs);
|
||||||
|
ui->LStickDownBox->addItems(StickOutputs);
|
||||||
|
ui->LStickLeftBox->addItems(StickOutputs);
|
||||||
|
ui->LStickRightBox->addItems(StickOutputs);
|
||||||
|
ui->RStickUpBox->addItems(StickOutputs);
|
||||||
|
ui->RStickDownBox->addItems(StickOutputs);
|
||||||
|
ui->RStickLeftBox->addItems(StickOutputs);
|
||||||
|
ui->RStickRightBox->addItems(StickOutputs);
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
ui->ProfileComboBox->setCurrentText("Common Config");
|
||||||
|
ui->TitleLabel->setText("Common Config");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlSettings::SetUIValuestoMappings() {
|
||||||
|
std::string config_id;
|
||||||
|
config_id = (ui->ProfileComboBox->currentText() == "Common Config")
|
||||||
|
? "default"
|
||||||
|
: ui->ProfileComboBox->currentText().toStdString();
|
||||||
|
|
||||||
|
const auto config_file = Config::GetFoolproofKbmConfigFile(config_id);
|
||||||
|
std::ifstream file(config_file);
|
||||||
|
|
||||||
|
bool CrossExists = false, CircleExists = false, SquareExists = false, TriangleExists = false,
|
||||||
|
L1Exists = false, L2Exists = false, L3Exists = false, R1Exists = false, R2Exists = false,
|
||||||
|
R3Exists = false, DPadUpExists = false, DPadDownExists = false, DPadLeftExists = false,
|
||||||
|
DPadRightExists = false, StartExists = false, BackExists = false, LStickXExists = false,
|
||||||
|
LStickYExists = false, RStickXExists = false, RStickYExists = false;
|
||||||
|
int lineCount = 0;
|
||||||
|
std::string line = "";
|
||||||
|
while (std::getline(file, line)) {
|
||||||
|
lineCount++;
|
||||||
|
|
||||||
|
line.erase(std::remove(line.begin(), line.end(), ' '), line.end());
|
||||||
|
if (line.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
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);
|
||||||
|
std::string input_string = line.substr(equal_pos + 1);
|
||||||
|
|
||||||
|
if (std::find(ControllerInputs.begin(), ControllerInputs.end(), input_string) !=
|
||||||
|
ControllerInputs.end() ||
|
||||||
|
output_string == "analog_deadzone") {
|
||||||
|
if (input_string == "cross") {
|
||||||
|
ui->ABox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
CrossExists = true;
|
||||||
|
} else if (input_string == "circle") {
|
||||||
|
ui->BBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
CircleExists = true;
|
||||||
|
} else if (input_string == "square") {
|
||||||
|
ui->XBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
SquareExists = true;
|
||||||
|
} else if (input_string == "triangle") {
|
||||||
|
ui->YBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
TriangleExists = true;
|
||||||
|
} else if (input_string == "l1") {
|
||||||
|
ui->LBBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
L1Exists = true;
|
||||||
|
} else if (input_string == "l2") {
|
||||||
|
ui->LTBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
L2Exists = true;
|
||||||
|
} else if (input_string == "r1") {
|
||||||
|
ui->RBBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
R1Exists = true;
|
||||||
|
} else if (input_string == "r2") {
|
||||||
|
ui->RTBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
R2Exists = true;
|
||||||
|
} else if (input_string == "l3") {
|
||||||
|
ui->LClickBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
L3Exists = true;
|
||||||
|
} else if (input_string == "r3") {
|
||||||
|
ui->RClickBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
R3Exists = true;
|
||||||
|
} else if (input_string == "pad_up") {
|
||||||
|
ui->DpadUpBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
DPadUpExists = true;
|
||||||
|
} else if (input_string == "pad_down") {
|
||||||
|
ui->DpadDownBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
DPadDownExists = true;
|
||||||
|
} else if (input_string == "pad_left") {
|
||||||
|
ui->DpadLeftBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
DPadLeftExists = true;
|
||||||
|
} else if (input_string == "pad_right") {
|
||||||
|
ui->DpadRightBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
DPadRightExists = true;
|
||||||
|
} else if (input_string == "options") {
|
||||||
|
ui->StartBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
StartExists = true;
|
||||||
|
} else if (input_string == "back") {
|
||||||
|
ui->BackBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
BackExists = true;
|
||||||
|
} else if (input_string == "axis_left_x") {
|
||||||
|
ui->LStickRightBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
ui->LStickLeftBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
LStickXExists = true;
|
||||||
|
} else if (input_string == "axis_left_y") {
|
||||||
|
ui->LStickUpBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
ui->LStickDownBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
LStickYExists = true;
|
||||||
|
} else if (input_string == "axis_right_x") {
|
||||||
|
ui->RStickRightBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
ui->RStickLeftBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
RStickXExists = true;
|
||||||
|
} else if (input_string == "axis_right_y") {
|
||||||
|
ui->RStickUpBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
ui->RStickDownBox->setCurrentText(QString::fromStdString(output_string));
|
||||||
|
RStickYExists = true;
|
||||||
|
} else if (input_string.contains("leftjoystick")) {
|
||||||
|
std::size_t comma_pos = line.find(',');
|
||||||
|
int deadzonevalue = std::stoi(line.substr(comma_pos + 1));
|
||||||
|
ui->LeftDeadzoneSlider->setValue(deadzonevalue);
|
||||||
|
ui->LeftDeadzoneValue->setText(QString::number(deadzonevalue));
|
||||||
|
} else if (input_string.contains("rightjoystick")) {
|
||||||
|
std::size_t comma_pos = line.find(',');
|
||||||
|
int deadzonevalue = std::stoi(line.substr(comma_pos + 1));
|
||||||
|
ui->RightDeadzoneSlider->setValue(deadzonevalue);
|
||||||
|
ui->RightDeadzoneValue->setText(QString::number(deadzonevalue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an entry does not exist in the config file, we assume the user wants it unmapped
|
||||||
|
if (!CrossExists)
|
||||||
|
ui->ABox->setCurrentText("unmapped");
|
||||||
|
if (!CircleExists)
|
||||||
|
ui->BBox->setCurrentText("unmapped");
|
||||||
|
if (!SquareExists)
|
||||||
|
ui->XBox->setCurrentText("unmapped");
|
||||||
|
if (!TriangleExists)
|
||||||
|
ui->YBox->setCurrentText("unmapped");
|
||||||
|
if (!L1Exists)
|
||||||
|
ui->LBBox->setCurrentText("unmapped");
|
||||||
|
if (!L2Exists)
|
||||||
|
ui->LTBox->setCurrentText("unmapped");
|
||||||
|
if (!L3Exists)
|
||||||
|
ui->LClickBox->setCurrentText("unmapped");
|
||||||
|
if (!R1Exists)
|
||||||
|
ui->RBBox->setCurrentText("unmapped");
|
||||||
|
if (!R2Exists)
|
||||||
|
ui->RTBox->setCurrentText("unmapped");
|
||||||
|
if (!R3Exists)
|
||||||
|
ui->RClickBox->setCurrentText("unmapped");
|
||||||
|
if (!DPadUpExists)
|
||||||
|
ui->DpadUpBox->setCurrentText("unmapped");
|
||||||
|
if (!DPadDownExists)
|
||||||
|
ui->DpadDownBox->setCurrentText("unmapped");
|
||||||
|
if (!DPadLeftExists)
|
||||||
|
ui->DpadLeftBox->setCurrentText("unmapped");
|
||||||
|
if (!DPadRightExists)
|
||||||
|
ui->DpadRightBox->setCurrentText("unmapped");
|
||||||
|
if (!BackExists)
|
||||||
|
ui->BackBox->setCurrentText("unmapped");
|
||||||
|
if (!StartExists)
|
||||||
|
ui->StartBox->setCurrentText("unmapped");
|
||||||
|
|
||||||
|
if (!LStickXExists) {
|
||||||
|
ui->LStickRightBox->setCurrentText("unmapped");
|
||||||
|
ui->LStickLeftBox->setCurrentText("unmapped");
|
||||||
|
}
|
||||||
|
if (!LStickYExists) {
|
||||||
|
ui->LStickUpBox->setCurrentText("unmapped");
|
||||||
|
ui->LStickDownBox->setCurrentText("unmapped");
|
||||||
|
}
|
||||||
|
if (!RStickXExists) {
|
||||||
|
ui->RStickRightBox->setCurrentText("unmapped");
|
||||||
|
ui->RStickLeftBox->setCurrentText("unmapped");
|
||||||
|
}
|
||||||
|
if (!RStickYExists) {
|
||||||
|
ui->RStickUpBox->setCurrentText("unmapped");
|
||||||
|
ui->RStickDownBox->setCurrentText("unmapped");
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlSettings::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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlSettings::~ControlSettings() {}
|
52
src/qt_gui/control_settings.h
Normal file
52
src/qt_gui/control_settings.h
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include "game_info.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class ControlSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ControlSettings : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ControlSettings(std::shared_ptr<GameInfoClass> game_info_get,
|
||||||
|
QWidget* parent = nullptr);
|
||||||
|
~ControlSettings();
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void SaveControllerConfig(bool CloseOnSave);
|
||||||
|
void SetDefault();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Ui::ControlSettings> ui;
|
||||||
|
std::shared_ptr<GameInfoClass> m_game_info;
|
||||||
|
|
||||||
|
void AddBoxItems();
|
||||||
|
void SetUIValuestoMappings();
|
||||||
|
void GetGameTitle();
|
||||||
|
|
||||||
|
const std::vector<std::string> 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"};
|
||||||
|
|
||||||
|
const QStringList ButtonOutputs = {"cross", "circle", "square", "triangle", "l1",
|
||||||
|
"r1", "l2", "r2", "l3",
|
||||||
|
|
||||||
|
"r3", "options", "pad_up",
|
||||||
|
|
||||||
|
"pad_down",
|
||||||
|
|
||||||
|
"pad_left", "pad_right", "touchpad", "unmapped"};
|
||||||
|
|
||||||
|
const QStringList StickOutputs = {"axis_left_x", "axis_left_y", "axis_right_x", "axis_right_y",
|
||||||
|
"unmapped"};
|
||||||
|
};
|
1379
src/qt_gui/control_settings.ui
Normal file
1379
src/qt_gui/control_settings.ui
Normal file
File diff suppressed because it is too large
Load diff
|
@ -38,17 +38,34 @@ GameGridFrame::GameGridFrame(std::shared_ptr<GameInfoClass> game_info_get,
|
||||||
|
|
||||||
void GameGridFrame::onCurrentCellChanged(int currentRow, int currentColumn, int previousRow,
|
void GameGridFrame::onCurrentCellChanged(int currentRow, int currentColumn, int previousRow,
|
||||||
int previousColumn) {
|
int previousColumn) {
|
||||||
crtRow = currentRow;
|
// Early exit for invalid indices
|
||||||
crtColumn = currentColumn;
|
if (currentRow < 0 || currentColumn < 0) {
|
||||||
columnCnt = this->columnCount();
|
|
||||||
|
|
||||||
auto itemID = (crtRow * columnCnt) + currentColumn;
|
|
||||||
if (itemID > m_game_info->m_games.count() - 1) {
|
|
||||||
cellClicked = false;
|
cellClicked = false;
|
||||||
validCellSelected = false;
|
validCellSelected = false;
|
||||||
BackgroundMusicPlayer::getInstance().stopMusic();
|
BackgroundMusicPlayer::getInstance().stopMusic();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
crtRow = currentRow;
|
||||||
|
crtColumn = currentColumn;
|
||||||
|
columnCnt = this->columnCount();
|
||||||
|
|
||||||
|
// Prevent integer overflow
|
||||||
|
if (columnCnt <= 0 || crtRow > (std::numeric_limits<int>::max() / columnCnt)) {
|
||||||
|
cellClicked = false;
|
||||||
|
validCellSelected = false;
|
||||||
|
BackgroundMusicPlayer::getInstance().stopMusic();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto itemID = (crtRow * columnCnt) + currentColumn;
|
||||||
|
if (itemID < 0 || itemID > m_game_info->m_games.count() - 1) {
|
||||||
|
cellClicked = false;
|
||||||
|
validCellSelected = false;
|
||||||
|
BackgroundMusicPlayer::getInstance().stopMusic();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
cellClicked = true;
|
cellClicked = true;
|
||||||
validCellSelected = true;
|
validCellSelected = true;
|
||||||
SetGridBackgroundImage(crtRow, crtColumn);
|
SetGridBackgroundImage(crtRow, crtColumn);
|
||||||
|
@ -65,6 +82,8 @@ void GameGridFrame::PlayBackgroundMusic(QString path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameGridFrame::PopulateGameGrid(QVector<GameInfo> m_games_search, bool fromSearch) {
|
void GameGridFrame::PopulateGameGrid(QVector<GameInfo> m_games_search, bool fromSearch) {
|
||||||
|
this->crtRow = -1;
|
||||||
|
this->crtColumn = -1;
|
||||||
QVector<GameInfo> m_games_;
|
QVector<GameInfo> m_games_;
|
||||||
this->clearContents();
|
this->clearContents();
|
||||||
if (fromSearch)
|
if (fromSearch)
|
||||||
|
@ -136,43 +155,48 @@ void GameGridFrame::PopulateGameGrid(QVector<GameInfo> m_games_search, bool from
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameGridFrame::SetGridBackgroundImage(int row, int column) {
|
void GameGridFrame::SetGridBackgroundImage(int row, int column) {
|
||||||
|
|
||||||
int itemID = (row * this->columnCount()) + column;
|
int itemID = (row * this->columnCount()) + column;
|
||||||
QWidget* item = this->cellWidget(row, column);
|
QWidget* item = this->cellWidget(row, column);
|
||||||
if (item) {
|
if (!item) {
|
||||||
QString pic1Path;
|
// handle case where no item was clicked
|
||||||
Common::FS::PathToQString(pic1Path, (*m_games_shared)[itemID].pic_path);
|
return;
|
||||||
const auto blurredPic1Path = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
|
|
||||||
(*m_games_shared)[itemID].serial / "pic1.png";
|
|
||||||
QString blurredPic1PathQt;
|
|
||||||
Common::FS::PathToQString(blurredPic1PathQt, blurredPic1Path);
|
|
||||||
|
|
||||||
backgroundImage = QImage(blurredPic1PathQt);
|
|
||||||
if (backgroundImage.isNull()) {
|
|
||||||
QImage image(pic1Path);
|
|
||||||
backgroundImage = m_game_list_utils.BlurImage(image, image.rect(), 16);
|
|
||||||
|
|
||||||
std::filesystem::path img_path =
|
|
||||||
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
|
|
||||||
(*m_games_shared)[itemID].serial;
|
|
||||||
std::filesystem::create_directories(img_path);
|
|
||||||
if (!backgroundImage.save(blurredPic1PathQt, "PNG")) {
|
|
||||||
// qDebug() << "Error: Unable to save image.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RefreshGridBackgroundImage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If background images are hidden, clear the background image
|
||||||
|
if (!Config::getShowBackgroundImage()) {
|
||||||
|
backgroundImage = QImage();
|
||||||
|
m_last_opacity = -1; // Reset opacity tracking when disabled
|
||||||
|
m_current_game_path.clear(); // Reset current game path
|
||||||
|
RefreshGridBackgroundImage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& game = (*m_games_shared)[itemID];
|
||||||
|
const int opacity = Config::getBackgroundImageOpacity();
|
||||||
|
|
||||||
|
// Recompute if opacity changed or we switched to a different game
|
||||||
|
if (opacity != m_last_opacity || game.pic_path != m_current_game_path) {
|
||||||
|
QImage original_image(QString::fromStdString(game.pic_path.string()));
|
||||||
|
if (!original_image.isNull()) {
|
||||||
|
backgroundImage = m_game_list_utils.ChangeImageOpacity(
|
||||||
|
original_image, original_image.rect(), opacity / 100.0f);
|
||||||
|
m_last_opacity = opacity;
|
||||||
|
m_current_game_path = game.pic_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshGridBackgroundImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameGridFrame::RefreshGridBackgroundImage() {
|
void GameGridFrame::RefreshGridBackgroundImage() {
|
||||||
if (!backgroundImage.isNull()) {
|
QPalette palette;
|
||||||
QPalette palette;
|
if (!backgroundImage.isNull() && Config::getShowBackgroundImage()) {
|
||||||
palette.setBrush(QPalette::Base,
|
palette.setBrush(QPalette::Base,
|
||||||
QBrush(backgroundImage.scaled(size(), Qt::IgnoreAspectRatio)));
|
QBrush(backgroundImage.scaled(size(), Qt::IgnoreAspectRatio)));
|
||||||
QColor transparentColor = QColor(135, 206, 235, 40);
|
|
||||||
palette.setColor(QPalette::Highlight, transparentColor);
|
|
||||||
this->setPalette(palette);
|
|
||||||
}
|
}
|
||||||
|
QColor transparentColor = QColor(135, 206, 235, 40);
|
||||||
|
palette.setColor(QPalette::Highlight, transparentColor);
|
||||||
|
this->setPalette(palette);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameGridFrame::IsValidCellSelected() {
|
bool GameGridFrame::IsValidCellSelected() {
|
||||||
|
|
|
@ -33,6 +33,8 @@ private:
|
||||||
std::shared_ptr<CompatibilityInfoClass> m_compat_info;
|
std::shared_ptr<CompatibilityInfoClass> m_compat_info;
|
||||||
std::shared_ptr<QVector<GameInfo>> m_games_shared;
|
std::shared_ptr<QVector<GameInfo>> m_games_shared;
|
||||||
bool validCellSelected = false;
|
bool validCellSelected = false;
|
||||||
|
int m_last_opacity = -1; // Track last opacity to avoid unnecessary recomputation
|
||||||
|
std::filesystem::path m_current_game_path; // Track current game path to detect changes
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit GameGridFrame(std::shared_ptr<GameInfoClass> game_info_get,
|
explicit GameGridFrame(std::shared_ptr<GameInfoClass> game_info_get,
|
||||||
|
|
|
@ -19,7 +19,10 @@ public:
|
||||||
QVector<GameInfo> m_games;
|
QVector<GameInfo> m_games;
|
||||||
|
|
||||||
static bool CompareStrings(GameInfo& a, GameInfo& b) {
|
static bool CompareStrings(GameInfo& a, GameInfo& b) {
|
||||||
return a.name < b.name;
|
std::string name_a = a.name, name_b = b.name;
|
||||||
|
std::transform(name_a.begin(), name_a.end(), name_a.begin(), ::tolower);
|
||||||
|
std::transform(name_b.begin(), name_b.end(), name_b.begin(), ::tolower);
|
||||||
|
return name_a < name_b;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GameInfo readGameInfo(const std::filesystem::path& filePath) {
|
static GameInfo readGameInfo(const std::filesystem::path& filePath) {
|
||||||
|
@ -72,4 +75,4 @@ public:
|
||||||
}
|
}
|
||||||
return game;
|
return game;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -89,6 +89,7 @@ void GameListFrame::onCurrentCellChanged(int currentRow, int currentColumn, int
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
m_current_item = item; // Store current item
|
||||||
SetListBackgroundImage(item);
|
SetListBackgroundImage(item);
|
||||||
PlayBackgroundMusic(item);
|
PlayBackgroundMusic(item);
|
||||||
}
|
}
|
||||||
|
@ -104,6 +105,7 @@ void GameListFrame::PlayBackgroundMusic(QTableWidgetItem* item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameListFrame::PopulateGameList(bool isInitialPopulation) {
|
void GameListFrame::PopulateGameList(bool isInitialPopulation) {
|
||||||
|
this->m_current_item = nullptr;
|
||||||
// Do not show status column if it is not enabled
|
// Do not show status column if it is not enabled
|
||||||
this->setColumnHidden(2, !Config::getCompatibilityEnabled());
|
this->setColumnHidden(2, !Config::getCompatibilityEnabled());
|
||||||
this->setColumnHidden(6, !Config::GetLoadGameSizeEnabled());
|
this->setColumnHidden(6, !Config::GetLoadGameSizeEnabled());
|
||||||
|
@ -167,38 +169,41 @@ void GameListFrame::SetListBackgroundImage(QTableWidgetItem* item) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString pic1Path;
|
// If background images are hidden, clear the background image
|
||||||
Common::FS::PathToQString(pic1Path, m_game_info->m_games[item->row()].pic_path);
|
if (!Config::getShowBackgroundImage()) {
|
||||||
const auto blurredPic1Path = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
|
backgroundImage = QImage();
|
||||||
m_game_info->m_games[item->row()].serial / "pic1.png";
|
m_last_opacity = -1; // Reset opacity tracking when disabled
|
||||||
QString blurredPic1PathQt;
|
m_current_game_path.clear(); // Reset current game path
|
||||||
Common::FS::PathToQString(blurredPic1PathQt, blurredPic1Path);
|
RefreshListBackgroundImage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
backgroundImage = QImage(blurredPic1PathQt);
|
const auto& game = m_game_info->m_games[item->row()];
|
||||||
if (backgroundImage.isNull()) {
|
const int opacity = Config::getBackgroundImageOpacity();
|
||||||
QImage image(pic1Path);
|
|
||||||
backgroundImage = m_game_list_utils.BlurImage(image, image.rect(), 16);
|
|
||||||
|
|
||||||
std::filesystem::path img_path =
|
// Recompute if opacity changed or we switched to a different game
|
||||||
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
|
if (opacity != m_last_opacity || game.pic_path != m_current_game_path) {
|
||||||
m_game_info->m_games[item->row()].serial;
|
QImage original_image(QString::fromStdString(game.pic_path.string()));
|
||||||
std::filesystem::create_directories(img_path);
|
if (!original_image.isNull()) {
|
||||||
if (!backgroundImage.save(blurredPic1PathQt, "PNG")) {
|
backgroundImage = m_game_list_utils.ChangeImageOpacity(
|
||||||
// qDebug() << "Error: Unable to save image.";
|
original_image, original_image.rect(), opacity / 100.0f);
|
||||||
|
m_last_opacity = opacity;
|
||||||
|
m_current_game_path = game.pic_path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RefreshListBackgroundImage();
|
RefreshListBackgroundImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameListFrame::RefreshListBackgroundImage() {
|
void GameListFrame::RefreshListBackgroundImage() {
|
||||||
if (!backgroundImage.isNull()) {
|
QPalette palette;
|
||||||
QPalette palette;
|
if (!backgroundImage.isNull() && Config::getShowBackgroundImage()) {
|
||||||
palette.setBrush(QPalette::Base,
|
palette.setBrush(QPalette::Base,
|
||||||
QBrush(backgroundImage.scaled(size(), Qt::IgnoreAspectRatio)));
|
QBrush(backgroundImage.scaled(size(), Qt::IgnoreAspectRatio)));
|
||||||
QColor transparentColor = QColor(135, 206, 235, 40);
|
|
||||||
palette.setColor(QPalette::Highlight, transparentColor);
|
|
||||||
this->setPalette(palette);
|
|
||||||
}
|
}
|
||||||
|
QColor transparentColor = QColor(135, 206, 235, 40);
|
||||||
|
palette.setColor(QPalette::Highlight, transparentColor);
|
||||||
|
this->setPalette(palette);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameListFrame::SortNameAscending(int columnIndex) {
|
void GameListFrame::SortNameAscending(int columnIndex) {
|
||||||
|
@ -392,3 +397,7 @@ QString GameListFrame::GetPlayTime(const std::string& serial) {
|
||||||
file.close();
|
file.close();
|
||||||
return playTime;
|
return playTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QTableWidgetItem* GameListFrame::GetCurrentItem() {
|
||||||
|
return m_current_item;
|
||||||
|
}
|
|
@ -44,11 +44,14 @@ private:
|
||||||
QList<QAction*> m_columnActs;
|
QList<QAction*> m_columnActs;
|
||||||
GameInfoClass* game_inf_get = nullptr;
|
GameInfoClass* game_inf_get = nullptr;
|
||||||
bool ListSortedAsc = true;
|
bool ListSortedAsc = true;
|
||||||
|
QTableWidgetItem* m_current_item = nullptr;
|
||||||
|
int m_last_opacity = -1; // Track last opacity to avoid unnecessary recomputation
|
||||||
|
std::filesystem::path m_current_game_path; // Track current game path to detect changes
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void PopulateGameList(bool isInitialPopulation = true);
|
void PopulateGameList(bool isInitialPopulation = true);
|
||||||
void ResizeIcons(int iconSize);
|
void ResizeIcons(int iconSize);
|
||||||
|
QTableWidgetItem* GetCurrentItem();
|
||||||
QImage backgroundImage;
|
QImage backgroundImage;
|
||||||
GameListUtils m_game_list_utils;
|
GameListUtils m_game_list_utils;
|
||||||
GuiContextMenus m_gui_context_menus;
|
GuiContextMenus m_gui_context_menus;
|
||||||
|
|
|
@ -201,4 +201,30 @@ public:
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Opacity is a float between 0 and 1
|
||||||
|
static QImage ChangeImageOpacity(const QImage& image, const QRect& rect, float opacity) {
|
||||||
|
// Convert to ARGB32 format to ensure alpha channel support
|
||||||
|
QImage result = image.convertToFormat(QImage::Format_ARGB32);
|
||||||
|
|
||||||
|
// Ensure opacity is between 0 and 1
|
||||||
|
opacity = std::clamp(opacity, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
// Convert opacity to integer alpha value (0-255)
|
||||||
|
int alpha = static_cast<int>(opacity * 255);
|
||||||
|
|
||||||
|
// Process only the specified rectangle area
|
||||||
|
for (int y = rect.top(); y <= rect.bottom(); ++y) {
|
||||||
|
QRgb* line = reinterpret_cast<QRgb*>(result.scanLine(y));
|
||||||
|
for (int x = rect.left(); x <= rect.right(); ++x) {
|
||||||
|
// Get current pixel
|
||||||
|
QRgb pixel = line[x];
|
||||||
|
// Keep RGB values, but modify alpha while preserving relative transparency
|
||||||
|
int newAlpha = (qAlpha(pixel) * alpha) / 255;
|
||||||
|
line[x] = qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), newAlpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -77,10 +77,14 @@ public:
|
||||||
QMenu* copyMenu = new QMenu(tr("Copy info..."), widget);
|
QMenu* copyMenu = new QMenu(tr("Copy info..."), widget);
|
||||||
QAction* copyName = new QAction(tr("Copy Name"), widget);
|
QAction* copyName = new QAction(tr("Copy Name"), widget);
|
||||||
QAction* copySerial = new QAction(tr("Copy Serial"), widget);
|
QAction* copySerial = new QAction(tr("Copy Serial"), widget);
|
||||||
|
QAction* copyVersion = new QAction(tr("Copy Version"), widget);
|
||||||
|
QAction* copySize = new QAction(tr("Copy Size"), widget);
|
||||||
QAction* copyNameAll = new QAction(tr("Copy All"), widget);
|
QAction* copyNameAll = new QAction(tr("Copy All"), widget);
|
||||||
|
|
||||||
copyMenu->addAction(copyName);
|
copyMenu->addAction(copyName);
|
||||||
copyMenu->addAction(copySerial);
|
copyMenu->addAction(copySerial);
|
||||||
|
copyMenu->addAction(copyVersion);
|
||||||
|
copyMenu->addAction(copySize);
|
||||||
copyMenu->addAction(copyNameAll);
|
copyMenu->addAction(copyNameAll);
|
||||||
|
|
||||||
menu.addMenu(copyMenu);
|
menu.addMenu(copyMenu);
|
||||||
|
@ -346,6 +350,16 @@ public:
|
||||||
clipboard->setText(QString::fromStdString(m_games[itemID].serial));
|
clipboard->setText(QString::fromStdString(m_games[itemID].serial));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selected == copyVersion) {
|
||||||
|
QClipboard* clipboard = QGuiApplication::clipboard();
|
||||||
|
clipboard->setText(QString::fromStdString(m_games[itemID].version));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selected == copySize) {
|
||||||
|
QClipboard* clipboard = QGuiApplication::clipboard();
|
||||||
|
clipboard->setText(QString::fromStdString(m_games[itemID].size));
|
||||||
|
}
|
||||||
|
|
||||||
if (selected == copyNameAll) {
|
if (selected == copyNameAll) {
|
||||||
QClipboard* clipboard = QGuiApplication::clipboard();
|
QClipboard* clipboard = QGuiApplication::clipboard();
|
||||||
QString combinedText = QString("Name:%1 | Serial:%2 | Version:%3 | Size:%4")
|
QString combinedText = QString("Name:%1 | Serial:%2 | Version:%3 | Size:%4")
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "common/scm_rev.h"
|
#include "common/scm_rev.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "common/version.h"
|
#include "common/version.h"
|
||||||
|
#include "control_settings.h"
|
||||||
#include "core/file_format/pkg.h"
|
#include "core/file_format/pkg.h"
|
||||||
#include "core/loader.h"
|
#include "core/loader.h"
|
||||||
#include "game_install_dialog.h"
|
#include "game_install_dialog.h"
|
||||||
|
@ -62,12 +63,16 @@ bool MainWindow::Init() {
|
||||||
window_title = fmt::format("shadPS4 v{}", Common::VERSION);
|
window_title = fmt::format("shadPS4 v{}", Common::VERSION);
|
||||||
} else {
|
} else {
|
||||||
std::string remote_url(Common::g_scm_remote_url);
|
std::string remote_url(Common::g_scm_remote_url);
|
||||||
if (remote_url == "https://github.com/shadps4-emu/shadPS4.git" ||
|
std::string remote_host;
|
||||||
remote_url.length() == 0) {
|
try {
|
||||||
|
remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
|
||||||
|
} catch (...) {
|
||||||
|
remote_host = "unknown";
|
||||||
|
}
|
||||||
|
if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
|
||||||
window_title = fmt::format("shadPS4 v{} {} {}", Common::VERSION, Common::g_scm_branch,
|
window_title = fmt::format("shadPS4 v{} {} {}", Common::VERSION, Common::g_scm_branch,
|
||||||
Common::g_scm_desc);
|
Common::g_scm_desc);
|
||||||
} else {
|
} else {
|
||||||
std::string remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
|
|
||||||
window_title = fmt::format("shadPS4 v{} {}/{} {}", Common::VERSION, remote_host,
|
window_title = fmt::format("shadPS4 v{} {}/{} {}", Common::VERSION, remote_host,
|
||||||
Common::g_scm_branch, Common::g_scm_desc);
|
Common::g_scm_branch, Common::g_scm_desc);
|
||||||
}
|
}
|
||||||
|
@ -292,13 +297,30 @@ void MainWindow::CreateConnects() {
|
||||||
connect(settingsDialog, &SettingsDialog::CompatibilityChanged, this,
|
connect(settingsDialog, &SettingsDialog::CompatibilityChanged, this,
|
||||||
&MainWindow::RefreshGameTable);
|
&MainWindow::RefreshGameTable);
|
||||||
|
|
||||||
|
connect(settingsDialog, &SettingsDialog::BackgroundOpacityChanged, this,
|
||||||
|
[this](int opacity) {
|
||||||
|
Config::setBackgroundImageOpacity(opacity);
|
||||||
|
if (m_game_list_frame) {
|
||||||
|
QTableWidgetItem* current = m_game_list_frame->GetCurrentItem();
|
||||||
|
if (current) {
|
||||||
|
m_game_list_frame->SetListBackgroundImage(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_game_grid_frame) {
|
||||||
|
if (m_game_grid_frame->IsValidCellSelected()) {
|
||||||
|
m_game_grid_frame->SetGridBackgroundImage(m_game_grid_frame->crtRow,
|
||||||
|
m_game_grid_frame->crtColumn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
settingsDialog->exec();
|
settingsDialog->exec();
|
||||||
});
|
});
|
||||||
|
|
||||||
// this is the editor for kbm keybinds
|
// this is the editor for kbm keybinds
|
||||||
connect(ui->controllerButton, &QPushButton::clicked, this, [this]() {
|
connect(ui->controllerButton, &QPushButton::clicked, this, [this]() {
|
||||||
EditorDialog* editorWindow = new EditorDialog(this);
|
auto configWindow = new ControlSettings(m_game_info, this);
|
||||||
editorWindow->exec(); // Show the editor window modally
|
configWindow->exec();
|
||||||
});
|
});
|
||||||
|
|
||||||
#ifdef ENABLE_UPDATER
|
#ifdef ENABLE_UPDATER
|
||||||
|
|
|
@ -72,7 +72,7 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
|
||||||
ui->buttonBox->button(QDialogButtonBox::StandardButton::Close)->setFocus();
|
ui->buttonBox->button(QDialogButtonBox::StandardButton::Close)->setFocus();
|
||||||
|
|
||||||
// Add list of available GPUs
|
// Add list of available GPUs
|
||||||
ui->graphicsAdapterBox->addItem("Auto Select"); // -1, auto selection
|
ui->graphicsAdapterBox->addItem(tr("Auto Select")); // -1, auto selection
|
||||||
for (const auto& device : physical_devices) {
|
for (const auto& device : physical_devices) {
|
||||||
ui->graphicsAdapterBox->addItem(device);
|
ui->graphicsAdapterBox->addItem(device);
|
||||||
}
|
}
|
||||||
|
@ -173,6 +173,9 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
|
||||||
{
|
{
|
||||||
connect(ui->chooseHomeTabComboBox, &QComboBox::currentTextChanged, this,
|
connect(ui->chooseHomeTabComboBox, &QComboBox::currentTextChanged, this,
|
||||||
[](const QString& hometab) { Config::setChooseHomeTab(hometab.toStdString()); });
|
[](const QString& hometab) { Config::setChooseHomeTab(hometab.toStdString()); });
|
||||||
|
|
||||||
|
connect(ui->showBackgroundImageCheckBox, &QCheckBox::stateChanged, this,
|
||||||
|
[](int state) { Config::setShowBackgroundImage(state == Qt::Checked); });
|
||||||
}
|
}
|
||||||
// Input TAB
|
// Input TAB
|
||||||
{
|
{
|
||||||
|
@ -251,6 +254,7 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
|
||||||
#ifdef ENABLE_UPDATER
|
#ifdef ENABLE_UPDATER
|
||||||
ui->updaterGroupBox->installEventFilter(this);
|
ui->updaterGroupBox->installEventFilter(this);
|
||||||
#endif
|
#endif
|
||||||
|
ui->GUIBackgroundImageGroupBox->installEventFilter(this);
|
||||||
ui->GUIMusicGroupBox->installEventFilter(this);
|
ui->GUIMusicGroupBox->installEventFilter(this);
|
||||||
ui->disableTrophycheckBox->installEventFilter(this);
|
ui->disableTrophycheckBox->installEventFilter(this);
|
||||||
ui->enableCompatibilityCheckBox->installEventFilter(this);
|
ui->enableCompatibilityCheckBox->installEventFilter(this);
|
||||||
|
@ -410,6 +414,8 @@ void SettingsDialog::LoadValuesFromConfig() {
|
||||||
|
|
||||||
ui->removeFolderButton->setEnabled(!ui->gameFoldersListWidget->selectedItems().isEmpty());
|
ui->removeFolderButton->setEnabled(!ui->gameFoldersListWidget->selectedItems().isEmpty());
|
||||||
ResetInstallFolders();
|
ResetInstallFolders();
|
||||||
|
ui->backgroundImageOpacitySlider->setValue(Config::getBackgroundImageOpacity());
|
||||||
|
ui->showBackgroundImageCheckBox->setChecked(Config::getShowBackgroundImage());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::InitializeEmulatorLanguages() {
|
void SettingsDialog::InitializeEmulatorLanguages() {
|
||||||
|
@ -504,6 +510,8 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) {
|
||||||
} else if (elementName == "updaterGroupBox") {
|
} else if (elementName == "updaterGroupBox") {
|
||||||
text = tr("updaterGroupBox");
|
text = tr("updaterGroupBox");
|
||||||
#endif
|
#endif
|
||||||
|
} else if (elementName == "GUIBackgroundImageGroupBox") {
|
||||||
|
text = tr("GUIBackgroundImageGroupBox");
|
||||||
} else if (elementName == "GUIMusicGroupBox") {
|
} else if (elementName == "GUIMusicGroupBox") {
|
||||||
text = tr("GUIMusicGroupBox");
|
text = tr("GUIMusicGroupBox");
|
||||||
} else if (elementName == "disableTrophycheckBox") {
|
} else if (elementName == "disableTrophycheckBox") {
|
||||||
|
@ -638,6 +646,9 @@ void SettingsDialog::UpdateSettings() {
|
||||||
Config::setChooseHomeTab(ui->chooseHomeTabComboBox->currentText().toStdString());
|
Config::setChooseHomeTab(ui->chooseHomeTabComboBox->currentText().toStdString());
|
||||||
Config::setCompatibilityEnabled(ui->enableCompatibilityCheckBox->isChecked());
|
Config::setCompatibilityEnabled(ui->enableCompatibilityCheckBox->isChecked());
|
||||||
Config::setCheckCompatibilityOnStartup(ui->checkCompatibilityOnStartupCheckBox->isChecked());
|
Config::setCheckCompatibilityOnStartup(ui->checkCompatibilityOnStartupCheckBox->isChecked());
|
||||||
|
Config::setBackgroundImageOpacity(ui->backgroundImageOpacitySlider->value());
|
||||||
|
emit BackgroundOpacityChanged(ui->backgroundImageOpacitySlider->value());
|
||||||
|
Config::setShowBackgroundImage(ui->showBackgroundImageCheckBox->isChecked());
|
||||||
|
|
||||||
#ifdef ENABLE_DISCORD_RPC
|
#ifdef ENABLE_DISCORD_RPC
|
||||||
auto* rpc = Common::Singleton<DiscordRPCHandler::RPC>::Instance();
|
auto* rpc = Common::Singleton<DiscordRPCHandler::RPC>::Instance();
|
||||||
|
|
|
@ -33,6 +33,7 @@ public:
|
||||||
signals:
|
signals:
|
||||||
void LanguageChanged(const std::string& locale);
|
void LanguageChanged(const std::string& locale);
|
||||||
void CompatibilityChanged();
|
void CompatibilityChanged();
|
||||||
|
void BackgroundOpacityChanged(int opacity);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void LoadValuesFromConfig();
|
void LoadValuesFromConfig();
|
||||||
|
|
|
@ -583,6 +583,76 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="GUIBackgroundImageGroupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Background Image</string>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="backgroundImageVLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="showBackgroundImageCheckBox">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Show Background Image</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="opacityLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="backgroundImageOpacityLabel">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Opacity</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSlider" name="backgroundImageOpacitySlider">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>50</number>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="GUIMusicGroupBox">
|
<widget class="QGroupBox" name="GUIMusicGroupBox">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
|
|
@ -128,6 +128,14 @@
|
||||||
<source>Copy Serial</source>
|
<source>Copy Serial</source>
|
||||||
<translation>Seriennummer kopieren</translation>
|
<translation>Seriennummer kopieren</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Copy Version</source>
|
||||||
|
<translation>Version kopieren</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Copy Size</source>
|
||||||
|
<translation>Größe kopieren</translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Copy All</source>
|
<source>Copy All</source>
|
||||||
<translation>Alles kopieren</translation>
|
<translation>Alles kopieren</translation>
|
||||||
|
@ -1421,4 +1429,31 @@
|
||||||
<translation>TB</translation>
|
<translation>TB</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>CompatibilityInfoClass</name>
|
||||||
|
<message>
|
||||||
|
<source>Unknown</source>
|
||||||
|
<translation>Unbekannt</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Nothing</source>
|
||||||
|
<translation>Nichts</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Boots</source>
|
||||||
|
<translation>Startet</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Menus</source>
|
||||||
|
<translation>Menüs</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Ingame</source>
|
||||||
|
<translation>ImSpiel</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Playable</source>
|
||||||
|
<translation>Spielbar</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|
|
@ -124,6 +124,14 @@
|
||||||
<source>Copy Serial</source>
|
<source>Copy Serial</source>
|
||||||
<translation>Copy Serial</translation>
|
<translation>Copy Serial</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Copy Version</source>
|
||||||
|
<translation>Copy Version</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Copy Size</source>
|
||||||
|
<translation>Copy Size</translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Copy All</source>
|
<source>Copy All</source>
|
||||||
<translation>Copy All</translation>
|
<translation>Copy All</translation>
|
||||||
|
@ -749,6 +757,18 @@
|
||||||
<source>Disable Trophy Pop-ups</source>
|
<source>Disable Trophy Pop-ups</source>
|
||||||
<translation>Disable Trophy Pop-ups</translation>
|
<translation>Disable Trophy Pop-ups</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Background Image</source>
|
||||||
|
<translation>Background Image</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Show Background Image</source>
|
||||||
|
<translation>Show Background Image</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Opacity</source>
|
||||||
|
<translation>Opacity</translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Play title music</source>
|
<source>Play title music</source>
|
||||||
<translation>Play title music</translation>
|
<translation>Play title music</translation>
|
||||||
|
@ -845,6 +865,10 @@
|
||||||
<source>updaterGroupBox</source>
|
<source>updaterGroupBox</source>
|
||||||
<translation>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.</translation>
|
<translation>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.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>GUIBackgroundImageGroupBox</source>
|
||||||
|
<translation>Background Image:\nControl the opacity of the game background image.</translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>GUIMusicGroupBox</source>
|
<source>GUIMusicGroupBox</source>
|
||||||
<translation>Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI.</translation>
|
<translation>Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI.</translation>
|
||||||
|
|
|
@ -748,6 +748,18 @@
|
||||||
<source>Disable Trophy Pop-ups</source>
|
<source>Disable Trophy Pop-ups</source>
|
||||||
<translation>Disable Trophy Pop-ups</translation>
|
<translation>Disable Trophy Pop-ups</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Background Image</source>
|
||||||
|
<translation>Imagen de fondo</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Show Background Image</source>
|
||||||
|
<translation>Mostrar Imagen de Fondo</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Opacity</source>
|
||||||
|
<translation>Opacidad</translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Play title music</source>
|
<source>Play title music</source>
|
||||||
<translation>Reproducir la música de apertura</translation>
|
<translation>Reproducir la música de apertura</translation>
|
||||||
|
@ -844,6 +856,10 @@
|
||||||
<source>updaterGroupBox</source>
|
<source>updaterGroupBox</source>
|
||||||
<translation>Actualización:\nRelease: Versiones oficiales lanzadas cada mes que pueden estar muy desactualizadas, pero son más confiables y están probadas.\nNightly: Versiones de desarrollo que tienen todas las últimas funciones y correcciones, pero pueden contener errores y son menos estables.</translation>
|
<translation>Actualización:\nRelease: Versiones oficiales lanzadas cada mes que pueden estar muy desactualizadas, pero son más confiables y están probadas.\nNightly: Versiones de desarrollo que tienen todas las últimas funciones y correcciones, pero pueden contener errores y son menos estables.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>GUIBackgroundImageGroupBox</source>
|
||||||
|
<translation>Imagen de fondo:\nControle la opacidad de la imagen de fondo del juego.</translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>GUIMusicGroupBox</source>
|
<source>GUIMusicGroupBox</source>
|
||||||
<translation>Reproducir Música del Título:\nSi un juego lo admite, habilita la reproducción de música especial al seleccionar el juego en la interfaz gráfica.</translation>
|
<translation>Reproducir Música del Título:\nSi un juego lo admite, habilita la reproducción de música especial al seleccionar el juego en la interfaz gráfica.</translation>
|
||||||
|
|
|
@ -742,7 +742,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Title Music</source>
|
<source>Title Music</source>
|
||||||
<translation>Title Music</translation>
|
<translation>Musique du titre</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Disable Trophy Pop-ups</source>
|
<source>Disable Trophy Pop-ups</source>
|
||||||
|
@ -958,7 +958,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>crashDiagnosticsCheckBox</source>
|
<source>crashDiagnosticsCheckBox</source>
|
||||||
<translation>Diagnostic de crash:\nCrée un fichier .yaml avec des informations sur l'état de Vulkan au moment du crash.\nUtile pour déboguer les erreurs "Device lost". Si cette option est activée, vous devez aussi activer Marqueur de débogage hôte ET invité.\nNe marche pas pour les GPUs Intel.\nVous devez activer le Vulkan Validation Layers ainsi que le Vulkan SDK pour que cela fonctionne.</translation>
|
<translation>Diagnostic de crash:\nCrée un fichier .yaml avec des informations sur l'état de Vulkan au moment du crash.\nUtile pour déboguer les erreurs "Device lost". Si cette option est activée, vous devez aussi activer Marqueur de débogage hôte ET invité.\nNe marche pas pour les GPUs Intel.\nVous devez activer la couche de validation Vulkan ainsi que le Vulkan SDK pour que cela fonctionne.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>copyGPUBuffersCheckBox</source>
|
<source>copyGPUBuffersCheckBox</source>
|
||||||
|
@ -1405,4 +1405,31 @@
|
||||||
<translation>TB</translation>
|
<translation>TB</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>CompatibilityInfoClass</name>
|
||||||
|
<message>
|
||||||
|
<source>Unknown</source>
|
||||||
|
<translation>Inconnu</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Nothing</source>
|
||||||
|
<translation>Rien</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Boots</source>
|
||||||
|
<translation>Démarre</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Menus</source>
|
||||||
|
<translation>Menu</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Ingame</source>
|
||||||
|
<translation>En jeu</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Playable</source>
|
||||||
|
<translation>Jouable</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!DOCTYPE TS>
|
<!DOCTYPE TS>
|
||||||
<TS version="2.1" language="nb">
|
<TS version="2.1" language="nb">
|
||||||
<!-- SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
<!-- SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||||
SPDX-License-Identifier: GPL-2.0-or-later -->
|
SPDX-License-Identifier: GPL-2.0-or-later -->
|
||||||
<context>
|
<context>
|
||||||
<name>AboutDialog</name>
|
<name>AboutDialog</name>
|
||||||
|
@ -1298,6 +1298,14 @@
|
||||||
<source>Game can be completed with playable performance and no major glitches</source>
|
<source>Game can be completed with playable performance and no major glitches</source>
|
||||||
<translation>Spillet kan fullføres med spillbar ytelse og uten store feil</translation>
|
<translation>Spillet kan fullføres med spillbar ytelse og uten store feil</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Click to go to issue</source>
|
||||||
|
<translation>Klikk for å gå til rapporten</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Last updated</source>
|
||||||
|
<translation>Sist oppdatert</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>CheckUpdate</name>
|
<name>CheckUpdate</name>
|
||||||
|
@ -1425,4 +1433,43 @@
|
||||||
<translation>TB</translation>
|
<translation>TB</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>CompatibilityInfoClass</name>
|
||||||
|
<message>
|
||||||
|
<source>Unknown</source>
|
||||||
|
<translation>Ukjent</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Nothing</source>
|
||||||
|
<translation>Ingenting</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Boots</source>
|
||||||
|
<translation>Starter opp</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Menus</source>
|
||||||
|
<translation>Meny</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Ingame</source>
|
||||||
|
<translation>I spill</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Playable</source>
|
||||||
|
<translation>Spillbar</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Fetching compatibility data, please wait</source>
|
||||||
|
<translation>Henter kompatibilitetsdata, vennligst vent</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cancel</source>
|
||||||
|
<translation>Avbryt</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Loading...</source>
|
||||||
|
<translation>Laster...</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -52,7 +52,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Select which directory you want to install to.</source>
|
<source>Select which directory you want to install to.</source>
|
||||||
<translation>选择你想要安装到的目录。</translation>
|
<translation>选择您想要安装到的目录。</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -186,7 +186,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>requiresEnableSeparateUpdateFolder_MSG</source>
|
<source>requiresEnableSeparateUpdateFolder_MSG</source>
|
||||||
<translation>这个功能需要“启用单独的更新目录”配置选项才能正常运行,如果你想要使用这个功能,请启用它。</translation>
|
<translation>这个功能需要“启用单独的更新目录”配置选项才能正常运行,如果您想要使用这个功能,请启用它。</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>This game has no update to delete!</source>
|
<source>This game has no update to delete!</source>
|
||||||
|
@ -210,7 +210,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Are you sure you want to delete %1's %2 directory?</source>
|
<source>Are you sure you want to delete %1's %2 directory?</source>
|
||||||
<translation>你确定要删除 %1 的%2目录?</translation>
|
<translation>您确定要删除 %1 的%2目录?</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -702,24 +702,25 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Enable Crash Diagnostics</source>
|
<source>Enable Crash Diagnostics</source>
|
||||||
<translation>Enable Crash Diagnostics</translation>
|
<translation>启用崩溃诊断</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Collect Shaders</source>
|
<source>Collect Shaders</source>
|
||||||
<translation>Collect Shaders</translation>
|
<translation>收集着色器</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Copy GPU Buffers</source>
|
<source>Copy GPU Buffers</source>
|
||||||
<translation>Copy GPU Buffers</translation>
|
<translation>复制 GPU 缓冲区</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Host Debug Markers</source>
|
<source>Host Debug Markers</source>
|
||||||
<translation>Host Debug Markers</translation>
|
<translation>Host 调试标记</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Guest Debug Markers</source>
|
<source>Guest Debug Markers</source>
|
||||||
<translation>Guest Debug Markers</translation>
|
<translation>Geust 调试标记</translation>
|
||||||
</message>
|
</message>
|
||||||
|
|
||||||
<message>
|
<message>
|
||||||
<source>Update</source>
|
<source>Update</source>
|
||||||
<translation>更新</translation>
|
<translation>更新</translation>
|
||||||
|
@ -954,23 +955,23 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>collectShaderCheckBox</source>
|
<source>collectShaderCheckBox</source>
|
||||||
<translation>Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10).</translation>
|
<translation>收集着色器:\n您需要启用此功能才能使用调试菜单(Ctrl + F10)编辑着色器。</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>crashDiagnosticsCheckBox</source>
|
<source>crashDiagnosticsCheckBox</source>
|
||||||
<translation>Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work.</translation>
|
<translation>崩溃诊断:\n创建一个包含崩溃时 Vulkan 状态的 .yaml 文件。\n对于调试“Device lost”错误很有用。如果您启用了此功能,您应该同时启用 Host 和 Guest 调试标记。\n此功能在 Intel 显卡上不可用。\n您需要启用 Vulkan 验证层并安装 Vulkan SDK 才能使用此功能。</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>copyGPUBuffersCheckBox</source>
|
<source>copyGPUBuffersCheckBox</source>
|
||||||
<translation>Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes.</translation>
|
<translation>复制 GPU 缓冲区:\n绕过涉及 GPU 提交的竞态条件。\n对于 PM4 type 0 崩溃可能有帮助,也可能没有帮助。</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>hostMarkersCheckBox</source>
|
<source>hostMarkersCheckBox</source>
|
||||||
<translation>Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc.</translation>
|
<translation>Host 调试标记:\n在 Vulkan 命令周围插入模拟器端信息,如特定 AMD GPU 命令的标记,以及为资源提供调试名称。\n如果您已启用此功能,应同时启用崩溃诊断。\n对 RenderDoc 等程序很有用。</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>guestMarkersCheckBox</source>
|
<source>guestMarkersCheckBox</source>
|
||||||
<translation>Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc.</translation>
|
<translation>Guest 调试标记:\n在命令缓冲区中插入游戏本身添加的任何调试标记。\n如果您已启用此功能,应同时启用崩溃诊断。\n对 RenderDoc 等程序很有用。</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>saveDataBox</source>
|
<source>saveDataBox</source>
|
||||||
|
@ -1284,7 +1285,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Game can be completed with playable performance and no major glitches</source>
|
<source>Game can be completed with playable performance and no major glitches</source>
|
||||||
<translation>游戏能在可玩的性能下完成且没有重大 Bug</translation>
|
<translation>游戏能在可玩的性能下通关且没有重大 Bug</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -1413,4 +1414,31 @@
|
||||||
<translation>TB</translation>
|
<translation>TB</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>CompatibilityInfoClass</name>
|
||||||
|
<message>
|
||||||
|
<source>Unknown</source>
|
||||||
|
<translation>未知</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Nothing</source>
|
||||||
|
<translation>无法启动</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Boots</source>
|
||||||
|
<translation>可启动</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Menus</source>
|
||||||
|
<translation>可进入菜单</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Ingame</source>
|
||||||
|
<translation>可进入游戏内</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Playable</source>
|
||||||
|
<translation>可通关</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|
|
@ -148,41 +148,46 @@ void CFG::EmitDivergenceLabels() {
|
||||||
const size_t end_index = GetIndex(end);
|
const size_t end_index = GetIndex(end);
|
||||||
|
|
||||||
s32 curr_begin = -1;
|
s32 curr_begin = -1;
|
||||||
|
s32 last_exec_idx = -1;
|
||||||
for (size_t index = GetIndex(start); index < end_index; index++) {
|
for (size_t index = GetIndex(start); index < end_index; index++) {
|
||||||
const auto& inst = inst_list[index];
|
const auto& inst = inst_list[index];
|
||||||
const bool is_close = is_close_scope(inst);
|
if (curr_begin != -1) {
|
||||||
if ((is_close || index == end_index - 1) && curr_begin != -1) {
|
// Keep note of the last instruction that does not ignore exec, so we know where
|
||||||
// If there are no instructions inside scope don't do anything.
|
// to end the divergence block without impacting trailing instructions that do.
|
||||||
if (index - curr_begin == 1) {
|
if (!IgnoresExecMask(inst)) {
|
||||||
curr_begin = -1;
|
last_exec_idx = index;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
// If all instructions in the scope ignore exec masking, we shouldn't insert a
|
// Consider a close scope on certain instruction types or at the last instruction
|
||||||
// scope.
|
// before the next label.
|
||||||
const auto start = inst_list.begin() + curr_begin + 1;
|
if (is_close_scope(inst) || index == end_index - 1) {
|
||||||
if (!std::ranges::all_of(start, inst_list.begin() + index, IgnoresExecMask)) {
|
// Only insert a scope if, since the open-scope instruction, there is at least
|
||||||
// Add a label to the instruction right after the open scope call.
|
// one instruction that does not ignore exec.
|
||||||
// It is the start of a new basic block.
|
if (index - curr_begin > 1 && last_exec_idx != -1) {
|
||||||
const auto& save_inst = inst_list[curr_begin];
|
// Add a label to the instruction right after the open scope call.
|
||||||
const Label label = index_to_pc[curr_begin] + save_inst.length;
|
// It is the start of a new basic block.
|
||||||
AddLabel(label);
|
const auto& save_inst = inst_list[curr_begin];
|
||||||
// Add a label to the close scope instruction.
|
AddLabel(index_to_pc[curr_begin] + save_inst.length);
|
||||||
// There are 3 cases where we need to close a scope.
|
// Add a label to the close scope instruction.
|
||||||
// * Close scope instruction inside the block
|
// There are 3 cases where we need to close a scope.
|
||||||
// * Close scope instruction at the end of the block (cbranch or endpgm)
|
// * Close scope instruction inside the block
|
||||||
// * Normal instruction at the end of the block
|
// * Close scope instruction at the end of the block (cbranch or endpgm)
|
||||||
// For the last case we must NOT add a label as that would cause
|
// * Normal instruction at the end of the block
|
||||||
// the instruction to be separated into its own basic block.
|
// If the instruction we want to close the scope at is at the end of the
|
||||||
if (is_close) {
|
// block, we do not need to insert a new label.
|
||||||
AddLabel(index_to_pc[index]);
|
if (last_exec_idx != end_index - 1) {
|
||||||
|
// Add the label after the last instruction affected by exec.
|
||||||
|
const auto& last_exec_inst = inst_list[last_exec_idx];
|
||||||
|
AddLabel(index_to_pc[last_exec_idx] + last_exec_inst.length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// Reset scope begin.
|
||||||
|
curr_begin = -1;
|
||||||
}
|
}
|
||||||
// Reset scope begin.
|
|
||||||
curr_begin = -1;
|
|
||||||
}
|
}
|
||||||
// Mark a potential start of an exec scope.
|
// Mark a potential start of an exec scope.
|
||||||
if (is_open_scope(inst)) {
|
if (is_open_scope(inst)) {
|
||||||
curr_begin = index;
|
curr_begin = index;
|
||||||
|
last_exec_idx = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,11 @@ u32 SwizzleMrtComponent(const FragmentRuntimeInfo::PsColorBuffer& color_buffer,
|
||||||
|
|
||||||
void Translator::ExportMrtValue(IR::Attribute attribute, u32 comp, const IR::F32& value,
|
void Translator::ExportMrtValue(IR::Attribute attribute, u32 comp, const IR::F32& value,
|
||||||
const FragmentRuntimeInfo::PsColorBuffer& color_buffer) {
|
const FragmentRuntimeInfo::PsColorBuffer& color_buffer) {
|
||||||
const auto converted = ApplyWriteNumberConversion(ir, value, color_buffer.num_conversion);
|
auto converted = ApplyWriteNumberConversion(ir, value, color_buffer.num_conversion);
|
||||||
|
if (color_buffer.needs_unorm_fixup) {
|
||||||
|
// FIXME: Fix-up for GPUs where float-to-unorm rounding is off from expected.
|
||||||
|
converted = ir.FPSub(converted, ir.Imm32(1.f / 127500.f));
|
||||||
|
}
|
||||||
ir.SetAttribute(attribute, converted, comp);
|
ir.SetAttribute(attribute, converted, comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -219,7 +219,7 @@ void FlattenExtendedUserdataPass(IR::Program& program) {
|
||||||
};
|
};
|
||||||
auto base0 = IR::BreadthFirstSearch(ptr_composite->Arg(0), pred);
|
auto base0 = IR::BreadthFirstSearch(ptr_composite->Arg(0), pred);
|
||||||
auto base1 = IR::BreadthFirstSearch(ptr_composite->Arg(1), pred);
|
auto base1 = IR::BreadthFirstSearch(ptr_composite->Arg(1), pred);
|
||||||
ASSERT_MSG(base0 && base1 && "ReadConst not from constant memory");
|
ASSERT_MSG(base0 && base1, "ReadConst not from constant memory");
|
||||||
|
|
||||||
IR::Inst* ptr_lo = base0.value();
|
IR::Inst* ptr_lo = base0.value();
|
||||||
ptr_lo = pass_info.DeduplicateInstruction(ptr_lo);
|
ptr_lo = pass_info.DeduplicateInstruction(ptr_lo);
|
||||||
|
@ -250,4 +250,4 @@ void FlattenExtendedUserdataPass(IR::Program& program) {
|
||||||
info.RefreshFlatBuf();
|
info.RefreshFlatBuf();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Shader::Optimization
|
} // namespace Shader::Optimization
|
||||||
|
|
|
@ -9,9 +9,10 @@ namespace Shader::IR {
|
||||||
|
|
||||||
std::string NameOf(Type type) {
|
std::string NameOf(Type type) {
|
||||||
static constexpr std::array names{
|
static constexpr std::array names{
|
||||||
"Opaque", "Label", "Reg", "Pred", "Attribute", "U1", "U8", "U16", "U32",
|
"Opaque", "ScalarReg", "VectorReg", "Attribute", "Patch", "U1", "U8",
|
||||||
"U64", "F16", "F32", "F64", "U32x2", "U32x3", "U32x4", "F16x2", "F16x3",
|
"U16", "U32", "U64", "F16", "F32", "F64", "U32x2",
|
||||||
"F16x4", "F32x2", "F32x3", "F32x4", "F64x2", "F64x3", "F64x4", "StringLiteral"};
|
"U32x3", "U32x4", "F16x2", "F16x3", "F16x4", "F32x2", "F32x3",
|
||||||
|
"F32x4", "F64x2", "F64x3", "F64x4", "StringLiteral"};
|
||||||
const size_t bits{static_cast<size_t>(type)};
|
const size_t bits{static_cast<size_t>(type)};
|
||||||
if (bits == 0) {
|
if (bits == 0) {
|
||||||
return "Void";
|
return "Void";
|
||||||
|
|
|
@ -185,6 +185,7 @@ struct FragmentRuntimeInfo {
|
||||||
AmdGpu::NumberConversion num_conversion;
|
AmdGpu::NumberConversion num_conversion;
|
||||||
AmdGpu::CompMapping swizzle;
|
AmdGpu::CompMapping swizzle;
|
||||||
AmdGpu::Liverpool::ShaderExportFormat export_format;
|
AmdGpu::Liverpool::ShaderExportFormat export_format;
|
||||||
|
bool needs_unorm_fixup;
|
||||||
|
|
||||||
auto operator<=>(const PsColorBuffer&) const noexcept = default;
|
auto operator<=>(const PsColorBuffer&) const noexcept = default;
|
||||||
};
|
};
|
||||||
|
|
|
@ -30,5 +30,6 @@
|
||||||
<file>images/ko-fi.png</file>
|
<file>images/ko-fi.png</file>
|
||||||
<file>images/youtube.png</file>
|
<file>images/youtube.png</file>
|
||||||
<file>images/website.png</file>
|
<file>images/website.png</file>
|
||||||
|
<file>images/ps4_controller.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -330,6 +330,16 @@ bool PipelineCache::RefreshGraphicsKey() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Metal seems to have an issue where 8-bit unorm/snorm/sRGB outputs to render target
|
||||||
|
// need a bias applied to round correctly; detect and set the flag for that here.
|
||||||
|
const auto needs_unorm_fixup = instance.GetDriverID() == vk::DriverId::eMoltenvk &&
|
||||||
|
(col_buf.GetNumberFmt() == AmdGpu::NumberFormat::Unorm ||
|
||||||
|
col_buf.GetNumberFmt() == AmdGpu::NumberFormat::Snorm ||
|
||||||
|
col_buf.GetNumberFmt() == AmdGpu::NumberFormat::Srgb) &&
|
||||||
|
(col_buf.GetDataFmt() == AmdGpu::DataFormat::Format8 ||
|
||||||
|
col_buf.GetDataFmt() == AmdGpu::DataFormat::Format8_8 ||
|
||||||
|
col_buf.GetDataFmt() == AmdGpu::DataFormat::Format8_8_8_8);
|
||||||
|
|
||||||
key.color_formats[remapped_cb] =
|
key.color_formats[remapped_cb] =
|
||||||
LiverpoolToVK::SurfaceFormat(col_buf.GetDataFmt(), col_buf.GetNumberFmt());
|
LiverpoolToVK::SurfaceFormat(col_buf.GetDataFmt(), col_buf.GetNumberFmt());
|
||||||
key.color_buffers[remapped_cb] = {
|
key.color_buffers[remapped_cb] = {
|
||||||
|
@ -337,6 +347,7 @@ bool PipelineCache::RefreshGraphicsKey() {
|
||||||
.num_conversion = col_buf.GetNumberConversion(),
|
.num_conversion = col_buf.GetNumberConversion(),
|
||||||
.swizzle = col_buf.Swizzle(),
|
.swizzle = col_buf.Swizzle(),
|
||||||
.export_format = regs.color_export_format.GetFormat(cb),
|
.export_format = regs.color_export_format.GetFormat(cb),
|
||||||
|
.needs_unorm_fixup = needs_unorm_fixup,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue