From 53c7daa603c3c4984147c3c0955a58ad4ba6424c Mon Sep 17 00:00:00 2001 From: emiyl Date: Sun, 25 Sep 2022 20:38:31 +0100 Subject: [PATCH 001/616] [docs] Add brew installation instructions for macos (#303) --- BUILD.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index f4d7c72e..f180b221 100644 --- a/BUILD.md +++ b/BUILD.md @@ -65,7 +65,10 @@ To compile Cemu, a recent enough compiler and STL with C++20 support is required below, built in LLVM, and Xcode LLVM don't support the C++20 feature set required. Currently, LLVM 15 isn't supported due to compatibility issues with Boost dependency. The OpenGL graphics API isn't support on MacOS, Vulkan must be used. Additionally Vulkan must be used through the -Molten-VK compatibility layer. +Molten-VK compatibility layer. + +### Installing brew +`/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"` ### Installing dependencies `brew install git cmake llvm@14 ninja nasm molten-vk` From 25dae98ce015c18ff1ae62a19e63cf12d2ae7c9a Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Sun, 25 Sep 2022 22:53:10 -0700 Subject: [PATCH 002/616] Fix crash on GTK when a gfx pack preset value is changed (#300) --- src/gui/GraphicPacksWindow2.cpp | 34 ++++++++++++++++++++++++++++++++- src/gui/GraphicPacksWindow2.h | 1 + 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/gui/GraphicPacksWindow2.cpp b/src/gui/GraphicPacksWindow2.cpp index 3759580e..bab55156 100644 --- a/src/gui/GraphicPacksWindow2.cpp +++ b/src/gui/GraphicPacksWindow2.cpp @@ -529,6 +529,38 @@ void GraphicPacksWindow2::OnTreeChoiceChanged(wxTreeEvent& event) m_graphic_pack_tree->SelectItem(item); } +// In some environments with GTK (e.g. a flatpak app with org.freedesktop.Platform 22.08 runtime), +// destroying the event source inside the handler crashes the app. +// As a workaround to that, the wxWindow that needs to be destroyed is hidden and then +// destroyed at a later time, outside the handler. +void GraphicPacksWindow2::ClearPresets() +{ + size_t item_count = m_preset_sizer->GetItemCount(); + std::vector sizers; + sizers.reserve(item_count); + for (size_t i = 0; i < item_count; i++) + sizers.push_back(m_preset_sizer->GetItem(i)->GetSizer()); + + for (auto&& sizer : sizers) + { + auto static_box_sizer = dynamic_cast(sizer); + if (static_box_sizer) + { + wxStaticBox* parent_window = static_box_sizer->GetStaticBox(); + if (parent_window) + { + m_preset_sizer->Detach(sizer); + parent_window->Hide(); + CallAfter([=]() + { + parent_window->DestroyChildren(); + delete static_box_sizer; + }); + } + } + } +} + void GraphicPacksWindow2::OnActivePresetChanged(wxCommandEvent& event) { if (!m_shown_graphic_pack) @@ -542,7 +574,7 @@ void GraphicPacksWindow2::OnActivePresetChanged(wxCommandEvent& event) if(m_shown_graphic_pack->SetActivePreset(string_data->GetData().c_str().AsChar(), preset)) { wxWindowUpdateLocker lock(this); - m_preset_sizer->Clear(true); + ClearPresets(); LoadPresetSelections(m_shown_graphic_pack); //m_preset_sizer->GetContainingWindow()->Layout(); //m_right_panel->FitInside(); diff --git a/src/gui/GraphicPacksWindow2.h b/src/gui/GraphicPacksWindow2.h index 82b13c72..a068f2b6 100644 --- a/src/gui/GraphicPacksWindow2.h +++ b/src/gui/GraphicPacksWindow2.h @@ -27,6 +27,7 @@ private: bool m_filter_installed_games; std::vector m_installed_games; + void ClearPresets(); void FillGraphicPackList() const; void GetChildren(const wxTreeItemId& id, std::vector& children) const; void ExpandChildren(const std::vector& ids, size_t& counter) const; From 35afb99c99ce4c08b6eb94efb3172022a121d9f8 Mon Sep 17 00:00:00 2001 From: goeiecool9999 Date: Tue, 27 Sep 2022 12:48:35 +0200 Subject: [PATCH 003/616] [docs] Add llvm as a required package for Arch Linux (#308) --- BUILD.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index f180b221..daa846cb 100644 --- a/BUILD.md +++ b/BUILD.md @@ -33,7 +33,7 @@ To compile Cemu, a recent enough compiler and STL with C++20 support is required `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/bin/clang-12 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-12 -G Ninja -DCMAKE_MAKE_PROGRAM=/usr/bin/ninja` #### For Arch and derivatives: -`sudo pacman -S git cmake clang ninja nasm base-devel linux-headers gtk3 libsecret libgcrypt systemd freeglut zip unzip libpulse` +`sudo pacman -S git cmake llvm clang ninja nasm base-devel linux-headers gtk3 libsecret libgcrypt systemd freeglut zip unzip libpulse` #### For Fedora and derivatives: `sudo dnf install git cmake clang ninja-build nasm kernel-headers gtk3-devel libsecret-devel libgcrypt-devel systemd-devel freeglut-devel perl-core zlib-devel cubeb-devel` From 6ecc4be0da18cb697fbaba1224ec871ae53a41ae Mon Sep 17 00:00:00 2001 From: goeiecool9999 Date: Tue, 27 Sep 2022 13:58:50 +0200 Subject: [PATCH 004/616] Posix/Linux: Add setting to disable coredumps --- .../ExceptionHandler_posix.cpp | 77 ++++++++++++++----- src/config/CemuConfig.cpp | 12 ++- src/config/CemuConfig.h | 27 +++++++ src/gui/GeneralSettings2.cpp | 8 ++ 4 files changed, 102 insertions(+), 22 deletions(-) diff --git a/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp b/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp index b9ecb782..e2ea3d33 100644 --- a/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp +++ b/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp @@ -1,36 +1,73 @@ #include #include +#include -void handler_SIGSEGV(int sig) +#include "config/CemuConfig.h" + +// handle signals that would dump core, print stacktrace and then dump depending on config +void handlerDumpingSignal(int sig) { - printf("SIGSEGV!\n"); + char* sigName = strsignal(sig); + if (sigName) + { + printf("%s!\n", sigName); + } + else + { + // should never be the case + printf("Unknown core dumping signal!\n"); + } - void *array[32]; - size_t size; + void *array[32]; + size_t size; - // get void*'s for all entries on the stack - size = backtrace(array, 32); + // get void*'s for all entries on the stack + size = backtrace(array, 32); - // print out all the frames to stderr - fprintf(stderr, "Error: signal %d:\n", sig); - backtrace_symbols_fd(array, size, STDERR_FILENO); - exit(1); + // print out all the frames to stderr + fprintf(stderr, "Error: signal %d:\n", sig); + backtrace_symbols_fd(array, size, STDERR_FILENO); + + if (GetConfig().crash_dump == CrashDump::Enabled) + { + // reset signal handler to default and re-raise signal to dump core + signal(sig, SIG_DFL); + raise(sig); + return; + } + // exit process ignoring all issues + _Exit(1); } void handler_SIGINT(int sig) { - /* - * Received when pressing CTRL + C in a console - * Ideally should be exiting cleanly after saving settings but currently - * there's no clean exit pathway (at least on linux) and exiting the app - * by any mean ends up with a SIGABRT from the standard library destroying - * threads. - */ - _Exit(0); + /* + * Received when pressing CTRL + C in a console + * Ideally should be exiting cleanly after saving settings but currently + * there's no clean exit pathway (at least on linux) and exiting the app + * by any mean ends up with a SIGABRT from the standard library destroying + * threads. + */ + _Exit(0); } void ExceptionHandler_init() { - signal(SIGSEGV, handler_SIGSEGV); - signal(SIGINT, handler_SIGINT); + struct sigaction action; + action.sa_flags = 0; + sigfillset(&action.sa_mask); // don't allow signals to be interrupted + + action.sa_handler = handler_SIGINT; + sigaction(SIGINT, &action, nullptr); + + action.sa_handler = handlerDumpingSignal; + sigaction(SIGABRT, &action, nullptr); + sigaction(SIGBUS, &action, nullptr); + sigaction(SIGFPE, &action, nullptr); + sigaction(SIGILL, &action, nullptr); + sigaction(SIGIOT, &action, nullptr); + sigaction(SIGQUIT, &action, nullptr); + sigaction(SIGSEGV, &action, nullptr); + sigaction(SIGSYS, &action, nullptr); + sigaction(SIGTRAP, &action, nullptr); } diff --git a/src/config/CemuConfig.cpp b/src/config/CemuConfig.cpp index f996fc94..dcd283ec 100644 --- a/src/config/CemuConfig.cpp +++ b/src/config/CemuConfig.cpp @@ -316,7 +316,11 @@ void CemuConfig::Load(XMLConfigParser& parser) // debug auto debug = parser.get("Debug"); - crash_dump = debug.get("CrashDump", crash_dump); +#if BOOST_OS_WINDOWS + crash_dump = debug.get("CrashDumpWindows", crash_dump); +#elif BOOST_OS_UNIX + crash_dump = debug.get("CrashDumpUnix", crash_dump); +#endif // input auto input = parser.get("Input"); @@ -487,7 +491,11 @@ void CemuConfig::Save(XMLConfigParser& parser) // debug auto debug = config.set("Debug"); - debug.set("CrashDump", crash_dump.GetValue()); +#if BOOST_OS_WINDOWS + debug.set("CrashDumpWindows", crash_dump.GetValue()); +#elif BOOST_OS_UNIX + debug.set("CrashDumpUnix", crash_dump.GetValue()); +#endif // input auto input = config.set("Input"); diff --git a/src/config/CemuConfig.h b/src/config/CemuConfig.h index 8c43f0a5..caabf082 100644 --- a/src/config/CemuConfig.h +++ b/src/config/CemuConfig.h @@ -172,6 +172,7 @@ enum class CafeConsoleLanguage }; ENABLE_ENUM_ITERATORS(CafeConsoleLanguage, CafeConsoleLanguage::JA, CafeConsoleLanguage::TW); +#if BOOST_OS_WINDOWS enum class CrashDump { Disabled, @@ -179,6 +180,14 @@ enum class CrashDump Full }; ENABLE_ENUM_ITERATORS(CrashDump, CrashDump::Disabled, CrashDump::Full); +#elif BOOST_OS_UNIX +enum class CrashDump +{ + Disabled, + Enabled +}; +ENABLE_ENUM_ITERATORS(CrashDump, CrashDump::Disabled, CrashDump::Enabled); +#endif template <> struct fmt::formatter : formatter { @@ -290,6 +299,7 @@ struct fmt::formatter : formatter { } }; +#if BOOST_OS_WINDOWS template <> struct fmt::formatter : formatter { template @@ -306,6 +316,23 @@ struct fmt::formatter : formatter { return formatter::format(name, ctx); } }; +#elif BOOST_OS_UNIX +template <> +struct fmt::formatter : formatter { + template + auto format(const CrashDump v, FormatContext &ctx) { + string_view name; + switch (v) + { + case CrashDump::Disabled: name = "Disabled"; break; + case CrashDump::Enabled: name = "Enabled"; break; + default: name = "unknown"; break; + + } + return formatter::format(name, ctx); + } +}; +#endif struct CemuConfig diff --git a/src/gui/GeneralSettings2.cpp b/src/gui/GeneralSettings2.cpp index f81e7364..6c4f5bbb 100644 --- a/src/gui/GeneralSettings2.cpp +++ b/src/gui/GeneralSettings2.cpp @@ -709,10 +709,18 @@ wxPanel* GeneralSettings2::AddDebugPage(wxNotebook* notebook) debug_row->Add(new wxStaticText(panel, wxID_ANY, _("Crash dump"), wxDefaultPosition, wxDefaultSize, 0), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); +#if BOOST_OS_WINDOWS wxString dump_choices[] = { _("Disabled"), _("Lite"), _("Full") }; +#elif BOOST_OS_UNIX + wxString dump_choices[] = { _("Disabled"), _("Enabled") }; +#endif m_crash_dump = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(dump_choices), dump_choices); m_crash_dump->SetSelection(0); +#if BOOST_OS_WINDOWS m_crash_dump->SetToolTip(_("Creates a dump when Cemu crashes\nOnly enable when requested by a developer!\nThe Full option will create a very large dump file (includes a full RAM dump of the Cemu process)")); +#elif BOOST_OS_UNIX + m_crash_dump->SetToolTip(_("Creates a core dump when Cemu crashes\nOnly enable when requested by a developer!")); +#endif debug_row->Add(m_crash_dump, 0, wxALL | wxEXPAND, 5); debug_panel_sizer->Add(debug_row, 0, wxALL | wxEXPAND, 5); From a28d67bafd6a7d81707064d079a8d6aeefc5e29f Mon Sep 17 00:00:00 2001 From: UltraHDR <108294295+UltraHDR@users.noreply.github.com> Date: Tue, 27 Sep 2022 21:18:35 +0100 Subject: [PATCH 005/616] Remove -DPUBLIC_RELEASE=ON from macOS command (#309) --- BUILD.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index daa846cb..3dc69b18 100644 --- a/BUILD.md +++ b/BUILD.md @@ -76,7 +76,7 @@ Molten-VK compatibility layer. ### Build Cemu using cmake and clang 1. `git clone --recursive https://github.com/cemu-project/Cemu` 2. `cd Cemu` -3. `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DPUBLIC_RELEASE=ON +3. `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/local/opt/llvm@14/bin/clang -DCMAKE_CXX_COMPILER=/usr/local/opt/llvm@14/bin/clang++ -G Ninja` 4. `cmake --build build` 5. You should now have a Cemu executable file in the /bin folder, which you can run using `./bin/Cemu_release`. From 3fb4b5e26c0125dda68b3858e8c3d9589244c211 Mon Sep 17 00:00:00 2001 From: Tillsunset <35825944+Tillsunset@users.noreply.github.com> Date: Thu, 29 Sep 2022 04:36:27 -0500 Subject: [PATCH 006/616] MacOS+Linux: Use CLOCK_MONOTONIC_RAW over CLOCK_MONOTONIC (#313) On MacOS this fixes the framerate being too high due to discontinuities in the timer that drives the emulated vsync. It also fixes behavior of the GetTickCount() wrapper. --- src/Common/unix/platform.cpp | 2 +- src/util/highresolutiontimer/HighResolutionTimer.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Common/unix/platform.cpp b/src/Common/unix/platform.cpp index d97f4e65..a111a625 100644 --- a/src/Common/unix/platform.cpp +++ b/src/Common/unix/platform.cpp @@ -4,6 +4,6 @@ uint32_t GetTickCount() { struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); + clock_gettime(CLOCK_MONOTONIC_RAW, &ts); return (1000 * ts.tv_sec + ts.tv_nsec / 1000000); } \ No newline at end of file diff --git a/src/util/highresolutiontimer/HighResolutionTimer.cpp b/src/util/highresolutiontimer/HighResolutionTimer.cpp index 21cabff5..510fdfa5 100644 --- a/src/util/highresolutiontimer/HighResolutionTimer.cpp +++ b/src/util/highresolutiontimer/HighResolutionTimer.cpp @@ -9,7 +9,7 @@ HighResolutionTimer HighResolutionTimer::now() return HighResolutionTimer(pc.QuadPart); #else timespec pc; - clock_gettime(CLOCK_MONOTONIC, &pc); + clock_gettime(CLOCK_MONOTONIC_RAW, &pc); uint64 nsec = (uint64)pc.tv_sec * (uint64)1000000000 + (uint64)pc.tv_nsec; return HighResolutionTimer(nsec); #endif @@ -28,7 +28,7 @@ uint64 HighResolutionTimer::m_freq = []() -> uint64 { return (uint64)(freq.QuadPart); #else timespec pc; - clock_getres(CLOCK_MONOTONIC, &pc); + clock_getres(CLOCK_MONOTONIC_RAW, &pc); return (uint64)1000000000 / (uint64)pc.tv_nsec; #endif }(); From 37672572204230a2c4cd6d1282ccf48b51afb259 Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Thu, 29 Sep 2022 06:00:46 -0500 Subject: [PATCH 007/616] nfp: Fix corruption, correct structs and use write counters (#310) --- src/Cafe/OS/libs/nn_nfp/AmiiboCrypto.h | 4 +- src/Cafe/OS/libs/nn_nfp/nn_nfp.cpp | 116 +++++++++++++++++-------- 2 files changed, 82 insertions(+), 38 deletions(-) diff --git a/src/Cafe/OS/libs/nn_nfp/AmiiboCrypto.h b/src/Cafe/OS/libs/nn_nfp/AmiiboCrypto.h index 1c918e1e..9a326237 100644 --- a/src/Cafe/OS/libs/nn_nfp/AmiiboCrypto.h +++ b/src/Cafe/OS/libs/nn_nfp/AmiiboCrypto.h @@ -261,7 +261,7 @@ void amiiboEncrypt(AmiiboRawNFCData* nfcOutput) amiiboCrypto_internalToNfcFormat(&internalCopy, nfcOutput); // restore NFC values that aren't part of the internal representation - memcpy(nfcOutput->lockBytes, nfp_data.amiiboNFCData.lockBytes, 4); + memcpy(nfcOutput->dynamicLock, nfp_data.amiiboNFCData.dynamicLock, 4); memcpy(nfcOutput->cfg0, nfp_data.amiiboNFCData.cfg0, 4); memcpy(nfcOutput->cfg1, nfp_data.amiiboNFCData.cfg1, 4); -} \ No newline at end of file +} diff --git a/src/Cafe/OS/libs/nn_nfp/nn_nfp.cpp b/src/Cafe/OS/libs/nn_nfp/nn_nfp.cpp index 0c567b5a..faeed95c 100644 --- a/src/Cafe/OS/libs/nn_nfp/nn_nfp.cpp +++ b/src/Cafe/OS/libs/nn_nfp/nn_nfp.cpp @@ -25,16 +25,20 @@ void nnNfpUnlock() struct AmiiboInternal { - /* +0x000 */ uint32 ukn000; - /* +0x004 */ uint32 ukn004; + /* +0x000 */ uint16 lockBytes; + /* +0x002 */ uint16 staticLock; + /* +0x004 */ uint32 cc; /* +0x008 */ uint8 dataHMAC[32]; - /* +0x028 */ uint32 ukn028; + /* +0x028 */ uint8 ukn_A5; // always 0xA5 + /* +0x029 */ uint8 writeCounterHigh; + /* +0x029 */ uint8 writeCounterLow; + /* +0x02B */ uint8 unk02B; /* encrypted region starts here */ - struct + struct { /* +0x02C */ uint8 flags; /* +0x02D */ uint8 countryCode; - /* +0x02E */ uint16be writeCounter; + /* +0x02E */ uint16be crcWriteCounter; /* +0x030 */ uint16be date1; /* +0x032 */ uint16be date2; /* +0x034 */ uint32be crc; @@ -42,7 +46,7 @@ struct AmiiboInternal /* +0x04C */ uint8 mii[0x60]; /* +0x0AC */ uint32be appDataTitleIdHigh; /* +0x0B0 */ uint32be appDataTitleIdLow; - /* +0x0B4 */ uint16be writeCounter2; + /* +0x0B4 */ uint16be appWriteCounter; /* +0x0B6 */ uint16be appDataIdHigh; /* +0x0B8 */ uint16be appDataIdLow; /* +0x0BA */ uint16be ukn0BA; @@ -70,11 +74,19 @@ struct AmiiboInternal /* +0x0DC */ uint8 applicationData[0xD8]; /* encrypted region ends here */ /* +0x1B4 */ uint8 tagHMAC[32]; - /* +0x1D4 */ uint32 ukn1D4; - /* +0x1D8 */ uint32 ukn1D8; - /* +0x1DC */ uint32 ukn1DC; - /* +0x1E0 */ uint8 ukn1E0[0x20]; - /* +0x200 */ uint8 ukn200[0x8]; + /* +0x1D4 */ uint8 ntagSerial[7]; + /* +0x1DB */ uint8 nintendoId; + struct + { + /* +0x1DC */ uint8 gameAndCharacterId[2]; + /* +0x1DE */ uint8 characterVariation; + /* +0x1DF */ uint8 amiiboFigureType; + /* +0x1E0 */ uint8 amiiboModelNumber[2]; + /* +0x1E2 */ uint8 amiiboSeries; + /* +0x1E3 */ uint8 ukn_02; // always 0x02 ? + /* +0x1E4 */ uint8 ukn5C[4]; + }amiiboIdentificationBlock; + /* +0x1E8 */ uint8 keygenSalt[32]; }; static_assert(sizeof(AmiiboInternal) == 0x208); @@ -87,7 +99,7 @@ static_assert(offsetof(AmiiboInternal, tagHMAC) == 0x1B4); union AmiiboRawNFCData { // each page is 4 bytes - struct + struct { uint8 page0[4]; uint8 page1[4]; @@ -107,21 +119,24 @@ union AmiiboRawNFCData uint8 page15[4]; uint8 page16[4]; }; - struct + struct { - uint8 rawByte[16*4]; + uint8 rawByte[16 * 4]; }; - struct + struct { - /*+0x000 */ uint8 ntagSerial[9]; - /*+0x009 */ uint8 ukn009; - /*+0x00A */ uint8 lockBytes[2]; - /*+0x00C */ uint8 cc[4]; - /*+0x010 */ uint8 ukn010[4]; - /*+0x014 */ uint8 ukn014[32]; // crypto related? - /*+0x034 */ uint8 tagHMAC[32]; // data hmac - /*+0x054 */ - struct + /* +0x000 */ uint8 ntagSerial[7]; + /* +0x007 */ uint8 nintendoId; + /* +0x008 */ uint8 lockBytes[2]; + /* +0x00A */ uint8 staticLock[2]; + /* +0x00C */ uint8 cc[4]; // compatibility container + /* +0x010 */ uint8 ukn_A5; // always 0xA5 + /* +0x011 */ uint8 writeCounter[2]; + /* +0x013 */ uint8 unk013; + /* +0x014 */ uint8 encryptedSettings[32]; + /* +0x034 */ uint8 tagHMAC[32]; // data hmac + /* +0x054 */ + struct { /* +0x54 */ uint8 gameAndCharacterId[2]; /* +0x56 */ uint8 characterVariation; @@ -129,15 +144,20 @@ union AmiiboRawNFCData /* +0x58 */ uint8 amiiboModelNumber[2]; /* +0x5A */ uint8 amiiboSeries; /* +0x5B */ uint8 ukn_02; // always 0x02 ? - - /* +0x5C */ uint8 ukn00[0x80-0x5C]; // not part of identification block? + /* +0x5C */ uint8 ukn5C[4]; }amiiboIdentificationBlock; - /*+0x080 */ uint8 dataHMAC[32]; - /*+0x0A0 */ uint8 ukn0A0[0x114]; - /*+0x1B4 */ uint8 ukn1B4[0x54]; - /*+0x208 */ uint8 lockBytes208[4]; - /*+0x20C */ uint8 cfg0[4]; - /*+0x210 */ uint8 cfg1[4]; + /* +0x060 */ uint8 keygenSalt[32]; + /* +0x080 */ uint8 dataHMAC[32]; + /* +0x0A0 */ uint8 encryptedMii[0x60]; + /* +0x100 */ uint8 encryptedTitleId[8]; + /* +0x108 */ uint8 encryptedApplicationWriteCounter[2]; + /* +0x10A */ uint8 encryptedApplicationAreaId[4]; + /* +0x10E */ uint8 ukn10E[2]; + /* +0x110 */ uint8 unk110[32]; + /* +0x130 */ uint8 encryptedApplicationArea[0xD8]; + /* +0x208 */ uint8 dynamicLock[4]; + /* +0x20C */ uint8 cfg0[4]; + /* +0x210 */ uint8 cfg1[4]; }; }; @@ -400,7 +420,7 @@ typedef struct typedef struct { /* +0x00 */ nfpDate_t date; - /* +0x04 */ uint16be writeCount; + /* +0x04 */ uint8 writeCount[2]; /* +0x06 */ uint8 characterId[3]; /* +0x09 */ uint8 amiiboSeries; /* +0x0A */ uint16be number; @@ -435,6 +455,9 @@ void nnNfpExport_GetNfpCommonInfo(PPCInterpreter_t* hCPU) forceLogDebug_printf("GetNfpCommonInfo(0x%08x)"); + commonInfo->writeCount[0] = nfp_data.amiiboNFCData.writeCounter[0]; + commonInfo->writeCount[1] = nfp_data.amiiboNFCData.writeCounter[1]; + commonInfo->characterId[0] = nfp_data.amiiboNFCData.amiiboIdentificationBlock.gameAndCharacterId[0]; commonInfo->characterId[1] = nfp_data.amiiboNFCData.amiiboIdentificationBlock.gameAndCharacterId[1]; commonInfo->characterId[2] = nfp_data.amiiboNFCData.amiiboIdentificationBlock.characterVariation; @@ -590,6 +613,8 @@ void nnNfpExport_WriteApplicationArea(PPCInterpreter_t* hCPU) for (uint32 i = len; i < sizeof(nfp_data.amiiboInternal.applicationData); i++) nfp_data.amiiboInternal.applicationData[i] = rand() & 0xFF; + nfp_data.amiiboInternal.amiiboSettings.appWriteCounter = nfp_data.amiiboInternal.amiiboSettings.appWriteCounter + 1; + osLib_returnFromFunction(hCPU, BUILD_NN_RESULT(NN_RESULT_LEVEL_SUCCESS, NN_RESULT_MODULE_NN_NFP, 0)); } @@ -629,6 +654,7 @@ void nnNfpExport_CreateApplicationArea(PPCInterpreter_t* hCPU) nfp_data.amiiboInternal.amiiboSettings.setAppDataAppId(createInfo->appAreaId); nfp_data.amiiboInternal.amiiboSettings.flags |= 0x20; // set application data exists bit + nfp_data.amiiboInternal.amiiboSettings.appWriteCounter = nfp_data.amiiboInternal.amiiboSettings.appWriteCounter + 1; nfp_data.hasOpenApplicationArea = false; @@ -666,6 +692,7 @@ void nnNfpExport_DeleteApplicationArea(PPCInterpreter_t* hCPU) nfp_data.amiiboInternal.amiiboSettings.setAppDataAppId(0); nfp_data.amiiboInternal.amiiboSettings.flags &= ~0x20; + nfp_data.amiiboInternal.amiiboSettings.appWriteCounter = nfp_data.amiiboInternal.amiiboSettings.appWriteCounter + 1; // this API forces a flush if (!nnNfp_writeCurrentAmiibo()) @@ -760,6 +787,17 @@ bool nnNfp_touchNfcTagFromFile(const wchar_t* filePath, uint32* nfcError) memcpy(&rawData, nfcData->data(), sizeof(AmiiboRawNFCData)); // verify if the file is a valid ntag215/amiibo file + if (rawData.dynamicLock[0] != 0x01 || rawData.dynamicLock[1] != 0x00 || rawData.dynamicLock[2] != 0x0F || rawData.dynamicLock[3] != 0xBD) + { + // Temporary workaround to fix corrupted files by old cemu versions + rawData.dynamicLock[0] = 0x01; + rawData.dynamicLock[1] = 0x00; + rawData.dynamicLock[2] = 0x0F; + rawData.dynamicLock[3] = 0xBD; + + // *nfcError = NFC_ERROR_INVALID_FILE_FORMAT; + // return false; + } if (rawData.cfg0[0] != 0x00 || rawData.cfg0[1] != 0x00 || rawData.cfg0[2] != 0x00 || rawData.cfg0[3] != 0x04) { *nfcError = NFC_ERROR_INVALID_FILE_FORMAT; @@ -770,7 +808,7 @@ bool nnNfp_touchNfcTagFromFile(const wchar_t* filePath, uint32* nfcError) *nfcError = NFC_ERROR_INVALID_FILE_FORMAT; return false; } - if (rawData.lockBytes[0] != 0x0F || rawData.lockBytes[1] != 0xE0 ) + if (rawData.staticLock[0] != 0x0F || rawData.staticLock[1] != 0xE0) { *nfcError = NFC_ERROR_INVALID_FILE_FORMAT; return false; @@ -789,10 +827,10 @@ bool nnNfp_touchNfcTagFromFile(const wchar_t* filePath, uint32* nfcError) serialNumber[3] = rawData.ntagSerial[4]; serialNumber[4] = rawData.ntagSerial[5]; serialNumber[5] = rawData.ntagSerial[6]; - serialNumber[6] = rawData.ntagSerial[7]; + serialNumber[6] = rawData.nintendoId; uint8 serialCheckByte0 = rawData.ntagSerial[3]; - uint8 serialCheckByte1 = rawData.ntagSerial[8]; + uint8 serialCheckByte1 = rawData.lockBytes[0]; uint8 bcc0 = serialNumber[0] ^ serialNumber[1] ^ serialNumber[2] ^ 0x88; uint8 bcc1 = serialNumber[3] ^ serialNumber[4] ^ serialNumber[5] ^ serialNumber[6]; @@ -830,6 +868,12 @@ bool nnNfp_writeCurrentAmiibo() nnNfpUnlock(); return false; } + + uint16 writeCounter = nfp_data.amiiboInternal.writeCounterLow + (nfp_data.amiiboInternal.writeCounterHigh << 8); + writeCounter++; + nfp_data.amiiboInternal.writeCounterLow = writeCounter & 0xFF; + nfp_data.amiiboInternal.writeCounterHigh = (writeCounter >> 8) & 0xFF; + // open file for writing FileStream* fs = FileStream::openFile2(nfp_data.amiiboPath, true); if (!fs) From ecfbbd4e265f1b58618fa2f7770951956d9a553f Mon Sep 17 00:00:00 2001 From: emiyl Date: Fri, 30 Sep 2022 14:28:56 +0100 Subject: [PATCH 008/616] MoltenVK: Workaround for unsupported formats (#315) --- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 60 +++++++++++++++---- .../HW/Latte/Renderer/Vulkan/VulkanRenderer.h | 2 + 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index fbc268cb..03ffb7f5 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -1929,6 +1929,20 @@ void VulkanRenderer::QueryAvailableFormats() { m_supportedFormatInfo.fmt_r4g4_unorm_pack = true; } + // R4G4B4A4 + fmtProp = {}; + vkGetPhysicalDeviceFormatProperties(m_physical_device, VK_FORMAT_R4G4B4A4_UNORM_PACK16, &fmtProp); + if (fmtProp.optimalTilingFeatures != 0) + { + m_supportedFormatInfo.fmt_r4g4b4a4_unorm_pack = true; + } + // A1R5G5B5 + fmtProp = {}; + vkGetPhysicalDeviceFormatProperties(m_physical_device, VK_FORMAT_A1R5G5B5_UNORM_PACK16, &fmtProp); + if (fmtProp.optimalTilingFeatures != 0) + { + m_supportedFormatInfo.fmt_a1r5g5b5_unorm_pack = true; + } // print info about unsupported formats to log for (auto& it : requestedFormatList) { @@ -2565,8 +2579,14 @@ void VulkanRenderer::GetTextureFormatInfoVK(Latte::E_GX2SURFFMT format, bool isD case Latte::E_GX2SURFFMT::R4_G4_UNORM: if (m_supportedFormatInfo.fmt_r4g4_unorm_pack == false) { - formatInfoOut->vkImageFormat = VK_FORMAT_R4G4B4A4_UNORM_PACK16; - formatInfoOut->decoder = TextureDecoder_R4_G4_UNORM_toRGBA4444_vk::getInstance(); + if (m_supportedFormatInfo.fmt_r4g4b4a4_unorm_pack == false) { + formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_UNORM; + formatInfoOut->decoder = nullptr; + } + else { + formatInfoOut->vkImageFormat = VK_FORMAT_R4G4B4A4_UNORM_PACK16; + formatInfoOut->decoder = TextureDecoder_R4_G4_UNORM_toRGBA4444_vk::getInstance(); + } } else { @@ -2618,23 +2638,41 @@ void VulkanRenderer::GetTextureFormatInfoVK(Latte::E_GX2SURFFMT format, bool isD formatInfoOut->decoder = TextureDecoder_R5_G6_B5_swappedRB::getInstance(); break; case Latte::E_GX2SURFFMT::R5_G5_B5_A1_UNORM: - // used in Super Mario 3D World for the hidden Luigi sprites - // since order of channels is reversed in Vulkan compared to GX2 the format we need is A1B5G5R5 - formatInfoOut->vkImageFormat = VK_FORMAT_A1R5G5B5_UNORM_PACK16; - formatInfoOut->decoder = TextureDecoder_R5_G5_B5_A1_UNORM_swappedRB::getInstance(); + if (m_supportedFormatInfo.fmt_a1r5g5b5_unorm_pack == false) { + formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_UNORM; + formatInfoOut->decoder = nullptr; + } + else { + // used in Super Mario 3D World for the hidden Luigi sprites + // since order of channels is reversed in Vulkan compared to GX2 the format we need is A1B5G5R5 + formatInfoOut->vkImageFormat = VK_FORMAT_A1R5G5B5_UNORM_PACK16; + formatInfoOut->decoder = TextureDecoder_R5_G5_B5_A1_UNORM_swappedRB::getInstance(); + } break; case Latte::E_GX2SURFFMT::A1_B5_G5_R5_UNORM: - // used by VC64 (e.g. Ocarina of Time) - formatInfoOut->vkImageFormat = VK_FORMAT_A1R5G5B5_UNORM_PACK16; // A 15 R 10..14, G 5..9 B 0..4 - formatInfoOut->decoder = TextureDecoder_A1_B5_G5_R5_UNORM_vulkan::getInstance(); + if (m_supportedFormatInfo.fmt_a1r5g5b5_unorm_pack == false) { + formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_UNORM; + formatInfoOut->decoder = nullptr; + } + else { + // used by VC64 (e.g. Ocarina of Time) + formatInfoOut->vkImageFormat = VK_FORMAT_A1R5G5B5_UNORM_PACK16; // A 15 R 10..14, G 5..9 B 0..4 + formatInfoOut->decoder = TextureDecoder_A1_B5_G5_R5_UNORM_vulkan::getInstance(); + } break; case Latte::E_GX2SURFFMT::R11_G11_B10_FLOAT: formatInfoOut->vkImageFormat = VK_FORMAT_B10G11R11_UFLOAT_PACK32; // verify if order of channels is still the same as GX2 formatInfoOut->decoder = TextureDecoder_R11_G11_B10_FLOAT::getInstance(); break; case Latte::E_GX2SURFFMT::R4_G4_B4_A4_UNORM: - formatInfoOut->vkImageFormat = VK_FORMAT_R4G4B4A4_UNORM_PACK16; // verify order of channels - formatInfoOut->decoder = TextureDecoder_R4_G4_B4_A4_UNORM::getInstance(); + if (m_supportedFormatInfo.fmt_r4g4b4a4_unorm_pack == false) { + formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_UNORM; + formatInfoOut->decoder = nullptr; + } + else { + formatInfoOut->vkImageFormat = VK_FORMAT_R4G4B4A4_UNORM_PACK16; + formatInfoOut->decoder = TextureDecoder_R4_G4_B4_A4_UNORM::getInstance(); + } break; // special formats - R10G10B10_A2 case Latte::E_GX2SURFFMT::R10_G10_B10_A2_UNORM: diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index 6b6abf7a..a1f878e8 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -15,6 +15,8 @@ struct VkSupportedFormatInfo_t { bool fmt_d24_unorm_s8_uint{}; bool fmt_r4g4_unorm_pack{}; + bool fmt_r4g4b4a4_unorm_pack{}; + bool fmt_a1r5g5b5_unorm_pack{}; }; struct VkDescriptorSetInfo From cceb4f6d0ea20f5616998be3760615559a155523 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Fri, 30 Sep 2022 15:59:16 +0200 Subject: [PATCH 009/616] Vulkan: Always disable blending for integer formats (#317) Should fix a warning in the Vulkan validation layer and avoid a sigtrap in MoltenVk --- src/Cafe/HW/Latte/Renderer/Vulkan/VKRBase.h | 19 +++++++++-- .../Vulkan/VulkanPipelineCompiler.cpp | 32 +++++++++++++++++-- .../Renderer/Vulkan/VulkanPipelineCompiler.h | 2 +- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 8 +++-- 4 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VKRBase.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VKRBase.h index 1c91207e..f79bd2dc 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VKRBase.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VKRBase.h @@ -1,4 +1,5 @@ #pragma once +#include "Cafe/HW/Latte/ISA/LatteReg.h" #include "util/math/vector2.h" class VKRMoveableRefCounterRef @@ -150,14 +151,28 @@ public: struct AttachmentInfo_t { - AttachmentEntryColor_t colorAttachment[8]; + AttachmentEntryColor_t colorAttachment[Latte::GPU_LIMITS::NUM_COLOR_ATTACHMENTS]; AttachmentEntryDepth_t depthAttachment; }; + VkFormat GetColorFormat(size_t index) + { + if (index >= Latte::GPU_LIMITS::NUM_COLOR_ATTACHMENTS) + return VK_FORMAT_UNDEFINED; + return m_colorAttachmentFormat[index]; + } + + VkFormat GetDepthFormat() + { + return m_depthAttachmentFormat; + } + public: - VKRObjectRenderPass(AttachmentInfo_t& attachmentInfo, sint32 colorAttachmentCount = 8); + VKRObjectRenderPass(AttachmentInfo_t& attachmentInfo, sint32 colorAttachmentCount = Latte::GPU_LIMITS::NUM_COLOR_ATTACHMENTS); ~VKRObjectRenderPass() override; VkRenderPass m_renderPass{ VK_NULL_HANDLE }; + VkFormat m_colorAttachmentFormat[Latte::GPU_LIMITS::NUM_COLOR_ATTACHMENTS]; + VkFormat m_depthAttachmentFormat; uint64 m_hashForPipeline; // helper var. Holds hash of all the renderpass creation parameters (mainly the formats) that affect the pipeline state }; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp index 7fb506b7..0deacddd 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp @@ -609,7 +609,29 @@ void PipelineCompiler::InitRasterizerState(const LatteContextRegister& latteRegi multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; } -void PipelineCompiler::InitBlendState(const LatteContextRegister& latteRegister, PipelineInfo* pipelineInfo, bool& usesBlendConstants) +bool _IsVkIntegerFormat(VkFormat fmt) +{ + return + // 8bit integer formats + fmt == VK_FORMAT_R8_UINT || fmt == VK_FORMAT_R8_SINT || + fmt == VK_FORMAT_R8G8_UINT || fmt == VK_FORMAT_R8G8_SINT || + fmt == VK_FORMAT_R8G8B8_UINT || fmt == VK_FORMAT_R8G8B8_SINT || + fmt == VK_FORMAT_R8G8B8A8_UINT || fmt == VK_FORMAT_R8G8B8A8_SINT || + fmt == VK_FORMAT_B8G8R8A8_UINT || fmt == VK_FORMAT_B8G8R8A8_SINT || + // 16bit integer formats + fmt == VK_FORMAT_R16_UINT || fmt == VK_FORMAT_R16_SINT || + fmt == VK_FORMAT_R16G16_UINT || fmt == VK_FORMAT_R16G16_SINT || + fmt == VK_FORMAT_R16G16B16_UINT || fmt == VK_FORMAT_R16G16B16_SINT || + fmt == VK_FORMAT_R16G16B16A16_UINT || fmt == VK_FORMAT_R16G16B16A16_SINT || + // 32bit integer formats + fmt == VK_FORMAT_R32_UINT || fmt == VK_FORMAT_R32_SINT || + fmt == VK_FORMAT_R32G32_UINT || fmt == VK_FORMAT_R32G32_SINT || + fmt == VK_FORMAT_R32G32B32_UINT || fmt == VK_FORMAT_R32G32B32_SINT || + fmt == VK_FORMAT_R32G32B32A32_UINT || fmt == VK_FORMAT_R32G32B32A32_SINT; +} + + +void PipelineCompiler::InitBlendState(const LatteContextRegister& latteRegister, PipelineInfo* pipelineInfo, bool& usesBlendConstants, VKRObjectRenderPass* renderPassObj) { const Latte::LATTE_CB_COLOR_CONTROL& colorControlReg = latteRegister.CB_COLOR_CONTROL; uint32 blendEnableMask = colorControlReg.get_BLEND_MASK(); @@ -625,6 +647,12 @@ void PipelineCompiler::InitBlendState(const LatteContextRegister& latteRegister, else entry.blendEnable = VK_FALSE; + if (entry.blendEnable != VK_FALSE && _IsVkIntegerFormat(renderPassObj->GetColorFormat(i))) + { + // force-disable blending for integer formats + entry.blendEnable = VK_FALSE; + } + const auto& blendControlReg = latteRegister.CB_BLENDN_CONTROL[i]; entry.colorWriteMask = (renderTargetMask >> (i * 4)) & 0xF; @@ -873,7 +901,7 @@ bool PipelineCompiler::InitFromCurrentGPUState(PipelineInfo* pipelineInfo, const bool usesDepthBias = false; InitRasterizerState(latteRegister, vkRenderer, isPrimitiveRect, usesDepthBias); bool usesBlendConstants = false; - InitBlendState(latteRegister, pipelineInfo, usesBlendConstants); + InitBlendState(latteRegister, pipelineInfo, usesBlendConstants, renderPassObj); InitDescriptorSetLayouts(vkRenderer, pipelineInfo, pipelineInfo->vertexShader, pipelineInfo->pixelShader, pipelineInfo->geometryShader); // ########################################################################################################################################## diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.h index 4020b27e..304a7b31 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.h @@ -23,7 +23,7 @@ private: void InitInputAssemblyState(const Latte::LATTE_VGT_PRIMITIVE_TYPE::E_PRIMITIVE_TYPE primitiveMode); void InitViewportState(); void InitRasterizerState(const LatteContextRegister& latteRegister, VulkanRenderer* vkRenderer, bool isPrimitiveRect, bool& usesDepthBias); - void InitBlendState(const LatteContextRegister& latteRegister, PipelineInfo* pipelineInfo, bool& usesBlendConstants); + void InitBlendState(const LatteContextRegister& latteRegister, PipelineInfo* pipelineInfo, bool& usesBlendConstants, VKRObjectRenderPass* renderPassObj); void InitDescriptorSetLayouts(VulkanRenderer* vkRenderer, PipelineInfo* vkrPipelineInfo, LatteDecompilerShader* vertexShader, LatteDecompilerShader* pixelShader, LatteDecompilerShader* geometryShader); void InitDepthStencilState(); void InitDynamicState(PipelineInfo* pipelineInfo, bool usesBlendConstants, bool usesDepthBias); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 03ffb7f5..8d352654 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -4085,7 +4085,7 @@ VKRObjectRenderPass::VKRObjectRenderPass(AttachmentInfo_t& attachmentInfo, sint3 { // generate helper hash for pipeline state uint64 stateHash = 0; - for (int i = 0; i < 8; ++i) + for (int i = 0; i < Latte::GPU_LIMITS::NUM_COLOR_ATTACHMENTS; ++i) { if (attachmentInfo.colorAttachment[i].isPresent || attachmentInfo.colorAttachment[i].viewObj) { @@ -4102,7 +4102,7 @@ VKRObjectRenderPass::VKRObjectRenderPass(AttachmentInfo_t& attachmentInfo, sint3 // setup Vulkan renderpass std::vector attachments_descriptions; - std::array color_attachments_references{}; + std::array color_attachments_references{}; cemu_assert(colorAttachmentCount <= color_attachments_references.size()); sint32 numColorAttachments = 0; for (int i = 0; i < 8; ++i) @@ -4110,8 +4110,10 @@ VKRObjectRenderPass::VKRObjectRenderPass(AttachmentInfo_t& attachmentInfo, sint3 if (attachmentInfo.colorAttachment[i].viewObj == nullptr && attachmentInfo.colorAttachment[i].isPresent == false) { color_attachments_references[i].attachment = VK_ATTACHMENT_UNUSED; + m_colorAttachmentFormat[i] = VK_FORMAT_UNDEFINED; continue; } + m_colorAttachmentFormat[i] = attachmentInfo.colorAttachment[i].format; color_attachments_references[i].attachment = (uint32)attachments_descriptions.size(); color_attachments_references[i].layout = VK_IMAGE_LAYOUT_GENERAL; @@ -4135,12 +4137,14 @@ VKRObjectRenderPass::VKRObjectRenderPass(AttachmentInfo_t& attachmentInfo, sint3 if (attachmentInfo.depthAttachment.viewObj == nullptr && attachmentInfo.depthAttachment.isPresent == false) { depth_stencil_attachments_references.attachment = VK_ATTACHMENT_UNUSED; + m_depthAttachmentFormat = VK_FORMAT_UNDEFINED; } else { hasDepthStencilAttachment = true; depth_stencil_attachments_references.attachment = (uint32)attachments_descriptions.size(); depth_stencil_attachments_references.layout = VK_IMAGE_LAYOUT_GENERAL; + m_depthAttachmentFormat = attachmentInfo.depthAttachment.format; VkAttachmentDescription entry{}; entry.format = attachmentInfo.depthAttachment.format; From 9541c8ae8556ffd72aab4e9eb092ec45eeceaf22 Mon Sep 17 00:00:00 2001 From: emiyl Date: Fri, 30 Sep 2022 17:07:00 +0100 Subject: [PATCH 010/616] MoltenVk: Workaround for unsupported format R5_G6_B5_UNORM (#318) --- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 19 ++++++++++++++++--- .../HW/Latte/Renderer/Vulkan/VulkanRenderer.h | 1 + 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 8d352654..cf5fc72b 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -1929,6 +1929,13 @@ void VulkanRenderer::QueryAvailableFormats() { m_supportedFormatInfo.fmt_r4g4_unorm_pack = true; } + // R5G6B5 + fmtProp = {}; + vkGetPhysicalDeviceFormatProperties(m_physical_device, VK_FORMAT_R5G6B5_UNORM_PACK16, &fmtProp); + if (fmtProp.optimalTilingFeatures != 0) + { + m_supportedFormatInfo.fmt_r5g6b5_unorm_pack = true; + } // R4G4B4A4 fmtProp = {}; vkGetPhysicalDeviceFormatProperties(m_physical_device, VK_FORMAT_R4G4B4A4_UNORM_PACK16, &fmtProp); @@ -2633,9 +2640,15 @@ void VulkanRenderer::GetTextureFormatInfoVK(Latte::E_GX2SURFFMT format, bool isD break; // special formats case Latte::E_GX2SURFFMT::R5_G6_B5_UNORM: - // Vulkan has R in MSB, GPU7 has it in LSB - formatInfoOut->vkImageFormat = VK_FORMAT_R5G6B5_UNORM_PACK16; - formatInfoOut->decoder = TextureDecoder_R5_G6_B5_swappedRB::getInstance(); + if (m_supportedFormatInfo.fmt_r5g6b5_unorm_pack == false) { + formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_UNORM; + formatInfoOut->decoder = nullptr; + } + else { + // Vulkan has R in MSB, GPU7 has it in LSB + formatInfoOut->vkImageFormat = VK_FORMAT_R5G6B5_UNORM_PACK16; + formatInfoOut->decoder = TextureDecoder_R5_G6_B5_swappedRB::getInstance(); + } break; case Latte::E_GX2SURFFMT::R5_G5_B5_A1_UNORM: if (m_supportedFormatInfo.fmt_a1r5g5b5_unorm_pack == false) { diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index a1f878e8..af597bf9 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -15,6 +15,7 @@ struct VkSupportedFormatInfo_t { bool fmt_d24_unorm_s8_uint{}; bool fmt_r4g4_unorm_pack{}; + bool fmt_r5g6b5_unorm_pack{}; bool fmt_r4g4b4a4_unorm_pack{}; bool fmt_a1r5g5b5_unorm_pack{}; }; From 11f6e2b7ee7af5327527301b6612dd520f7eb67d Mon Sep 17 00:00:00 2001 From: Tillsunset <35825944+Tillsunset@users.noreply.github.com> Date: Sat, 1 Oct 2022 18:48:13 -0500 Subject: [PATCH 011/616] Vulkan: Implement texture decoder for R5G6B5_UNORM to R8G8B8A8_UNORM (#320) --- src/Cafe/HW/Latte/Core/LatteTextureLoader.h | 51 +++++++++++++++++++ .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/Cafe/HW/Latte/Core/LatteTextureLoader.h b/src/Cafe/HW/Latte/Core/LatteTextureLoader.h index 25321006..82144f3a 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureLoader.h +++ b/src/Cafe/HW/Latte/Core/LatteTextureLoader.h @@ -978,6 +978,57 @@ public: } }; +class TextureDecoder_R5G6B5_UNORM_To_R8G8B8A8_UNORM : public TextureDecoder, public SingletonClass +{ +public: + sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override + { + return 4; + } + + void decode(LatteTextureLoaderCtx* textureLoader, uint8* outputData) override + { + for (sint32 y = 0; y < textureLoader->height; y += textureLoader->stepY) + { + sint32 yc = y; + for (sint32 x = 0; x < textureLoader->width; x += textureLoader->stepX) + { + uint8* blockData = LatteTextureLoader_GetInput(textureLoader, x, y); + sint32 pixelOffset = (x + yc * textureLoader->width) * 4; + uint16 v0 = (*(uint16*)(blockData + 0)); + + uint8 c0 = (v0 & 0x1F); + uint8 c1 = (v0 >> 5) & 0x3F; + uint8 c2 = (v0 >> 11) & 0x1F; + + c0 = (c0 << 3) | c0 >> 3;// blue + c1 = (c1 << 2) | c1 >> 4;// green + c2 = (c2 << 3) | c2 >> 3;// red + + *(uint8*)(outputData + pixelOffset + 0) = c0;// blue + *(uint8*)(outputData + pixelOffset + 1) = c1;// green + *(uint8*)(outputData + pixelOffset + 2) = c2;// red + *(uint8*)(outputData + pixelOffset + 3) = 255;//alpha + } + } + } + + void decodePixelToRGBA(uint8* blockData, uint8* outputPixel, uint8 blockOffsetX, uint8 blockOffsetY) override + { + uint16 v0 = *(uint16*)(blockData + 0); + uint8 c0 = (v0 & 0x1F);// red + uint8 c1 = (v0 >> 5) & 0x3F;// green + uint8 c2 = (v0 >> 11) & 0x1F; // blue + c0 = (c0 << 3) | c0 >> 3; + c1 = (c1 << 2) | c1 >> 4; + c2 = (c2 << 3) | c2 >> 3; + *(outputPixel + 0) = c0;// red + *(outputPixel + 1) = c1;// green + *(outputPixel + 2) = c2;// blue + *(outputPixel + 3) = 255;// alpha + } +}; + class TextureDecoder_R5_G5_B5_A1_UNORM : public TextureDecoder, public SingletonClass { public: diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index cf5fc72b..33cdbd0e 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -2642,7 +2642,7 @@ void VulkanRenderer::GetTextureFormatInfoVK(Latte::E_GX2SURFFMT format, bool isD case Latte::E_GX2SURFFMT::R5_G6_B5_UNORM: if (m_supportedFormatInfo.fmt_r5g6b5_unorm_pack == false) { formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_UNORM; - formatInfoOut->decoder = nullptr; + formatInfoOut->decoder = TextureDecoder_R5G6B5_UNORM_To_R8G8B8A8_UNORM::getInstance(); } else { // Vulkan has R in MSB, GPU7 has it in LSB From fb5ecca1577c06d853f71b311bca5165eae9ff31 Mon Sep 17 00:00:00 2001 From: Tillsunset <35825944+Tillsunset@users.noreply.github.com> Date: Sun, 2 Oct 2022 12:18:35 -0500 Subject: [PATCH 012/616] Vulkan: Use correct texture clear in LatteDraw_handleSpecialState8_clearAsDepth (#321) --- src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp index f9f61646..91a1f9a6 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp @@ -580,7 +580,10 @@ void LatteDraw_handleSpecialState8_clearAsDepth() } else { - g_renderer->texture_clearColorSlice(view->baseTexture, sliceIndex + view->firstSlice, mipIndex + view->firstMip, clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + if (view->baseTexture->isDepth) + g_renderer->texture_clearDepthSlice(view->baseTexture, sliceIndex + view->firstSlice, mipIndex + view->firstMip, true, view->baseTexture->hasStencil, 0.0f, 0); + else + g_renderer->texture_clearColorSlice(view->baseTexture, sliceIndex + view->firstSlice, mipIndex + view->firstMip, clearColor[0], clearColor[1], clearColor[2], clearColor[3]); } } } From 8a0fe21589767eed5c3118f11168b3c18f4bd92c Mon Sep 17 00:00:00 2001 From: purofle Date: Mon, 3 Oct 2022 19:05:42 +0800 Subject: [PATCH 013/616] [docs] add `--needed` in ArchLinux dependencies (#324) --- BUILD.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index 3dc69b18..08c6d64d 100644 --- a/BUILD.md +++ b/BUILD.md @@ -33,7 +33,7 @@ To compile Cemu, a recent enough compiler and STL with C++20 support is required `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_C_COMPILER=/usr/bin/clang-12 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-12 -G Ninja -DCMAKE_MAKE_PROGRAM=/usr/bin/ninja` #### For Arch and derivatives: -`sudo pacman -S git cmake llvm clang ninja nasm base-devel linux-headers gtk3 libsecret libgcrypt systemd freeglut zip unzip libpulse` +`sudo pacman -S --needed git cmake llvm clang ninja nasm base-devel linux-headers gtk3 libsecret libgcrypt systemd freeglut zip unzip libpulse` #### For Fedora and derivatives: `sudo dnf install git cmake clang ninja-build nasm kernel-headers gtk3-devel libsecret-devel libgcrypt-devel systemd-devel freeglut-devel perl-core zlib-devel cubeb-devel` From 4519a59d746dc0eb2a427c0f379c6c4c600d89dd Mon Sep 17 00:00:00 2001 From: emiyl Date: Tue, 4 Oct 2022 14:24:14 +0100 Subject: [PATCH 014/616] [ih264] per-function target attribute on clang and GCC (#328) --- dependencies/ih264d/CMakeLists.txt | 4 --- .../ih264_chroma_intra_pred_filters_ssse3.c | 11 ++++++++ .../common/x86/ih264_deblk_chroma_ssse3.c | 18 ++++++++++++ .../common/x86/ih264_deblk_luma_ssse3.c | 12 ++++++++ .../x86/ih264_ihadamard_scaling_sse42.c | 8 ++++++ .../x86/ih264_ihadamard_scaling_ssse3.c | 7 +++++ .../x86/ih264_inter_pred_filters_ssse3.c | 16 +++++++++++ .../x86/ih264_iquant_itrans_recon_dc_ssse3.c | 9 ++++++ .../x86/ih264_iquant_itrans_recon_sse42.c | 8 ++++++ .../x86/ih264_iquant_itrans_recon_ssse3.c | 8 ++++++ .../x86/ih264_luma_intra_pred_filters_ssse3.c | 28 +++++++++++++++++++ .../ih264d/common/x86/ih264_mem_fns_ssse3.c | 8 ++++++ .../ih264d/common/x86/ih264_padding_ssse3.c | 10 +++++++ .../common/x86/ih264_resi_trans_quant_sse42.c | 10 +++++++ .../common/x86/ih264_weighted_pred_sse42.c | 12 ++++++++ .../x86/ih264d_function_selector_sse42.c | 7 +++++ .../x86/ih264d_function_selector_ssse3.c | 7 +++++ 17 files changed, 179 insertions(+), 4 deletions(-) diff --git a/dependencies/ih264d/CMakeLists.txt b/dependencies/ih264d/CMakeLists.txt index 4f538f69..295b028a 100644 --- a/dependencies/ih264d/CMakeLists.txt +++ b/dependencies/ih264d/CMakeLists.txt @@ -6,10 +6,6 @@ set(LIBAVCDEC_X86_INCLUDES "common/x86" "decoder/x86") include_directories("common/" "decoder/" ${LIBAVCDEC_X86_INCLUDES}) -if((CMAKE_C_COMPILER_ID MATCHES "GNU") OR (CMAKE_C_COMPILER_ID MATCHES "Clang")) - add_compile_options(-mssse3 -mavx2) -endif() - add_library (ih264d "common/ih264_buf_mgr.c" "common/ih264_buf_mgr.h" diff --git a/dependencies/ih264d/common/x86/ih264_chroma_intra_pred_filters_ssse3.c b/dependencies/ih264d/common/x86/ih264_chroma_intra_pred_filters_ssse3.c index d43ce207..502420b1 100644 --- a/dependencies/ih264d/common/x86/ih264_chroma_intra_pred_filters_ssse3.c +++ b/dependencies/ih264d/common/x86/ih264_chroma_intra_pred_filters_ssse3.c @@ -56,6 +56,11 @@ #include "ih264_platform_macros.h" #include "ih264_intra_pred_filters.h" +#ifdef __GNUC__ +#define ATTRIBUTE_SSSE3 __attribute__((target("ssse3"))) +#else +#define ATTRIBUTE_SSSE3 +#endif /*****************************************************************************/ /* Chroma Intra prediction 8x8 filters */ @@ -93,6 +98,8 @@ * ****************************************************************************** */ + +ATTRIBUTE_SSSE3 void ih264_intra_pred_chroma_8x8_mode_horz_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -169,6 +176,8 @@ void ih264_intra_pred_chroma_8x8_mode_horz_ssse3(UWORD8 *pu1_src, * ******************************************************************************* */ + +ATTRIBUTE_SSSE3 void ih264_intra_pred_chroma_8x8_mode_vert_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -237,6 +246,8 @@ void ih264_intra_pred_chroma_8x8_mode_vert_ssse3(UWORD8 *pu1_src, * ****************************************************************************** */ + +ATTRIBUTE_SSSE3 void ih264_intra_pred_chroma_8x8_mode_plane_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, diff --git a/dependencies/ih264d/common/x86/ih264_deblk_chroma_ssse3.c b/dependencies/ih264d/common/x86/ih264_deblk_chroma_ssse3.c index a36447a2..d73d9d35 100644 --- a/dependencies/ih264d/common/x86/ih264_deblk_chroma_ssse3.c +++ b/dependencies/ih264d/common/x86/ih264_deblk_chroma_ssse3.c @@ -53,6 +53,12 @@ #include "ih264_deblk_edge_filters.h" #include "ih264_macros.h" +#ifdef __GNUC__ +#define ATTRIBUTE_SSSE3 __attribute__((target("ssse3"))) +#else +#define ATTRIBUTE_SSSE3 +#endif + /*****************************************************************************/ /* Function Definitions */ /*****************************************************************************/ @@ -91,6 +97,8 @@ /* 12 02 2015 Naveen Kumar P Initial version */ /* */ /*****************************************************************************/ + +ATTRIBUTE_SSSE3 void ih264_deblk_chroma_vert_bs4_ssse3(UWORD8 *pu1_src, WORD32 src_strd, WORD32 alpha_cb, @@ -274,6 +282,8 @@ void ih264_deblk_chroma_vert_bs4_ssse3(UWORD8 *pu1_src, /* 12 02 2015 Naveen Kumar P Initial version */ /* */ /*****************************************************************************/ + +ATTRIBUTE_SSSE3 void ih264_deblk_chroma_horz_bs4_ssse3(UWORD8 *pu1_src, WORD32 src_strd, WORD32 alpha_cb, @@ -424,6 +434,8 @@ void ih264_deblk_chroma_horz_bs4_ssse3(UWORD8 *pu1_src, /* 12 02 2015 Naveen Kumar P Initial version */ /* */ /*****************************************************************************/ + +ATTRIBUTE_SSSE3 void ih264_deblk_chroma_vert_bslt4_ssse3(UWORD8 *pu1_src, WORD32 src_strd, WORD32 alpha_cb, @@ -645,6 +657,8 @@ void ih264_deblk_chroma_vert_bslt4_ssse3(UWORD8 *pu1_src, /* 12 02 2015 Naveen Kumar P Initial version */ /* */ /*****************************************************************************/ + +ATTRIBUTE_SSSE3 void ih264_deblk_chroma_horz_bslt4_ssse3(UWORD8 *pu1_src, WORD32 src_strd, WORD32 alpha_cb, @@ -829,6 +843,8 @@ void ih264_deblk_chroma_horz_bslt4_ssse3(UWORD8 *pu1_src, /* 12 02 2015 Naveen Kumar P Initial version */ /* */ /*****************************************************************************/ + +ATTRIBUTE_SSSE3 void ih264_deblk_chroma_vert_bs4_mbaff_ssse3(UWORD8 *pu1_src, WORD32 src_strd, WORD32 alpha_cb, @@ -963,6 +979,8 @@ void ih264_deblk_chroma_vert_bs4_mbaff_ssse3(UWORD8 *pu1_src, /* 12 02 2015 Naveen Kumar P Initial version */ /* */ /*****************************************************************************/ + +ATTRIBUTE_SSSE3 void ih264_deblk_chroma_vert_bslt4_mbaff_ssse3(UWORD8 *pu1_src, WORD32 src_strd, WORD32 alpha_cb, diff --git a/dependencies/ih264d/common/x86/ih264_deblk_luma_ssse3.c b/dependencies/ih264d/common/x86/ih264_deblk_luma_ssse3.c index e29bebbe..c135f6b6 100644 --- a/dependencies/ih264d/common/x86/ih264_deblk_luma_ssse3.c +++ b/dependencies/ih264d/common/x86/ih264_deblk_luma_ssse3.c @@ -53,6 +53,12 @@ #include "ih264_deblk_edge_filters.h" #include "ih264_macros.h" +#ifdef __GNUC__ +#define ATTRIBUTE_SSSE3 __attribute__((target("ssse3"))) +#else +#define ATTRIBUTE_SSSE3 +#endif + /*****************************************************************************/ /* Function Definitions */ /*****************************************************************************/ @@ -87,6 +93,7 @@ /* 12 02 2015 Naveen Kumar P Initial version */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_deblk_luma_vert_bs4_ssse3(UWORD8 *pu1_src, WORD32 src_strd, WORD32 alpha, @@ -508,6 +515,7 @@ void ih264_deblk_luma_vert_bs4_ssse3(UWORD8 *pu1_src, /* 12 02 2015 Naveen Kumar P Initial version */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_deblk_luma_horz_bs4_ssse3(UWORD8 *pu1_src, WORD32 src_strd, WORD32 alpha, @@ -847,6 +855,7 @@ void ih264_deblk_luma_horz_bs4_ssse3(UWORD8 *pu1_src, /* 12 02 2015 Naveen Kumar P Initial version */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_deblk_luma_vert_bslt4_ssse3(UWORD8 *pu1_src, WORD32 src_strd, WORD32 alpha, @@ -1142,6 +1151,7 @@ void ih264_deblk_luma_vert_bslt4_ssse3(UWORD8 *pu1_src, /* 12 02 2015 Naveen Kumar P Initial version */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_deblk_luma_horz_bslt4_ssse3(UWORD8 *pu1_src, WORD32 src_strd, WORD32 alpha, @@ -1439,6 +1449,7 @@ void ih264_deblk_luma_horz_bslt4_ssse3(UWORD8 *pu1_src, /* 12 02 2015 Naveen Kumar P Initial version */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_deblk_luma_vert_bs4_mbaff_ssse3(UWORD8 *pu1_src, WORD32 src_strd, WORD32 alpha, @@ -1758,6 +1769,7 @@ void ih264_deblk_luma_vert_bs4_mbaff_ssse3(UWORD8 *pu1_src, /* 12 02 2015 Naveen Kumar P Initial version */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_deblk_luma_vert_bslt4_mbaff_ssse3(UWORD8 *pu1_src, WORD32 src_strd, WORD32 alpha, diff --git a/dependencies/ih264d/common/x86/ih264_ihadamard_scaling_sse42.c b/dependencies/ih264d/common/x86/ih264_ihadamard_scaling_sse42.c index 3c4bb1c6..bf8e88ed 100644 --- a/dependencies/ih264d/common/x86/ih264_ihadamard_scaling_sse42.c +++ b/dependencies/ih264d/common/x86/ih264_ihadamard_scaling_sse42.c @@ -52,6 +52,12 @@ #include #include +#ifdef __GNUC__ +#define ATTRIBUTE_SSE42 __attribute__((target("sse4.2"))) +#else +#define ATTRIBUTE_SSE42 +#endif + /* ******************************************************************************** * @@ -87,6 +93,7 @@ * ******************************************************************************* */ +ATTRIBUTE_SSE42 void ih264_ihadamard_scaling_4x4_sse42(WORD16* pi2_src, WORD16* pi2_out, const UWORD16 *pu2_iscal_mat, @@ -202,6 +209,7 @@ void ih264_ihadamard_scaling_4x4_sse42(WORD16* pi2_src, _mm_storeu_si128((__m128i *) (&pi2_out[8]), src_r2_r3); } +ATTRIBUTE_SSE42 void ih264_ihadamard_scaling_2x2_uv_sse42(WORD16* pi2_src, WORD16* pi2_out, const UWORD16 *pu2_iscal_mat, diff --git a/dependencies/ih264d/common/x86/ih264_ihadamard_scaling_ssse3.c b/dependencies/ih264d/common/x86/ih264_ihadamard_scaling_ssse3.c index b4d483f1..4dc6d827 100644 --- a/dependencies/ih264d/common/x86/ih264_ihadamard_scaling_ssse3.c +++ b/dependencies/ih264d/common/x86/ih264_ihadamard_scaling_ssse3.c @@ -50,6 +50,12 @@ #include "ih264_trans_quant_itrans_iquant.h" #include +#ifdef __GNUC__ +#define ATTRIBUTE_SSSE3 __attribute__((target("ssse3"))) +#else +#define ATTRIBUTE_SSSE3 +#endif + /* ******************************************************************************** * @@ -85,6 +91,7 @@ * ******************************************************************************* */ +ATTRIBUTE_SSSE3 void ih264_ihadamard_scaling_4x4_ssse3(WORD16* pi2_src, WORD16* pi2_out, const UWORD16 *pu2_iscal_mat, diff --git a/dependencies/ih264d/common/x86/ih264_inter_pred_filters_ssse3.c b/dependencies/ih264d/common/x86/ih264_inter_pred_filters_ssse3.c index 480a8c7c..7927eeb6 100644 --- a/dependencies/ih264d/common/x86/ih264_inter_pred_filters_ssse3.c +++ b/dependencies/ih264d/common/x86/ih264_inter_pred_filters_ssse3.c @@ -54,6 +54,12 @@ #include "ih264_platform_macros.h" #include "ih264_inter_pred_filters.h" +#ifdef __GNUC__ +#define ATTRIBUTE_SSSE3 __attribute__((target("ssse3"))) +#else +#define ATTRIBUTE_SSSE3 +#endif + /*****************************************************************************/ /* Constant Data variables */ /*****************************************************************************/ @@ -87,6 +93,7 @@ /* Senthoor */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_inter_pred_luma_copy_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -213,6 +220,7 @@ void ih264_inter_pred_luma_copy_ssse3(UWORD8 *pu1_src, /* Senthoor */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_inter_pred_luma_horz_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -478,6 +486,7 @@ void ih264_inter_pred_luma_horz_ssse3(UWORD8 *pu1_src, /* Senthoor */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_inter_pred_luma_vert_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -764,6 +773,7 @@ void ih264_inter_pred_luma_vert_ssse3(UWORD8 *pu1_src, /* Senthoor */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_inter_pred_luma_horz_hpel_vert_hpel_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -1488,6 +1498,7 @@ void ih264_inter_pred_luma_horz_hpel_vert_hpel_ssse3(UWORD8 *pu1_src, /* Senthoor */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_inter_pred_luma_horz_qpel_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -1782,6 +1793,7 @@ void ih264_inter_pred_luma_horz_qpel_ssse3(UWORD8 *pu1_src, /* Senthoor */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_inter_pred_luma_vert_qpel_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -2107,6 +2119,7 @@ void ih264_inter_pred_luma_vert_qpel_ssse3(UWORD8 *pu1_src, /* Senthoor */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_inter_pred_luma_horz_qpel_vert_qpel_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -2675,6 +2688,7 @@ void ih264_inter_pred_luma_horz_qpel_vert_qpel_ssse3(UWORD8 *pu1_src, /* Senthoor */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_inter_pred_luma_horz_qpel_vert_hpel_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -3285,6 +3299,7 @@ void ih264_inter_pred_luma_horz_qpel_vert_hpel_ssse3(UWORD8 *pu1_src, /* Senthoor */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_inter_pred_luma_horz_hpel_vert_qpel_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -3991,6 +4006,7 @@ void ih264_inter_pred_luma_horz_hpel_vert_qpel_ssse3(UWORD8 *pu1_src, /* Senthoor */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_inter_pred_chroma_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, diff --git a/dependencies/ih264d/common/x86/ih264_iquant_itrans_recon_dc_ssse3.c b/dependencies/ih264d/common/x86/ih264_iquant_itrans_recon_dc_ssse3.c index bcfe503f..10dd2647 100644 --- a/dependencies/ih264d/common/x86/ih264_iquant_itrans_recon_dc_ssse3.c +++ b/dependencies/ih264d/common/x86/ih264_iquant_itrans_recon_dc_ssse3.c @@ -50,6 +50,12 @@ #include "ih264_trans_quant_itrans_iquant.h" #include +#ifdef __GNUC__ +#define ATTRIBUTE_SSSE3 __attribute__((target("ssse3"))) +#else +#define ATTRIBUTE_SSSE3 +#endif + /* ******************************************************************************** * @@ -98,6 +104,7 @@ * ******************************************************************************* */ +ATTRIBUTE_SSSE3 void ih264_iquant_itrans_recon_4x4_dc_ssse3(WORD16 *pi2_src, UWORD8 *pu1_pred, UWORD8 *pu1_out, @@ -224,6 +231,7 @@ void ih264_iquant_itrans_recon_4x4_dc_ssse3(WORD16 *pi2_src, ******************************************************************************* */ +ATTRIBUTE_SSSE3 void ih264_iquant_itrans_recon_8x8_dc_ssse3 (WORD16 *pi2_src, UWORD8 *pu1_pred, UWORD8 *pu1_out, @@ -385,6 +393,7 @@ void ih264_iquant_itrans_recon_8x8_dc_ssse3 (WORD16 *pi2_src, * ******************************************************************************* */ +ATTRIBUTE_SSSE3 void ih264_iquant_itrans_recon_chroma_4x4_dc_ssse3(WORD16 *pi2_src, UWORD8 *pu1_pred, UWORD8 *pu1_out, diff --git a/dependencies/ih264d/common/x86/ih264_iquant_itrans_recon_sse42.c b/dependencies/ih264d/common/x86/ih264_iquant_itrans_recon_sse42.c index a7b9e824..e97ca4d0 100644 --- a/dependencies/ih264d/common/x86/ih264_iquant_itrans_recon_sse42.c +++ b/dependencies/ih264d/common/x86/ih264_iquant_itrans_recon_sse42.c @@ -50,6 +50,12 @@ #include "ih264_trans_quant_itrans_iquant.h" #include +#ifdef __GNUC__ +#define ATTRIBUTE_SSE42 __attribute__((target("sse4.2"))) +#else +#define ATTRIBUTE_SSE42 +#endif + /* ******************************************************************************** * @@ -97,6 +103,7 @@ * ******************************************************************************* */ +ATTRIBUTE_SSE42 void ih264_iquant_itrans_recon_4x4_sse42(WORD16 *pi2_src, UWORD8 *pu1_pred, UWORD8 *pu1_out, @@ -348,6 +355,7 @@ void ih264_iquant_itrans_recon_4x4_sse42(WORD16 *pi2_src, * ******************************************************************************* */ +ATTRIBUTE_SSE42 void ih264_iquant_itrans_recon_chroma_4x4_sse42(WORD16 *pi2_src, UWORD8 *pu1_pred, UWORD8 *pu1_out, diff --git a/dependencies/ih264d/common/x86/ih264_iquant_itrans_recon_ssse3.c b/dependencies/ih264d/common/x86/ih264_iquant_itrans_recon_ssse3.c index 506be495..96773253 100644 --- a/dependencies/ih264d/common/x86/ih264_iquant_itrans_recon_ssse3.c +++ b/dependencies/ih264d/common/x86/ih264_iquant_itrans_recon_ssse3.c @@ -50,6 +50,12 @@ #include "ih264_trans_quant_itrans_iquant.h" #include +#ifdef __GNUC__ +#define ATTRIBUTE_SSSE3 __attribute__((target("ssse3"))) +#else +#define ATTRIBUTE_SSSE3 +#endif + /* ******************************************************************************** * @@ -97,6 +103,7 @@ * ******************************************************************************* */ +ATTRIBUTE_SSSE3 void ih264_iquant_itrans_recon_4x4_ssse3(WORD16 *pi2_src, UWORD8 *pu1_pred, UWORD8 *pu1_out, @@ -366,6 +373,7 @@ void ih264_iquant_itrans_recon_4x4_ssse3(WORD16 *pi2_src, ******************************************************************************* */ +ATTRIBUTE_SSSE3 void ih264_iquant_itrans_recon_8x8_ssse3(WORD16 *pi2_src, UWORD8 *pu1_pred, UWORD8 *pu1_out, diff --git a/dependencies/ih264d/common/x86/ih264_luma_intra_pred_filters_ssse3.c b/dependencies/ih264d/common/x86/ih264_luma_intra_pred_filters_ssse3.c index a1721d52..417e986b 100644 --- a/dependencies/ih264d/common/x86/ih264_luma_intra_pred_filters_ssse3.c +++ b/dependencies/ih264d/common/x86/ih264_luma_intra_pred_filters_ssse3.c @@ -75,6 +75,12 @@ #include "ih264_platform_macros.h" #include "ih264_intra_pred_filters.h" +#ifdef __GNUC__ +#define ATTRIBUTE_SSSE3 __attribute__((target("ssse3"))) +#else +#define ATTRIBUTE_SSSE3 +#endif + /******************* LUMA INTRAPREDICTION *******************/ @@ -114,6 +120,7 @@ * ******************************************************************************* */ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_4x4_mode_vert_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -173,6 +180,7 @@ void ih264_intra_pred_luma_4x4_mode_vert_ssse3(UWORD8 *pu1_src, * ******************************************************************************* */ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_4x4_mode_horz_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -238,6 +246,7 @@ void ih264_intra_pred_luma_4x4_mode_horz_ssse3(UWORD8 *pu1_src, * None * *******************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_4x4_mode_dc_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -316,6 +325,7 @@ void ih264_intra_pred_luma_4x4_mode_dc_ssse3(UWORD8 *pu1_src, * None * *******************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_4x4_mode_diag_dl_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -400,6 +410,7 @@ void ih264_intra_pred_luma_4x4_mode_diag_dl_ssse3(UWORD8 *pu1_src, * None * *******************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_4x4_mode_diag_dr_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -487,6 +498,7 @@ void ih264_intra_pred_luma_4x4_mode_diag_dr_ssse3(UWORD8 *pu1_src, * None * *******************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_4x4_mode_vert_r_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -579,6 +591,7 @@ void ih264_intra_pred_luma_4x4_mode_vert_r_ssse3(UWORD8 *pu1_src, * None * *******************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_4x4_mode_horz_d_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -675,6 +688,7 @@ void ih264_intra_pred_luma_4x4_mode_horz_d_ssse3(UWORD8 *pu1_src, * None * *******************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_4x4_mode_vert_l_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -764,6 +778,7 @@ void ih264_intra_pred_luma_4x4_mode_vert_l_ssse3(UWORD8 *pu1_src, * None * *******************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_4x4_mode_horz_u_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -864,6 +879,7 @@ void ih264_intra_pred_luma_4x4_mode_horz_u_ssse3(UWORD8 *pu1_src, * ******************************************************************************* */ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_8x8_mode_vert_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -921,6 +937,7 @@ void ih264_intra_pred_luma_8x8_mode_vert_ssse3(UWORD8 *pu1_src, * ******************************************************************************* */ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_8x8_mode_horz_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -985,6 +1002,7 @@ void ih264_intra_pred_luma_8x8_mode_horz_ssse3(UWORD8 *pu1_src, * None * *******************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_8x8_mode_dc_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -1078,6 +1096,7 @@ void ih264_intra_pred_luma_8x8_mode_dc_ssse3(UWORD8 *pu1_src, * None * *******************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_8x8_mode_diag_dl_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -1176,6 +1195,7 @@ void ih264_intra_pred_luma_8x8_mode_diag_dl_ssse3(UWORD8 *pu1_src, * None * *******************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_8x8_mode_diag_dr_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -1278,6 +1298,7 @@ void ih264_intra_pred_luma_8x8_mode_diag_dr_ssse3(UWORD8 *pu1_src, * None * *******************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_8x8_mode_vert_r_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -1398,6 +1419,7 @@ void ih264_intra_pred_luma_8x8_mode_vert_r_ssse3(UWORD8 *pu1_src, * None * *******************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_8x8_mode_horz_d_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -1502,6 +1524,7 @@ void ih264_intra_pred_luma_8x8_mode_horz_d_ssse3(UWORD8 *pu1_src, * *******************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_8x8_mode_vert_l_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -1598,6 +1621,7 @@ void ih264_intra_pred_luma_8x8_mode_vert_l_ssse3(UWORD8 *pu1_src, * None * *******************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_8x8_mode_horz_u_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -1699,6 +1723,7 @@ void ih264_intra_pred_luma_8x8_mode_horz_u_ssse3(UWORD8 *pu1_src, * None * *******************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_16x16_mode_vert_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -1778,6 +1803,7 @@ void ih264_intra_pred_luma_16x16_mode_vert_ssse3(UWORD8 *pu1_src, * None * *******************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_16x16_mode_horz_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -1875,6 +1901,7 @@ void ih264_intra_pred_luma_16x16_mode_horz_ssse3(UWORD8 *pu1_src, * None * *******************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_16x16_mode_dc_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -1998,6 +2025,7 @@ void ih264_intra_pred_luma_16x16_mode_dc_ssse3(UWORD8 *pu1_src, * None * *******************************************************************************/ +ATTRIBUTE_SSSE3 void ih264_intra_pred_luma_16x16_mode_plane_ssse3(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, diff --git a/dependencies/ih264d/common/x86/ih264_mem_fns_ssse3.c b/dependencies/ih264d/common/x86/ih264_mem_fns_ssse3.c index 8ca1f3e5..be3d622c 100644 --- a/dependencies/ih264d/common/x86/ih264_mem_fns_ssse3.c +++ b/dependencies/ih264d/common/x86/ih264_mem_fns_ssse3.c @@ -50,6 +50,12 @@ #include +#ifdef __GNUC__ +#define ATTRIBUTE_SSSE3 __attribute__((target("ssse3"))) +#else +#define ATTRIBUTE_SSSE3 +#endif + /** ******************************************************************************* * @@ -78,6 +84,7 @@ +ATTRIBUTE_SSSE3 void ih264_memcpy_mul_8_ssse3(UWORD8 *pu1_dst, UWORD8 *pu1_src, UWORD32 num_bytes) { int col; @@ -117,6 +124,7 @@ void ih264_memcpy_mul_8_ssse3(UWORD8 *pu1_dst, UWORD8 *pu1_src, UWORD32 num_byte */ +ATTRIBUTE_SSSE3 void ih264_memset_mul_8_ssse3(UWORD8 *pu1_dst, UWORD8 value, UWORD32 num_bytes) { int col; diff --git a/dependencies/ih264d/common/x86/ih264_padding_ssse3.c b/dependencies/ih264d/common/x86/ih264_padding_ssse3.c index 43ded8e7..d2aa368a 100644 --- a/dependencies/ih264d/common/x86/ih264_padding_ssse3.c +++ b/dependencies/ih264d/common/x86/ih264_padding_ssse3.c @@ -49,6 +49,12 @@ #include +#ifdef __GNUC__ +#define ATTRIBUTE_SSSE3 __attribute__((target("ssse3"))) +#else +#define ATTRIBUTE_SSSE3 +#endif + /** ******************************************************************************* @@ -89,6 +95,7 @@ ******************************************************************************* */ +ATTRIBUTE_SSSE3 void ih264_pad_left_luma_ssse3(UWORD8 *pu1_src, WORD32 src_strd, WORD32 ht, @@ -156,6 +163,7 @@ void ih264_pad_left_luma_ssse3(UWORD8 *pu1_src, ******************************************************************************* */ +ATTRIBUTE_SSSE3 void ih264_pad_left_chroma_ssse3(UWORD8 *pu1_src, WORD32 src_strd, WORD32 ht, @@ -222,6 +230,7 @@ void ih264_pad_left_chroma_ssse3(UWORD8 *pu1_src, ******************************************************************************* */ +ATTRIBUTE_SSSE3 void ih264_pad_right_luma_ssse3(UWORD8 *pu1_src, WORD32 src_strd, WORD32 ht, @@ -289,6 +298,7 @@ void ih264_pad_right_luma_ssse3(UWORD8 *pu1_src, ******************************************************************************* */ +ATTRIBUTE_SSSE3 void ih264_pad_right_chroma_ssse3(UWORD8 *pu1_src, WORD32 src_strd, WORD32 ht, diff --git a/dependencies/ih264d/common/x86/ih264_resi_trans_quant_sse42.c b/dependencies/ih264d/common/x86/ih264_resi_trans_quant_sse42.c index f4f5cbfa..fa3442f0 100644 --- a/dependencies/ih264d/common/x86/ih264_resi_trans_quant_sse42.c +++ b/dependencies/ih264d/common/x86/ih264_resi_trans_quant_sse42.c @@ -51,6 +51,12 @@ #include "ih264_structs.h" #include "ih264_trans_quant_itrans_iquant.h" #include + +#ifdef __GNUC__ +#define ATTRIBUTE_SSE42 __attribute__((target("sse4.2"))) +#else +#define ATTRIBUTE_SSE42 +#endif /** ******************************************************************************* * @@ -103,6 +109,7 @@ * ******************************************************************************* */ +ATTRIBUTE_SSE42 void ih264_resi_trans_quant_4x4_sse42(UWORD8 *pu1_src, UWORD8 *pu1_pred, WORD16 *pi2_out, WORD32 src_strd, WORD32 pred_strd, const UWORD16 *pu2_scale_matrix, const UWORD16 *pu2_threshold_matrix, @@ -376,6 +383,7 @@ void ih264_resi_trans_quant_4x4_sse42(UWORD8 *pu1_src, UWORD8 *pu1_pred, * ******************************************************************************* */ +ATTRIBUTE_SSE42 void ih264_resi_trans_quant_chroma_4x4_sse42(UWORD8 *pu1_src,UWORD8 *pu1_pred,WORD16 *pi2_out, WORD32 src_strd,WORD32 pred_strd, const UWORD16 *pu2_scale_matrix, @@ -663,6 +671,7 @@ void ih264_resi_trans_quant_chroma_4x4_sse42(UWORD8 *pu1_src,UWORD8 *pu1_pred,WO * */ +ATTRIBUTE_SSE42 void ih264_hadamard_quant_4x4_sse42(WORD16 *pi2_src, WORD16 *pi2_dst, const UWORD16 *pu2_scale_matrix, const UWORD16 *pu2_threshold_matrix, UWORD32 u4_qbits, @@ -892,6 +901,7 @@ void ih264_hadamard_quant_4x4_sse42(WORD16 *pi2_src, WORD16 *pi2_dst, * */ +ATTRIBUTE_SSE42 void ih264_hadamard_quant_2x2_uv_sse42(WORD16 *pi2_src, WORD16 *pi2_dst, const UWORD16 *pu2_scale_matrix, const UWORD16 *pu2_threshold_matrix, UWORD32 u4_qbits, diff --git a/dependencies/ih264d/common/x86/ih264_weighted_pred_sse42.c b/dependencies/ih264d/common/x86/ih264_weighted_pred_sse42.c index 48f1f542..8e10db28 100644 --- a/dependencies/ih264d/common/x86/ih264_weighted_pred_sse42.c +++ b/dependencies/ih264d/common/x86/ih264_weighted_pred_sse42.c @@ -50,6 +50,12 @@ #include "ih264_platform_macros.h" #include "ih264_weighted_pred.h" +#ifdef __GNUC__ +#define ATTRIBUTE_SSE42 __attribute__((target("sse4.2"))) +#else +#define ATTRIBUTE_SSE42 +#endif + /*****************************************************************************/ /* Function definitions . */ /*****************************************************************************/ @@ -82,6 +88,7 @@ /* Senthoor */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSE42 void ih264_default_weighted_pred_luma_sse42(UWORD8 *pu1_src1, UWORD8 *pu1_src2, UWORD8 *pu1_dst, @@ -245,6 +252,7 @@ void ih264_default_weighted_pred_luma_sse42(UWORD8 *pu1_src1, /* Senthoor */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSE42 void ih264_default_weighted_pred_chroma_sse42(UWORD8 *pu1_src1, UWORD8 *pu1_src2, UWORD8 *pu1_dst, @@ -375,6 +383,7 @@ void ih264_default_weighted_pred_chroma_sse42(UWORD8 *pu1_src1, /* Senthoor */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSE42 void ih264_weighted_pred_luma_sse42(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -605,6 +614,7 @@ void ih264_weighted_pred_luma_sse42(UWORD8 *pu1_src, /* Senthoor */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSE42 void ih264_weighted_pred_chroma_sse42(UWORD8 *pu1_src, UWORD8 *pu1_dst, WORD32 src_strd, @@ -814,6 +824,7 @@ void ih264_weighted_pred_chroma_sse42(UWORD8 *pu1_src, /* Senthoor */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSE42 void ih264_weighted_bi_pred_luma_sse42(UWORD8 *pu1_src1, UWORD8 *pu1_src2, UWORD8 *pu1_dst, @@ -1101,6 +1112,7 @@ void ih264_weighted_bi_pred_luma_sse42(UWORD8 *pu1_src1, /* Senthoor */ /* */ /*****************************************************************************/ +ATTRIBUTE_SSE42 void ih264_weighted_bi_pred_chroma_sse42(UWORD8 *pu1_src1, UWORD8 *pu1_src2, UWORD8 *pu1_dst, diff --git a/dependencies/ih264d/decoder/x86/ih264d_function_selector_sse42.c b/dependencies/ih264d/decoder/x86/ih264d_function_selector_sse42.c index 0c493d22..c7636f38 100644 --- a/dependencies/ih264d/decoder/x86/ih264d_function_selector_sse42.c +++ b/dependencies/ih264d/decoder/x86/ih264d_function_selector_sse42.c @@ -60,6 +60,12 @@ #include "ih264d_structs.h" +#ifdef __GNUC__ +#define ATTRIBUTE_SSE42 __attribute__((target("sse4.2"))) +#else +#define ATTRIBUTE_SSE42 +#endif + /** ******************************************************************************* @@ -79,6 +85,7 @@ * ******************************************************************************* */ +ATTRIBUTE_SSE42 void ih264d_init_function_ptr_sse42(dec_struct_t *ps_codec) { ps_codec->pf_default_weighted_pred_luma = ih264_default_weighted_pred_luma_sse42; diff --git a/dependencies/ih264d/decoder/x86/ih264d_function_selector_ssse3.c b/dependencies/ih264d/decoder/x86/ih264d_function_selector_ssse3.c index 17862139..cd8043c6 100644 --- a/dependencies/ih264d/decoder/x86/ih264d_function_selector_ssse3.c +++ b/dependencies/ih264d/decoder/x86/ih264d_function_selector_ssse3.c @@ -60,6 +60,12 @@ #include "ih264d_structs.h" +#ifdef __GNUC__ +#define ATTRIBUTE_SSSE3 __attribute__((target("ssse3"))) +#else +#define ATTRIBUTE_SSSE3 +#endif + /** ******************************************************************************* @@ -79,6 +85,7 @@ * ******************************************************************************* */ +ATTRIBUTE_SSSE3 void ih264d_init_function_ptr_ssse3(dec_struct_t *ps_codec) { From 00968acc1d095324888e95e65275bdd2513156fb Mon Sep 17 00:00:00 2001 From: emiyl Date: Fri, 7 Oct 2022 01:39:06 +0100 Subject: [PATCH 015/616] dedicated decoder for R4G4 and R4G4B4A4 to R8G8B8A8 (#331) --- src/Cafe/HW/Latte/Core/LatteTextureLoader.h | 117 +++++++++++++++++- .../Latte/Renderer/OpenGL/OpenGLRenderer.cpp | 6 +- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 12 +- 3 files changed, 121 insertions(+), 14 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/LatteTextureLoader.h b/src/Cafe/HW/Latte/Core/LatteTextureLoader.h index 82144f3a..353ea924 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureLoader.h +++ b/src/Cafe/HW/Latte/Core/LatteTextureLoader.h @@ -556,7 +556,7 @@ public: } }; -class TextureDecoder_R4_G4_UNORM_toRGBA4444 : public TextureDecoder, public SingletonClass +class TextureDecoder_R4_G4_UNORM_To_RGBA4 : public TextureDecoder, public SingletonClass { public: sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override @@ -594,7 +594,7 @@ public: } }; -class TextureDecoder_R4_G4_UNORM_toRGBA4444_vk : public TextureDecoder, public SingletonClass +class TextureDecoder_R4_G4_UNORM_To_RGBA4_vk : public TextureDecoder, public SingletonClass { public: sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override @@ -632,6 +632,52 @@ public: } }; +class TextureDecoder_R4G4_UNORM_To_RGBA8 : public TextureDecoder, public SingletonClass +{ +public: + sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override + { + return 4; + } + + void decode(LatteTextureLoaderCtx* textureLoader, uint8* outputData) override + { + for (sint32 y = 0; y < textureLoader->height; y += textureLoader->stepY) + { + sint32 yc = y; + for (sint32 x = 0; x < textureLoader->width; x += textureLoader->stepX) + { + uint8* blockData = LatteTextureLoader_GetInput(textureLoader, x, y); + sint32 pixelOffset = (x + yc * textureLoader->width) * 4; + uint8 v0 = (*(uint8*)(blockData + 0)); + + uint8 red4 = (v0 >> 4) & 0xF; + uint8 green4 = (v0 & 0xF); + + red4 = (red4 << 4) | red4; + green4 = (green4 << 4) | green4; + + *(uint8*)(outputData + pixelOffset + 0) = red4; + *(uint8*)(outputData + pixelOffset + 1) = green4; + *(uint8*)(outputData + pixelOffset + 2) = 0; + *(uint8*)(outputData + pixelOffset + 3) = 255; + } + } + } + + void decodePixelToRGBA(uint8* blockData, uint8* outputPixel, uint8 blockOffsetX, uint8 blockOffsetY) override + { + uint8 v0 = *(blockData + 0); + uint8 red4 = (v0 >> 4) & 0xF; + uint8 green4 = (v0 & 0xF); + red4 = (red4 << 4) | red4; + green4 = (green4 << 4) | green4; + *(outputPixel + 0) = red4; + *(outputPixel + 1) = green4; + *(outputPixel + 2) = 0; + *(outputPixel + 3) = 255; + } +}; class TextureDecoder_R4_G4_B4_A4_UNORM : public TextureDecoder, public SingletonClass { @@ -677,6 +723,67 @@ public: } }; + +class TextureDecoder_R4G4B4A4_UNORM_To_RGBA8 : public TextureDecoder, public SingletonClass +{ +public: + sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override + { + return 4; + } + + void decode(LatteTextureLoaderCtx* textureLoader, uint8* outputData) override + { + for (sint32 y = 0; y < textureLoader->height; y += textureLoader->stepY) + { + sint32 yc = y; + for (sint32 x = 0; x < textureLoader->width; x += textureLoader->stepX) + { + uint8* blockData = LatteTextureLoader_GetInput(textureLoader, x, y); + sint32 pixelOffset = (x + yc * textureLoader->width) * 4; + uint8 v0 = (*(uint8*)(blockData + 0)); + uint8 v1 = (*(uint8*)(blockData + 1)); + + uint8 red4 = (v0 & 0xF); + uint8 green4 = (v0 >> 4) & 0xF; + uint8 blue4 = (v1) & 0xF; + uint8 alpha4 = (v1 >> 4) & 0xF; + + red4 = (red4 << 4) | red4; + green4 = (green4 << 4) | green4; + blue4 = (blue4 << 4) | blue4; + alpha4 = (alpha4 << 4) | alpha4; + + *(uint8*)(outputData + pixelOffset + 0) = red4; + *(uint8*)(outputData + pixelOffset + 1) = green4; + *(uint8*)(outputData + pixelOffset + 2) = blue4; + *(uint8*)(outputData + pixelOffset + 3) = alpha4; + } + } + } + + void decodePixelToRGBA(uint8* blockData, uint8* outputPixel, uint8 blockOffsetX, uint8 blockOffsetY) override + { + uint8 v0 = *(blockData + 0); + uint8 v1 = *(blockData + 1); + + uint8 red4 = (v0 & 0xF); + uint8 green4 = (v0 >> 4) & 0xF; + uint8 blue4 = (v1) & 0xF; + uint8 alpha4 = (v1 >> 4) & 0xF; + + red4 = (red4 << 4) | red4; + green4 = (green4 << 4) | green4; + blue4 = (blue4 << 4) | blue4; + alpha4 = (alpha4 << 4) | alpha4; + + *(outputPixel + 0) = red4; + *(outputPixel + 1) = green4; + *(outputPixel + 2) = blue4; + *(outputPixel + 3) = alpha4; + } +}; + class TextureDecoder_R8_G8_B8_A8 : public TextureDecoder, public SingletonClass { public: @@ -978,7 +1085,7 @@ public: } }; -class TextureDecoder_R5G6B5_UNORM_To_R8G8B8A8_UNORM : public TextureDecoder, public SingletonClass +class TextureDecoder_R5G6B5_UNORM_To_RGBA8 : public TextureDecoder, public SingletonClass { public: sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override @@ -1236,7 +1343,7 @@ public: } }; -class TextureDecoder_R10_G10_B10_A2_SNORM_toRGBA16 : public TextureDecoder, public SingletonClass +class TextureDecoder_R10_G10_B10_A2_SNORM_To_RGBA16 : public TextureDecoder, public SingletonClass { public: sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override @@ -1281,7 +1388,7 @@ public: } }; -class TextureDecoder_A2_B10_G10_R10_UNORM_toRGBA16 : public TextureDecoder, public SingletonClass +class TextureDecoder_A2_B10_G10_R10_UNORM_To_RGBA16 : public TextureDecoder, public SingletonClass { public: sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp index 269f30e8..c72d52ca 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp @@ -878,7 +878,7 @@ TextureDecoder* OpenGLRenderer::texture_chooseDecodedFormat(Latte::E_GX2SURFFMT } if (format == Latte::E_GX2SURFFMT::R4_G4_UNORM) - texDecoder = TextureDecoder_R4_G4_UNORM_toRGBA4444::getInstance(); + texDecoder = TextureDecoder_R4_G4_UNORM_To_RGBA4::getInstance(); else if (format == Latte::E_GX2SURFFMT::R4_G4_B4_A4_UNORM) texDecoder = TextureDecoder_R4_G4_B4_A4_UNORM::getInstance(); else if (format == Latte::E_GX2SURFFMT::R16_G16_B16_A16_FLOAT) @@ -964,9 +964,9 @@ TextureDecoder* OpenGLRenderer::texture_chooseDecodedFormat(Latte::E_GX2SURFFMT else if (format == Latte::E_GX2SURFFMT::R10_G10_B10_A2_UNORM) texDecoder = TextureDecoder_R10_G10_B10_A2_UNORM::getInstance(); else if (format == Latte::E_GX2SURFFMT::A2_B10_G10_R10_UNORM) - texDecoder = TextureDecoder_A2_B10_G10_R10_UNORM_toRGBA16::getInstance(); + texDecoder = TextureDecoder_A2_B10_G10_R10_UNORM_To_RGBA16::getInstance(); else if (format == Latte::E_GX2SURFFMT::R10_G10_B10_A2_SNORM) - texDecoder = TextureDecoder_R10_G10_B10_A2_SNORM_toRGBA16::getInstance(); + texDecoder = TextureDecoder_R10_G10_B10_A2_SNORM_To_RGBA16::getInstance(); else if (format == Latte::E_GX2SURFFMT::R10_G10_B10_A2_SRGB) texDecoder = TextureDecoder_R10_G10_B10_A2_UNORM::getInstance(); else if (format == Latte::E_GX2SURFFMT::R11_G11_B10_FLOAT) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 33cdbd0e..759dac59 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -2588,11 +2588,11 @@ void VulkanRenderer::GetTextureFormatInfoVK(Latte::E_GX2SURFFMT format, bool isD { if (m_supportedFormatInfo.fmt_r4g4b4a4_unorm_pack == false) { formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_UNORM; - formatInfoOut->decoder = nullptr; + formatInfoOut->decoder = TextureDecoder_R4G4_UNORM_To_RGBA8::getInstance(); } else { formatInfoOut->vkImageFormat = VK_FORMAT_R4G4B4A4_UNORM_PACK16; - formatInfoOut->decoder = TextureDecoder_R4_G4_UNORM_toRGBA4444_vk::getInstance(); + formatInfoOut->decoder = TextureDecoder_R4_G4_UNORM_To_RGBA4_vk::getInstance(); } } else @@ -2642,7 +2642,7 @@ void VulkanRenderer::GetTextureFormatInfoVK(Latte::E_GX2SURFFMT format, bool isD case Latte::E_GX2SURFFMT::R5_G6_B5_UNORM: if (m_supportedFormatInfo.fmt_r5g6b5_unorm_pack == false) { formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_UNORM; - formatInfoOut->decoder = TextureDecoder_R5G6B5_UNORM_To_R8G8B8A8_UNORM::getInstance(); + formatInfoOut->decoder = TextureDecoder_R5G6B5_UNORM_To_RGBA8::getInstance(); } else { // Vulkan has R in MSB, GPU7 has it in LSB @@ -2680,7 +2680,7 @@ void VulkanRenderer::GetTextureFormatInfoVK(Latte::E_GX2SURFFMT format, bool isD case Latte::E_GX2SURFFMT::R4_G4_B4_A4_UNORM: if (m_supportedFormatInfo.fmt_r4g4b4a4_unorm_pack == false) { formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_UNORM; - formatInfoOut->decoder = nullptr; + formatInfoOut->decoder = TextureDecoder_R4G4B4A4_UNORM_To_RGBA8::getInstance(); } else { formatInfoOut->vkImageFormat = VK_FORMAT_R4G4B4A4_UNORM_PACK16; @@ -2694,11 +2694,11 @@ void VulkanRenderer::GetTextureFormatInfoVK(Latte::E_GX2SURFFMT format, bool isD break; case Latte::E_GX2SURFFMT::R10_G10_B10_A2_SNORM: formatInfoOut->vkImageFormat = VK_FORMAT_R16G16B16A16_SNORM; // Vulkan has VK_FORMAT_A2R10G10B10_SNORM_PACK32 but it doesnt work? - formatInfoOut->decoder = TextureDecoder_R10_G10_B10_A2_SNORM_toRGBA16::getInstance(); + formatInfoOut->decoder = TextureDecoder_R10_G10_B10_A2_SNORM_To_RGBA16::getInstance(); break; case Latte::E_GX2SURFFMT::R10_G10_B10_A2_SRGB: //formatInfoOut->vkImageFormat = VK_FORMAT_R16G16B16A16_SNORM; // Vulkan has no uncompressed SRGB format with more than 8 bits per channel - //formatInfoOut->decoder = TextureDecoder_R10_G10_B10_A2_SNORM_toRGBA16::getInstance(); + //formatInfoOut->decoder = TextureDecoder_R10_G10_B10_A2_SNORM_To_RGBA16::getInstance(); //break; formatInfoOut->vkImageFormat = VK_FORMAT_A2B10G10R10_UNORM_PACK32; // todo - verify formatInfoOut->decoder = TextureDecoder_R10_G10_B10_A2_UNORM::getInstance(); From 551f82110974b515f07c58130dd29447e305e1b1 Mon Sep 17 00:00:00 2001 From: bslhq Date: Fri, 7 Oct 2022 20:30:06 +0800 Subject: [PATCH 016/616] Auto resize last column (#265) --- src/config/CemuConfig.cpp | 21 ++- src/config/CemuConfig.h | 17 +- src/gui/TitleManager.cpp | 2 +- src/gui/components/wxGameList.cpp | 249 ++++++++++++++++++------------ src/gui/components/wxGameList.h | 9 +- 5 files changed, 185 insertions(+), 113 deletions(-) diff --git a/src/config/CemuConfig.cpp b/src/config/CemuConfig.cpp index dcd283ec..b257ead3 100644 --- a/src/config/CemuConfig.cpp +++ b/src/config/CemuConfig.cpp @@ -89,12 +89,21 @@ void CemuConfig::Load(XMLConfigParser& parser) auto gamelist = parser.get("GameList"); game_list_style = gamelist.get("style", 0); game_list_column_order = gamelist.get("order", ""); - column_width.name = gamelist.get("name_width", -3); - column_width.version = gamelist.get("version_width", -3); - column_width.dlc = gamelist.get("dlc_width", -3); - column_width.game_time = gamelist.get("game_time_width", -3); - column_width.game_started = gamelist.get("game_started_width", -3); - column_width.region = gamelist.get("region_width", -3); + + // return default width if value in config file out of range + auto loadColumnSize = [&gamelist] (const char *name, uint32 defaultWidth) + { + sint64 val = gamelist.get(name, DefaultColumnSize::name); + if (val < 0 || val > (sint64) std::numeric_limits::max) + return defaultWidth; + return static_cast(val); + }; + column_width.name = loadColumnSize("name_width", DefaultColumnSize::name); + column_width.version = loadColumnSize("version_width", DefaultColumnSize::version); + column_width.dlc = loadColumnSize("dlc_width", DefaultColumnSize::dlc); + column_width.game_time = loadColumnSize("game_time_width", DefaultColumnSize::game_time); + column_width.game_started = loadColumnSize("game_started_width", DefaultColumnSize::game_started); + column_width.region = loadColumnSize("region_width", DefaultColumnSize::region); recent_launch_files.clear(); auto launch_parser = parser.get("RecentLaunchFiles"); diff --git a/src/config/CemuConfig.h b/src/config/CemuConfig.h index caabf082..e7cb1ca5 100644 --- a/src/config/CemuConfig.h +++ b/src/config/CemuConfig.h @@ -334,6 +334,16 @@ struct fmt::formatter : formatter { }; #endif +namespace DefaultColumnSize { + enum : uint32 { + name = 500u, + version = 60u, + dlc = 50u, + game_time = 140u, + game_started = 160u, + region = 80u, + }; +}; struct CemuConfig { @@ -402,7 +412,12 @@ struct CemuConfig std::string game_list_column_order; struct { - int name = -3, version = -3, dlc = -3, game_time = -3, game_started = -3, region = -3; + uint32 name = DefaultColumnSize::name; + uint32 version = DefaultColumnSize::version; + uint32 dlc = DefaultColumnSize::dlc; + uint32 game_time = DefaultColumnSize::game_time; + uint32 game_started = DefaultColumnSize::game_started; + uint32 region = DefaultColumnSize::region; } column_width{}; // graphics diff --git a/src/gui/TitleManager.cpp b/src/gui/TitleManager.cpp index 73e9d45c..519faf2b 100644 --- a/src/gui/TitleManager.cpp +++ b/src/gui/TitleManager.cpp @@ -219,7 +219,7 @@ wxPanel* TitleManager::CreateDownloadManagerPage() } TitleManager::TitleManager(wxWindow* parent, TitleManagerPage default_page) - : wxFrame(parent, wxID_ANY, _("Title manager"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL) + : wxFrame(parent, wxID_ANY, _("Title Manager"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL) { SetIcon(wxICON(X_BOX)); diff --git a/src/gui/components/wxGameList.cpp b/src/gui/components/wxGameList.cpp index 074722e0..904fd5b7 100644 --- a/src/gui/components/wxGameList.cpp +++ b/src/gui/components/wxGameList.cpp @@ -50,7 +50,16 @@ void _stripPathFilename(fs::path& path) wxGameList::wxGameList(wxWindow* parent, wxWindowID id) : wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize, GetStyleFlags(Style::kList)), m_style(Style::kList) { - CreateListColumns(); + const auto& config = GetConfig(); + + InsertColumn(ColumnHiddenName, "", wxLIST_FORMAT_LEFT, 0); + InsertColumn(ColumnIcon, "", wxLIST_FORMAT_LEFT, kListIconWidth); + InsertColumn(ColumnName, _("Game"), wxLIST_FORMAT_LEFT, config.column_width.name); + InsertColumn(ColumnVersion, _("Version"), wxLIST_FORMAT_RIGHT, config.column_width.version); + InsertColumn(ColumnDLC, _("DLC"), wxLIST_FORMAT_RIGHT, config.column_width.dlc); + InsertColumn(ColumnGameTime, _("You've played"), wxLIST_FORMAT_LEFT, config.column_width.game_time); + InsertColumn(ColumnGameStarted, _("Last played"), wxLIST_FORMAT_LEFT, config.column_width.game_started); + InsertColumn(ColumnRegion, _("Region"), wxLIST_FORMAT_LEFT, config.column_width.region); const char transparent_bitmap[kIconWidth * kIconWidth * 4] = {0}; wxBitmap blank(transparent_bitmap, kIconWidth, kIconWidth); @@ -87,6 +96,7 @@ wxGameList::wxGameList(wxWindow* parent, wxWindowID id) Bind(wxEVT_LIST_COL_BEGIN_DRAG, &wxGameList::OnColumnBeginResize, this); Bind(wxEVT_LIST_COL_END_DRAG, &wxGameList::OnColumnResize, this); Bind(wxEVT_LIST_COL_RIGHT_CLICK, &wxGameList::OnColumnRightClick, this); + Bind(wxEVT_SIZE, &wxGameList::OnGameListSize, this); m_callbackIdTitleList = CafeTitleList::RegisterCallback([](CafeTitleListCallbackEvent* evt, void* ctx) { ((wxGameList*)ctx)->HandleTitleListCallback(evt); }, this); @@ -124,7 +134,7 @@ void wxGameList::LoadConfig() if (!config.game_list_column_order.empty()) { wxArrayInt order; - order.reserve(ColumnFavorite); + order.reserve(ColumnCounts); const auto order_string = std::string_view(config.game_list_column_order).substr(1); @@ -135,10 +145,88 @@ void wxGameList::LoadConfig() order.push_back(ConvertString(token, 10)); } - #ifdef wxHAS_LISTCTRL_COLUMN_ORDER - if(order.GetCount() == ColumnFavorite) +#ifdef wxHAS_LISTCTRL_COLUMN_ORDER + if(order.GetCount() == ColumnCounts) SetColumnsOrder(order); - #endif +#endif + } +} + +void wxGameList::OnGameListSize(wxSizeEvent &event) +{ + event.Skip(); + + // when using a sizer-based layout, do not change the size of the wxComponent in its own wxSizeEvent handler to avoid some UI issues. + int last_col_index = 0; + for(int i = GetColumnCount() - 1; i > 0; i--) + { +#ifdef wxHAS_LISTCTRL_COLUMN_ORDER + if(GetColumnWidth(GetColumnIndexFromOrder(i)) > 0) + { + last_col_index = GetColumnIndexFromOrder(i); + break; + } +#else + if(GetColumnWidth(i) > 0) + { + last_col_index = i; + break; + } +#endif + } + wxListEvent column_resize_event(wxEVT_LIST_COL_END_DRAG); + column_resize_event.SetColumn(last_col_index); + wxPostEvent(this, column_resize_event); +} + +void wxGameList::AdjustLastColumnWidth() +{ + wxWindowUpdateLocker windowlock(this); + int last_col_index = 0; + int last_col_width = GetClientSize().GetWidth(); + for (int i = 1; i < GetColumnCount(); i++) + { +#ifdef wxHAS_LISTCTRL_COLUMN_ORDER + if (GetColumnWidth(GetColumnIndexFromOrder(i)) > 0) + { + last_col_index = GetColumnIndexFromOrder(i); + last_col_width -= GetColumnWidth(last_col_index); + } +#else + if (GetColumnWidth(i) > 0) + { + last_col_index = i; + last_col_width -= GetColumnWidth(i); + } +#endif + } + last_col_width += GetColumnWidth(last_col_index); + if (last_col_width < GetColumnDefaultWidth(last_col_index)) // keep a minimum width + last_col_width = GetColumnDefaultWidth(last_col_index); + SetColumnWidth(last_col_index, last_col_width); +} + +// todo: scale all columns using a ratio instead of hardcoding exact widths +int wxGameList::GetColumnDefaultWidth(int column) +{ + switch (column) + { + case ColumnIcon: + return kListIconWidth; + case ColumnName: + return DefaultColumnSize::name; + case ColumnVersion: + return DefaultColumnSize::version; + case ColumnDLC: + return DefaultColumnSize::dlc; + case ColumnGameTime: + return DefaultColumnSize::game_time; + case ColumnGameStarted: + return DefaultColumnSize::game_started; + case ColumnRegion: + return DefaultColumnSize::region; + default: + return 80; } } @@ -339,7 +427,6 @@ void wxGameList::SortEntries(int column) column = s_last_column; else { - if (s_last_column == column) { s_last_column = 0; @@ -366,60 +453,6 @@ void wxGameList::SortEntries(int column) } } -void wxGameList::CreateListColumns() -{ - DeleteAllColumns(); - - const auto& config = GetConfig(); - wxListItem col0; - col0.SetId(ColumnHiddenName); - col0.SetWidth(0); - InsertColumn(ColumnHiddenName, col0); - - wxListItem col1; - col1.SetId(ColumnIcon); - col1.SetWidth(kListIconWidth); - InsertColumn(ColumnIcon, col1); - - wxListItem col2; - col2.SetId(ColumnName); - col2.SetText(_("Game")); - col2.SetWidth(config.column_width.name); - InsertColumn(ColumnName, col2); - - wxListItem col3; - col3.SetId(ColumnVersion); - col3.SetText(_("Version")); - col3.SetWidth(config.column_width.version); - col3.SetAlign(wxLIST_FORMAT_RIGHT); - InsertColumn(ColumnVersion, col3); - - wxListItem col4; - col4.SetId(ColumnDLC); - col4.SetText(_("DLC")); - col4.SetWidth(config.column_width.dlc); - col4.SetAlign(wxLIST_FORMAT_RIGHT); - InsertColumn(ColumnDLC, col4); - - wxListItem col5; - col5.SetId(ColumnGameTime); - col5.SetText(_("You've played")); - col5.SetWidth(config.column_width.game_time); - InsertColumn(ColumnGameTime, col5); - - wxListItem col6; - col6.SetId(ColumnGameStarted); - col6.SetText(_("Last played")); - col6.SetWidth(config.column_width.game_started); - InsertColumn(ColumnGameStarted, col6); - - wxListItem col7; - col7.SetId(ColumnRegion); - col7.SetText(_("Region")); - col7.SetWidth(config.column_width.region); - InsertColumn(ColumnRegion, col7); -} - void wxGameList::OnKeyDown(wxListEvent& event) { event.Skip(); @@ -688,9 +721,7 @@ void wxGameList::OnColumnRightClick(wxListEvent& event) menu.AppendCheckItem(ShowDlc, _("Show &dlc"))->Check(GetColumnWidth(ColumnDLC) > 0); menu.AppendCheckItem(ShowGameTime, _("Show &game time"))->Check(GetColumnWidth(ColumnGameTime) > 0); menu.AppendCheckItem(ShowLastPlayed, _("Show &last played"))->Check(GetColumnWidth(ColumnGameStarted) > 0); - menu.AppendCheckItem(ColumnRegion, _("Show ®ion"))->Check(GetColumnWidth(ColumnRegion) > 0); - //menu.AppendSeparator(); - //menu.Append(ResetOrder, _("&Reset order")); + menu.AppendCheckItem(ShowRegion, _("Show ®ion"))->Check(GetColumnWidth(ColumnRegion) > 0); menu.Bind(wxEVT_COMMAND_MENU_SELECTED, [this](wxCommandEvent& event) { @@ -703,44 +734,46 @@ void wxGameList::OnColumnRightClick(wxListEvent& event) switch (event.GetId()) { case ShowName: - config.column_width.name = menu->IsChecked(ShowName) ? 500 : 0; + config.column_width.name = menu->IsChecked(ShowName) ? DefaultColumnSize::name : 0; break; case ShowVersion: - config.column_width.version = menu->IsChecked(ShowVersion) ? 60 : 0; + config.column_width.version = menu->IsChecked(ShowVersion) ? DefaultColumnSize::version : 0; break; case ShowDlc: - config.column_width.dlc = menu->IsChecked(ShowDlc) ? 50 : 0; + config.column_width.dlc = menu->IsChecked(ShowDlc) ? DefaultColumnSize::dlc : 0; break; case ShowGameTime: - config.column_width.game_time = menu->IsChecked(ShowGameTime) ? 140 : 0; + config.column_width.game_time = menu->IsChecked(ShowGameTime) ? DefaultColumnSize::game_time : 0; break; case ShowLastPlayed: - config.column_width.game_started = menu->IsChecked(ShowLastPlayed) ? 160 : 0; + config.column_width.game_started = menu->IsChecked(ShowLastPlayed) ? DefaultColumnSize::game_started : 0; break; - case ColumnRegion: - config.column_width.region = menu->IsChecked(ColumnRegion) ? 80 : 0; + case ShowRegion: + config.column_width.region = menu->IsChecked(ShowRegion) ? DefaultColumnSize::region : 0; break; case ResetWidth: { switch (column) { + case ColumnIcon: + break; case ColumnName: - config.column_width.name = 500; + config.column_width.name = DefaultColumnSize::name; break; case ColumnVersion: - config.column_width.version = 60; + config.column_width.version = DefaultColumnSize::version; break; case ColumnDLC: - config.column_width.dlc = 50; + config.column_width.dlc = DefaultColumnSize::dlc; break; case ColumnGameTime: - config.column_width.game_time = 140; + config.column_width.game_time = DefaultColumnSize::game_time; break; case ColumnGameStarted: - config.column_width.game_started = 160; + config.column_width.game_started = DefaultColumnSize::game_started; break; case ColumnRegion: - config.column_width.region = 80; + config.column_width.region = DefaultColumnSize::region; break; default: return; @@ -751,13 +784,14 @@ void wxGameList::OnColumnRightClick(wxListEvent& event) case ResetOrder: { config.game_list_column_order.clear(); - wxArrayInt order(ColumnFavorite); + wxArrayInt order(ColumnCounts); std::iota(order.begin(), order.end(), 0); #ifdef wxHAS_LISTCTRL_COLUMN_ORDER SetColumnsOrder(order); #endif - Refresh(); - return; + //ApplyGameListColumnWidths(); + //Refresh(); + //return; } } @@ -771,33 +805,46 @@ void wxGameList::OnColumnRightClick(wxListEvent& event) void wxGameList::ApplyGameListColumnWidths() { - auto set_width = [this](int id, int width) - { - if (width == -3) - wxAutosizeColumn(this, id); - else - this->SetColumnWidth(id, width); - }; - const auto& config = GetConfig(); wxWindowUpdateLocker lock(this); - set_width(ColumnName, config.column_width.name); - set_width(ColumnVersion, config.column_width.version); - set_width(ColumnDLC, config.column_width.dlc); - set_width(ColumnGameTime, config.column_width.game_time); - set_width(ColumnGameStarted, config.column_width.game_started); - set_width(ColumnRegion, config.column_width.region); + SetColumnWidth(ColumnIcon, kListIconWidth); + SetColumnWidth(ColumnName, config.column_width.name); + SetColumnWidth(ColumnVersion, config.column_width.version); + SetColumnWidth(ColumnDLC, config.column_width.dlc); + SetColumnWidth(ColumnGameTime, config.column_width.game_time); + SetColumnWidth(ColumnGameStarted, config.column_width.game_started); + SetColumnWidth(ColumnRegion, config.column_width.region); + + AdjustLastColumnWidth(); } void wxGameList::OnColumnBeginResize(wxListEvent& event) { const int column = event.GetColumn(); const int width = GetColumnWidth(column); - if (width == 0) + int last_col_index = 0; + for(int i = GetColumnCount() - 1; i > 0; i--) + { +#ifdef wxHAS_LISTCTRL_COLUMN_ORDER + if(GetColumnWidth(GetColumnIndexFromOrder(i)) > 0) + { + last_col_index = GetColumnIndexFromOrder(i); + break; + } +#else + if(GetColumnWidth(i) > 0) + { + last_col_index = i; + break; + } +#endif + } + if (width == 0 || column == ColumnIcon || column == last_col_index) // dont resize hidden name, icon, and last column event.Veto(); else event.Skip(); } + void wxGameList::OnColumnResize(wxListEvent& event) { event.Skip(); @@ -823,19 +870,15 @@ void wxGameList::OnColumnResize(wxListEvent& event) case ColumnGameStarted: config.column_width.game_started = width; break; + case ColumnRegion: + config.column_width.region = width; + break; default: - return; + break; } g_config.Save(); -} - -void wxGameList::OnColumnDrag(wxListEvent& event) -{ - const auto column = event.GetColumn(); - const auto width = GetColumnWidth(column); - if (column == ColumnHiddenName || width == 0) - event.Veto(); + AdjustLastColumnWidth(); } void wxGameList::OnClose(wxCloseEvent& event) diff --git a/src/gui/components/wxGameList.h b/src/gui/components/wxGameList.h index 1f422585..2cf9f6b7 100644 --- a/src/gui/components/wxGameList.h +++ b/src/gui/components/wxGameList.h @@ -53,7 +53,7 @@ public: long FindListItemByTitleId(uint64 title_id) const; void OnClose(wxCloseEvent& event); - + private: std::atomic_bool m_exit = false; Style m_style; @@ -74,7 +74,8 @@ private: ColumnGameTime, ColumnGameStarted, ColumnRegion, - ColumnFavorite + //ColumnFavorite, + ColumnCounts }; int s_last_column = ColumnName; @@ -143,6 +144,10 @@ private: void OnMouseMove(wxMouseEvent& event); void OnLeaveWindow(wxMouseEvent& event); + void OnGameListSize(wxSizeEvent& event); + void AdjustLastColumnWidth(); + int GetColumnDefaultWidth(int column); + static inline std::once_flag s_launch_file_once; }; From b724a657e6b33e011808dbc4a398a344c4f7593d Mon Sep 17 00:00:00 2001 From: Tillsunset <35825944+Tillsunset@users.noreply.github.com> Date: Sat, 8 Oct 2022 08:07:54 -0500 Subject: [PATCH 017/616] MoltenVk: Add missing texture decoders (#332) --- src/Cafe/HW/Latte/Core/LatteTextureLoader.h | 102 ++++++++++++++++++ .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 4 +- 2 files changed, 104 insertions(+), 2 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/LatteTextureLoader.h b/src/Cafe/HW/Latte/Core/LatteTextureLoader.h index 353ea924..f6de57d6 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureLoader.h +++ b/src/Cafe/HW/Latte/Core/LatteTextureLoader.h @@ -1203,6 +1203,58 @@ public: } }; +class TextureDecoder_R5_G5_B5_A1_UNORM_swappedRB_To_RGBA8 : public TextureDecoder, public SingletonClass +{ +public: +//2656 + sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override + { + return 4; + } + + void decode(LatteTextureLoaderCtx* textureLoader, uint8* outputData) override + { + for (sint32 y = 0; y < textureLoader->height; y += textureLoader->stepY) + { + sint32 yc = y; + for (sint32 x = 0; x < textureLoader->width; x += textureLoader->stepX) + { + uint16* blockData = (uint16*)LatteTextureLoader_GetInput(textureLoader, x, y); + sint32 pixelOffset = (x + yc * textureLoader->width) * 4; + uint32 colorData = (*(uint16*)(blockData + 0)); + // swap order of components + uint8 red = (colorData >> 0) & 0x1F; + uint8 green = (colorData >> 5) & 0x1F; + uint8 blue = (colorData >> 10) & 0x1F; + uint8 alpha = (colorData >> 15) & 0x1; + + red = red << 3 | red >> 2; + green = green << 3 | green >> 2; + blue = blue << 3 | blue >> 2; + alpha = alpha * 0xff; + + // MSB...LSB : ABGR + colorData = (alpha << 24) | (blue << 16) | (green << 8) | red; + *(uint32*)(outputData + pixelOffset + 0) = colorData; + } + } + } + + void decodePixelToRGBA(uint8* blockData, uint8* outputPixel, uint8 blockOffsetX, uint8 blockOffsetY) override + { + uint16 colorData = (*(uint16*)blockData); + uint8 red = (colorData >> 0) & 0x1F; + uint8 green = (colorData >> 5) & 0x1F; + uint8 blue = (colorData >> 10) & 0x1F; + uint8 alpha = (colorData >> 15) & 0x1; + *(outputPixel + 0) = (red << 3) | (red >> 2); + *(outputPixel + 1) = (green << 3) | (green >> 2); + *(outputPixel + 2) = (blue << 3) | (blue >> 2); + *(outputPixel + 3) = alpha * 0xff; + } + +}; + class uint16_R5_G5_B5_A1_swapOpenGL { public: @@ -1316,6 +1368,56 @@ public: } }; +class TextureDecoder_A1_B5_G5_R5_UNORM_vulkan_To_RGBA8 : public TextureDecoder, public SingletonClass +{ +public: + sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override + { + return 4; + } + + void decode(LatteTextureLoaderCtx* textureLoader, uint8* outputData) override + { + for (sint32 y = 0; y < textureLoader->height; y += textureLoader->stepY) + { + sint32 yc = y; + for (sint32 x = 0; x < textureLoader->width; x += textureLoader->stepX) + { + uint16* blockData = (uint16*)LatteTextureLoader_GetInput(textureLoader, x, y); + sint32 pixelOffset = (x + yc * textureLoader->width) * 4; + uint32 colorData = (*(uint16*)(blockData + 0)); + // swap order of components + uint8 red = (colorData >> 11) & 0x1F; + uint8 green = (colorData >> 6) & 0x1F; + uint8 blue = (colorData >> 1) & 0x1F; + uint8 alpha = (colorData >> 0) & 0x1; + + red = red << 3 | red >> 2; + green = green << 3 | green >> 2; + blue = blue << 3 | blue >> 2; + alpha = alpha * 0xff; + + // MSB...LSB ABGR + colorData = red | (green << 8) | (blue << 16) | (alpha << 24); + *(uint32*)(outputData + pixelOffset + 0) = colorData; + } + } + } + + void decodePixelToRGBA(uint8* blockData, uint8* outputPixel, uint8 blockOffsetX, uint8 blockOffsetY) override + { + uint16 colorData = (*(uint16*)blockData); + uint8 red5 = (colorData >> 11) & 0x1F; + uint8 green5 = (colorData >> 6) & 0x1F; + uint8 blue5 = (colorData >> 1) & 0x1F; + uint8 alpha1 = (colorData >> 0) & 0x1; + *(outputPixel + 0) = (red5 << 3) | (red5 >> 2); + *(outputPixel + 1) = (green5 << 3) | (green5 >> 2); + *(outputPixel + 2) = (blue5 << 3) | (blue5 >> 2); + *(outputPixel + 3) = (alpha1 << 3); + } +}; + class TextureDecoder_R10_G10_B10_A2_UNORM : public TextureDecoder, public SingletonClass { diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 759dac59..249d06b7 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -2653,7 +2653,7 @@ void VulkanRenderer::GetTextureFormatInfoVK(Latte::E_GX2SURFFMT format, bool isD case Latte::E_GX2SURFFMT::R5_G5_B5_A1_UNORM: if (m_supportedFormatInfo.fmt_a1r5g5b5_unorm_pack == false) { formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_UNORM; - formatInfoOut->decoder = nullptr; + formatInfoOut->decoder = TextureDecoder_R5_G5_B5_A1_UNORM_swappedRB_To_RGBA8::getInstance(); } else { // used in Super Mario 3D World for the hidden Luigi sprites @@ -2665,7 +2665,7 @@ void VulkanRenderer::GetTextureFormatInfoVK(Latte::E_GX2SURFFMT format, bool isD case Latte::E_GX2SURFFMT::A1_B5_G5_R5_UNORM: if (m_supportedFormatInfo.fmt_a1r5g5b5_unorm_pack == false) { formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_UNORM; - formatInfoOut->decoder = nullptr; + formatInfoOut->decoder = TextureDecoder_A1_B5_G5_R5_UNORM_vulkan_To_RGBA8::getInstance(); } else { // used by VC64 (e.g. Ocarina of Time) From 638e9e1f87d3ee279c1247969e9fd59065be492e Mon Sep 17 00:00:00 2001 From: Tillsunset <35825944+Tillsunset@users.noreply.github.com> Date: Sun, 9 Oct 2022 01:45:26 -0500 Subject: [PATCH 018/616] Workaround for the h264 video crash on macOS (#348) --- src/Cafe/OS/libs/h264_avc/H264Dec.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Cafe/OS/libs/h264_avc/H264Dec.cpp b/src/Cafe/OS/libs/h264_avc/H264Dec.cpp index 8dbd8ecf..1441f343 100644 --- a/src/Cafe/OS/libs/h264_avc/H264Dec.cpp +++ b/src/Cafe/OS/libs/h264_avc/H264Dec.cpp @@ -194,6 +194,7 @@ namespace H264 #ifdef _WIN32 return _aligned_malloc(size, alignment); #else + size += ((size % alignment) == 0) ? 0 : alignment - (size % alignment); return aligned_alloc(alignment, size); #endif } From 0c9fb3143f491d60959b3f2ca0aa8a04ab38b47c Mon Sep 17 00:00:00 2001 From: Tillsunset <35825944+Tillsunset@users.noreply.github.com> Date: Sun, 9 Oct 2022 03:43:45 -0500 Subject: [PATCH 019/616] memory optimization, using posix_memalign (#350) --- src/Cafe/OS/libs/h264_avc/H264Dec.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/Cafe/OS/libs/h264_avc/H264Dec.cpp b/src/Cafe/OS/libs/h264_avc/H264Dec.cpp index 1441f343..e9b8ec8b 100644 --- a/src/Cafe/OS/libs/h264_avc/H264Dec.cpp +++ b/src/Cafe/OS/libs/h264_avc/H264Dec.cpp @@ -194,8 +194,22 @@ namespace H264 #ifdef _WIN32 return _aligned_malloc(size, alignment); #else - size += ((size % alignment) == 0) ? 0 : alignment - (size % alignment); - return aligned_alloc(alignment, size); + // alignment is atleast sizeof(void*) + alignment = std::max(alignment, sizeof(void*)); + + //smallest multiple of 2 at least as large as alignment + alignment--; + alignment |= alignment << 1; + alignment |= alignment >> 1; + alignment |= alignment >> 2; + alignment |= alignment >> 4; + alignment |= alignment >> 8; + alignment |= alignment >> 16; + alignment ^= (alignment >> 1); + + void* temp; + posix_memalign(&temp, (size_t)alignment, (size_t)size); + return temp; #endif } From 431c5a101f7f3806aea67d234177bd5de57aaf9f Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Mon, 10 Oct 2022 02:35:04 +0200 Subject: [PATCH 020/616] Linux: Print demangled symbols on backtrace (#312) --- src/CMakeLists.txt | 4 ++ src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h | 6 +-- .../ExceptionHandler_posix.cpp | 51 +++++++++++++++++-- src/Common/precompiled.h | 6 +++ 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 024432d0..32d6fe60 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,6 +35,10 @@ elseif(UNIX) add_compile_options(-Wno-ambiguous-reversed-operator) endif() + if(NOT APPLE) + add_link_options(-rdynamic) + endif() + add_compile_options(-Wno-multichar -Wno-invalid-offsetof -Wno-switch -Wno-ignored-attributes -Wno-deprecated-enum-enum-conversion) endif() diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h index c1f2dee6..437f00b1 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h @@ -15,9 +15,9 @@ extern bool g_vulkan_available; #endif #ifdef VKFUNC_DEFINE - #define VKFUNC(__FUNC__) PFN_##__FUNC__ __FUNC__ = nullptr - #define VKFUNC_INSTANCE(__FUNC__) PFN_##__FUNC__ __FUNC__ = nullptr - #define VKFUNC_DEVICE(__FUNC__) PFN_##__FUNC__ __FUNC__ = nullptr + #define VKFUNC(__FUNC__) NOEXPORT PFN_##__FUNC__ __FUNC__ = nullptr + #define VKFUNC_INSTANCE(__FUNC__) NOEXPORT PFN_##__FUNC__ __FUNC__ = nullptr + #define VKFUNC_DEVICE(__FUNC__) NOEXPORT PFN_##__FUNC__ __FUNC__ = nullptr #else #if defined(VKFUNC_INIT) #if BOOST_OS_WINDOWS diff --git a/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp b/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp index e2ea3d33..86d83bb3 100644 --- a/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp +++ b/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp @@ -1,9 +1,43 @@ #include #include #include +#include #include "config/CemuConfig.h" +void demangleAndPrintBacktrace(char** backtrace, size_t size) +{ + for (char** i = backtrace; i < backtrace + size; i++) + { + std::string traceLine{*i}; + size_t parenthesesOpen = traceLine.find_last_of('('); + size_t parenthesesClose = traceLine.find_last_of(')'); + size_t offsetPlus = traceLine.find_last_of('+'); + if (!parenthesesOpen || !parenthesesClose || !offsetPlus || + offsetPlus < parenthesesOpen || offsetPlus > parenthesesClose) + { + // something unexpected was read. fall back to default string + std::cerr << traceLine << std::endl; + continue; + } + + std::string symbolName = traceLine.substr(parenthesesOpen+1,offsetPlus-parenthesesOpen-1); + int status = -1; + char* demangled = abi::__cxa_demangle(symbolName.c_str(), nullptr, nullptr, &status); + if (demangled) + { + std::cerr << traceLine.substr(0, parenthesesOpen+1); + std::cerr << demangled; + std::cerr << traceLine.substr(offsetPlus) << std::endl; + free(demangled); + } + else + { + std::cerr << traceLine << std::endl; + } + } +} + // handle signals that would dump core, print stacktrace and then dump depending on config void handlerDumpingSignal(int sig) { @@ -18,15 +52,26 @@ void handlerDumpingSignal(int sig) printf("Unknown core dumping signal!\n"); } - void *array[32]; + void *array[128]; size_t size; // get void*'s for all entries on the stack - size = backtrace(array, 32); + size = backtrace(array, 128); // print out all the frames to stderr fprintf(stderr, "Error: signal %d:\n", sig); - backtrace_symbols_fd(array, size, STDERR_FILENO); + + char** symbol_trace = backtrace_symbols(array, size); + + if (symbol_trace) + { + demangleAndPrintBacktrace(symbol_trace, size); + free(symbol_trace); + } + else + { + std::cerr << "Failed to read backtrace" << std::endl; + } if (GetConfig().crash_dump == CrashDump::Enabled) { diff --git a/src/Common/precompiled.h b/src/Common/precompiled.h index 2a92ea3e..bd956657 100644 --- a/src/Common/precompiled.h +++ b/src/Common/precompiled.h @@ -246,6 +246,12 @@ inline uint64 _udiv128(uint64 highDividend, uint64 lowDividend, uint64 divisor, #error No definition for DLLEXPORT #endif +#if BOOST_OS_WINDOWS + #define NOEXPORT +#elif defined(__GNUC__) + #define NOEXPORT __attribute__ ((visibility ("hidden"))) +#endif + #ifdef __GNUC__ #include #endif From 52cc7c59964c72240874dc5e9c9178523df81404 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Tue, 11 Oct 2022 01:43:15 +0200 Subject: [PATCH 021/616] Follow imgui recommendation and streamline build dependencies (#355) --- .gitmodules | 3 +++ CMakeLists.txt | 1 - dependencies/imgui | 1 + src/Cafe/CMakeLists.txt | 1 - .../HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp | 14 -------------- src/imgui/CMakeLists.txt | 11 ++++++++++- src/imgui/imgui_extension.cpp | 4 ++-- vcpkg.json | 1 - 8 files changed, 16 insertions(+), 20 deletions(-) create mode 160000 dependencies/imgui diff --git a/.gitmodules b/.gitmodules index dd32088b..f352d478 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "dependencies/Vulkan-Headers"] path = dependencies/Vulkan-Headers url = https://github.com/KhronosGroup/Vulkan-Headers +[submodule "dependencies/imgui"] + path = dependencies/imgui + url = https://github.com/ocornut/imgui diff --git a/CMakeLists.txt b/CMakeLists.txt index e5ddc6fd..76dcd33a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,7 +83,6 @@ find_package(Threads REQUIRED) find_package(SDL2 REQUIRED) find_package(CURL REQUIRED) find_package(pugixml REQUIRED) -find_package(imgui REQUIRED) find_package(RapidJSON REQUIRED) find_package(Boost COMPONENTS program_options filesystem nowide REQUIRED) find_package(libzip REQUIRED) diff --git a/dependencies/imgui b/dependencies/imgui new file mode 160000 index 00000000..8a44c31c --- /dev/null +++ b/dependencies/imgui @@ -0,0 +1 @@ +Subproject commit 8a44c31c95c8e0217f6e1fc814cbbbcca4981f14 diff --git a/src/Cafe/CMakeLists.txt b/src/Cafe/CMakeLists.txt index 6657bc9c..c4f9ce30 100644 --- a/src/Cafe/CMakeLists.txt +++ b/src/Cafe/CMakeLists.txt @@ -491,7 +491,6 @@ target_link_libraries(CemuCafe PRIVATE fmt::fmt glslang::SPIRV ih264d - imgui::imgui OpenSSL::Crypto OpenSSL::SSL PNG::PNG diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp index 7af5204b..e6677576 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp @@ -1,25 +1,11 @@ #include "Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h" -#if __has_include() -#include -#else -#define GLSLANG_VERSION_LESS_OR_EQUAL_TO (false) -#endif - #include -#if GLSLANG_VERSION_LESS_OR_EQUAL_TO(11, 0, 0) #include -#else -#include -#include -#endif - #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h" -#include - #include "config/ActiveSettings.h" #include "config/CemuConfig.h" #include "util/helpers/ConcurrentQueue.h" diff --git a/src/imgui/CMakeLists.txt b/src/imgui/CMakeLists.txt index 97b56455..db7686bd 100644 --- a/src/imgui/CMakeLists.txt +++ b/src/imgui/CMakeLists.txt @@ -11,6 +11,16 @@ set_property(TARGET imguiImpl PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$second; - ImGuiKey mapped_key = current_key + ImGuiKey_NamedKey_BEGIN; - current_key = (current_key + 1) % ImGuiKey_NamedKey_COUNT ; + ImGuiKey mapped_key = (ImGuiKey)((uint32)current_key + ImGuiKey_NamedKey_BEGIN); + current_key = (current_key + 1) % (uint32)ImGuiKey_NamedKey_COUNT; keyboard_mapping[key_code] = mapped_key; return mapped_key; }; diff --git a/vcpkg.json b/vcpkg.json index 519a5618..33587607 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -3,7 +3,6 @@ "version-string": "1.0", "builtin-baseline": "1b0252ca70ca2244a711535462c7f981eb439e83", "dependencies": [ - "imgui", "pugixml", "zlib", "libpng", From b07e9efba4e9c1f952f14c99ebd529f71bfe630b Mon Sep 17 00:00:00 2001 From: MythicalPlayz <57963367+MythicalPlayz@users.noreply.github.com> Date: Tue, 11 Oct 2022 04:04:47 +0200 Subject: [PATCH 022/616] Add support for choosing network service (incl Pretendo+Custom) (#302) --- .gitignore | 1 + src/Cafe/IOSU/legacy/iosu_boss.cpp | 22 ++++++++- src/Cemu/napi/napi_ec.cpp | 67 ++++++++++++++++++++++++-- src/Cemu/napi/napi_helper.cpp | 12 +++-- src/Cemu/napi/napi_idbe.cpp | 35 +++++++++++++- src/Cemu/napi/napi_version.cpp | 19 +++++++- src/config/ActiveSettings.cpp | 6 ++- src/config/ActiveSettings.h | 3 +- src/config/CMakeLists.txt | 2 + src/config/CemuConfig.cpp | 4 +- src/config/CemuConfig.h | 1 + src/config/LaunchSettings.cpp | 24 ++++++++++ src/config/LaunchSettings.h | 5 +- src/config/NetworkSettings.cpp | 43 +++++++++++++++++ src/config/NetworkSettings.h | 77 ++++++++++++++++++++++++++++++ src/gui/GeneralSettings2.cpp | 23 +++++++++ src/gui/GeneralSettings2.h | 2 + src/gui/guiWrapper.cpp | 14 +++++- src/main.cpp | 7 ++- 19 files changed, 345 insertions(+), 22 deletions(-) create mode 100644 src/config/NetworkSettings.cpp create mode 100644 src/config/NetworkSettings.h diff --git a/.gitignore b/.gitignore index 293864a8..e252f4cb 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ bin/Cemu_*.ilk bin/Cemu.exe.backup bin/mlc01/* bin/settings.xml +bin/network_services.xml bin/title_list_cache.xml bin/debugger/* bin/sdcard/* diff --git a/src/Cafe/IOSU/legacy/iosu_boss.cpp b/src/Cafe/IOSU/legacy/iosu_boss.cpp index 7280ac53..d93d7284 100644 --- a/src/Cafe/IOSU/legacy/iosu_boss.cpp +++ b/src/Cafe/IOSU/legacy/iosu_boss.cpp @@ -10,6 +10,7 @@ #include #include "config/ActiveSettings.h" +#include "config/NetworkSettings.h" #include "curl/curl.h" #include "openssl/bn.h" #include "openssl/x509.h" @@ -497,9 +498,13 @@ namespace iosu curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, task_header_callback); curl_easy_setopt(curl, CURLOPT_HEADERDATA, &(*it)); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 0x3C); + if (GetNetworkConfig().disablesslver.GetValue() && ActiveSettings::GetNetworkService() == NetworkService::Custom || ActiveSettings::GetNetworkService() == NetworkService::Pretendo){ //Remove Pretendo Function once SSL is in the Service + curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L); + } else { curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, task_sslctx_function); curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, &it->task_settings); curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_0); + } char url[512]; if(it->task_settings.taskType == kRawDlTaskSetting) @@ -561,8 +566,21 @@ namespace iosu char boss_code[0x20]; strncpy(boss_code, (char*)&it->task_settings.settings[TaskSetting::kBossCode], TaskSetting::kBossCodeLen); - - sprintf(url, "https://npts.app.nintendo.net/p01/tasksheet/%s/%s/%s?c=%s&l=%s", "1", boss_code, it->task_id, countryCode, languageCode); + switch (ActiveSettings::GetNetworkService()) + { + case NetworkService::Nintendo: + sprintf(url, NintendoURLs::BOSSURL.append("/%s/%s/%s?c=%s&l=%s").c_str(), "1", boss_code, it->task_id, countryCode, languageCode); + break; + case NetworkService::Pretendo: + sprintf(url, PretendoURLs::BOSSURL.append("/%s/%s/%s?c=%s&l=%s").c_str(), "1", boss_code, it->task_id, countryCode, languageCode); + break; + case NetworkService::Custom: + sprintf(url, GetNetworkConfig().urls.BOSS.GetValue().append("/%s/%s/%s?c=%s&l=%s").c_str(), "1", boss_code, it->task_id, countryCode, languageCode); + break; + default: + sprintf(url, NintendoURLs::BOSSURL.append("/%s/%s/%s?c=%s&l=%s").c_str(), "1", boss_code, it->task_id, countryCode, languageCode); + break; + } } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list_headerParam); diff --git a/src/Cemu/napi/napi_ec.cpp b/src/Cemu/napi/napi_ec.cpp index 2d559e81..c355da4f 100644 --- a/src/Cemu/napi/napi_ec.cpp +++ b/src/Cemu/napi/napi_ec.cpp @@ -8,7 +8,8 @@ #include "Cemu/ncrypto/ncrypto.h" #include "util/crypto/md5.h" #include "config/LaunchSettings.h" - +#include "config/ActiveSettings.h" +#include "config/NetworkSettings.h" #include "pugixml.hpp" #include @@ -26,14 +27,42 @@ namespace NAPI { if (!s_serviceURL_NusURL.empty()) return s_serviceURL_NusURL; - return "https://nus.wup.shop.nintendo.net/nus/services/NetUpdateSOAP"; + switch (ActiveSettings::GetNetworkService()) + { + case NetworkService::Nintendo: + return NintendoURLs::NUSURL; + break; + case NetworkService::Pretendo: + return PretendoURLs::NUSURL; + break; + case NetworkService::Custom: + return GetNetworkConfig().urls.NUS; + break; + default: + return NintendoURLs::NUSURL; + break; + } } std::string _getIASUrl() { if (!s_serviceURL_IasURL.empty()) return s_serviceURL_IasURL; - return "https://ias.wup.shop.nintendo.net/ias/services/IdentityAuthenticationSOAP"; + switch (ActiveSettings::GetNetworkService()) + { + case NetworkService::Nintendo: + return NintendoURLs::IASURL; + break; + case NetworkService::Pretendo: + return PretendoURLs::IASURL; + break; + case NetworkService::Custom: + return GetNetworkConfig().urls.IAS; + break; + default: + return NintendoURLs::IASURL; + break; + } } std::string _getECSUrl() @@ -48,14 +77,42 @@ namespace NAPI { if (!s_serviceURL_UncachedContentPrefixURL.empty()) return s_serviceURL_UncachedContentPrefixURL; - return "https://ccs.wup.shop.nintendo.net/ccs/download"; + switch (ActiveSettings::GetNetworkService()) + { + case NetworkService::Nintendo: + return NintendoURLs::CCSUURL; + break; + case NetworkService::Pretendo: + return PretendoURLs::CCSUURL; + break; + case NetworkService::Custom: + return GetNetworkConfig().urls.CCSU; + break; + default: + return NintendoURLs::CCSUURL; + break; + } } std::string _getCCSUrl() // used for game data downloads { if (!s_serviceURL_ContentPrefixURL.empty()) return s_serviceURL_ContentPrefixURL; - return "http://ccs.cdn.wup.shop.nintendo.net/ccs/download"; + switch (ActiveSettings::GetNetworkService()) + { + case NetworkService::Nintendo: + return NintendoURLs::CCSURL; + break; + case NetworkService::Pretendo: + return PretendoURLs::CCSURL; + break; + case NetworkService::Custom: + return GetNetworkConfig().urls.CCS; + break; + default: + return NintendoURLs::CCSURL; + break; + } } /* NUS */ diff --git a/src/Cemu/napi/napi_helper.cpp b/src/Cemu/napi/napi_helper.cpp index 188c8840..016fc597 100644 --- a/src/Cemu/napi/napi_helper.cpp +++ b/src/Cemu/napi/napi_helper.cpp @@ -7,7 +7,9 @@ #include "Cemu/ncrypto/ncrypto.h" #include "napi_helper.h" #include "util/highresolutiontimer/HighResolutionTimer.h" - +#include "config/ActiveSettings.h" +#include "config/NetworkSettings.h" +#include "config/LaunchSettings.h" #include "pugixml.hpp" #include @@ -97,7 +99,10 @@ void CurlRequestHelper::initate(std::string url, SERVER_SSL_CONTEXT sslContext) curl_easy_setopt(m_curl, CURLOPT_TIMEOUT, 60); // SSL - if (sslContext == SERVER_SSL_CONTEXT::ACT || sslContext == SERVER_SSL_CONTEXT::TAGAYA) + if (GetNetworkConfig().disablesslver.GetValue() && ActiveSettings::GetNetworkService() == NetworkService::Custom || ActiveSettings::GetNetworkService() == NetworkService::Pretendo){ //Remove once Pretendo has SSL + curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, 0L); + } + else if (sslContext == SERVER_SSL_CONTEXT::ACT || sslContext == SERVER_SSL_CONTEXT::TAGAYA) { curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_FUNCTION, _sslctx_function_NUS); curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_DATA, NULL); @@ -219,9 +224,10 @@ CurlSOAPHelper::CurlSOAPHelper() curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, this); // SSL + if (!GetNetworkConfig().disablesslver.GetValue() && ActiveSettings::GetNetworkService() != NetworkService::Pretendo && ActiveSettings::GetNetworkService() != NetworkService::Custom) { //Remove once Pretendo has SSL curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_FUNCTION, _sslctx_function_SOAP); curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_DATA, NULL); - + } if(GetConfig().proxy_server.GetValue() != "") { curl_easy_setopt(m_curl, CURLOPT_PROXY, GetConfig().proxy_server.GetValue().c_str()); diff --git a/src/Cemu/napi/napi_idbe.cpp b/src/Cemu/napi/napi_idbe.cpp index 90c9c2f2..6832bf73 100644 --- a/src/Cemu/napi/napi_idbe.cpp +++ b/src/Cemu/napi/napi_idbe.cpp @@ -10,6 +10,8 @@ #include "openssl/sha.h" #include "util/crypto/aes128.h" #include "util/helpers/StringHelpers.h" +#include "config/ActiveSettings.h" +#include "config/NetworkSettings.h" namespace NAPI { @@ -55,7 +57,21 @@ namespace NAPI std::vector IDBE_RequestRawEncrypted(uint64 titleId) { CurlRequestHelper req; - req.initate(fmt::format("https://idbe-wup.cdn.nintendo.net/icondata/{0:02X}/{1:016X}.idbe", (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE); + switch (ActiveSettings::GetNetworkService()) + { + case NetworkService::Nintendo: + req.initate(fmt::format(fmt::runtime(NintendoURLs::IDBEURL + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE); + break; + case NetworkService::Pretendo: + req.initate(fmt::format(fmt::runtime(PretendoURLs::IDBEURL + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE); + break; + case NetworkService::Custom: + req.initate(fmt::format(fmt::runtime(GetNetworkConfig().urls.IDBE.GetValue() + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE); + break; + default: + req.initate(fmt::format(fmt::runtime(NintendoURLs::IDBEURL + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE); + break; + } if (!req.submitRequest(false)) { cemuLog_log(LogType::Force, fmt::format("Failed to request IDBE icon for title {0:016X}", titleId)); @@ -84,7 +100,22 @@ namespace NAPI } CurlRequestHelper req; - req.initate(fmt::format("https://idbe-wup.cdn.nintendo.net/icondata/{0:02X}/{1:016X}.idbe", (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE); + switch (ActiveSettings::GetNetworkService()) + { + case NetworkService::Nintendo: + req.initate(fmt::format(fmt::runtime(NintendoURLs::IDBEURL + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE); + break; + case NetworkService::Pretendo: + req.initate(fmt::format(fmt::runtime(PretendoURLs::IDBEURL + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE); + break; + case NetworkService::Custom: + req.initate(fmt::format(fmt::runtime(GetNetworkConfig().urls.IDBE.GetValue() + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE); + break; + default: + req.initate(fmt::format(fmt::runtime(NintendoURLs::IDBEURL + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE); + break; + } + if (!req.submitRequest(false)) { cemuLog_log(LogType::Force, fmt::format("Failed to request IDBE icon for title {0:016X}", titleId)); diff --git a/src/Cemu/napi/napi_version.cpp b/src/Cemu/napi/napi_version.cpp index f1c1b171..73dd68cf 100644 --- a/src/Cemu/napi/napi_version.cpp +++ b/src/Cemu/napi/napi_version.cpp @@ -7,6 +7,8 @@ #include "Cemu/ncrypto/ncrypto.h" #include +#include "config/ActiveSettings.h" +#include "config/NetworkSettings.h" namespace NAPI { @@ -14,7 +16,22 @@ namespace NAPI { NAPI_VersionListVersion_Result result; CurlRequestHelper req; - req.initate(fmt::format("https://tagaya.wup.shop.nintendo.net/tagaya/versionlist/{}/{}/latest_version", NCrypto::GetRegionAsString(authInfo.region), authInfo.country), CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA); + switch (ActiveSettings::GetNetworkService()) + { + case NetworkService::Nintendo: + req.initate(fmt::format(fmt::runtime(NintendoURLs::TAGAYAURL + "/{}/{}/latest_version"), NCrypto::GetRegionAsString(authInfo.region), authInfo.country), CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA); + break; + case NetworkService::Pretendo: + req.initate(fmt::format(fmt::runtime(PretendoURLs::TAGAYAURL + "/{}/{}/latest_version"), NCrypto::GetRegionAsString(authInfo.region), authInfo.country), CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA); + break; + case NetworkService::Custom: + req.initate(fmt::format(fmt::runtime(GetNetworkConfig().urls.TAGAYA.GetValue() + "/{}/{}/latest_version"), NCrypto::GetRegionAsString(authInfo.region), authInfo.country), CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA); + break; + default: + req.initate(fmt::format(fmt::runtime(NintendoURLs::TAGAYAURL + "/{}/{}/latest_version"), NCrypto::GetRegionAsString(authInfo.region), authInfo.country), CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA); + break; + } + if (!req.submitRequest(false)) { cemuLog_log(LogType::Force, fmt::format("Failed to request version of update list")); diff --git a/src/config/ActiveSettings.cpp b/src/config/ActiveSettings.cpp index c7ff4fe6..16119725 100644 --- a/src/config/ActiveSettings.cpp +++ b/src/config/ActiveSettings.cpp @@ -20,7 +20,7 @@ void ActiveSettings::LoadOnce() g_config.SetFilename(GetPath("settings.xml").generic_wstring()); g_config.Load(); - + LaunchSettings::ChangeNetworkServiceURL(GetConfig().account.active_service); std::wstring additionalErrorInfo; s_has_required_online_files = iosuCrypt_checkRequirementsForOnlineMode(additionalErrorInfo) == IOS_CRYPTO_ONLINE_REQ_OK; } @@ -121,6 +121,10 @@ bool ActiveSettings::HasRequiredOnlineFiles() return s_has_required_online_files; } +NetworkService ActiveSettings::GetNetworkService() { + return static_cast(GetConfig().account.active_service.GetValue()); +} + bool ActiveSettings::DumpShadersEnabled() { return s_dump_shaders; diff --git a/src/config/ActiveSettings.h b/src/config/ActiveSettings.h index 69f6d34a..ae3feb94 100644 --- a/src/config/ActiveSettings.h +++ b/src/config/ActiveSettings.h @@ -1,6 +1,7 @@ #pragma once #include "config/CemuConfig.h" +#include "config/NetworkSettings.h" // global active settings for fast access (reflects settings from command line and game profile) class ActiveSettings @@ -92,7 +93,7 @@ public: [[nodiscard]] static uint32 GetPersistentId(); [[nodiscard]] static bool IsOnlineEnabled(); [[nodiscard]] static bool HasRequiredOnlineFiles(); - + [[nodiscard]] static NetworkService GetNetworkService(); // dump options [[nodiscard]] static bool DumpShadersEnabled(); [[nodiscard]] static bool DumpTexturesEnabled(); diff --git a/src/config/CMakeLists.txt b/src/config/CMakeLists.txt index c329d58b..f02b95d4 100644 --- a/src/config/CMakeLists.txt +++ b/src/config/CMakeLists.txt @@ -6,6 +6,8 @@ add_library(CemuConfig ConfigValue.h LaunchSettings.cpp LaunchSettings.h + NetworkSettings.cpp + NetworkSettings.h PermanentConfig.cpp PermanentConfig.h PermanentStorage.cpp diff --git a/src/config/CemuConfig.cpp b/src/config/CemuConfig.cpp index b257ead3..84324418 100644 --- a/src/config/CemuConfig.cpp +++ b/src/config/CemuConfig.cpp @@ -322,7 +322,7 @@ void CemuConfig::Load(XMLConfigParser& parser) auto acc = parser.get("Account"); account.m_persistent_id = acc.get("PersistentId", account.m_persistent_id); account.online_enabled = acc.get("OnlineEnabled", account.online_enabled); - + account.active_service = acc.get("ActiveService",account.active_service); // debug auto debug = parser.get("Debug"); #if BOOST_OS_WINDOWS @@ -497,7 +497,7 @@ void CemuConfig::Save(XMLConfigParser& parser) auto acc = config.set("Account"); acc.set("PersistentId", account.m_persistent_id.GetValue()); acc.set("OnlineEnabled", account.online_enabled.GetValue()); - + acc.set("ActiveService",account.active_service.GetValue()); // debug auto debug = config.set("Debug"); #if BOOST_OS_WINDOWS diff --git a/src/config/CemuConfig.h b/src/config/CemuConfig.h index e7cb1ca5..0dc0843b 100644 --- a/src/config/CemuConfig.h +++ b/src/config/CemuConfig.h @@ -471,6 +471,7 @@ struct CemuConfig { ConfigValueBounds m_persistent_id{ Account::kMinPersistendId, Account::kMinPersistendId, 0xFFFFFFFF }; ConfigValue online_enabled{false}; + ConfigValue active_service{0}; }account{}; // input diff --git a/src/config/LaunchSettings.cpp b/src/config/LaunchSettings.cpp index 7d7c0c98..06d9a5a3 100644 --- a/src/config/LaunchSettings.cpp +++ b/src/config/LaunchSettings.cpp @@ -9,6 +9,7 @@ #include #include "config/ActiveSettings.h" +#include "config/NetworkSettings.h" #include "util/crypto/aes128.h" #include "Cafe/Filesystem/FST/FST.h" @@ -265,3 +266,26 @@ bool LaunchSettings::ExtractorTool(std::wstring_view wud_path, std::string_view } +void LaunchSettings::ChangeNetworkServiceURL(int ID){ + NetworkService Network = static_cast(ID); + switch (Network) + { + case NetworkService::Nintendo: + serviceURL_ACT = NintendoURLs::ACTURL; + serviceURL_ECS = NintendoURLs::ECSURL; + break; + case NetworkService::Pretendo: + serviceURL_ACT = PretendoURLs::ACTURL; + serviceURL_ECS = PretendoURLs::ECSURL; + break; + case NetworkService::Custom: + serviceURL_ACT = GetNetworkConfig().urls.ACT.GetValue(); + serviceURL_ECS = GetNetworkConfig().urls.ECS.GetValue(); + break; + default: + serviceURL_ACT = NintendoURLs::ACTURL; + serviceURL_ECS = NintendoURLs::ECSURL; + break; + } + +} \ No newline at end of file diff --git a/src/config/LaunchSettings.h b/src/config/LaunchSettings.h index 299dd72c..b42523ca 100644 --- a/src/config/LaunchSettings.h +++ b/src/config/LaunchSettings.h @@ -30,6 +30,7 @@ public: static std::string GetActURLPrefix() { return serviceURL_ACT; } static std::string GetServiceURL_ecs() { return serviceURL_ECS; } + static void ChangeNetworkServiceURL(int ID); private: inline static std::optional s_load_game_file{}; @@ -46,8 +47,8 @@ private: inline static std::optional s_persistent_id{}; // service URLS - inline static std::string serviceURL_ACT = "https://account.nintendo.net"; - inline static std::string serviceURL_ECS = "https://ecs.wup.shop.nintendo.net/ecs/services/ECommerceSOAP"; + inline static std::string serviceURL_ACT; + inline static std::string serviceURL_ECS; // todo - npts and other boss urls diff --git a/src/config/NetworkSettings.cpp b/src/config/NetworkSettings.cpp new file mode 100644 index 00000000..f3a3b8b6 --- /dev/null +++ b/src/config/NetworkSettings.cpp @@ -0,0 +1,43 @@ +#include "NetworkSettings.h" +#include "LaunchSettings.h" +#include "CemuConfig.h" +#include +#include "Common/FileStream.h" +XMLNetworkConfig_t n_config(L"network_services.xml"); + +void NetworkConfig::LoadOnce() { + s_full_path = boost::dll::program_location().generic_wstring(); + s_path = s_full_path.parent_path(); + n_config.SetFilename(GetPath("network_services.xml").generic_wstring()); + if(XMLExists()) { + n_config.Load(); + } +} + +void NetworkConfig::Load(XMLConfigParser& parser){ + auto config = parser.get("content"); + networkname = config.get("networkname","[Nintendo]"); + disablesslver = config.get("disablesslverification",disablesslver); + auto u = config.get("urls"); + urls.ACT = u.get("act",NintendoURLs::ACTURL); + urls.ECS = u.get("ecs",NintendoURLs::ECSURL); + urls.NUS = u.get("nus",NintendoURLs::NUSURL); + urls.IAS = u.get("ias",NintendoURLs::IASURL); + urls.CCSU = u.get("ccsu",NintendoURLs::CCSUURL); + urls.CCS = u.get("ccs",NintendoURLs::CCSURL); + urls.IDBE = u.get("idbe",NintendoURLs::IDBEURL); + urls.BOSS = u.get("boss",NintendoURLs::BOSSURL); + urls.TAGAYA = u.get("tagaya",NintendoURLs::TAGAYAURL); + if (static_cast(GetConfig().account.active_service.GetValue()) == NetworkService::Custom) { + LaunchSettings::ChangeNetworkServiceURL(2); + } +} +bool NetworkConfig::XMLExists() { + if (!fs::exists(GetPath("network_services.xml"))) { + if (static_cast(GetConfig().account.active_service.GetValue()) == NetworkService::Custom) { + LaunchSettings::ChangeNetworkServiceURL(0); + GetConfig().account.active_service = 0; + } + return false; + } else {return true;} +} \ No newline at end of file diff --git a/src/config/NetworkSettings.h b/src/config/NetworkSettings.h new file mode 100644 index 00000000..e65da8ab --- /dev/null +++ b/src/config/NetworkSettings.h @@ -0,0 +1,77 @@ +#pragma once + +#include "ConfigValue.h" +#include "XMLConfig.h" + + +enum class NetworkService { +Nintendo, +Pretendo, +Custom, +}; +struct NetworkConfig { + NetworkConfig() + { + + }; + + NetworkConfig(const NetworkConfig&) = delete; + + ConfigValue networkname; + ConfigValue disablesslver = false; + struct + { + ConfigValue ACT; + ConfigValue ECS; + ConfigValue NUS; + ConfigValue IAS; + ConfigValue CCSU; + ConfigValue CCS; + ConfigValue IDBE; + ConfigValue BOSS; + ConfigValue TAGAYA; + }urls{}; + + public: + static void LoadOnce(); + void Load(XMLConfigParser& parser); + void Save(XMLConfigParser& parser); + + static bool XMLExists(); + private: + inline static fs::path s_path; + inline static fs::path s_full_path; + [[nodiscard]] static fs::path GetPath(std::string_view p) + { + std::basic_string_view s((const char8_t*)p.data(), p.size()); + return s_path / fs::path(s); + } +}; + +struct NintendoURLs { + inline static std::string ACTURL = "https://account.nintendo.net"; + inline static std::string ECSURL = "https://ecs.wup.shop.nintendo.net/ecs/services/ECommerceSOAP"; + inline static std::string NUSURL = "https://nus.wup.shop.nintendo.net/nus/services/NetUpdateSOAP"; + inline static std::string IASURL = "https://ias.wup.shop.nintendo.net/ias/services/IdentityAuthenticationSOAP"; + inline static std::string CCSUURL = "https://ccs.wup.shop.nintendo.net/ccs/download"; + inline static std::string CCSURL = "http://ccs.cdn.wup.shop.nintendo.net/ccs/download"; + inline static std::string IDBEURL = "https://idbe-wup.cdn.nintendo.net/icondata"; + inline static std::string BOSSURL = "https://npts.app.nintendo.net/p01/tasksheet"; + inline static std::string TAGAYAURL = "https://tagaya.wup.shop.nintendo.net/tagaya/versionlist"; +}; + +struct PretendoURLs { + inline static std::string ACTURL = "https://account.pretendo.cc"; + inline static std::string ECSURL = "https://ecs.wup.shop.pretendo.cc/ecs/services/ECommerceSOAP"; + inline static std::string NUSURL = "https://nus.c.shop.pretendo.cc/nus/services/NetUpdateSOAP"; + inline static std::string IASURL = "https://ias.c.shop.pretendo.cc/ias/services/IdentityAuthenticationSOAP"; + inline static std::string CCSUURL = "https://ccs.c.shop.pretendo.cc/ccs/download"; + inline static std::string CCSURL = "http://ccs.cdn.c.shop.pretendo.cc/ccs/download"; + inline static std::string IDBEURL = "https://idbe-wup.cdn.pretendo.cc/icondata"; + inline static std::string BOSSURL = "https://npts.app.pretendo.cc/p01/tasksheet"; + inline static std::string TAGAYAURL = "https://tagaya.wup.shop.pretendo.cc/tagaya/versionlist"; +}; + +typedef XMLDataConfig XMLNetworkConfig_t; +extern XMLNetworkConfig_t n_config; +inline NetworkConfig& GetNetworkConfig() { return n_config.data();}; \ No newline at end of file diff --git a/src/gui/GeneralSettings2.cpp b/src/gui/GeneralSettings2.cpp index 6c4f5bbb..ffb58e48 100644 --- a/src/gui/GeneralSettings2.cpp +++ b/src/gui/GeneralSettings2.cpp @@ -14,6 +14,7 @@ #include #include "config/CemuConfig.h" +#include "config/NetworkSettings.h" #include "audio/IAudioAPI.h" #if BOOST_OS_WINDOWS @@ -604,6 +605,18 @@ wxPanel* GeneralSettings2::AddAccountPage(wxNotebook* notebook) content->Add(m_delete_account, 0, wxEXPAND | wxALL | wxALIGN_RIGHT, 5); m_delete_account->Bind(wxEVT_BUTTON, &GeneralSettings2::OnAccountDelete, this); + if (NetworkConfig::XMLExists()) { + wxString choices[] = { _("Nintendo"), _("Pretendo"), _("Custom") }; + m_active_service= new wxRadioBox(online_panel, wxID_ANY, _("Network Service"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 3, wxRA_SPECIFY_COLS); + } + else { + wxString choices[] = { _("Nintendo"), _("Pretendo") }; + m_active_service= new wxRadioBox(online_panel, wxID_ANY, _("Network Service"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 2, wxRA_SPECIFY_COLS); + } + m_active_service->SetToolTip(_("Connect to which Network Service")); + m_active_service->Bind(wxEVT_RADIOBOX, &GeneralSettings2::OnAccountServiceChanged,this); + content->Add(m_active_service, 0, wxEXPAND | wxALL, 5); + box_sizer->Add(content, 1, wxEXPAND, 5); online_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); @@ -613,6 +626,7 @@ wxPanel* GeneralSettings2::AddAccountPage(wxNotebook* notebook) m_active_account->Enable(false); m_create_account->Enable(false); m_delete_account->Enable(false); + m_active_service->Enable(false); } } @@ -919,6 +933,7 @@ void GeneralSettings2::StoreConfig() config.account.m_persistent_id = dynamic_cast(m_active_account->GetClientObject(active_account))->GetAccount().GetPersistentId(); config.account.online_enabled = m_online_enabled->GetValue(); + config.account.active_service = m_active_service->GetSelection(); // debug config.crash_dump = (CrashDump)m_crash_dump->GetSelection(); @@ -1488,6 +1503,7 @@ void GeneralSettings2::ApplyConfig() } m_online_enabled->SetValue(config.account.online_enabled); + m_active_service->SetSelection(config.account.active_service); UpdateAccountInformation(); // debug @@ -1722,6 +1738,13 @@ void GeneralSettings2::OnActiveAccountChanged(wxCommandEvent& event) m_has_account_change = true; } +void GeneralSettings2::OnAccountServiceChanged(wxCommandEvent& event) +{ + LaunchSettings::ChangeNetworkServiceURL(m_active_service->GetSelection()); + + UpdateAccountInformation(); +} + void GeneralSettings2::OnMLCPathSelect(wxCommandEvent& event) { if (!CemuApp::SelectMLCPath(this)) diff --git a/src/gui/GeneralSettings2.h b/src/gui/GeneralSettings2.h index ed326d5a..fb3e8092 100644 --- a/src/gui/GeneralSettings2.h +++ b/src/gui/GeneralSettings2.h @@ -65,6 +65,7 @@ private: // Account wxButton* m_create_account, * m_delete_account; wxChoice* m_active_account; + wxRadioBox* m_active_service; wxCheckBox* m_online_enabled; wxCollapsiblePane* m_account_information; wxPropertyGrid* m_account_grid; @@ -93,6 +94,7 @@ private: void OnMLCPathChar(wxKeyEvent& event); void OnShowOnlineValidator(wxCommandEvent& event); void OnOnlineEnable(wxCommandEvent& event); + void OnAccountServiceChanged(wxCommandEvent& event); // updates cemu audio devices void UpdateAudioDevice(); diff --git a/src/gui/guiWrapper.cpp b/src/gui/guiWrapper.cpp index 835dc499..2a1598c0 100644 --- a/src/gui/guiWrapper.cpp +++ b/src/gui/guiWrapper.cpp @@ -5,6 +5,7 @@ #include "gui/debugger/DebuggerWindow2.h" #include "Cafe/HW/Latte/Core/Latte.h" #include "config/ActiveSettings.h" +#include "config/NetworkSettings.h" #include "config/CemuConfig.h" #include "Cafe/HW/Latte/Renderer/Renderer.h" #include "Cafe/CafeSystem.h" @@ -96,9 +97,18 @@ void gui_updateWindowTitles(bool isIdle, bool isLoading, double fps) const uint64 titleId = CafeSystem::GetForegroundTitleId(); windowText.append(fmt::format(" - FPS: {:.2f} {} {} [TitleId: {:08x}-{:08x}]", (double)fps, renderer, graphicMode, (uint32)(titleId >> 32), (uint32)(titleId & 0xFFFFFFFF))); - if (ActiveSettings::IsOnlineEnabled()) + if (ActiveSettings::IsOnlineEnabled()){ windowText.append(" [Online]"); - + if (ActiveSettings::GetNetworkService() == NetworkService::Nintendo) { + windowText.append("[Nintendo]"); + } + else if (ActiveSettings::GetNetworkService() == NetworkService::Pretendo) { + windowText.append("[Pretendo]"); + } + else if (ActiveSettings::GetNetworkService() == NetworkService::Custom) { + windowText.append("[" + GetNetworkConfig().networkname.GetValue() + "]"); + } + } windowText.append(" "); windowText.append(CafeSystem::GetForegroundTitleName()); // append region diff --git a/src/main.cpp b/src/main.cpp index f782c33e..87243bb8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,7 @@ #include "Cafe/GameProfile/GameProfile.h" #include "Cafe/GraphicPack/GraphicPack2.h" #include "config/CemuConfig.h" +#include "config/NetworkSettings.h" #include "gui/CemuApp.h" #include "Cafe/HW/Latte/Core/LatteOverlay.h" #include "config/LaunchSettings.h" @@ -216,6 +217,8 @@ void mainEmulatorCommonInit() ExceptionHandler_init(); // read config g_config.Load(); + if (NetworkConfig::XMLExists()) + n_config.Load(); // symbol storage rplSymbolStorage_init(); // static initialization @@ -345,6 +348,7 @@ int wWinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ L if (!LaunchSettings::HandleCommandline(lpCmdLine)) return 0; ActiveSettings::LoadOnce(); + NetworkConfig::LoadOnce(); HandlePostUpdate(); return mainEmulatorHLE(); } @@ -356,6 +360,7 @@ int main(int argc, char* argv[]) if (!LaunchSettings::HandleCommandline(argc, argv)) return 0; ActiveSettings::LoadOnce(); + NetworkConfig::LoadOnce(); HandlePostUpdate(); return mainEmulatorHLE(); } @@ -371,7 +376,7 @@ int main(int argc, char *argv[]) return 0; ActiveSettings::LoadOnce(); - + NetworkConfig::LoadOnce(); HandlePostUpdate(); return mainEmulatorHLE(); } From 2b9edced8144c885e5b3b14d3e3a75217e2065ec Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Tue, 11 Oct 2022 09:17:34 +0200 Subject: [PATCH 023/616] Minor tweaks and code clean up (#357) --- .gitignore | 1 + src/Cafe/IOSU/legacy/iosu_boss.cpp | 33 +++++++------ src/Cemu/napi/napi_idbe.cpp | 75 ++++++++++-------------------- src/Cemu/napi/napi_version.cpp | 14 +++--- src/config/LaunchSettings.cpp | 10 +--- src/config/NetworkSettings.cpp | 69 +++++++++++++++------------ src/gui/GeneralSettings2.cpp | 14 +++--- src/gui/guiWrapper.cpp | 19 ++++---- 8 files changed, 105 insertions(+), 130 deletions(-) diff --git a/.gitignore b/.gitignore index e252f4cb..0f5e5a69 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ bin/network_services.xml bin/title_list_cache.xml bin/debugger/* bin/sdcard/* +bin/screenshots/* !bin/shaderCache/info.txt bin/shaderCache/* diff --git a/src/Cafe/IOSU/legacy/iosu_boss.cpp b/src/Cafe/IOSU/legacy/iosu_boss.cpp index d93d7284..acf8a99d 100644 --- a/src/Cafe/IOSU/legacy/iosu_boss.cpp +++ b/src/Cafe/IOSU/legacy/iosu_boss.cpp @@ -498,23 +498,27 @@ namespace iosu curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, task_header_callback); curl_easy_setopt(curl, CURLOPT_HEADERDATA, &(*it)); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 0x3C); - if (GetNetworkConfig().disablesslver.GetValue() && ActiveSettings::GetNetworkService() == NetworkService::Custom || ActiveSettings::GetNetworkService() == NetworkService::Pretendo){ //Remove Pretendo Function once SSL is in the Service + if (GetNetworkConfig().disablesslver.GetValue() && ActiveSettings::GetNetworkService() == NetworkService::Custom || ActiveSettings::GetNetworkService() == NetworkService::Pretendo) // remove Pretendo Function once SSL is in the Service + { curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L); - } else { - curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, task_sslctx_function); - curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, &it->task_settings); - curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_0); + } + else + { + curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, task_sslctx_function); + curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, &it->task_settings); + curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_0); } - char url[512]; + std::string requestUrl; if(it->task_settings.taskType == kRawDlTaskSetting) { char serviceToken[TaskSetting::kServiceTokenLen]; strncpy(serviceToken, (char*)&it->task_settings.settings[TaskSetting::kServiceToken], TaskSetting::kServiceTokenLen); list_headerParam = append_header_param(list_headerParam, "X-Nintendo-ServiceToken: {}", serviceToken); + char url[TaskSetting::kURLLen + 1]{}; strncpy(url, (char*)&it->task_settings.settings[TaskSetting::kURL], TaskSetting::kURLLen); - forceLogDebug_printf("\tserviceToken: %s", serviceToken); + requestUrl.assign(url); } else { @@ -566,26 +570,25 @@ namespace iosu char boss_code[0x20]; strncpy(boss_code, (char*)&it->task_settings.settings[TaskSetting::kBossCode], TaskSetting::kBossCodeLen); + switch (ActiveSettings::GetNetworkService()) { - case NetworkService::Nintendo: - sprintf(url, NintendoURLs::BOSSURL.append("/%s/%s/%s?c=%s&l=%s").c_str(), "1", boss_code, it->task_id, countryCode, languageCode); - break; case NetworkService::Pretendo: - sprintf(url, PretendoURLs::BOSSURL.append("/%s/%s/%s?c=%s&l=%s").c_str(), "1", boss_code, it->task_id, countryCode, languageCode); + requestUrl = PretendoURLs::BOSSURL; break; case NetworkService::Custom: - sprintf(url, GetNetworkConfig().urls.BOSS.GetValue().append("/%s/%s/%s?c=%s&l=%s").c_str(), "1", boss_code, it->task_id, countryCode, languageCode); + requestUrl = GetNetworkConfig().urls.BOSS.GetValue(); break; + case NetworkService::Nintendo: default: - sprintf(url, NintendoURLs::BOSSURL.append("/%s/%s/%s?c=%s&l=%s").c_str(), "1", boss_code, it->task_id, countryCode, languageCode); + requestUrl = NintendoURLs::BOSSURL; break; } + requestUrl.append(fmt::format(fmt::runtime("/{}/{}/{}?c={}&l={}"), "1", boss_code, it->task_id, countryCode, languageCode)); } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list_headerParam); - curl_easy_setopt(curl, CURLOPT_URL, url); - forceLogDebug_printf("task_run url %s", url); + curl_easy_setopt(curl, CURLOPT_URL, requestUrl.c_str()); int curl_result = curl_easy_perform(curl); curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &it->http_status_code); diff --git a/src/Cemu/napi/napi_idbe.cpp b/src/Cemu/napi/napi_idbe.cpp index 6832bf73..6644eb06 100644 --- a/src/Cemu/napi/napi_idbe.cpp +++ b/src/Cemu/napi/napi_idbe.cpp @@ -57,21 +57,23 @@ namespace NAPI std::vector IDBE_RequestRawEncrypted(uint64 titleId) { CurlRequestHelper req; - switch (ActiveSettings::GetNetworkService()) - { - case NetworkService::Nintendo: - req.initate(fmt::format(fmt::runtime(NintendoURLs::IDBEURL + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE); - break; - case NetworkService::Pretendo: - req.initate(fmt::format(fmt::runtime(PretendoURLs::IDBEURL + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE); - break; - case NetworkService::Custom: - req.initate(fmt::format(fmt::runtime(GetNetworkConfig().urls.IDBE.GetValue() + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE); - break; - default: - req.initate(fmt::format(fmt::runtime(NintendoURLs::IDBEURL + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE); - break; - } + std::string requestUrl; + switch (ActiveSettings::GetNetworkService()) + { + case NetworkService::Pretendo: + requestUrl = PretendoURLs::IDBEURL; + break; + case NetworkService::Custom: + requestUrl = GetNetworkConfig().urls.IDBE.GetValue(); + break; + case NetworkService::Nintendo: + default: + requestUrl = NintendoURLs::IDBEURL; + break; + } + requestUrl.append(fmt::format(fmt::runtime("/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId)); + req.initate(requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE); + if (!req.submitRequest(false)) { cemuLog_log(LogType::Force, fmt::format("Failed to request IDBE icon for title {0:016X}", titleId)); @@ -99,59 +101,30 @@ namespace NAPI return std::nullopt; } - CurlRequestHelper req; - switch (ActiveSettings::GetNetworkService()) - { - case NetworkService::Nintendo: - req.initate(fmt::format(fmt::runtime(NintendoURLs::IDBEURL + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE); - break; - case NetworkService::Pretendo: - req.initate(fmt::format(fmt::runtime(PretendoURLs::IDBEURL + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE); - break; - case NetworkService::Custom: - req.initate(fmt::format(fmt::runtime(GetNetworkConfig().urls.IDBE.GetValue() + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE); - break; - default: - req.initate(fmt::format(fmt::runtime(NintendoURLs::IDBEURL + "/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE); - break; - } - - if (!req.submitRequest(false)) - { - cemuLog_log(LogType::Force, fmt::format("Failed to request IDBE icon for title {0:016X}", titleId)); + std::vector idbeData = IDBE_RequestRawEncrypted(titleId); + if (idbeData.size() < 0x22) return std::nullopt; - } - /* - format: - +0x00 uint8 version (0) - +0x01 uint8 keyIndex - +0x02 uint8[32] hashSHA256 - +0x22 uint8[] EncryptedIconData - */ - auto& receivedData = req.getReceivedData(); - if (receivedData.size() < 0x22) - return std::nullopt; - if (receivedData[0] != 0) + if (idbeData[0] != 0) { cemuLog_log(LogType::Force, "IDBE_Request: File has invalid version"); return std::nullopt; } - uint8 keyIndex = receivedData[1]; + uint8 keyIndex = idbeData[1]; if (keyIndex >= 4) { cemuLog_log(LogType::Force, "IDBE_Request: Key index out of range"); return std::nullopt; } - if (receivedData.size() < (0x22 + sizeof(IDBEIconDataV0))) + if (idbeData.size() < (0x22 + sizeof(IDBEIconDataV0))) { cemuLog_log(LogType::Force, "IDBE_Request: File size does not match"); return std::nullopt; } // extract hash and encrypted icon data uint8 hash[32]; - std::memcpy(hash, receivedData.data() + 0x2, 32); + std::memcpy(hash, idbeData.data() + 0x2, 32); IDBEIconDataV0 iconDataV0; - std::memcpy(&iconDataV0, receivedData.data() + 0x22, sizeof(IDBEIconDataV0)); + std::memcpy(&iconDataV0, idbeData.data() + 0x22, sizeof(IDBEIconDataV0)); // decrypt icon data and hash _decryptIDBEAndHash(&iconDataV0, hash, keyIndex); // verify hash of decrypted data diff --git a/src/Cemu/napi/napi_version.cpp b/src/Cemu/napi/napi_version.cpp index 73dd68cf..1979b822 100644 --- a/src/Cemu/napi/napi_version.cpp +++ b/src/Cemu/napi/napi_version.cpp @@ -16,21 +16,23 @@ namespace NAPI { NAPI_VersionListVersion_Result result; CurlRequestHelper req; + + std::string requestUrl; switch (ActiveSettings::GetNetworkService()) { - case NetworkService::Nintendo: - req.initate(fmt::format(fmt::runtime(NintendoURLs::TAGAYAURL + "/{}/{}/latest_version"), NCrypto::GetRegionAsString(authInfo.region), authInfo.country), CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA); - break; case NetworkService::Pretendo: - req.initate(fmt::format(fmt::runtime(PretendoURLs::TAGAYAURL + "/{}/{}/latest_version"), NCrypto::GetRegionAsString(authInfo.region), authInfo.country), CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA); + requestUrl = PretendoURLs::TAGAYAURL; break; case NetworkService::Custom: - req.initate(fmt::format(fmt::runtime(GetNetworkConfig().urls.TAGAYA.GetValue() + "/{}/{}/latest_version"), NCrypto::GetRegionAsString(authInfo.region), authInfo.country), CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA); + requestUrl = GetNetworkConfig().urls.TAGAYA.GetValue(); break; + case NetworkService::Nintendo: default: - req.initate(fmt::format(fmt::runtime(NintendoURLs::TAGAYAURL + "/{}/{}/latest_version"), NCrypto::GetRegionAsString(authInfo.region), authInfo.country), CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA); + requestUrl = NintendoURLs::TAGAYAURL; break; } + requestUrl.append(fmt::format(fmt::runtime("/{}/{}/latest_version"), NCrypto::GetRegionAsString(authInfo.region), authInfo.country)); + req.initate(requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA); if (!req.submitRequest(false)) { diff --git a/src/config/LaunchSettings.cpp b/src/config/LaunchSettings.cpp index 06d9a5a3..4447f9bc 100644 --- a/src/config/LaunchSettings.cpp +++ b/src/config/LaunchSettings.cpp @@ -196,8 +196,6 @@ bool LaunchSettings::HandleCommandline(const std::vector& args) errorMsg.append("Error while trying to parse command line parameter:\n"); errorMsg.append(ex.what()); wxMessageBox(errorMsg, wxT("Parameter error"), wxICON_ERROR); - //cemuLog_log(LogType::Force, ex.what()); - //std::cout << "Command line parameter error: " << ex.what() << std::endl; return false; } @@ -270,10 +268,6 @@ void LaunchSettings::ChangeNetworkServiceURL(int ID){ NetworkService Network = static_cast(ID); switch (Network) { - case NetworkService::Nintendo: - serviceURL_ACT = NintendoURLs::ACTURL; - serviceURL_ECS = NintendoURLs::ECSURL; - break; case NetworkService::Pretendo: serviceURL_ACT = PretendoURLs::ACTURL; serviceURL_ECS = PretendoURLs::ECSURL; @@ -282,10 +276,10 @@ void LaunchSettings::ChangeNetworkServiceURL(int ID){ serviceURL_ACT = GetNetworkConfig().urls.ACT.GetValue(); serviceURL_ECS = GetNetworkConfig().urls.ECS.GetValue(); break; + case NetworkService::Nintendo: default: serviceURL_ACT = NintendoURLs::ACTURL; serviceURL_ECS = NintendoURLs::ECSURL; break; } - -} \ No newline at end of file +} diff --git a/src/config/NetworkSettings.cpp b/src/config/NetworkSettings.cpp index f3a3b8b6..210263ef 100644 --- a/src/config/NetworkSettings.cpp +++ b/src/config/NetworkSettings.cpp @@ -3,41 +3,48 @@ #include "CemuConfig.h" #include #include "Common/FileStream.h" + XMLNetworkConfig_t n_config(L"network_services.xml"); -void NetworkConfig::LoadOnce() { - s_full_path = boost::dll::program_location().generic_wstring(); +void NetworkConfig::LoadOnce() +{ + s_full_path = boost::dll::program_location().generic_wstring(); s_path = s_full_path.parent_path(); - n_config.SetFilename(GetPath("network_services.xml").generic_wstring()); - if(XMLExists()) { - n_config.Load(); - } + n_config.SetFilename(GetPath("network_services.xml").generic_wstring()); + if (XMLExists()) + n_config.Load(); } -void NetworkConfig::Load(XMLConfigParser& parser){ - auto config = parser.get("content"); - networkname = config.get("networkname","[Nintendo]"); - disablesslver = config.get("disablesslverification",disablesslver); - auto u = config.get("urls"); - urls.ACT = u.get("act",NintendoURLs::ACTURL); - urls.ECS = u.get("ecs",NintendoURLs::ECSURL); - urls.NUS = u.get("nus",NintendoURLs::NUSURL); - urls.IAS = u.get("ias",NintendoURLs::IASURL); - urls.CCSU = u.get("ccsu",NintendoURLs::CCSUURL); - urls.CCS = u.get("ccs",NintendoURLs::CCSURL); - urls.IDBE = u.get("idbe",NintendoURLs::IDBEURL); - urls.BOSS = u.get("boss",NintendoURLs::BOSSURL); - urls.TAGAYA = u.get("tagaya",NintendoURLs::TAGAYAURL); - if (static_cast(GetConfig().account.active_service.GetValue()) == NetworkService::Custom) { - LaunchSettings::ChangeNetworkServiceURL(2); - } +void NetworkConfig::Load(XMLConfigParser& parser) +{ + auto config = parser.get("content"); + networkname = config.get("networkname", "Custom"); + disablesslver = config.get("disablesslverification", disablesslver); + auto u = config.get("urls"); + urls.ACT = u.get("act", NintendoURLs::ACTURL); + urls.ECS = u.get("ecs", NintendoURLs::ECSURL); + urls.NUS = u.get("nus", NintendoURLs::NUSURL); + urls.IAS = u.get("ias", NintendoURLs::IASURL); + urls.CCSU = u.get("ccsu", NintendoURLs::CCSUURL); + urls.CCS = u.get("ccs", NintendoURLs::CCSURL); + urls.IDBE = u.get("idbe", NintendoURLs::IDBEURL); + urls.BOSS = u.get("boss", NintendoURLs::BOSSURL); + urls.TAGAYA = u.get("tagaya", NintendoURLs::TAGAYAURL); + if (static_cast(GetConfig().account.active_service.GetValue()) == NetworkService::Custom) + LaunchSettings::ChangeNetworkServiceURL(2); } -bool NetworkConfig::XMLExists() { - if (!fs::exists(GetPath("network_services.xml"))) { - if (static_cast(GetConfig().account.active_service.GetValue()) == NetworkService::Custom) { - LaunchSettings::ChangeNetworkServiceURL(0); - GetConfig().account.active_service = 0; - } - return false; - } else {return true;} + +bool NetworkConfig::XMLExists() +{ + std::error_code ec; + if (!fs::exists(GetPath("network_services.xml"), ec)) + { + if (static_cast(GetConfig().account.active_service.GetValue()) == NetworkService::Custom) + { + LaunchSettings::ChangeNetworkServiceURL(0); + GetConfig().account.active_service = 0; + } + return false; + } + return true; } \ No newline at end of file diff --git a/src/gui/GeneralSettings2.cpp b/src/gui/GeneralSettings2.cpp index ffb58e48..776afc2e 100644 --- a/src/gui/GeneralSettings2.cpp +++ b/src/gui/GeneralSettings2.cpp @@ -605,14 +605,12 @@ wxPanel* GeneralSettings2::AddAccountPage(wxNotebook* notebook) content->Add(m_delete_account, 0, wxEXPAND | wxALL | wxALIGN_RIGHT, 5); m_delete_account->Bind(wxEVT_BUTTON, &GeneralSettings2::OnAccountDelete, this); - if (NetworkConfig::XMLExists()) { - wxString choices[] = { _("Nintendo"), _("Pretendo"), _("Custom") }; - m_active_service= new wxRadioBox(online_panel, wxID_ANY, _("Network Service"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 3, wxRA_SPECIFY_COLS); - } - else { - wxString choices[] = { _("Nintendo"), _("Pretendo") }; - m_active_service= new wxRadioBox(online_panel, wxID_ANY, _("Network Service"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 2, wxRA_SPECIFY_COLS); - } + wxString choices[] = { _("Nintendo"), _("Pretendo"), _("Custom") }; + m_active_service = new wxRadioBox(online_panel, wxID_ANY, _("Network Service"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 3, wxRA_SPECIFY_COLS); + if (!NetworkConfig::XMLExists()) + m_active_service->Enable(2, false); + + m_active_service->SetToolTip(_("Connect to which Network Service")); m_active_service->Bind(wxEVT_RADIOBOX, &GeneralSettings2::OnAccountServiceChanged,this); content->Add(m_active_service, 0, wxEXPAND | wxALL, 5); diff --git a/src/gui/guiWrapper.cpp b/src/gui/guiWrapper.cpp index 2a1598c0..4d840cf4 100644 --- a/src/gui/guiWrapper.cpp +++ b/src/gui/guiWrapper.cpp @@ -97,17 +97,14 @@ void gui_updateWindowTitles(bool isIdle, bool isLoading, double fps) const uint64 titleId = CafeSystem::GetForegroundTitleId(); windowText.append(fmt::format(" - FPS: {:.2f} {} {} [TitleId: {:08x}-{:08x}]", (double)fps, renderer, graphicMode, (uint32)(titleId >> 32), (uint32)(titleId & 0xFFFFFFFF))); - if (ActiveSettings::IsOnlineEnabled()){ - windowText.append(" [Online]"); - if (ActiveSettings::GetNetworkService() == NetworkService::Nintendo) { - windowText.append("[Nintendo]"); - } - else if (ActiveSettings::GetNetworkService() == NetworkService::Pretendo) { - windowText.append("[Pretendo]"); - } - else if (ActiveSettings::GetNetworkService() == NetworkService::Custom) { - windowText.append("[" + GetNetworkConfig().networkname.GetValue() + "]"); - } + if (ActiveSettings::IsOnlineEnabled()) + { + if (ActiveSettings::GetNetworkService() == NetworkService::Nintendo) + windowText.append(" [Online]"); + else if (ActiveSettings::GetNetworkService() == NetworkService::Pretendo) + windowText.append(" [Online-Pretendo]"); + else if (ActiveSettings::GetNetworkService() == NetworkService::Custom) + windowText.append(" [Online-" + GetNetworkConfig().networkname.GetValue() + "]"); } windowText.append(" "); windowText.append(CafeSystem::GetForegroundTitleName()); From d6ba61cf6435a163bf6aea17de32c19d1e61e456 Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Tue, 11 Oct 2022 23:03:26 -0700 Subject: [PATCH 024/616] Add support for non portable mode (#356) --- CMakeLists.txt | 5 ++ src/Cafe/Account/Account.cpp | 4 +- src/Cafe/CafeSystem.cpp | 6 +- src/Cafe/Filesystem/FST/KeyCache.cpp | 2 +- src/Cafe/GameProfile/GameProfile.cpp | 11 ++- src/Cafe/GraphicPack/GraphicPack2.cpp | 4 +- src/Cafe/HW/Latte/Core/LatteShaderCache.cpp | 10 +-- .../Renderer/OpenGL/RendererShaderGL.cpp | 2 +- src/Cafe/HW/Latte/Renderer/Renderer.cpp | 2 +- .../Renderer/Vulkan/RendererShaderVk.cpp | 2 +- .../Vulkan/VulkanPipelineStableCache.cpp | 4 +- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 4 +- src/Cafe/HW/MMU/MMU.cpp | 2 +- src/Cafe/IOSU/legacy/iosu_crypto.cpp | 8 +- src/Cafe/OS/RPL/rpl.cpp | 2 +- src/Cafe/OS/libs/coreinit/coreinit_FS.cpp | 4 +- src/Cafe/TitleList/TitleInfo.cpp | 2 +- src/Cemu/Logging/CemuLogging.cpp | 2 +- .../ExceptionHandler_win32.cpp | 6 +- src/config/ActiveSettings.cpp | 33 +++++++- src/config/ActiveSettings.h | 81 ++++++++++--------- src/config/NetworkSettings.cpp | 8 +- src/config/NetworkSettings.h | 8 -- src/gui/CemuApp.cpp | 79 ++++++++++++++---- src/gui/CemuApp.h | 8 +- src/gui/CemuUpdateWindow.cpp | 4 +- src/gui/ChecksumTool.cpp | 6 +- src/gui/DownloadGraphicPacksWindow.cpp | 10 +-- src/gui/GraphicPacksWindow2.cpp | 3 +- src/gui/MainWindow.cpp | 12 +-- src/gui/MemorySearcherTool.cpp | 4 +- src/gui/debugger/DebuggerWindow2.cpp | 4 +- src/gui/input/InputSettings2.cpp | 4 +- src/input/InputManager.cpp | 16 ++-- src/main.cpp | 27 ++----- src/util/helpers/helpers.cpp | 3 +- src/util/helpers/helpers.h | 2 +- src/util/libusbWrapper/libusbWrapper.cpp | 2 +- 38 files changed, 233 insertions(+), 163 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 76dcd33a..b973a3f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 3.21.1) option(ENABLE_VCPKG "Enable the vcpkg package manager" ON) +option(PORTABLE "All data created and maintained by Cemu will be in the directory where the executable file is located" ON) set(EXPERIMENTAL_VERSION "" CACHE STRING "") # used by CI script to set experimental version if (EXPERIMENTAL_VERSION) @@ -30,6 +31,10 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) add_compile_definitions($<$:CEMU_DEBUG_ASSERT>) # if build type is debug, set CEMU_DEBUG_ASSERT +if(PORTABLE) + add_compile_definitions(PORTABLE) +endif() + set_property(GLOBAL PROPERTY USE_FOLDERS ON) # enable link time optimization for release builds diff --git a/src/Cafe/Account/Account.cpp b/src/Cafe/Account/Account.cpp index 2cf424f1..f1ce0be8 100644 --- a/src/Cafe/Account/Account.cpp +++ b/src/Cafe/Account/Account.cpp @@ -424,7 +424,7 @@ OnlineValidator Account::ValidateOnlineFiles() const { OnlineValidator result{}; - const auto otp = ActiveSettings::GetPath("otp.bin"); + const auto otp = ActiveSettings::GetUserDataPath("otp.bin"); if (!fs::exists(otp)) result.otp = OnlineValidator::FileState::Missing; else if (fs::file_size(otp) != 1024) @@ -432,7 +432,7 @@ OnlineValidator Account::ValidateOnlineFiles() const else result.otp = OnlineValidator::FileState::Ok; - const auto seeprom = ActiveSettings::GetPath("seeprom.bin"); + const auto seeprom = ActiveSettings::GetUserDataPath("seeprom.bin"); if (!fs::exists(seeprom)) result.seeprom = OnlineValidator::FileState::Missing; else if (fs::file_size(seeprom) != 512) diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index 2cadc106..e2e1c5ef 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -289,7 +289,7 @@ uint32 loadSharedData() for (sint32 i = 0; i < sizeof(shareddataDef) / sizeof(shareddataDef[0]); i++) { bool existsInMLC = fs::exists(ActiveSettings::GetMlcPath(shareddataDef[i].mlcPath)); - bool existsInResources = fs::exists(ActiveSettings::GetPath(shareddataDef[i].resourcePath)); + bool existsInResources = fs::exists(ActiveSettings::GetDataPath(shareddataDef[i].resourcePath)); if (!existsInMLC && !existsInResources) { @@ -314,7 +314,7 @@ uint32 loadSharedData() // alternatively fall back to our shared fonts if (!fontFile) { - path = ActiveSettings::GetPath(shareddataDef[i].resourcePath); + path = ActiveSettings::GetDataPath(shareddataDef[i].resourcePath); fontFile = FileStream::openFile2(path); } if (!fontFile) @@ -340,7 +340,7 @@ uint32 loadSharedData() return memory_getVirtualOffsetFromPointer(dataWritePtr); } // alternative method: load RAM dump - const auto path = ActiveSettings::GetPath("shareddata.bin"); + const auto path = ActiveSettings::GetUserDataPath("shareddata.bin"); FileStream* ramDumpFile = FileStream::openFile2(path); if (ramDumpFile) { diff --git a/src/Cafe/Filesystem/FST/KeyCache.cpp b/src/Cafe/Filesystem/FST/KeyCache.cpp index 587a5dd6..5d8d51c1 100644 --- a/src/Cafe/Filesystem/FST/KeyCache.cpp +++ b/src/Cafe/Filesystem/FST/KeyCache.cpp @@ -59,7 +59,7 @@ void KeyCache_Prepare() sKeyCachePrepared = true; g_keyCache.clear(); // load keys - auto keysPath = ActiveSettings::GetPath("keys.txt"); + auto keysPath = ActiveSettings::GetUserDataPath("keys.txt"); FileStream* fs_keys = FileStream::openFile2(keysPath); if( !fs_keys ) { diff --git a/src/Cafe/GameProfile/GameProfile.cpp b/src/Cafe/GameProfile/GameProfile.cpp index 9f2550c2..d8c735ce 100644 --- a/src/Cafe/GameProfile/GameProfile.cpp +++ b/src/Cafe/GameProfile/GameProfile.cpp @@ -180,12 +180,12 @@ void gameProfile_load() bool GameProfile::Load(uint64_t title_id) { - auto gameProfilePath = ActiveSettings::GetPath("gameProfiles/{:016x}.ini", title_id); + auto gameProfilePath = ActiveSettings::GetConfigPath("gameProfiles/{:016x}.ini", title_id); std::optional> profileContents = FileStream::LoadIntoMemory(gameProfilePath); if (!profileContents) { - gameProfilePath = ActiveSettings::GetPath("gameProfiles/default/{:016x}.ini", title_id); + gameProfilePath = ActiveSettings::GetDataPath("gameProfiles/default/{:016x}.ini", title_id); profileContents = FileStream::LoadIntoMemory(gameProfilePath); if (!profileContents) return false; @@ -276,7 +276,12 @@ bool GameProfile::Load(uint64_t title_id) void GameProfile::Save(uint64_t title_id) { - auto gameProfilePath = ActiveSettings::GetPath("gameProfiles/{:016x}.ini", title_id); + auto gameProfileDir = ActiveSettings::GetConfigPath("gameProfiles"); + if (std::error_code ex_ec; !fs::exists(gameProfileDir, ex_ec) && !ex_ec) { + std::error_code cr_ec; + fs::create_directories(gameProfileDir, cr_ec); + } + auto gameProfilePath = gameProfileDir / fmt::format("{:016x}.ini", title_id); FileStream* fs = FileStream::createFile2(gameProfilePath); if (!fs) { diff --git a/src/Cafe/GraphicPack/GraphicPack2.cpp b/src/Cafe/GraphicPack/GraphicPack2.cpp index 959441d7..a81ec03c 100644 --- a/src/Cafe/GraphicPack/GraphicPack2.cpp +++ b/src/Cafe/GraphicPack/GraphicPack2.cpp @@ -63,7 +63,7 @@ void GraphicPack2::LoadGraphicPack(fs::path graphicPackPath) void GraphicPack2::LoadAll() { std::error_code ec; - fs::path basePath = ActiveSettings::GetPath("graphicPacks"); + fs::path basePath = ActiveSettings::GetUserDataPath("graphicPacks"); for (fs::recursive_directory_iterator it(basePath, ec); it != end(it); ++it) { if (!it->is_directory(ec)) @@ -93,7 +93,7 @@ bool GraphicPack2::LoadGraphicPack(const std::wstring& filename, IniParser& rule if (it == config_entries.cend()) { // check for relative path - it = config_entries.find(MakeRelativePath(gp->GetFilename2()).lexically_normal()); + it = config_entries.find(MakeRelativePath(ActiveSettings::GetUserDataPath(), gp->GetFilename2()).lexically_normal()); } if (it != config_entries.cend()) diff --git a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp index e39d0e86..ba85898c 100644 --- a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp +++ b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp @@ -197,17 +197,17 @@ void LatteShaderCache_load() LatteShaderCache_initCompileQueue(); // create directories std::error_code ec; - fs::create_directories(ActiveSettings::GetPath("shaderCache/transferable"), ec); - fs::create_directories(ActiveSettings::GetPath("shaderCache/precompiled"), ec); + fs::create_directories(ActiveSettings::GetCachePath("shaderCache/transferable"), ec); + fs::create_directories(ActiveSettings::GetCachePath("shaderCache/precompiled"), ec); // initialize renderer specific caches if (g_renderer->GetType() == RendererAPI::Vulkan) RendererShaderVk::ShaderCacheLoading_begin(cacheTitleId); else if (g_renderer->GetType() == RendererAPI::OpenGL) RendererShaderGL::ShaderCacheLoading_begin(cacheTitleId); // get cache file name - const auto pathGeneric = ActiveSettings::GetPath("shaderCache/transferable/{:016x}_shaders.bin", cacheTitleId); - const auto pathGenericPre1_25_0 = ActiveSettings::GetPath("shaderCache/transferable/{:016x}.bin", cacheTitleId); // before 1.25.0 - const auto pathGenericPre1_16_0 = ActiveSettings::GetPath("shaderCache/transferable/{:08x}.bin", CafeSystem::GetRPXHashBase()); // before 1.16.0 + const auto pathGeneric = ActiveSettings::GetCachePath("shaderCache/transferable/{:016x}_shaders.bin", cacheTitleId); + const auto pathGenericPre1_25_0 = ActiveSettings::GetCachePath("shaderCache/transferable/{:016x}.bin", cacheTitleId); // before 1.25.0 + const auto pathGenericPre1_16_0 = ActiveSettings::GetCachePath("shaderCache/transferable/{:08x}.bin", CafeSystem::GetRPXHashBase()); // before 1.16.0 LatteShaderCache_handleDeprecatedCacheFiles(pathGeneric, pathGenericPre1_25_0, pathGenericPre1_16_0); // calculate extraVersion for transferable and precompiled shader cache diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.cpp index e9e86f53..dc088ae3 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.cpp +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.cpp @@ -279,7 +279,7 @@ void RendererShaderGL::ShaderCacheLoading_begin(uint64 cacheTitleId) { const uint32 cacheMagic = GeneratePrecompiledCacheId(); const std::string cacheFilename = fmt::format("{:016x}_gl.bin", cacheTitleId); - const std::wstring cachePath = ActiveSettings::GetPath("shaderCache/precompiled/{}", cacheFilename).generic_wstring(); + const std::wstring cachePath = ActiveSettings::GetCachePath("shaderCache/precompiled/{}", cacheFilename).generic_wstring(); g_programBinaryCache = FileCache::Open(cachePath, true, cacheMagic); if (g_programBinaryCache == nullptr) cemuLog_log(LogType::Force, "Unable to open OpenGL precompiled cache {}", cacheFilename); diff --git a/src/Cafe/HW/Latte/Renderer/Renderer.cpp b/src/Cafe/HW/Latte/Renderer/Renderer.cpp index c7f7b814..8f99c069 100644 --- a/src/Cafe/HW/Latte/Renderer/Renderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Renderer.cpp @@ -133,7 +133,7 @@ void Renderer::SaveScreenshot(const std::vector& rgb_data, int width, int // save to png file if (save_screenshot) { - fs::path screendir = ActiveSettings::GetPath("screenshots"); + fs::path screendir = ActiveSettings::GetUserDataPath("screenshots"); if (!fs::exists(screendir)) fs::create_directory(screendir); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp index e6677576..7c577903 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp @@ -442,7 +442,7 @@ void RendererShaderVk::ShaderCacheLoading_begin(uint64 cacheTitleId) } uint32 spirvCacheMagic = GeneratePrecompiledCacheId(); const std::string cacheFilename = fmt::format("{:016x}_spirv.bin", cacheTitleId); - const std::wstring cachePath = ActiveSettings::GetPath("shaderCache/precompiled/{}", cacheFilename).generic_wstring(); + const std::wstring cachePath = ActiveSettings::GetCachePath("shaderCache/precompiled/{}", cacheFilename).generic_wstring(); s_spirvCache = FileCache::Open(cachePath, true, spirvCacheMagic); if (s_spirvCache == nullptr) cemuLog_log(LogType::Force, "Unable to open SPIR-V cache {}", cacheFilename); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp index bb6c966e..38f7c882 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp @@ -32,8 +32,8 @@ VulkanPipelineStableCache& VulkanPipelineStableCache::GetInstance() uint32 VulkanPipelineStableCache::BeginLoading(uint64 cacheTitleId) { std::error_code ec; - fs::create_directories(ActiveSettings::GetPath("shaderCache/transferable"), ec); - const auto pathCacheFile = ActiveSettings::GetPath("shaderCache/transferable/{:016x}_vkpipeline.bin", cacheTitleId); + fs::create_directories(ActiveSettings::GetCachePath("shaderCache/transferable"), ec); + const auto pathCacheFile = ActiveSettings::GetCachePath("shaderCache/transferable/{:016x}_vkpipeline.bin", cacheTitleId); // init cache loader state g_vkCacheState.pipelineLoadIndex = 0; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 249d06b7..5cbf7f94 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -2326,7 +2326,7 @@ void VulkanRenderer::WaitCommandBufferFinished(uint64 commandBufferId) void VulkanRenderer::PipelineCacheSaveThread(size_t cache_size) { - const auto dir = ActiveSettings::GetPath("shaderCache/driver/vk"); + const auto dir = ActiveSettings::GetCachePath("shaderCache/driver/vk"); if (!fs::exists(dir)) { try @@ -2403,7 +2403,7 @@ void VulkanRenderer::PipelineCacheSaveThread(size_t cache_size) void VulkanRenderer::CreatePipelineCache() { std::vector cacheData; - const auto dir = ActiveSettings::GetPath("shaderCache/driver/vk"); + const auto dir = ActiveSettings::GetCachePath("shaderCache/driver/vk"); if (fs::exists(dir)) { const auto filename = dir / fmt::format("{:016x}.bin", CafeSystem::GetForegroundTitleId()); diff --git a/src/Cafe/HW/MMU/MMU.cpp b/src/Cafe/HW/MMU/MMU.cpp index 87bf5722..9e22d907 100644 --- a/src/Cafe/HW/MMU/MMU.cpp +++ b/src/Cafe/HW/MMU/MMU.cpp @@ -409,7 +409,7 @@ void memory_writeDumpFile(uint32 startAddr, uint32 size, const fs::path& path) void memory_createDump() { const uint32 pageSize = MemMapper::GetPageSize(); - fs::path path = ActiveSettings::GetPath("dump/ramDump{:}", (uint32)time(nullptr)); + fs::path path = ActiveSettings::GetUserDataPath("dump/ramDump{:}", (uint32)time(nullptr)); fs::create_directories(path); for (auto& itr : g_mmuRanges) diff --git a/src/Cafe/IOSU/legacy/iosu_crypto.cpp b/src/Cafe/IOSU/legacy/iosu_crypto.cpp index e74a93e2..961ab70d 100644 --- a/src/Cafe/IOSU/legacy/iosu_crypto.cpp +++ b/src/Cafe/IOSU/legacy/iosu_crypto.cpp @@ -563,7 +563,7 @@ void iosuCrypto_loadSSLCertificates() void iosuCrypto_init() { // load OTP dump - if (std::ifstream otp_file(ActiveSettings::GetPath("otp.bin"), std::ifstream::in | std::ios::binary); otp_file.is_open()) + if (std::ifstream otp_file(ActiveSettings::GetUserDataPath("otp.bin"), std::ifstream::in | std::ios::binary); otp_file.is_open()) { otp_file.seekg(0, std::ifstream::end); const auto length = otp_file.tellg(); @@ -586,7 +586,7 @@ void iosuCrypto_init() hasOtpMem = false; } - if (std::ifstream seeprom_file(ActiveSettings::GetPath("seeprom.bin"), std::ifstream::in | std::ios::binary); seeprom_file.is_open()) + if (std::ifstream seeprom_file(ActiveSettings::GetUserDataPath("seeprom.bin"), std::ifstream::in | std::ios::binary); seeprom_file.is_open()) { seeprom_file.seekg(0, std::ifstream::end); const auto length = seeprom_file.tellg(); @@ -630,13 +630,13 @@ sint32 iosuCrypt_checkRequirementsForOnlineMode(std::wstring& additionalErrorInf { std::error_code ec; // check if otp.bin is present - const auto otp_file = ActiveSettings::GetPath("otp.bin"); + const auto otp_file = ActiveSettings::GetUserDataPath("otp.bin"); if(!fs::exists(otp_file, ec)) return IOS_CRYPTO_ONLINE_REQ_OTP_MISSING; if(fs::file_size(otp_file, ec) != 1024) return IOS_CRYPTO_ONLINE_REQ_OTP_CORRUPTED; // check if seeprom.bin is present - const auto seeprom_file = ActiveSettings::GetPath("seeprom.bin"); + const auto seeprom_file = ActiveSettings::GetUserDataPath("seeprom.bin"); if (!fs::exists(seeprom_file, ec)) return IOS_CRYPTO_ONLINE_REQ_SEEPROM_MISSING; if (fs::file_size(seeprom_file, ec) != 512) diff --git a/src/Cafe/OS/RPL/rpl.cpp b/src/Cafe/OS/RPL/rpl.cpp index 66d7a4ca..43e39d5b 100644 --- a/src/Cafe/OS/RPL/rpl.cpp +++ b/src/Cafe/OS/RPL/rpl.cpp @@ -2116,7 +2116,7 @@ void RPLLoader_LoadDependency(rplDependency_t* dependency) // attempt to load rpl from Cemu's /cafeLibs/ directory if (ActiveSettings::LoadSharedLibrariesEnabled()) { - const auto filePath = ActiveSettings::GetPath("cafeLibs/{}", dependency->filepath); + const auto filePath = ActiveSettings::GetUserDataPath("cafeLibs/{}", dependency->filepath); auto fileData = FileStream::LoadIntoMemory(filePath); if (fileData) { diff --git a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp index c49607e1..1853bae8 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp @@ -107,7 +107,7 @@ namespace coreinit return; std::error_code ec; - const auto path = ActiveSettings::GetPath("sdcard/"); + const auto path = ActiveSettings::GetUserDataPath("sdcard/"); fs::create_directories(path, ec); FSCDeviceHostFS_Mount("/vol/external01", _pathToUtf8(path), FSC_PRIORITY_BASE); @@ -140,7 +140,7 @@ namespace coreinit return FS_RESULT::ERR_PLACEHOLDER; std::error_code ec; - const auto path = ActiveSettings::GetPath("sdcard/"); + const auto path = ActiveSettings::GetUserDataPath("sdcard/"); fs::create_directories(path, ec); if (!FSCDeviceHostFS_Mount(mountPathOut, _pathToUtf8(path), FSC_PRIORITY_BASE)) return FS_RESULT::ERR_PLACEHOLDER; diff --git a/src/Cafe/TitleList/TitleInfo.cpp b/src/Cafe/TitleList/TitleInfo.cpp index ef97e6eb..10710c43 100644 --- a/src/Cafe/TitleList/TitleInfo.cpp +++ b/src/Cafe/TitleList/TitleInfo.cpp @@ -291,7 +291,7 @@ void TitleInfo::CalcUID() fs::path normalizedPath; if (m_fullPath.is_relative()) { - normalizedPath = ActiveSettings::GetPath(); + normalizedPath = ActiveSettings::GetUserDataPath(); normalizedPath /= m_fullPath; } else diff --git a/src/Cemu/Logging/CemuLogging.cpp b/src/Cemu/Logging/CemuLogging.cpp index 7e6669a8..28a86427 100644 --- a/src/Cemu/Logging/CemuLogging.cpp +++ b/src/Cemu/Logging/CemuLogging.cpp @@ -98,7 +98,7 @@ void cemuLog_createLogFile(bool triggeredByCrash) if (LogContext.file_stream.is_open()) return; - const auto path = ActiveSettings::GetPath("log.txt"); + const auto path = ActiveSettings::GetUserDataPath("log.txt"); LogContext.file_stream.open(path, std::ios::out); if (LogContext.file_stream.fail()) { diff --git a/src/Common/ExceptionHandler/ExceptionHandler_win32.cpp b/src/Common/ExceptionHandler/ExceptionHandler_win32.cpp index 25dca26f..565f4f8f 100644 --- a/src/Common/ExceptionHandler/ExceptionHandler_win32.cpp +++ b/src/Common/ExceptionHandler/ExceptionHandler_win32.cpp @@ -61,7 +61,7 @@ bool CreateMiniDump(CrashDump dump, EXCEPTION_POINTERS* pep) if (dump == CrashDump::Disabled) return true; - fs::path p = ActiveSettings::GetPath("crashdump"); + fs::path p = ActiveSettings::GetUserDataPath("crashdump"); std::error_code ec; fs::create_directories(p, ec); @@ -356,11 +356,11 @@ void createCrashlog(EXCEPTION_POINTERS* e, PCONTEXT context) const auto temp_time = std::chrono::system_clock::to_time_t(now); const auto& time = *std::gmtime(&temp_time); - fs::path p = ActiveSettings::GetPath("crashdump"); + fs::path p = ActiveSettings::GetUserDataPath("crashdump"); p /= fmt::format("log_{:04d}{:02d}{:02d}_{:02d}{:02d}{:02d}.txt", 1900 + time.tm_year, time.tm_mon + 1, time.tm_mday, time.tm_year, time.tm_hour, time.tm_min, time.tm_sec); std::error_code ec; - fs::copy_file(ActiveSettings::GetPath("log.txt"), p, ec); + fs::copy_file(ActiveSettings::GetUserDataPath("log.txt"), p, ec); } exit(0); diff --git a/src/config/ActiveSettings.cpp b/src/config/ActiveSettings.cpp index 16119725..bef9cf0b 100644 --- a/src/config/ActiveSettings.cpp +++ b/src/config/ActiveSettings.cpp @@ -1,6 +1,7 @@ #include "config/ActiveSettings.h" #include "Cafe/GameProfile/GameProfile.h" +#include "Cemu/Logging/CemuLogging.h" #include "LaunchSettings.h" #include "util/helpers/helpers.h" @@ -12,17 +13,41 @@ extern bool alwaysDisplayDRC; -void ActiveSettings::LoadOnce() +std::set +ActiveSettings::LoadOnce(const fs::path& user_data_path, + const fs::path& config_path, + const fs::path& cache_path, + const fs::path& data_path) { s_full_path = boost::dll::program_location().generic_wstring(); - s_path = s_full_path.parent_path(); + + s_user_data_path = user_data_path; + s_config_path = config_path; + s_cache_path = cache_path; + s_data_path = data_path; + std::set failed_write_access; + for (auto&& path : {user_data_path, config_path, cache_path}) + { + if (!fs::exists(path)) + { + std::error_code ec; + fs::create_directories(path, ec); + } + if (!TestWriteAccess(path)) + { + cemuLog_log(LogType::Force, "Failed to write to {}", path.generic_string()); + failed_write_access.insert(path); + } + } + s_filename = s_full_path.filename(); - g_config.SetFilename(GetPath("settings.xml").generic_wstring()); + g_config.SetFilename(GetConfigPath("settings.xml").generic_wstring()); g_config.Load(); LaunchSettings::ChangeNetworkServiceURL(GetConfig().account.active_service); std::wstring additionalErrorInfo; s_has_required_online_files = iosuCrypt_checkRequirementsForOnlineMode(additionalErrorInfo) == IOS_CRYPTO_ONLINE_REQ_OK; + return failed_write_access; } bool ActiveSettings::LoadSharedLibrariesEnabled() @@ -226,6 +251,6 @@ fs::path ActiveSettings::GetMlcPath() fs::path ActiveSettings::GetDefaultMLCPath() { - return GetPath("mlc01"); + return GetUserDataPath("mlc01"); } diff --git a/src/config/ActiveSettings.h b/src/config/ActiveSettings.h index ae3feb94..9621b4d0 100644 --- a/src/config/ActiveSettings.h +++ b/src/config/ActiveSettings.h @@ -1,69 +1,70 @@ #pragma once +#include #include "config/CemuConfig.h" #include "config/NetworkSettings.h" // global active settings for fast access (reflects settings from command line and game profile) class ActiveSettings { -public: - static void LoadOnce(); - - [[nodiscard]] static fs::path GetFullPath() { return s_full_path; } - [[nodiscard]] static fs::path GetPath() { return s_path; } - [[nodiscard]] static fs::path GetFilename() { return s_filename; } - - [[nodiscard]] static fs::path GetMlcPath(); - - [[nodiscard]] static fs::path GetPath(std::string_view p) - { - std::basic_string_view s((const char8_t*)p.data(), p.size()); - return s_path / fs::path(s); - } - - [[nodiscard]] static fs::path GetMlcPath(std::string_view p) - { - std::basic_string_view s((const char8_t*)p.data(), p.size()); - return GetMlcPath() / fs::path(s); - } - +private: template - [[nodiscard]] static fs::path GetPath(std::string_view format, TArgs&&... args) + static fs::path GetPath(const fs::path& path, std::string_view format, TArgs&&... args) { cemu_assert_debug(format.empty() || (format[0] != '/' && format[0] != '\\')); std::string tmpPathStr = fmt::format(fmt::runtime(format), std::forward(args)...); - std::basic_string_view s((const char8_t*)tmpPathStr.data(), tmpPathStr.size()); - return s_path / fs::path(s); + return path / _utf8ToPath(tmpPathStr); } - + template - [[nodiscard]] static fs::path GetPath(std::wstring_view format, TArgs&&... args) + static fs::path GetPath(const fs::path& path, std::wstring_view format, TArgs&&... args) { cemu_assert_debug(format.empty() || (format[0] != L'/' && format[0] != L'\\')); - return s_path / fmt::format(format, std::forward(args)...); + return path / fmt::format(fmt::runtime(format), std::forward(args)...); } - - template - [[nodiscard]] static fs::path GetMlcPath(std::string_view format, TArgs&&... args) + static fs::path GetPath(const fs::path& path, std::string_view p) { - cemu_assert_debug(format.empty() || (format[0] != '/' && format[0] != '\\')); - auto tmp = fmt::format(fmt::runtime(format), std::forward(args)...); - return GetMlcPath() / _utf8ToPath(tmp); + std::basic_string_view s((const char8_t*)p.data(), p.size()); + return path / fs::path(s); } - - template - [[nodiscard]] static fs::path GetMlcPath(std::wstring_view format, TArgs&&... args) + static fs::path GetPath(const fs::path& path) { - cemu_assert_debug(format.empty() || (format[0] != L'/' && format[0] != L'\\')); - return GetMlcPath() / fmt::format(fmt::runtime(format), std::forward(args)...); + return path; } - + +public: + // Set directories and return all directories that failed write access test + static std::set + LoadOnce(const fs::path& user_data_path, + const fs::path& config_path, + const fs::path& cache_path, + const fs::path& data_path); + + [[nodiscard]] static fs::path GetFullPath() { return s_full_path; } + [[nodiscard]] static fs::path GetFilename() { return s_filename; } + template + [[nodiscard]] static fs::path GetUserDataPath(TArgs&&... args){ return GetPath(s_user_data_path, std::forward(args)...); }; + template + [[nodiscard]] static fs::path GetConfigPath(TArgs&&... args){ return GetPath(s_config_path, std::forward(args)...); }; + template + [[nodiscard]] static fs::path GetCachePath(TArgs&&... args){ return GetPath(s_cache_path, std::forward(args)...); }; + template + [[nodiscard]] static fs::path GetDataPath(TArgs&&... args){ return GetPath(s_data_path, std::forward(args)...); }; + + [[nodiscard]] static fs::path GetMlcPath(); + + template + [[nodiscard]] static fs::path GetMlcPath(TArgs&&... args){ return GetPath(GetMlcPath(), std::forward(args)...); }; + // get mlc path to default cemu root dir/mlc01 [[nodiscard]] static fs::path GetDefaultMLCPath(); private: inline static fs::path s_full_path; // full filename - inline static fs::path s_path; // path + inline static fs::path s_user_data_path; + inline static fs::path s_config_path; + inline static fs::path s_cache_path; + inline static fs::path s_data_path; inline static fs::path s_filename; // cemu.exe inline static fs::path s_mlc_path; diff --git a/src/config/NetworkSettings.cpp b/src/config/NetworkSettings.cpp index 210263ef..2227b981 100644 --- a/src/config/NetworkSettings.cpp +++ b/src/config/NetworkSettings.cpp @@ -1,4 +1,5 @@ #include "NetworkSettings.h" +#include "ActiveSettings.h" #include "LaunchSettings.h" #include "CemuConfig.h" #include @@ -6,11 +7,10 @@ XMLNetworkConfig_t n_config(L"network_services.xml"); + void NetworkConfig::LoadOnce() { - s_full_path = boost::dll::program_location().generic_wstring(); - s_path = s_full_path.parent_path(); - n_config.SetFilename(GetPath("network_services.xml").generic_wstring()); + n_config.SetFilename(ActiveSettings::GetConfigPath("network_services.xml").generic_wstring()); if (XMLExists()) n_config.Load(); } @@ -37,7 +37,7 @@ void NetworkConfig::Load(XMLConfigParser& parser) bool NetworkConfig::XMLExists() { std::error_code ec; - if (!fs::exists(GetPath("network_services.xml"), ec)) + if (!fs::exists(ActiveSettings::GetConfigPath("network_services.xml"), ec)) { if (static_cast(GetConfig().account.active_service.GetValue()) == NetworkService::Custom) { diff --git a/src/config/NetworkSettings.h b/src/config/NetworkSettings.h index e65da8ab..f289e679 100644 --- a/src/config/NetworkSettings.h +++ b/src/config/NetworkSettings.h @@ -38,14 +38,6 @@ struct NetworkConfig { void Save(XMLConfigParser& parser); static bool XMLExists(); - private: - inline static fs::path s_path; - inline static fs::path s_full_path; - [[nodiscard]] static fs::path GetPath(std::string_view p) - { - std::basic_string_view s((const char8_t*)p.data(), p.size()); - return s_path / fs::path(s); - } }; struct NintendoURLs { diff --git a/src/gui/CemuApp.cpp b/src/gui/CemuApp.cpp index de60f8bc..50e9e04c 100644 --- a/src/gui/CemuApp.cpp +++ b/src/gui/CemuApp.cpp @@ -14,6 +14,7 @@ #include #include +#include #include "Cafe/TitleList/TitleList.h" #include "Cafe/TitleList/SaveList.h" @@ -24,6 +25,8 @@ wxIMPLEMENT_APP_NO_MAIN(CemuApp); extern WindowInfo g_window_info; extern std::shared_mutex g_mutex; +int mainEmulatorHLE(); +void HandlePostUpdate(); // Translation strings to extract for gettext: void unused_translation_dummy() { @@ -70,6 +73,42 @@ void unused_translation_dummy() bool CemuApp::OnInit() { + fs::path user_data_path, config_path, cache_path, data_path; + auto standardPaths = wxStandardPaths::Get(); +#ifdef PORTABLE + fs::path exePath(standardPaths.GetExecutablePath().ToStdString()); + user_data_path = config_path = cache_path = data_path = exePath.parent_path(); +#else + SetAppName("Cemu"); + wxString appName=GetAppName(); + #ifdef BOOST_OS_LINUX + standardPaths.SetFileLayout(wxStandardPaths::FileLayout::FileLayout_XDG); + auto getEnvDir = [&](const wxString& varName, const wxString& defaultValue) + { + wxString dir; + if (!wxGetEnv(varName, &dir) || dir.empty()) + return defaultValue; + return dir; + }; + wxString homeDir=wxFileName::GetHomeDir(); + user_data_path = (getEnvDir(wxS("XDG_DATA_HOME"), homeDir + wxS("/.local/share")) + "/" + appName).ToStdString(); + config_path = (getEnvDir(wxS("XDG_CONFIG_HOME"), homeDir + wxS("/.config")) + "/" + appName).ToStdString(); + #else + user_data_path = config_path = standardPaths.GetUserDataDir().ToStdString(); + #endif + data_path = standardPaths.GetDataDir().ToStdString(); + cache_path = standardPaths.GetUserDir(wxStandardPaths::Dir::Dir_Cache).ToStdString(); + cache_path /= appName.ToStdString(); +#endif + auto failed_write_access = ActiveSettings::LoadOnce(user_data_path, config_path, cache_path, data_path); + for (auto&& path : failed_write_access) + wxMessageBox(fmt::format("Cemu can't write to {} !", path.generic_string()), _("Warning"), wxOK | wxCENTRE | wxICON_EXCLAMATION, nullptr); + + NetworkConfig::LoadOnce(); + + HandlePostUpdate(); + mainEmulatorHLE(); + wxInitAllImageHandlers(); g_config.Load(); @@ -83,7 +122,7 @@ bool CemuApp::OnInit() { if (m_locale.Init(language)) { - m_locale.AddCatalogLookupPathPrefix("./resources"); + m_locale.AddCatalogLookupPathPrefix(ActiveSettings::GetDataPath("resources").generic_string()); m_locale.AddCatalog("cemu"); } } @@ -115,9 +154,6 @@ bool CemuApp::OnInit() Bind(wxEVT_ACTIVATE_APP, &CemuApp::ActivateApp, this); - if (!TestWriteAccess(ActiveSettings::GetPath())) - wxMessageBox(_("Cemu can't write to its directory.\nPlease move it to a different location or run Cemu as administrator!"), _("Warning"), wxOK | wxCENTRE | wxICON_EXCLAMATION, nullptr); - auto& config = GetConfig(); const bool first_start = !config.did_show_graphic_pack_download; @@ -187,7 +223,7 @@ int CemuApp::FilterEvent(wxEvent& event) std::vector CemuApp::GetAvailableLanguages() { - const auto path = ActiveSettings::GetPath("resources"); + const auto path = ActiveSettings::GetDataPath("resources"); if (!exists(path)) return {}; @@ -312,11 +348,11 @@ void CemuApp::CreateDefaultFiles(bool first_start) // cemu directories try { - const auto controllerProfileFolder = GetCemuPath(L"controllerProfiles").ToStdWstring(); + const auto controllerProfileFolder = GetConfigPath(L"controllerProfiles").ToStdWstring(); if (!fs::exists(controllerProfileFolder)) fs::create_directories(controllerProfileFolder); - const auto memorySearcherFolder = GetCemuPath(L"memorySearcher").ToStdWstring(); + const auto memorySearcherFolder = GetUserDataPath(L"memorySearcher").ToStdWstring(); if (!fs::exists(memorySearcherFolder)) fs::create_directories(memorySearcherFolder); } @@ -377,15 +413,6 @@ bool CemuApp::SelectMLCPath(wxWindow* parent) return false; } -wxString CemuApp::GetCemuPath() -{ - return ActiveSettings::GetPath().generic_wstring(); -} - -wxString CemuApp::GetCemuPath(const wxString& cat) -{ - return ActiveSettings::GetPath(cat.ToStdString()).generic_wstring(); -} wxString CemuApp::GetMLCPath() { @@ -397,6 +424,26 @@ wxString CemuApp::GetMLCPath(const wxString& cat) return ActiveSettings::GetMlcPath(cat.ToStdString()).generic_wstring(); } +wxString CemuApp::GetConfigPath() +{ + return ActiveSettings::GetConfigPath().generic_wstring(); +}; + +wxString CemuApp::GetConfigPath(const wxString& cat) +{ + return ActiveSettings::GetConfigPath(cat.ToStdString()).generic_wstring(); +}; + +wxString CemuApp::GetUserDataPath() +{ + return ActiveSettings::GetUserDataPath().generic_wstring(); +}; + +wxString CemuApp::GetUserDataPath(const wxString& cat) +{ + return ActiveSettings::GetUserDataPath(cat.ToStdString()).generic_wstring(); +}; + void CemuApp::ActivateApp(wxActivateEvent& event) { g_window_info.app_active = event.GetActive(); diff --git a/src/gui/CemuApp.h b/src/gui/CemuApp.h index 888293eb..32504883 100644 --- a/src/gui/CemuApp.h +++ b/src/gui/CemuApp.h @@ -19,8 +19,12 @@ public: static void CreateDefaultFiles(bool first_start = false); static bool SelectMLCPath(wxWindow* parent = nullptr); - static wxString GetCemuPath(); - static wxString GetCemuPath(const wxString& cat); + static wxString GetConfigPath(); + static wxString GetConfigPath(const wxString& cat); + + static wxString GetUserDataPath(); + static wxString GetUserDataPath(const wxString& cat); + static wxString GetMLCPath(); static wxString GetMLCPath(const wxString& cat); private: diff --git a/src/gui/CemuUpdateWindow.cpp b/src/gui/CemuUpdateWindow.cpp index ef537a58..2e2b40eb 100644 --- a/src/gui/CemuUpdateWindow.cpp +++ b/src/gui/CemuUpdateWindow.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -470,7 +471,8 @@ void CemuUpdateWindow::WorkerThread() break; // apply update - std::wstring target_directory = ActiveSettings::GetPath().generic_wstring(); + fs::path exePath = fs::path(wxStandardPaths::Get().GetExecutablePath().ToStdString()); + std::wstring target_directory = exePath.parent_path().generic_wstring(); if (target_directory[target_directory.size() - 1] == '/') target_directory = target_directory.substr(0, target_directory.size() - 1); // remove trailing / diff --git a/src/gui/ChecksumTool.cpp b/src/gui/ChecksumTool.cpp index b8393330..20e9f7ca 100644 --- a/src/gui/ChecksumTool.cpp +++ b/src/gui/ChecksumTool.cpp @@ -134,7 +134,7 @@ ChecksumTool::ChecksumTool(wxWindow* parent, wxTitleManagerList::TitleEntry& ent const auto title_id_str = fmt::format("{:016x}", m_json_entry.title_id); const auto default_file = fmt::format("{}_v{}.json", title_id_str, m_info.GetAppTitleVersion()); - const auto checksum_path = ActiveSettings::GetPath("resources/checksums/{}", default_file); + const auto checksum_path = ActiveSettings::GetUserDataPath("resources/checksums/{}", default_file); if (exists(checksum_path)) m_verify_online->Enable(); } @@ -186,7 +186,7 @@ void ChecksumTool::LoadOnlineData() const std::string latest_commit; - const auto checksum_path = ActiveSettings::GetPath("resources/checksums"); + const auto checksum_path = ActiveSettings::GetUserDataPath("resources/checksums"); if (exists(checksum_path)) { std::string current_commit; @@ -594,7 +594,7 @@ void ChecksumTool::OnVerifyOnline(wxCommandEvent& event) const auto title_id_str = fmt::format("{:016x}", m_json_entry.title_id); const auto default_file = fmt::format("{}_v{}.json", title_id_str, m_info.GetAppTitleVersion()); - const auto checksum_path = ActiveSettings::GetPath("resources/checksums/{}", default_file); + const auto checksum_path = ActiveSettings::GetUserDataPath("resources/checksums/{}", default_file); if(!exists(checksum_path)) return; diff --git a/src/gui/DownloadGraphicPacksWindow.cpp b/src/gui/DownloadGraphicPacksWindow.cpp index 973b7c57..4128cc04 100644 --- a/src/gui/DownloadGraphicPacksWindow.cpp +++ b/src/gui/DownloadGraphicPacksWindow.cpp @@ -65,7 +65,7 @@ bool DownloadGraphicPacksWindow::curlDownloadFile(const char *url, curlDownloadF bool checkGraphicPackDownloadedVersion(const char* nameVersion, bool& hasVersionFile) { hasVersionFile = false; - const auto path = ActiveSettings::GetPath("graphicPacks/downloadedGraphicPacks/version.txt"); + const auto path = ActiveSettings::GetUserDataPath("graphicPacks/downloadedGraphicPacks/version.txt"); std::unique_ptr file(FileStream::openFile2(path)); std::string versionInFile; @@ -78,7 +78,7 @@ bool checkGraphicPackDownloadedVersion(const char* nameVersion, bool& hasVersion void createGraphicPackDownloadedVersionFile(const char* nameVersion) { - const auto path = ActiveSettings::GetPath("graphicPacks/downloadedGraphicPacks/version.txt"); + const auto path = ActiveSettings::GetUserDataPath("graphicPacks/downloadedGraphicPacks/version.txt"); std::unique_ptr file(FileStream::createFile2(path)); if (file) file->writeString(nameVersion); @@ -90,7 +90,7 @@ void createGraphicPackDownloadedVersionFile(const char* nameVersion) void deleteDownloadedGraphicPacks() { - const auto path = ActiveSettings::GetPath("graphicPacks/downloadedGraphicPacks"); + const auto path = ActiveSettings::GetUserDataPath("graphicPacks/downloadedGraphicPacks"); std::error_code er; if (!fs::exists(path, er)) return; @@ -238,7 +238,7 @@ void DownloadGraphicPacksWindow::UpdateThread() return; } - auto path = ActiveSettings::GetPath("graphicPacks/downloadedGraphicPacks"); + auto path = ActiveSettings::GetUserDataPath("graphicPacks/downloadedGraphicPacks"); std::error_code er; //fs::remove_all(path, er); -> Don't delete the whole folder and recreate it immediately afterwards because sometimes it just fails deleteDownloadedGraphicPacks(); @@ -258,7 +258,7 @@ void DownloadGraphicPacksWindow::UpdateThread() std::strstr(sb.name, "..\\") != nullptr) continue; // bad path - path = ActiveSettings::GetPath("graphicPacks/downloadedGraphicPacks/{}", sb.name); + path = ActiveSettings::GetUserDataPath("graphicPacks/downloadedGraphicPacks/{}", sb.name); size_t sbNameLen = strlen(sb.name); if(sbNameLen == 0) diff --git a/src/gui/GraphicPacksWindow2.cpp b/src/gui/GraphicPacksWindow2.cpp index bab55156..e718605a 100644 --- a/src/gui/GraphicPacksWindow2.cpp +++ b/src/gui/GraphicPacksWindow2.cpp @@ -3,6 +3,7 @@ #include "gui/DownloadGraphicPacksWindow.h" #include "Cafe/GraphicPack/GraphicPack2.h" #include "config/CemuConfig.h" +#include "config/ActiveSettings.h" #include "Cafe/HW/Latte/Core/LatteAsyncCommands.h" @@ -326,7 +327,7 @@ void GraphicPacksWindow2::SaveStateToConfig() for (const auto& gp : GraphicPack2::GetGraphicPacks()) { - auto filename = MakeRelativePath(gp->GetFilename()).lexically_normal(); + auto filename = MakeRelativePath(ActiveSettings::GetUserDataPath(), gp->GetFilename()).lexically_normal(); if (gp->IsEnabled()) { data.graphic_pack_entries.try_emplace(filename); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index d8caabf4..5ebfe09d 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -989,8 +989,8 @@ void MainWindow::OnDebugSetting(wxCommandEvent& event) { try { - const auto path = CemuApp::GetCemuPath(L"dump\\curl").ToStdWstring(); - fs::create_directories(path); + const fs::path path(CemuApp::GetUserDataPath().ToStdString()); + fs::create_directories(path / "dump" / "curl"); } catch (const std::exception& ex) { @@ -1046,8 +1046,8 @@ void MainWindow::OnDebugDumpUsedTextures(wxCommandEvent& event) try { // create directory - const auto path = CemuApp::GetCemuPath(L"dump\\textures"); - fs::create_directories(path.ToStdWstring()); + const fs::path path(CemuApp::GetUserDataPath().ToStdString()); + fs::create_directories(path / "dump" / "textures"); } catch (const std::exception& ex) { @@ -1067,8 +1067,8 @@ void MainWindow::OnDebugDumpUsedShaders(wxCommandEvent& event) try { // create directory - const auto path = CemuApp::GetCemuPath(L"dump\\shaders"); - fs::create_directories(path.ToStdWstring()); + const fs::path path(CemuApp::GetUserDataPath().ToStdString()); + fs::create_directories(path / "dump" / "shaders"); } catch (const std::exception & ex) { diff --git a/src/gui/MemorySearcherTool.cpp b/src/gui/MemorySearcherTool.cpp index 5caa163f..093f7ffe 100644 --- a/src/gui/MemorySearcherTool.cpp +++ b/src/gui/MemorySearcherTool.cpp @@ -270,7 +270,7 @@ void MemorySearcherTool::OnFilter(wxCommandEvent& event) void MemorySearcherTool::Load() { - const auto memorySearcherPath = ActiveSettings::GetPath("memorySearcher/{:016x}.ini", CafeSystem::GetForegroundTitleId()); + const auto memorySearcherPath = ActiveSettings::GetUserDataPath("memorySearcher/{:016x}.ini", CafeSystem::GetForegroundTitleId()); auto memSearcherIniContents = FileStream::LoadIntoMemory(memorySearcherPath); if (!memSearcherIniContents) return; @@ -322,7 +322,7 @@ void MemorySearcherTool::Load() void MemorySearcherTool::Save() { - const auto memorySearcherPath = ActiveSettings::GetPath("memorySearcher/{:016x}.ini", CafeSystem::GetForegroundTitleId()); + const auto memorySearcherPath = ActiveSettings::GetUserDataPath("memorySearcher/{:016x}.ini", CafeSystem::GetForegroundTitleId()); FileStream* fs = FileStream::createFile2(memorySearcherPath); if (fs) { diff --git a/src/gui/debugger/DebuggerWindow2.cpp b/src/gui/debugger/DebuggerWindow2.cpp index 1f6c5f79..6263f0b7 100644 --- a/src/gui/debugger/DebuggerWindow2.cpp +++ b/src/gui/debugger/DebuggerWindow2.cpp @@ -271,7 +271,7 @@ DebuggerWindow2::DebuggerWindow2(wxFrame& parent, const wxRect& display_size) { this->wxWindowBase::SetBackgroundColour(*wxWHITE); - const auto file = ActiveSettings::GetPath("debugger/config.xml"); + const auto file = ActiveSettings::GetConfigPath("debugger/config.xml"); m_config.SetFilename(file.generic_wstring()); m_config.Load(); @@ -471,7 +471,7 @@ bool DebuggerWindow2::Show(bool show) std::wstring DebuggerWindow2::GetModuleStoragePath(std::string module_name, uint32_t crc_hash) const { if (module_name.empty() || crc_hash == 0) return std::wstring(); - return ActiveSettings::GetPath("debugger/{}_{:#10x}.xml", module_name, crc_hash).generic_wstring(); + return ActiveSettings::GetConfigPath("debugger/{}_{:#10x}.xml", module_name, crc_hash).generic_wstring(); } void DebuggerWindow2::OnBreakpointHit(wxCommandEvent& event) diff --git a/src/gui/input/InputSettings2.cpp b/src/gui/input/InputSettings2.cpp index 37bd604f..095aa52a 100644 --- a/src/gui/input/InputSettings2.cpp +++ b/src/gui/input/InputSettings2.cpp @@ -665,10 +665,10 @@ void InputSettings2::on_profile_delete(wxCommandEvent& event) } try { - const fs::path old_path = ActiveSettings::GetPath(fmt::format("controllerProfiles/{}.txt", selection)); + const fs::path old_path = ActiveSettings::GetConfigPath("controllerProfiles/{}.txt", selection); fs::remove(old_path); - const fs::path path = ActiveSettings::GetPath(fmt::format("controllerProfiles/{}.xml", selection)); + const fs::path path = ActiveSettings::GetConfigPath("controllerProfiles/{}.xml", selection); fs::remove(path); profile_names->ChangeValue(kDefaultProfileName); diff --git a/src/input/InputManager.cpp b/src/input/InputManager.cpp index 4ae43ce3..0861ba28 100644 --- a/src/input/InputManager.cpp +++ b/src/input/InputManager.cpp @@ -76,9 +76,9 @@ bool InputManager::load(size_t player_index, std::string_view filename) { fs::path file_path; if (filename.empty()) - file_path = ActiveSettings::GetPath(fmt::format("controllerProfiles/controller{}", player_index)); + file_path = ActiveSettings::GetConfigPath("controllerProfiles/controller{}", player_index); else - file_path = ActiveSettings::GetPath(fmt::format("controllerProfiles/{}", filename)); + file_path = ActiveSettings::GetConfigPath("controllerProfiles/{}", filename); auto old_file = file_path; old_file.replace_extension(".txt"); // test .txt extension @@ -448,7 +448,7 @@ bool InputManager::save(size_t player_index, std::string_view filename) if (!emulated_controller) return false; - fs::path file_path = ActiveSettings::GetPath("controllerProfiles"); + fs::path file_path = ActiveSettings::GetConfigPath("controllerProfiles"); fs::create_directories(file_path); const auto is_default_file = filename.empty(); @@ -664,8 +664,8 @@ EmulatedControllerPtr InputManager::delete_controller(size_t player_index, bool if(delete_profile) { std::error_code ec{}; - fs::remove(ActiveSettings::GetPath(fmt::format("controllerProfiles/controller{}.xml", player_index)), ec); - fs::remove(ActiveSettings::GetPath(fmt::format("controllerProfiles/controller{}.txt", player_index)), ec); + fs::remove(ActiveSettings::GetConfigPath("controllerProfiles/controller{}.xml", player_index), ec); + fs::remove(ActiveSettings::GetConfigPath("controllerProfiles/controller{}.txt", player_index), ec); } return result; @@ -680,8 +680,8 @@ EmulatedControllerPtr InputManager::delete_controller(size_t player_index, bool controller = {}; std::error_code ec{}; - fs::remove(ActiveSettings::GetPath(fmt::format("controllerProfiles/controller{}.xml", player_index)), ec); - fs::remove(ActiveSettings::GetPath(fmt::format("controllerProfiles/controller{}.txt", player_index)), ec); + fs::remove(ActiveSettings::GetConfigPath("controllerProfiles/controller{}.xml", player_index), ec); + fs::remove(ActiveSettings::GetConfigPath("controllerProfiles/controller{}.txt", player_index), ec); return result; } @@ -782,7 +782,7 @@ void InputManager::apply_game_profile() std::vector InputManager::get_profiles() { - const auto path = ActiveSettings::GetPath("controllerProfiles"); + const auto path = ActiveSettings::GetConfigPath("controllerProfiles"); if (!exists(path)) return {}; diff --git a/src/main.cpp b/src/main.cpp index 87243bb8..e1c8ca49 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -161,7 +161,7 @@ void _putenvSafe(const char* c) void reconfigureGLDrivers() { // reconfigure GL drivers to store - const fs::path nvCacheDir = ActiveSettings::GetPath("shaderCache/driver/nvidia/"); + const fs::path nvCacheDir = ActiveSettings::GetCachePath("shaderCache/driver/nvidia/"); std::error_code err; fs::create_directories(nvCacheDir, err); @@ -245,8 +245,6 @@ void unitTests() int mainEmulatorHLE() { - if (!TestWriteAccess(ActiveSettings::GetPath())) - wxMessageBox("Cemu doesn't have write access to it's own directory.\nPlease move it to a different location or run Cemu as administrator!", "Warning", wxOK|wxICON_ERROR); // todo - different error messages per OS LatteOverlay_init(); // run a couple of tests if in non-release mode #ifdef CEMU_DEBUG_ASSERT @@ -267,7 +265,7 @@ int mainEmulatorHLE() // init Cafe system (todo - the stuff above should be part of this too) CafeSystem::Initialize(); // init title list - CafeTitleList::Initialize(ActiveSettings::GetPath("title_list_cache.xml")); + CafeTitleList::Initialize(ActiveSettings::GetUserDataPath("title_list_cache.xml")); for (auto& it : GetConfig().game_paths) CafeTitleList::AddScanPath(it); fs::path mlcPath = ActiveSettings::GetMlcPath(); @@ -281,8 +279,6 @@ int mainEmulatorHLE() CafeSaveList::SetMLCPath(mlcPath); CafeSaveList::Refresh(); } - // Create UI - gui_create(); return 0; } @@ -347,10 +343,8 @@ int wWinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ L SDL_SetMainReady(); if (!LaunchSettings::HandleCommandline(lpCmdLine)) return 0; - ActiveSettings::LoadOnce(); - NetworkConfig::LoadOnce(); - HandlePostUpdate(); - return mainEmulatorHLE(); + gui_create(); + return 0; } // entrypoint for debug builds with console @@ -359,10 +353,8 @@ int main(int argc, char* argv[]) SDL_SetMainReady(); if (!LaunchSettings::HandleCommandline(argc, argv)) return 0; - ActiveSettings::LoadOnce(); - NetworkConfig::LoadOnce(); - HandlePostUpdate(); - return mainEmulatorHLE(); + gui_create(); + return 0; } #else @@ -374,11 +366,8 @@ int main(int argc, char *argv[]) #endif if (!LaunchSettings::HandleCommandline(argc, argv)) return 0; - - ActiveSettings::LoadOnce(); - NetworkConfig::LoadOnce(); - HandlePostUpdate(); - return mainEmulatorHLE(); + gui_create(); + return 0; } #endif diff --git a/src/util/helpers/helpers.cpp b/src/util/helpers/helpers.cpp index 99712296..fefe7985 100644 --- a/src/util/helpers/helpers.cpp +++ b/src/util/helpers/helpers.cpp @@ -306,11 +306,10 @@ bool TestWriteAccess(const fs::path& p) } // make path relative to Cemu directory -fs::path MakeRelativePath(const fs::path& path) +fs::path MakeRelativePath(const fs::path& base, const fs::path& path) { try { - const fs::path base = ActiveSettings::GetPath(); return fs::relative(path, base); } catch (const std::exception&) diff --git a/src/util/helpers/helpers.h b/src/util/helpers/helpers.h index 6240d56a..f81e5964 100644 --- a/src/util/helpers/helpers.h +++ b/src/util/helpers/helpers.h @@ -52,7 +52,7 @@ uint32_t GetPhysicalCoreCount(); // Creates a temporary file to test for write access bool TestWriteAccess(const fs::path& p); -fs::path MakeRelativePath(const fs::path& path); +fs::path MakeRelativePath(const fs::path& base, const fs::path& path); #ifdef HAS_DIRECTINPUT bool GUIDFromString(const char* string, GUID& guid); diff --git a/src/util/libusbWrapper/libusbWrapper.cpp b/src/util/libusbWrapper/libusbWrapper.cpp index e1d72985..8420216d 100644 --- a/src/util/libusbWrapper/libusbWrapper.cpp +++ b/src/util/libusbWrapper/libusbWrapper.cpp @@ -18,7 +18,7 @@ void libusbWrapper::init() m_module = LoadLibraryW(L"libusb-1.0.dll"); if (!m_module) { - const auto path = ActiveSettings::GetPath("resources/libusb-1.0.dll"); + const auto path = ActiveSettings::GetDataPath("resources/libusb-1.0.dll"); m_module = LoadLibraryW(path.generic_wstring().c_str()); if (!m_module) { From 8b3f36ad50372feb7b62e5efd70a697beed9c6eb Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Wed, 12 Oct 2022 04:10:57 -0700 Subject: [PATCH 025/616] Use correct preprocessor check for Linux (#360) --- src/gui/CemuApp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/CemuApp.cpp b/src/gui/CemuApp.cpp index 50e9e04c..2c766f5b 100644 --- a/src/gui/CemuApp.cpp +++ b/src/gui/CemuApp.cpp @@ -81,7 +81,7 @@ bool CemuApp::OnInit() #else SetAppName("Cemu"); wxString appName=GetAppName(); - #ifdef BOOST_OS_LINUX + #if BOOST_OS_LINUX standardPaths.SetFileLayout(wxStandardPaths::FileLayout::FileLayout_XDG); auto getEnvDir = [&](const wxString& varName, const wxString& defaultValue) { From f65dbe84372c2f50eb236a366d2cfe13409b1857 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 12 Oct 2022 14:18:24 +0200 Subject: [PATCH 026/616] Fix encoding error in input profile filenames - Controller profile filenames now encode unicode characters correctly - Removed dependency on boost::filesystem. There is still an indirect dependency on it from another boost module it seems - Refactored some code to use FileStream instead of ifstream/ofstream --- src/Cafe/GameProfile/GameProfile.cpp | 11 ++------ src/Common/precompiled.h | 1 - src/input/InputManager.cpp | 41 +++++++++++++++------------- src/util/helpers/helpers.h | 20 ++++++++++++++ 4 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/Cafe/GameProfile/GameProfile.cpp b/src/Cafe/GameProfile/GameProfile.cpp index d8c735ce..a1184362 100644 --- a/src/Cafe/GameProfile/GameProfile.cpp +++ b/src/Cafe/GameProfile/GameProfile.cpp @@ -277,10 +277,8 @@ bool GameProfile::Load(uint64_t title_id) void GameProfile::Save(uint64_t title_id) { auto gameProfileDir = ActiveSettings::GetConfigPath("gameProfiles"); - if (std::error_code ex_ec; !fs::exists(gameProfileDir, ex_ec) && !ex_ec) { - std::error_code cr_ec; - fs::create_directories(gameProfileDir, cr_ec); - } + if (std::error_code ex_ec; !fs::exists(gameProfileDir, ex_ec)) + fs::create_directories(gameProfileDir, ex_ec); auto gameProfilePath = gameProfileDir / fmt::format("{:016x}.ini", title_id); FileStream* fs = FileStream::createFile2(gameProfilePath); if (!fs) @@ -309,16 +307,11 @@ void GameProfile::Save(uint64_t title_id) fs->writeLine(""); fs->writeLine("[Graphics]"); - //WRITE_OPTIONAL_ENTRY(gpuBufferCacheAccuracy); WRITE_ENTRY(accurateShaderMul); WRITE_OPTIONAL_ENTRY(precompiledShaders); WRITE_OPTIONAL_ENTRY(graphics_api); fs->writeLine(""); - /*stream_writeLine(stream_gameProfile, "[Audio]"); - WRITE_ENTRY(disableAudio); - stream_writeLine(stream_gameProfile, "");*/ - fs->writeLine("[Controller]"); for (int i = 0; i < 8; ++i) { diff --git a/src/Common/precompiled.h b/src/Common/precompiled.h index bd956657..898a6883 100644 --- a/src/Common/precompiled.h +++ b/src/Common/precompiled.h @@ -69,7 +69,6 @@ #include #include #include -#include #include #include diff --git a/src/input/InputManager.cpp b/src/input/InputManager.cpp index 0861ba28..5d775c74 100644 --- a/src/input/InputManager.cpp +++ b/src/input/InputManager.cpp @@ -92,12 +92,12 @@ bool InputManager::load(size_t player_index, std::string_view filename) try { - std::ifstream file(file_path); - if (!file.is_open()) + auto xmlData = FileStream::LoadIntoMemory(file_path); + if (!xmlData || xmlData->empty()) return false; - + pugi::xml_document doc; - if (!doc.load(file)) + if (!doc.load_buffer(xmlData->data(), xmlData->size())) return false; const pugi::xml_node root = doc.document_element(); @@ -216,12 +216,15 @@ bool InputManager::migrate_config(const fs::path& file_path) { try { - std::ifstream file(file_path); - if (!file.is_open()) + auto xmlData = FileStream::LoadIntoMemory(file_path); + if (!xmlData || xmlData->empty()) return false; + std::string iniDataStr((const char*)xmlData->data(), xmlData->size()); + + std::stringstream iniData(iniDataStr); boost::property_tree::ptree m_data; - read_ini(file, m_data); + read_ini(iniData, m_data); const auto emulate_string = m_data.get("General.emulate"); const auto api_string = m_data.get("General.api"); @@ -455,7 +458,7 @@ bool InputManager::save(size_t player_index, std::string_view filename) if (is_default_file) file_path /= fmt::format("controller{}", player_index); else - file_path /= filename; + file_path /= _utf8ToPath(filename); file_path.replace_extension(".xml"); // force .xml extension @@ -540,15 +543,15 @@ bool InputManager::save(size_t player_index, std::string_view filename) } } } - - - std::ofstream file(file_path, std::ios::out | std::ios::trunc); - if (file.is_open()) - { - doc.save(file); - return true; - } - return false; + FileStream* fs = FileStream::createFile2(file_path); + if (!fs) + return false; + std::stringstream xmlData; + doc.save(xmlData); + std::string xmlStr = xmlData.str(); + fs->writeData(xmlStr.data(), xmlStr.size()); + delete fs; + return true; } bool InputManager::is_gameprofile_set(size_t player_index) const @@ -792,7 +795,7 @@ std::vector InputManager::get_profiles() const auto& p = entry.path(); if (p.has_extension() && (p.extension() == ".xml" || p.extension() == ".txt")) { - auto stem = p.filename().stem().string(); + auto stem = _pathToUtf8(p.filename().stem()); if (is_valid_profilename(stem)) { tmp.emplace(stem); @@ -808,7 +811,7 @@ std::vector InputManager::get_profiles() bool InputManager::is_valid_profilename(const std::string& name) { - if (!boost::filesystem::windows_name(name)) + if (!IsValidFilename(name)) return false; // dont allow default profile names diff --git a/src/util/helpers/helpers.h b/src/util/helpers/helpers.h index f81e5964..09b80fed 100644 --- a/src/util/helpers/helpers.h +++ b/src/util/helpers/helpers.h @@ -231,6 +231,26 @@ inline uint64 MakeU64(uint32 high, uint32 low) return ((uint64)high << 32) | ((uint64)low); } +static bool IsValidFilename(std::string_view sv) +{ + for (auto& it : sv) + { + uint8 c = (uint8)it; + if (c < 0x20) + return false; + if (c == '.' || c == '#' || c == '/' || c == '\\' || + c == '<' || c == '>' || c == '|' || c == ':' || + c == '\"') + return false; + } + if (!sv.empty()) + { + if (sv.back() == ' ' || sv.back() == '.') + return false; + } + return true; +} + // MAJOR; MINOR std::pair GetWindowsVersion(); bool IsWindows81OrGreater(); From 0412dec07852837b37c658e6b8240be1415a84fd Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 12 Oct 2022 15:23:04 +0200 Subject: [PATCH 027/616] Fix metainfo Comment out vcs-browser url type for now. It's a pretty recent addition to the standard and is considered an error by some older utilities --- dist/linux/info.cemu.Cemu.metainfo.xml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dist/linux/info.cemu.Cemu.metainfo.xml b/dist/linux/info.cemu.Cemu.metainfo.xml index d270a8ab..b93e572f 100644 --- a/dist/linux/info.cemu.Cemu.metainfo.xml +++ b/dist/linux/info.cemu.Cemu.metainfo.xml @@ -60,14 +60,11 @@ https://github.com/cemu-project/Cemu/issues https://cemu.info/faq.html https://wiki.cemu.info - https://github.com/cemu-project/Cemu + Game Emulator - - 4096 - 8192 From d251ce07e0cbbe8a85a4013fbfb15efa0f422b76 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 13 Oct 2022 12:18:34 +0200 Subject: [PATCH 028/616] XAudio2: Don't quit on failed CoInitializeEx() It returns an error code when already initialized --- src/audio/XAudio2API.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/audio/XAudio2API.cpp b/src/audio/XAudio2API.cpp index 795a0ebe..fd0a305b 100644 --- a/src/audio/XAudio2API.cpp +++ b/src/audio/XAudio2API.cpp @@ -187,7 +187,8 @@ const std::vector& XAudio2API::RefreshDevices( // this function must be called from the same thread as we called CoInitializeEx s_devices.clear(); - if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE))) + HRESULT r = CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); + if (r != RPC_E_CHANGED_MODE && FAILED(r)) return s_devices; try From a19ed46b2a55dc93e5792c84e57a7c45b89f97a0 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Fri, 14 Oct 2022 12:49:41 +0200 Subject: [PATCH 029/616] Windows: Fix file and folder dialog freeze (#369) Initializing the COM library immediately seems to be more robust than doing it on demand --- src/audio/CubebAPI.cpp | 18 ------------------ src/audio/CubebAPI.h | 1 - src/audio/XAudio27API.cpp | 9 --------- src/audio/XAudio27API.h | 1 - src/audio/XAudio2API.cpp | 14 -------------- src/audio/XAudio2API.h | 1 - src/main.cpp | 10 +++++++--- 7 files changed, 7 insertions(+), 47 deletions(-) diff --git a/src/audio/CubebAPI.cpp b/src/audio/CubebAPI.cpp index c40e0f55..8b2a235f 100644 --- a/src/audio/CubebAPI.cpp +++ b/src/audio/CubebAPI.cpp @@ -167,25 +167,11 @@ void CubebAPI::SetVolume(sint32 volume) bool CubebAPI::InitializeStatic() { -#if BOOST_OS_WINDOWS - s_com_initialized = (SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED))); -#endif - if (cubeb_init(&s_context, "Cemu Cubeb", nullptr)) { cemuLog_force("can't create cubeb audio api"); - -#if BOOST_OS_WINDOWS - if (s_com_initialized) - { - CoUninitialize(); - s_com_initialized = false; - } -#endif - return false; } - return true; } @@ -193,10 +179,6 @@ void CubebAPI::Destroy() { if (s_context) cubeb_destroy(s_context); -#if BOOST_OS_WINDOWS - if (s_com_initialized) - CoUninitialize(); -#endif } std::vector CubebAPI::GetDevices() diff --git a/src/audio/CubebAPI.h b/src/audio/CubebAPI.h index a828ce0d..2dce9374 100644 --- a/src/audio/CubebAPI.h +++ b/src/audio/CubebAPI.h @@ -41,7 +41,6 @@ public: static void Destroy(); private: - inline static bool s_com_initialized = false; inline static cubeb* s_context = nullptr; cubeb_stream* m_stream = nullptr; diff --git a/src/audio/XAudio27API.cpp b/src/audio/XAudio27API.cpp index 4a99bfa2..fadd02f8 100644 --- a/src/audio/XAudio27API.cpp +++ b/src/audio/XAudio27API.cpp @@ -5,7 +5,6 @@ static_assert(IAudioAPI::kBlockCount < XAUDIO2_MAX_QUEUED_BUFFERS, "too many xaudio2 buffers"); HMODULE XAudio27API::s_xaudio_dll = nullptr; -bool XAudio27API::s_com_initialized = false; std::unique_ptr XAudio27API::s_xaudio; XAudio27API::XAudio27API(uint32 device_id, uint32 samplerate, uint32 channels, uint32 samples_per_block, uint32 bits_per_sample) @@ -115,8 +114,6 @@ bool XAudio27API::InitializeStatic() if (s_xaudio) return true; - s_com_initialized = (SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE))); - #ifdef _DEBUG s_xaudio_dll = LoadLibraryExW(L"XAudioD2_7.DLL", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); if(!s_xaudio_dll) @@ -142,9 +139,6 @@ bool XAudio27API::InitializeStatic() if (s_xaudio_dll) FreeLibrary(s_xaudio_dll); - if (s_com_initialized) - CoUninitialize(); - return false; } } @@ -155,9 +149,6 @@ void XAudio27API::Destroy() if (s_xaudio_dll) FreeLibrary(s_xaudio_dll); - - if (s_com_initialized) - CoUninitialize(); } std::vector XAudio27API::GetDevices() diff --git a/src/audio/XAudio27API.h b/src/audio/XAudio27API.h index 80e423a3..badab8f6 100644 --- a/src/audio/XAudio27API.h +++ b/src/audio/XAudio27API.h @@ -58,7 +58,6 @@ private: }; static HMODULE s_xaudio_dll; - static bool s_com_initialized; static std::unique_ptr s_xaudio; std::unique_ptr m_xaudio; diff --git a/src/audio/XAudio2API.cpp b/src/audio/XAudio2API.cpp index fd0a305b..9759210d 100644 --- a/src/audio/XAudio2API.cpp +++ b/src/audio/XAudio2API.cpp @@ -23,7 +23,6 @@ static const GUID DEVINTERFACE_AUDIO_RENDER_GUID = { 0xe6327cad, 0xdcec, 0x4949, static_assert(IAudioAPI::kBlockCount < XAUDIO2_MAX_QUEUED_BUFFERS, "too many xaudio2 buffers"); HMODULE XAudio2API::s_xaudio_dll = nullptr; -bool XAudio2API::s_com_initialized = false; std::vector XAudio2API::s_devices; XAudio2API::XAudio2API(std::wstring device_id, uint32 samplerate, uint32 channels, uint32 samples_per_block, uint32 bits_per_sample) @@ -143,8 +142,6 @@ bool XAudio2API::InitializeStatic() { if (s_xaudio_dll) return true; - - s_com_initialized = (SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED))); // win 10 s_xaudio_dll = LoadLibraryEx(XAUDIO2_DLL, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); @@ -166,9 +163,6 @@ bool XAudio2API::InitializeStatic() if (s_xaudio_dll) FreeLibrary(s_xaudio_dll); - if (s_com_initialized) - CoUninitialize(); - return false; } } @@ -177,20 +171,12 @@ void XAudio2API::Destroy() { if (s_xaudio_dll) FreeLibrary(s_xaudio_dll); - - if (s_com_initialized) - CoUninitialize(); } const std::vector& XAudio2API::RefreshDevices() { - // this function must be called from the same thread as we called CoInitializeEx s_devices.clear(); - HRESULT r = CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); - if (r != RPC_E_CHANGED_MODE && FAILED(r)) - return s_devices; - try { struct IWbemLocator *wbem_locator = nullptr; diff --git a/src/audio/XAudio2API.h b/src/audio/XAudio2API.h index 2b23e1cc..1f7057f0 100644 --- a/src/audio/XAudio2API.h +++ b/src/audio/XAudio2API.h @@ -59,7 +59,6 @@ private: }; static HMODULE s_xaudio_dll; - static bool s_com_initialized; static std::vector s_devices; std::unique_ptr m_xaudio; diff --git a/src/main.cpp b/src/main.cpp index e1c8ca49..323494c1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -323,10 +323,10 @@ void HandlePostUpdate() fs::remove(filename, ec); } #else - while( fs::exists(filename) ) + while (fs::exists(filename)) { std::error_code ec; - fs::remove(filename, ec); + fs::remove(filename, ec); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } #endif @@ -338,8 +338,10 @@ void ToolShaderCacheMerger(); #if BOOST_OS_WINDOWS // entrypoint for release builds -int wWinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nShowCmd) +int wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nShowCmd) { + if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE))) + cemuLog_log(LogType::Force, "CoInitializeEx() failed"); SDL_SetMainReady(); if (!LaunchSettings::HandleCommandline(lpCmdLine)) return 0; @@ -350,6 +352,8 @@ int wWinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ L // entrypoint for debug builds with console int main(int argc, char* argv[]) { + if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE))) + cemuLog_log(LogType::Force, "CoInitializeEx() failed"); SDL_SetMainReady(); if (!LaunchSettings::HandleCommandline(argc, argv)) return 0; From ada8bbb3b49622e19deccb7358b1c804a766baab Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Fri, 14 Oct 2022 13:45:40 +0200 Subject: [PATCH 030/616] Linux/MacOS: Greatly improve performance (#370) std::unordered_set is implemented as a flat hashtable on libstdc++ which makes clearing expensive due to invoking memset on the entire table. To get the best performance across all platforms this replaces the unordered_set with a custom high-performance sparse bitset --- src/Cafe/HW/Latte/Core/LatteBufferCache.cpp | 73 ++++++++++++++++++--- 1 file changed, 65 insertions(+), 8 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/LatteBufferCache.cpp b/src/Cafe/HW/Latte/Core/LatteBufferCache.cpp index a70ff888..1e2c43b1 100644 --- a/src/Cafe/HW/Latte/Core/LatteBufferCache.cpp +++ b/src/Cafe/HW/Latte/Core/LatteBufferCache.cpp @@ -1005,8 +1005,67 @@ void LatteBufferCache_getStats(uint32& heapSize, uint32& allocationSize, uint32& } FSpinlock g_spinlockDCFlushQueue; -std::unordered_set* g_DCFlushQueue = new std::unordered_set(); // queued pages -std::unordered_set* g_DCFlushQueueAlternate = new std::unordered_set(); + +class SparseBitset +{ + static inline constexpr size_t TABLE_MASK = 0xFF; + +public: + bool Empty() const + { + return m_numNonEmptyVectors == 0; + } + + void Set(uint32 index) + { + auto& v = m_bits[index & TABLE_MASK]; + if (std::find(v.cbegin(), v.cend(), index) != v.end()) + return; + if (v.empty()) + { + m_nonEmptyVectors[m_numNonEmptyVectors] = &v; + m_numNonEmptyVectors++; + } + v.emplace_back(index); + } + + template + void ForAllAndClear(TFunc callbackFunc) + { + auto vCurrent = m_nonEmptyVectors + 0; + auto vEnd = m_nonEmptyVectors + m_numNonEmptyVectors; + while (vCurrent < vEnd) + { + std::vector* vec = *vCurrent; + vCurrent++; + for (const auto& it : *vec) + callbackFunc(it); + vec->clear(); + } + m_numNonEmptyVectors = 0; + } + + void Clear() + { + auto vCurrent = m_nonEmptyVectors + 0; + auto vEnd = m_nonEmptyVectors + m_numNonEmptyVectors; + while (vCurrent < vEnd) + { + std::vector* vec = *vCurrent; + vCurrent++; + vec->clear(); + } + m_numNonEmptyVectors = 0; + } + +private: + std::vector m_bits[TABLE_MASK + 1]; + std::vector* m_nonEmptyVectors[TABLE_MASK + 1]; + size_t m_numNonEmptyVectors{ 0 }; +}; + +SparseBitset* s_DCFlushQueue = new SparseBitset(); +SparseBitset* s_DCFlushQueueAlternate = new SparseBitset(); void LatteBufferCache_notifyDCFlush(MPTR address, uint32 size) { @@ -1017,20 +1076,18 @@ void LatteBufferCache_notifyDCFlush(MPTR address, uint32 size) uint32 lastPage = (address + size - 1) / CACHE_PAGE_SIZE; g_spinlockDCFlushQueue.acquire(); for (uint32 i = firstPage; i <= lastPage; i++) - g_DCFlushQueue->emplace(i); + s_DCFlushQueue->Set(i); g_spinlockDCFlushQueue.release(); } void LatteBufferCache_processDCFlushQueue() { - if (g_DCFlushQueue->empty()) // accessing this outside of the lock is technically undefined/unsafe behavior but on all known implementations this is fine and we can avoid the spinlock + if (s_DCFlushQueue->Empty()) // quick check to avoid locking if there is no work to do return; g_spinlockDCFlushQueue.acquire(); - std::swap(g_DCFlushQueue, g_DCFlushQueueAlternate); + std::swap(s_DCFlushQueue, s_DCFlushQueueAlternate); g_spinlockDCFlushQueue.release(); - for (auto& itr : *g_DCFlushQueueAlternate) - LatteBufferCache_invalidatePage(itr * CACHE_PAGE_SIZE); - g_DCFlushQueueAlternate->clear(); + s_DCFlushQueueAlternate->ForAllAndClear([](uint32 index) {LatteBufferCache_invalidatePage(index * CACHE_PAGE_SIZE); }); } void LatteBufferCache_notifyDrawDone() From df0e2f78818d662b90839fa46c7441777f07003c Mon Sep 17 00:00:00 2001 From: Tillsunset <35825944+Tillsunset@users.noreply.github.com> Date: Sat, 15 Oct 2022 00:20:20 -0500 Subject: [PATCH 031/616] Fix Cannot set locale to "" (#366) * Add en resource and change language selection for macos user that don't have US as their system region * default to English if the system language is unavailable --- bin/resources/en/cemu.mo | Bin 0 -> 56624 bytes src/gui/CemuApp.cpp | 20 +++++++++++--------- 2 files changed, 11 insertions(+), 9 deletions(-) create mode 100644 bin/resources/en/cemu.mo diff --git a/bin/resources/en/cemu.mo b/bin/resources/en/cemu.mo new file mode 100644 index 0000000000000000000000000000000000000000..93d8f7e59bd0864234171af1e3ba46ba15c562c8 GIT binary patch literal 56624 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 literal 0 HcmV?d00001 diff --git a/src/gui/CemuApp.cpp b/src/gui/CemuApp.cpp index 2c766f5b..c7a5adaa 100644 --- a/src/gui/CemuApp.cpp +++ b/src/gui/CemuApp.cpp @@ -115,22 +115,24 @@ bool CemuApp::OnInit() m_languages = GetAvailableLanguages(); const sint32 language = GetConfig().language; - if (language != wxLANGUAGE_ENGLISH) + const auto it = std::find_if(m_languages.begin(), m_languages.end(), [language](const wxLanguageInfo* info) { return info->Language == language; }); + if (it != m_languages.end() && wxLocale::IsAvailable(language)) { - const auto it = std::find_if(m_languages.begin(), m_languages.end(), [language](const wxLanguageInfo* info) { return info->Language == language; }); - if (it != m_languages.end() && wxLocale::IsAvailable(language)) + if (m_locale.Init(language)) { - if (m_locale.Init(language)) - { - m_locale.AddCatalogLookupPathPrefix(ActiveSettings::GetDataPath("resources").generic_string()); - m_locale.AddCatalog("cemu"); - } + m_locale.AddCatalogLookupPathPrefix(ActiveSettings::GetDataPath("resources").generic_string()); + m_locale.AddCatalog("cemu"); } } if (!m_locale.IsOk()) { - m_locale.Init(wxLANGUAGE_DEFAULT); + if (!wxLocale::IsAvailable(wxLANGUAGE_DEFAULT) || !m_locale.Init(wxLANGUAGE_DEFAULT)) + { + m_locale.Init(wxLANGUAGE_ENGLISH); + m_locale.AddCatalogLookupPathPrefix(ActiveSettings::GetDataPath("resources").generic_string()); + m_locale.AddCatalog("cemu"); + } } // fill colour db From f0938e1a23f6cdd03bfd1e21d84f2c0f65aeb45f Mon Sep 17 00:00:00 2001 From: Tillsunset <35825944+Tillsunset@users.noreply.github.com> Date: Sat, 15 Oct 2022 06:38:06 -0500 Subject: [PATCH 032/616] Fix CRC errors on MacOS(/Linux?) (#375) Fixes graphic packs (like FPS++) not working even when enabled. --- src/util/crypto/crc32.cpp | 51 +++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/src/util/crypto/crc32.cpp b/src/util/crypto/crc32.cpp index f6e88165..65c6a8f8 100644 --- a/src/util/crypto/crc32.cpp +++ b/src/util/crypto/crc32.cpp @@ -322,29 +322,34 @@ unsigned int crc32_calc_slice_by_8(unsigned int previousCrc32, const void* data, // process eight bytes at once (Slicing-by-8) while (length >= 8) { -#if __BYTE_ORDER == __BIG_ENDIAN - uint32_t one = *current++ ^ swap(crc); - uint32_t two = *current++; - crc = Crc32Lookup[0][two & 0xFF] ^ - Crc32Lookup[1][(two >> 8) & 0xFF] ^ - Crc32Lookup[2][(two >> 16) & 0xFF] ^ - Crc32Lookup[3][(two >> 24) & 0xFF] ^ - Crc32Lookup[4][one & 0xFF] ^ - Crc32Lookup[5][(one >> 8) & 0xFF] ^ - Crc32Lookup[6][(one >> 16) & 0xFF] ^ - Crc32Lookup[7][(one >> 24) & 0xFF]; -#else - uint32_t one = *current++ ^ crc; - uint32_t two = *current++; - crc = Crc32Lookup[0][(two >> 24) & 0xFF] ^ - Crc32Lookup[1][(two >> 16) & 0xFF] ^ - Crc32Lookup[2][(two >> 8) & 0xFF] ^ - Crc32Lookup[3][two & 0xFF] ^ - Crc32Lookup[4][(one >> 24) & 0xFF] ^ - Crc32Lookup[5][(one >> 16) & 0xFF] ^ - Crc32Lookup[6][(one >> 8) & 0xFF] ^ - Crc32Lookup[7][one & 0xFF]; -#endif + if constexpr (std::endian::native == std::endian::big){ + uint32_t one = *current++ ^ swap(crc); + uint32_t two = *current++; + crc = Crc32Lookup[0][two & 0xFF] ^ + Crc32Lookup[1][(two >> 8) & 0xFF] ^ + Crc32Lookup[2][(two >> 16) & 0xFF] ^ + Crc32Lookup[3][(two >> 24) & 0xFF] ^ + Crc32Lookup[4][one & 0xFF] ^ + Crc32Lookup[5][(one >> 8) & 0xFF] ^ + Crc32Lookup[6][(one >> 16) & 0xFF] ^ + Crc32Lookup[7][(one >> 24) & 0xFF]; + } + else if constexpr (std::endian::native == std::endian::little) + { + uint32_t one = *current++ ^ crc; + uint32_t two = *current++; + crc = Crc32Lookup[0][(two >> 24) & 0xFF] ^ + Crc32Lookup[1][(two >> 16) & 0xFF] ^ + Crc32Lookup[2][(two >> 8) & 0xFF] ^ + Crc32Lookup[3][two & 0xFF] ^ + Crc32Lookup[4][(one >> 24) & 0xFF] ^ + Crc32Lookup[5][(one >> 16) & 0xFF] ^ + Crc32Lookup[6][(one >> 8) & 0xFF] ^ + Crc32Lookup[7][one & 0xFF]; + } + else { + cemu_assert(false); + } length -= 8; } From e88d20cbfbada6b1d144c4944f080f70299c1506 Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Sun, 16 Oct 2022 09:02:33 +0200 Subject: [PATCH 033/616] Fix crashes when wxWidgets tries to free stack allocated dialogs (#377) --- src/gui/MainWindow.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 5ebfe09d..3291f3bb 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -763,7 +763,6 @@ void MainWindow::OpenSettings() frame.ShowModal(); const bool paths_modified = frame.ShouldReloadGamelist(); const bool mlc_modified = frame.MLCModified(); - frame.Destroy(); if (paths_modified) m_game_list->ReloadGameEntries(false); @@ -1722,7 +1721,6 @@ void MainWindow::OnTimer(wxTimerEvent& event) { CemuUpdateWindow update_window(this); update_window.ShowModal(); - update_window.Destroy(); } } @@ -1999,7 +1997,6 @@ void MainWindow::OnHelpUpdate(wxCommandEvent& event) { CemuUpdateWindow test(this); test.ShowModal(); - test.Destroy(); } void MainWindow::OnHelpGettingStarted(wxCommandEvent& event) From 753040f73a8700d53bb742d33e428e060308bb20 Mon Sep 17 00:00:00 2001 From: MythicalPlayz <57963367+MythicalPlayz@users.noreply.github.com> Date: Mon, 17 Oct 2022 11:26:32 +0200 Subject: [PATCH 034/616] Added Boot Image for Gamepad (#372) --- src/Cafe/HW/Latte/Core/LatteShaderCache.cpp | 119 +++++++++++++++++--- 1 file changed, 103 insertions(+), 16 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp index ba85898c..c8299032 100644 --- a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp +++ b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp @@ -43,7 +43,8 @@ struct struct { - ImTextureID textureId; + ImTextureID textureTVId; + ImTextureID textureDRCId; // shader loading sint32 loadedShaderFiles; sint32 shaderFileCount; @@ -230,12 +231,12 @@ void LatteShaderCache_load() g_shaderCacheLoaderState.loadedShaderFiles = 0; // get game background loading image - TGAFILE file{}; - g_shaderCacheLoaderState.textureId = nullptr; + TGAFILE TVfile{}; + g_shaderCacheLoaderState.textureTVId = nullptr; std::string tvTexPath = fmt::format("{}/meta/bootTvTex.tga", CafeSystem::GetMlcStoragePath(CafeSystem::GetForegroundTitleId())); - sint32 status; - auto fscfile = fsc_open(tvTexPath.c_str(), FSC_ACCESS_FLAG::OPEN_FILE | FSC_ACCESS_FLAG::READ_PERMISSION, &status); + sint32 statusTV; + auto fscfile = fsc_open(tvTexPath.c_str(), FSC_ACCESS_FLAG::OPEN_FILE | FSC_ACCESS_FLAG::READ_PERMISSION, &statusTV); if (fscfile) { uint32 size = fsc_getFileSize(fscfile); @@ -243,15 +244,35 @@ void LatteShaderCache_load() { std::vector tmpData(size); fsc_readFile(fscfile, tmpData.data(), size); - const bool backgroundLoaded = LoadTGAFile(tmpData, &file); + const bool backgroundLoaded = LoadTGAFile(tmpData, &TVfile); if (backgroundLoaded) - g_shaderCacheLoaderState.textureId = g_renderer->GenerateTexture(file.imageData, { file.imageWidth, file.imageHeight }); + g_shaderCacheLoaderState.textureTVId = g_renderer->GenerateTexture(TVfile.imageData, { TVfile.imageWidth, TVfile.imageHeight }); } fsc_close(fscfile); } + //get game background loading image for DRC + TGAFILE DRCfile{}; + g_shaderCacheLoaderState.textureDRCId = nullptr; + std::string drcTexPath = fmt::format("{}/meta/bootDRCTex.tga", CafeSystem::GetMlcStoragePath(CafeSystem::GetForegroundTitleId())); + sint32 statusDRC; + auto fscfile2 = fsc_open(drcTexPath.c_str(), FSC_ACCESS_FLAG::OPEN_FILE | FSC_ACCESS_FLAG::READ_PERMISSION, &statusDRC); + if (fscfile2) + { + uint32 size = fsc_getFileSize(fscfile2); + if (size > 0) + { + std::vector tmpData(size); + fsc_readFile(fscfile2, tmpData.data(), size); + const bool backgroundLoaded = LoadTGAFile(tmpData, &DRCfile); + + if (backgroundLoaded) + g_shaderCacheLoaderState.textureDRCId = g_renderer->GenerateTexture(DRCfile.imageData, { DRCfile.imageWidth, DRCfile.imageHeight }); + } + fsc_close(fscfile2); + } sint32 numLoadedShaders = 0; uint32 loadIndex = 0; @@ -312,7 +333,7 @@ void LatteShaderCache_load() ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 0,0 }); if (ImGui::Begin("Background texture", nullptr, kPopupFlags)) { - if (g_shaderCacheLoaderState.textureId) + if (g_shaderCacheLoaderState.textureTVId) { float imageDisplayWidth = io.DisplaySize.x; float imageDisplayHeight = 720 * imageDisplayWidth / 1280; @@ -327,19 +348,54 @@ void LatteShaderCache_load() paddingTopAndBottom = 0.0f; } - ImGui::GetWindowDrawList()->AddImage(g_shaderCacheLoaderState.textureId, ImVec2(paddingLeftAndRight, paddingTopAndBottom), ImVec2(io.DisplaySize.x-paddingLeftAndRight, io.DisplaySize.y-paddingTopAndBottom), { 0,1 }, { 1,0 }); + ImGui::GetWindowDrawList()->AddImage(g_shaderCacheLoaderState.textureTVId, ImVec2(paddingLeftAndRight, paddingTopAndBottom), ImVec2(io.DisplaySize.x-paddingLeftAndRight, io.DisplaySize.y-paddingTopAndBottom), { 0,1 }, { 1,0 }); } ImGui::End(); + ImGui::PopStyleVar(2); + g_renderer->ImguiEnd(); } - ImGui::PopStyleVar(2); - - g_renderer->ImguiEnd(); } + + g_renderer->BeginFrame(false); + if (g_renderer->ImguiBegin(false)) + { + ImGui::SetNextWindowPos({ 0,0 }, ImGuiCond_Always); + ImGui::SetNextWindowSize(io.DisplaySize, ImGuiCond_Always); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 0,0 }); + + if (ImGui::Begin("Background texture2", nullptr, kPopupFlags)) + { + if (g_shaderCacheLoaderState.textureDRCId) + { + float imageDisplayWidth = io.DisplaySize.x; + float imageDisplayHeight = 480 * imageDisplayWidth / 854; + + float paddingLeftAndRight = 0.0f; + float paddingTopAndBottom = (io.DisplaySize.y - imageDisplayHeight)/2.0f; + if (imageDisplayHeight > io.DisplaySize.y) + { + imageDisplayHeight = io.DisplaySize.y; + imageDisplayWidth = 854 * imageDisplayHeight / 480; + paddingLeftAndRight = (io.DisplaySize.x - imageDisplayWidth)/2.0f; + paddingTopAndBottom = 0.0f; + } + + ImGui::GetWindowDrawList()->AddImage(g_shaderCacheLoaderState.textureDRCId, ImVec2(paddingLeftAndRight, paddingTopAndBottom), ImVec2(io.DisplaySize.x-paddingLeftAndRight, io.DisplaySize.y-paddingTopAndBottom), { 0,1 }, { 1,0 }); + } + ImGui::End(); + ImGui::PopStyleVar(2); + g_renderer->ImguiEnd(); + } + } + g_renderer->SwapBuffers(true, true); } - if (g_shaderCacheLoaderState.textureId) - g_renderer->DeleteTexture(g_shaderCacheLoaderState.textureId); + if (g_shaderCacheLoaderState.textureTVId) + g_renderer->DeleteTexture(g_shaderCacheLoaderState.textureTVId); + if (g_shaderCacheLoaderState.textureDRCId) + g_renderer->DeleteTexture(g_shaderCacheLoaderState.textureDRCId); } void LatteShaderCache_ShowProgress(const std::function & loadUpdateFunc, bool isPipelines) @@ -374,7 +430,7 @@ void LatteShaderCache_ShowProgress(const std::function & loadUpdateF const auto progress_font = ImGui_GetFont(window_size.y / 32.0f); // = 24 by default const auto shader_count_font = ImGui_GetFont(window_size.y / 48.0f); // = 16 // render background texture - if (g_shaderCacheLoaderState.textureId) + if (g_shaderCacheLoaderState.textureTVId) { ImGui::SetNextWindowPos({ 0, 0 }, ImGuiCond_Always); ImGui::SetNextWindowSize(io.DisplaySize, ImGuiCond_Always); @@ -395,7 +451,7 @@ void LatteShaderCache_ShowProgress(const std::function & loadUpdateF paddingTopAndBottom = 0.0f; } - ImGui::GetWindowDrawList()->AddImage(g_shaderCacheLoaderState.textureId, ImVec2(paddingLeftAndRight, paddingTopAndBottom), ImVec2(io.DisplaySize.x-paddingLeftAndRight, io.DisplaySize.y-paddingTopAndBottom), { 0,1 }, { 1,0 }); + ImGui::GetWindowDrawList()->AddImage(g_shaderCacheLoaderState.textureTVId, ImVec2(paddingLeftAndRight, paddingTopAndBottom), ImVec2(io.DisplaySize.x-paddingLeftAndRight, io.DisplaySize.y-paddingTopAndBottom), { 0,1 }, { 1,0 }); ImGui::End(); } ImGui::PopStyleVar(2); @@ -484,6 +540,37 @@ void LatteShaderCache_ShowProgress(const std::function & loadUpdateF lastFrameUpdate = tick_cached(); } + g_renderer->BeginFrame(false); + if (g_renderer->ImguiBegin(false)) + { + ImGui::SetNextWindowPos({ 0,0 }, ImGuiCond_Always); + ImGui::SetNextWindowSize(io.DisplaySize, ImGuiCond_Always); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 0,0 }); + if (ImGui::Begin("Background texture2", nullptr, kPopupFlags)) + { + if (g_shaderCacheLoaderState.textureDRCId) + { + float imageDisplayWidth = io.DisplaySize.x; + float imageDisplayHeight = 480 * imageDisplayWidth / 854; + + float paddingLeftAndRight = 0.0f; + float paddingTopAndBottom = (io.DisplaySize.y - imageDisplayHeight)/2.0f; + if (imageDisplayHeight > io.DisplaySize.y) + { + imageDisplayHeight = io.DisplaySize.y; + imageDisplayWidth = 854 * imageDisplayHeight / 480; + paddingLeftAndRight = (io.DisplaySize.x - imageDisplayWidth)/2.0f; + paddingTopAndBottom = 0.0f; + } + ImGui::GetWindowDrawList()->AddImage(g_shaderCacheLoaderState.textureDRCId, ImVec2(paddingLeftAndRight, paddingTopAndBottom), ImVec2(io.DisplaySize.x-paddingLeftAndRight, io.DisplaySize.y-paddingTopAndBottom), { 0,1 }, { 1,0 }); + } + ImGui::End(); + ImGui::PopStyleVar(2); + g_renderer->ImguiEnd(); + } + } + // finish frame g_renderer->SwapBuffers(true, true); } From 665a34e51847061e31398a04506dcf2f53dd15f9 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Mon, 17 Oct 2022 13:25:49 +0200 Subject: [PATCH 035/616] Linux: Always use libpng from system (#381) * Always use system libpng on Linux * Remove dependency on boost-crc in DSU (reuse existing implementation) --- src/input/api/DSU/DSUMessages.cpp | 13 ++----------- src/main.cpp | 2 ++ src/util/crypto/crc32.cpp | 9 +++++++++ src/util/crypto/crc32.h | 7 ++++++- vcpkg.json | 8 +++++--- 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/input/api/DSU/DSUMessages.cpp b/src/input/api/DSU/DSUMessages.cpp index d0ea61af..f92c1e3f 100644 --- a/src/input/api/DSU/DSUMessages.cpp +++ b/src/input/api/DSU/DSUMessages.cpp @@ -1,6 +1,5 @@ #include "input/api/DSU/DSUMessages.h" - -#include +#include "util/crypto/crc32.h" constexpr uint32_t kMagicClient = 'CUSD'; constexpr uint32_t kMagicServer = 'SUSD'; @@ -17,15 +16,7 @@ void MessageHeader::Finalize(size_t size) uint32_t MessageHeader::CRC32(size_t size) const { - const auto tmp = m_crc32; - m_crc32 = 0; - - boost::crc_32_type crc; - crc.process_bytes(this, size); - const auto result = crc.checksum(); - - m_crc32 = tmp; - return result; + return crc32_calc(this, size); } bool MessageHeader::IsClientMessage() const { return m_magic == kMagicClient; } diff --git a/src/main.cpp b/src/main.cpp index 323494c1..801683d1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -234,6 +234,7 @@ void ppcAsmTest(); void gx2CopySurfaceTest(); void ExpressionParser_test(); void FSTVolumeTest(); +void CRCTest(); void unitTests() { @@ -241,6 +242,7 @@ void unitTests() gx2CopySurfaceTest(); ppcAsmTest(); FSTVolumeTest(); + CRCTest(); } int mainEmulatorHLE() diff --git a/src/util/crypto/crc32.cpp b/src/util/crypto/crc32.cpp index 65c6a8f8..52eb8a88 100644 --- a/src/util/crypto/crc32.cpp +++ b/src/util/crypto/crc32.cpp @@ -382,4 +382,13 @@ unsigned int crc32_calc(unsigned int c, const void* data, int length) p++; } return ~c; +} + +void CRCTest() +{ + std::vector testData; + for (uint8 i = 0; i < 89; i++) + testData.emplace_back(i); + uint32 r = crc32_calc(0, testData.data(), testData.size()); + cemu_assert(r == 0x3fc61683); } \ No newline at end of file diff --git a/src/util/crypto/crc32.h b/src/util/crypto/crc32.h index 6a254433..b8002261 100644 --- a/src/util/crypto/crc32.h +++ b/src/util/crypto/crc32.h @@ -1,3 +1,8 @@ #pragma once -unsigned int crc32_calc(unsigned int c, const void* data, int length); \ No newline at end of file +unsigned int crc32_calc(unsigned int c, const void* data, int length); + +inline unsigned int crc32_calc(const void* data, int length) +{ + return crc32_calc(0, data, length); +} \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json index 33587607..e85a4311 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -5,8 +5,11 @@ "dependencies": [ "pugixml", "zlib", - "libpng", - "zstd", + "zstd", + { + "name": "libpng", + "platform": "!linux" + }, { "name": "libzip", "default-features": false @@ -23,7 +26,6 @@ "boost-dll", "boost-optional", "boost-signals2", - "boost-crc", "boost-asio", "boost-ptr-container", "boost-property-tree", From 15b3a3f77d8c0c6e3a95061ff337d61b198ff665 Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Mon, 17 Oct 2022 16:05:35 +0200 Subject: [PATCH 036/616] Linux: Remove libpng dependency from wxwidgets (#382) --- dependencies/vcpkg_overlay_ports/wxwidgets/vcpkg.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dependencies/vcpkg_overlay_ports/wxwidgets/vcpkg.json b/dependencies/vcpkg_overlay_ports/wxwidgets/vcpkg.json index 3f04aece..95b2eea5 100644 --- a/dependencies/vcpkg_overlay_ports/wxwidgets/vcpkg.json +++ b/dependencies/vcpkg_overlay_ports/wxwidgets/vcpkg.json @@ -17,7 +17,10 @@ "platform": "!windows & !osx" }, "expat", - "libpng", + { + "name": "libpng", + "platform": "!linux" + }, "tiff", { "name": "vcpkg-cmake", From 271a4e4719463ece58db74b9c390f83bf6e956cf Mon Sep 17 00:00:00 2001 From: MythicalPlayz <57963367+MythicalPlayz@users.noreply.github.com> Date: Tue, 18 Oct 2022 17:08:09 +0200 Subject: [PATCH 037/616] Fixed Discord Rich Presence not working on games that are on MLC (#383) --- src/Cafe/CafeSystem.cpp | 5 ++- src/gui/MainWindow.cpp | 76 +---------------------------------------- src/gui/MainWindow.h | 1 - 3 files changed, 3 insertions(+), 79 deletions(-) diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index e2e1c5ef..acfd1d2f 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -704,9 +704,8 @@ namespace CafeSystem std::string GetForegroundTitleName() { if (sLaunchModeIsStandalone) - return "Missing meta data"; - // todo - use language based on Cemu console language - return sGameInfo_ForegroundTitle.GetBase().GetMetaInfo()->GetShortName(CafeConsoleLanguage::EN); + return "Unknown Game"; + return sGameInfo_ForegroundTitle.GetBase().GetMetaInfo()->GetShortName(GetConfig().console_language); } std::string GetForegroundTitleArgStr() diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 3291f3bb..fb18a7de 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -543,8 +543,7 @@ bool MainWindow::FileLoad(std::wstring fileName, wxLaunchGameEvent::INITIATED_BY m_game_list = nullptr; } - const auto game_name = GetGameName(fileName); - m_launched_game_name = boost::nowide::narrow(game_name); + m_launched_game_name = CafeSystem::GetForegroundTitleName(); #ifdef ENABLE_DISCORD_RPC if (m_discord) m_discord->UpdatePresence(DiscordPresence::Playing, m_launched_game_name); @@ -1496,79 +1495,6 @@ void MainWindow::DestroyCanvas() } } - -std::wstring MainWindow::GetGameName(std::wstring_view file_name) -{ - fs::path path{ std::wstring{file_name} }; - const auto extension = path.extension(); - if (extension == ".wud" || extension == ".wux") - { - std::unique_ptr volume(FSTVolume::OpenFromDiscImage(path)); - if (!volume) - return path.filename().generic_wstring(); - - bool foundFile = false; - std::vector metaContent = volume->ExtractFile("meta/meta.xml", &foundFile); - if (!foundFile) - return path.filename().generic_wstring(); - - namespace xml = tinyxml2; - xml::XMLDocument doc; - doc.Parse((const char*)metaContent.data(), metaContent.size()); - - // parse meta.xml - xml::XMLElement* root = doc.FirstChildElement("menu"); - if (root) - { - xml::XMLElement* element = root->FirstChildElement("longname_en"); - if (element) - { - - auto game_name = boost::nowide::widen(element->GetText()); - const auto it = game_name.find(L'\n'); - if (it != std::wstring::npos) - game_name.replace(it, 1, L" - "); - - return game_name; - } - } - return path.filename().generic_wstring(); - } - else if (extension == ".rpx") - { - if (path.has_parent_path() && path.parent_path().has_parent_path()) - { - auto meta_xml = path.parent_path().parent_path() / "meta/meta.xml"; - auto metaXmlData = FileStream::LoadIntoMemory(meta_xml); - if (metaXmlData) - { - namespace xml = tinyxml2; - xml::XMLDocument doc; - if (doc.Parse((const char*)metaXmlData->data(), metaXmlData->size()) == xml::XML_SUCCESS) - { - xml::XMLElement* root = doc.FirstChildElement("menu"); - if (root) - { - xml::XMLElement* element = root->FirstChildElement("longname_en"); - if (element) - { - - auto game_name = boost::nowide::widen(element->GetText()); - const auto it = game_name.find(L'\n'); - if (it != std::wstring::npos) - game_name.replace(it, 1, L" - "); - - return game_name; - } - } - } - } - } - } - - return path.filename().generic_wstring(); -} - void MainWindow::OnSizeEvent(wxSizeEvent& event) { if (!IsMaximized() && !gui_isFullScreen()) diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index bcf1212d..faf83a99 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -130,7 +130,6 @@ public: void CreateCanvas(); void DestroyCanvas(); - std::wstring GetGameName(std::wstring_view file_name); static void ShowCursor(bool state); uintptr_t GetRenderCanvasHWND(); From 9df1325d146a601233c3549c8949d3e1349b6afe Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Thu, 20 Oct 2022 13:12:16 +0200 Subject: [PATCH 038/616] Linux: Resolve backtrace symbols directly from .symtab instead of .dynsym (#385) --- src/CMakeLists.txt | 4 - src/Common/CMakeLists.txt | 7 ++ .../ExceptionHandler/ELFSymbolTable.cpp | 108 ++++++++++++++++++ src/Common/ExceptionHandler/ELFSymbolTable.h | 32 ++++++ .../ExceptionHandler_posix.cpp | 47 ++++++-- 5 files changed, 182 insertions(+), 16 deletions(-) create mode 100644 src/Common/ExceptionHandler/ELFSymbolTable.cpp create mode 100644 src/Common/ExceptionHandler/ELFSymbolTable.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 32d6fe60..024432d0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,10 +35,6 @@ elseif(UNIX) add_compile_options(-Wno-ambiguous-reversed-operator) endif() - if(NOT APPLE) - add_link_options(-rdynamic) - endif() - add_compile_options(-Wno-multichar -Wno-invalid-offsetof -Wno-switch -Wno-ignored-attributes -Wno-deprecated-enum-enum-conversion) endif() diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index ca7ea238..7e5825f1 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -43,6 +43,13 @@ PRIVATE ) endif() +if(UNIX AND NOT APPLE) + target_sources(CemuCommon PRIVATE + ExceptionHandler/ELFSymbolTable.cpp + ExceptionHandler/ELFSymbolTable.h + ) +endif() + # All the targets wanting to use the precompiled.h header # have to link to CemuCommon target_precompile_headers(CemuCommon PUBLIC precompiled.h) diff --git a/src/Common/ExceptionHandler/ELFSymbolTable.cpp b/src/Common/ExceptionHandler/ELFSymbolTable.cpp new file mode 100644 index 00000000..0f297bdd --- /dev/null +++ b/src/Common/ExceptionHandler/ELFSymbolTable.cpp @@ -0,0 +1,108 @@ +#include "Common/ExceptionHandler/ELFSymbolTable.h" +#include "Common/FileStream.h" +#include +#include + +uint16 ELFSymbolTable::FindSection(int type, const std::string_view& name) +{ + if (!shTable || !shStrTable) + return 0; + + for (uint16 i = 0; i < header->e_shnum; ++i) + { + auto& entry = shTable[i]; + if(entry.sh_type == type && std::string_view{&shStrTable[entry.sh_name]} == name) + { + return i; + } + } + return 0; +} + +void* ELFSymbolTable::SectionPointer(uint16 index) +{ + return SectionPointer(shTable[index]); +} + +void* ELFSymbolTable::SectionPointer(const Elf64_Shdr& section) +{ + return (void*)(mappedExecutable + section.sh_offset); +} + +ELFSymbolTable::ELFSymbolTable() +{ + // create file handle + int fd = open("/proc/self/exe", O_RDONLY); + if (!fd) + return; + + // retrieve file size. + struct stat filestats; + if (fstat(fd, &filestats)) + { + close(fd); + return; + } + mappedExecutableSize = filestats.st_size; + + // attempt to map the file + mappedExecutable = (uint8*)(mmap(nullptr, mappedExecutableSize, PROT_READ, MAP_PRIVATE, fd, 0)); + close(fd); + if (!mappedExecutable) + return; + + // verify signature + header = (Elf64_Ehdr*)(mappedExecutable); + constexpr uint8 signature[] = {0x7f, 0x45, 0x4c, 0x46}; + for (size_t i = 0; i < 4; ++i) + { + if (signature[i] != header->e_ident[i]) + { + return; + } + } + + shTable = (Elf64_Shdr*)(mappedExecutable + header->e_shoff); + + Elf64_Shdr& shStrn = shTable[header->e_shstrndx]; + shStrTable = (char*)(mappedExecutable + shStrn.sh_offset); + + strTable = (char*)SectionPointer(FindSection(SHT_STRTAB, ".strtab")); + + Elf64_Shdr& symTabShdr = shTable[FindSection(SHT_SYMTAB, ".symtab")]; + if (symTabShdr.sh_entsize == 0) + return; + + symTableLen = symTabShdr.sh_size / symTabShdr.sh_entsize; + symTable = (Elf64_Sym*)(SectionPointer(symTabShdr)); +} + +ELFSymbolTable::~ELFSymbolTable() +{ + if (mappedExecutable) + munmap(mappedExecutable, mappedExecutableSize); +} + +std::string_view ELFSymbolTable::OffsetToSymbol(uint64 ptr, uint64& fromStart) const +{ + if(!symTable || !strTable) + { + fromStart = -1; + return {}; + } + + for (auto entry = symTable+1; entry < symTable+symTableLen; ++entry) + { + if (ELF64_ST_TYPE(entry->st_info) != STT_FUNC) + continue; + auto begin = entry->st_value; + auto size = entry->st_size; + if(ptr >= begin && ptr < begin+size) + { + fromStart = ptr-begin; + return &strTable[entry->st_name]; + } + } + fromStart = -1; + return {}; +} diff --git a/src/Common/ExceptionHandler/ELFSymbolTable.h b/src/Common/ExceptionHandler/ELFSymbolTable.h new file mode 100644 index 00000000..89248bbb --- /dev/null +++ b/src/Common/ExceptionHandler/ELFSymbolTable.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include + +class ELFSymbolTable +{ +public: + std::string_view OffsetToSymbol(uint64 ptr, uint64& fromStart) const; + + ELFSymbolTable(); + ~ELFSymbolTable(); +private: + uint8* mappedExecutable = nullptr; + size_t mappedExecutableSize = 0; + + Elf64_Ehdr* header = nullptr; + + Elf64_Shdr* shTable = nullptr; + char* shStrTable = nullptr; + + Elf64_Sym* symTable = nullptr; + uint64 symTableLen = 0; + char* strTable = nullptr; + + uint16 FindSection(int type, const std::string_view& name); + + void* SectionPointer (uint16 index); + void* SectionPointer(const Elf64_Shdr& section); + + // ownership of mapped memory, cannot copy. + ELFSymbolTable(const ELFSymbolTable&) = delete; +}; diff --git a/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp b/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp index 86d83bb3..3a5620c2 100644 --- a/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp +++ b/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp @@ -2,41 +2,60 @@ #include #include #include - #include "config/CemuConfig.h" +#include "util/helpers/StringHelpers.h" +#if BOOST_OS_LINUX +#include "ELFSymbolTable.h" +#endif + +#if BOOST_OS_LINUX void demangleAndPrintBacktrace(char** backtrace, size_t size) { + ELFSymbolTable symTable; for (char** i = backtrace; i < backtrace + size; i++) { - std::string traceLine{*i}; + std::string_view traceLine{*i}; + + // basic check to see if the backtrace line matches expected format size_t parenthesesOpen = traceLine.find_last_of('('); size_t parenthesesClose = traceLine.find_last_of(')'); size_t offsetPlus = traceLine.find_last_of('+'); if (!parenthesesOpen || !parenthesesClose || !offsetPlus || offsetPlus < parenthesesOpen || offsetPlus > parenthesesClose) { - // something unexpected was read. fall back to default string + // fall back to default string std::cerr << traceLine << std::endl; continue; } - std::string symbolName = traceLine.substr(parenthesesOpen+1,offsetPlus-parenthesesOpen-1); - int status = -1; - char* demangled = abi::__cxa_demangle(symbolName.c_str(), nullptr, nullptr, &status); - if (demangled) + // attempt to resolve symbol from regular symbol table if missing from dynamic symbol table + uint64 newOffset = -1; + std::string_view symbolName = traceLine.substr(parenthesesOpen+1, offsetPlus-parenthesesOpen-1); + if (symbolName.empty()) { - std::cerr << traceLine.substr(0, parenthesesOpen+1); - std::cerr << demangled; - std::cerr << traceLine.substr(offsetPlus) << std::endl; - free(demangled); + uint64 symbolOffset = StringHelpers::ToInt64(traceLine.substr(offsetPlus+1,offsetPlus+1-parenthesesClose-1)); + symbolName = symTable.OffsetToSymbol(symbolOffset, newOffset); + } + + std::cerr << traceLine.substr(0, parenthesesOpen+1); + + std::cerr << boost::core::demangle(symbolName.empty() ? "" : symbolName.data()); + + // print relative or existing symbol offset. + std::cerr << '+'; + if (newOffset != -1) + { + std::cerr << std::hex << newOffset; + std::cerr << traceLine.substr(parenthesesClose) << std::endl; } else { - std::cerr << traceLine << std::endl; + std::cerr << traceLine.substr(offsetPlus+1) << std::endl; } } } +#endif // handle signals that would dump core, print stacktrace and then dump depending on config void handlerDumpingSignal(int sig) @@ -61,6 +80,7 @@ void handlerDumpingSignal(int sig) // print out all the frames to stderr fprintf(stderr, "Error: signal %d:\n", sig); +#if BOOST_OS_LINUX char** symbol_trace = backtrace_symbols(array, size); if (symbol_trace) @@ -72,6 +92,9 @@ void handlerDumpingSignal(int sig) { std::cerr << "Failed to read backtrace" << std::endl; } +#else + backtrace_symbols_fd(array, size, STDERR_FILENO); +#endif if (GetConfig().crash_dump == CrashDump::Enabled) { From dd1cb1cccf9ebc46f1b66b0a74d0e1ca86a0b99c Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Thu, 20 Oct 2022 13:18:44 +0200 Subject: [PATCH 039/616] Update title manager when clearing MLC path in settings (#319) --- src/gui/CemuApp.cpp | 28 ++++++++++++++++++++-------- src/gui/CemuApp.h | 1 + src/gui/GeneralSettings2.cpp | 17 ++++++++++------- src/util/helpers/helpers.cpp | 6 +++--- 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/gui/CemuApp.cpp b/src/gui/CemuApp.cpp index c7a5adaa..c502e8d2 100644 --- a/src/gui/CemuApp.cpp +++ b/src/gui/CemuApp.cpp @@ -376,6 +376,24 @@ void CemuApp::CreateDefaultFiles(bool first_start) } +bool CemuApp::TrySelectMLCPath(std::wstring path) +{ + if (path.empty()) + path = ActiveSettings::GetDefaultMLCPath().wstring(); + + if (!TestWriteAccess(fs::path{ path })) + return false; + + GetConfig().SetMLCPath(path); + CemuApp::CreateDefaultFiles(); + + // update TitleList and SaveList scanner with new MLC path + CafeTitleList::SetMLCPath(path); + CafeTitleList::Refresh(); + CafeSaveList::SetMLCPath(path); + CafeSaveList::Refresh(); + return true; +} bool CemuApp::SelectMLCPath(wxWindow* parent) { @@ -394,21 +412,15 @@ bool CemuApp::SelectMLCPath(wxWindow* parent) const auto path = path_dialog.GetPath().ToStdWstring(); - if (!TestWriteAccess(fs::path{ path })) + if (!TrySelectMLCPath(path)) { const auto result = wxMessageBox(_("Cemu can't write to the selected mlc path!\nDo you want to select another path?"), _("Error"), wxYES_NO | wxCENTRE | wxICON_ERROR); if (result == wxYES) continue; - + break; } - config.SetMLCPath(path); - // update TitleList and SaveList scanner with new MLC path - CafeTitleList::SetMLCPath(path); - CafeTitleList::Refresh(); - CafeSaveList::SetMLCPath(path); - CafeSaveList::Refresh(); return true; } diff --git a/src/gui/CemuApp.h b/src/gui/CemuApp.h index 32504883..f462ec39 100644 --- a/src/gui/CemuApp.h +++ b/src/gui/CemuApp.h @@ -17,6 +17,7 @@ public: static std::vector GetAvailableLanguages(); static void CreateDefaultFiles(bool first_start = false); + static bool TrySelectMLCPath(std::wstring path); static bool SelectMLCPath(wxWindow* parent = nullptr); static wxString GetConfigPath(); diff --git a/src/gui/GeneralSettings2.cpp b/src/gui/GeneralSettings2.cpp index 776afc2e..810c147e 100644 --- a/src/gui/GeneralSettings2.cpp +++ b/src/gui/GeneralSettings2.cpp @@ -1751,7 +1751,6 @@ void GeneralSettings2::OnMLCPathSelect(wxCommandEvent& event) m_mlc_path->SetValue(ActiveSettings::GetMlcPath().generic_string()); m_reload_gamelist = true; m_mlc_modified = true; - CemuApp::CreateDefaultFiles(); } void GeneralSettings2::OnMLCPathChar(wxKeyEvent& event) @@ -1761,14 +1760,18 @@ void GeneralSettings2::OnMLCPathChar(wxKeyEvent& event) if(event.GetKeyCode() == WXK_DELETE || event.GetKeyCode() == WXK_BACK) { - m_mlc_path->SetValue(wxEmptyString); + std::wstring newPath = L""; + if(!CemuApp::TrySelectMLCPath(newPath)) + { + const auto res = wxMessageBox(_("The default MLC path is inaccessible.\nDo you want to select a different path?"), _("Error"), wxYES_NO | wxCENTRE | wxICON_ERROR); + if (res == wxYES && CemuApp::SelectMLCPath(this)) + newPath = ActiveSettings::GetMlcPath().wstring(); + else + return; + } + m_mlc_path->SetValue(newPath); m_reload_gamelist = true; - - GetConfig().mlc_path = L""; - g_config.Save(); m_mlc_modified = true; - - CemuApp::CreateDefaultFiles(); } } diff --git a/src/util/helpers/helpers.cpp b/src/util/helpers/helpers.cpp index fefe7985..b556db36 100644 --- a/src/util/helpers/helpers.cpp +++ b/src/util/helpers/helpers.cpp @@ -280,15 +280,16 @@ uint32_t GetPhysicalCoreCount() bool TestWriteAccess(const fs::path& p) { + std::error_code ec; // must be path and must exist - if (!fs::exists(p) || !fs::is_directory(p)) + if (!fs::exists(p, ec) || !fs::is_directory(p, ec)) return false; // retry 3 times for (int i = 0; i < 3; ++i) { const auto filename = p / fmt::format("_{}.tmp", GenerateRandomString(8)); - if (fs::exists(filename)) + if (fs::exists(filename, ec)) continue; std::ofstream file(filename); @@ -297,7 +298,6 @@ bool TestWriteAccess(const fs::path& p) file.close(); - std::error_code ec; fs::remove(filename, ec); return true; } From c217b3ee3231d5b7e2eed21896f72e9de05f5ea1 Mon Sep 17 00:00:00 2001 From: MythicalPlayz <57963367+MythicalPlayz@users.noreply.github.com> Date: Fri, 21 Oct 2022 00:17:11 +0200 Subject: [PATCH 040/616] GameList: Use title name based on console language (#388) --- src/Cafe/CafeSystem.cpp | 8 +++++++- src/Cafe/TitleList/TitleInfo.cpp | 10 +++++++++- src/gui/MainWindow.cpp | 2 ++ src/gui/components/wxGameList.cpp | 5 +++++ src/gui/components/wxGameList.h | 1 + 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index acfd1d2f..68484830 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -705,7 +705,13 @@ namespace CafeSystem { if (sLaunchModeIsStandalone) return "Unknown Game"; - return sGameInfo_ForegroundTitle.GetBase().GetMetaInfo()->GetShortName(GetConfig().console_language); + std::string applicationName; + applicationName = sGameInfo_ForegroundTitle.GetBase().GetMetaInfo()->GetShortName(GetConfig().console_language); + if (applicationName.empty()) //Try to get the English Title + applicationName = sGameInfo_ForegroundTitle.GetBase().GetMetaInfo()->GetShortName(CafeConsoleLanguage::EN); + if (applicationName.empty()) //Unknown Game + applicationName = "Unknown Game"; + return applicationName; } std::string GetForegroundTitleArgStr() diff --git a/src/Cafe/TitleList/TitleInfo.cpp b/src/Cafe/TitleList/TitleInfo.cpp index 10710c43..36814ab8 100644 --- a/src/Cafe/TitleList/TitleInfo.cpp +++ b/src/Cafe/TitleList/TitleInfo.cpp @@ -589,7 +589,15 @@ std::string TitleInfo::GetTitleName() const { cemu_assert_debug(m_isValid); if (m_parsedMetaXml) - return m_parsedMetaXml->GetShortName(CafeConsoleLanguage::EN); + { + std::string titleNameCfgLanguage; + titleNameCfgLanguage = m_parsedMetaXml->GetShortName(GetConfig().console_language); + if (titleNameCfgLanguage.empty()) //Get English Title + titleNameCfgLanguage = m_parsedMetaXml->GetShortName(CafeConsoleLanguage::EN); + if (titleNameCfgLanguage.empty()) //Unknown Title + titleNameCfgLanguage = "Unknown Title"; + return titleNameCfgLanguage; + } if (m_cachedInfo) return m_cachedInfo->titleName; cemu_assert_suspicious(); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index fb18a7de..e8d1c920 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -931,6 +931,8 @@ void MainWindow::OnConsoleLanguage(wxCommandEvent& event) default: cemu_assert_debug(false); } + m_game_list->DeleteCachedStrings(); + m_game_list->ReloadGameEntries(false); g_config.Save(); } diff --git a/src/gui/components/wxGameList.cpp b/src/gui/components/wxGameList.cpp index 904fd5b7..50a6254a 100644 --- a/src/gui/components/wxGameList.cpp +++ b/src/gui/components/wxGameList.cpp @@ -1139,3 +1139,8 @@ bool wxGameList::QueryIconForTitle(TitleId titleId, int& icon, int& iconSmall) m_icon_cache_mtx.unlock(); return true; } + +void wxGameList::DeleteCachedStrings() +{ + m_name_cache.clear(); +} \ No newline at end of file diff --git a/src/gui/components/wxGameList.h b/src/gui/components/wxGameList.h index 2cf9f6b7..f8efd5d2 100644 --- a/src/gui/components/wxGameList.h +++ b/src/gui/components/wxGameList.h @@ -50,6 +50,7 @@ public: bool IsVisible(long item) const; // only available in wxwidgets 3.1.3 void ReloadGameEntries(bool cached = false); + void DeleteCachedStrings(); long FindListItemByTitleId(uint64 title_id) const; void OnClose(wxCloseEvent& event); From ffa213c794d6c11bea370ce445f36d2ede8acf6c Mon Sep 17 00:00:00 2001 From: Squall Leonhart Date: Fri, 21 Oct 2022 21:39:26 +1100 Subject: [PATCH 041/616] Generalised game profile cleanup and corrections (#389) --- bin/gameProfiles/default/0005000010101a00.ini | 8 +------- bin/gameProfiles/default/0005000010101b00.ini | 8 +------- bin/gameProfiles/default/0005000010104d00.ini | 1 - bin/gameProfiles/default/0005000010106100.ini | 3 --- bin/gameProfiles/default/000500001010EB00.ini | 3 --- bin/gameProfiles/default/000500001010F900.ini | 8 +------- bin/gameProfiles/default/000500001010ac00.ini | 8 +------- bin/gameProfiles/default/000500001010b100.ini | 5 +---- bin/gameProfiles/default/000500001010b200.ini | 8 +------- bin/gameProfiles/default/000500001010dc00.ini | 5 +---- bin/gameProfiles/default/000500001010dd00.ini | 5 +---- bin/gameProfiles/default/000500001010e600.ini | 5 +---- bin/gameProfiles/default/000500001010e700.ini | 8 +------- bin/gameProfiles/default/000500001010ec00.ini | 3 --- bin/gameProfiles/default/000500001010ed00.ini | 3 --- bin/gameProfiles/default/000500001010ef00.ini | 5 +---- bin/gameProfiles/default/000500001010f100.ini | 8 +------- bin/gameProfiles/default/000500001010f200.ini | 8 +------- bin/gameProfiles/default/000500001010f500.ini | 5 +---- bin/gameProfiles/default/000500001011000.ini | 8 +------- bin/gameProfiles/default/0005000010110100.ini | 5 +---- bin/gameProfiles/default/0005000010110600.ini | 5 +---- bin/gameProfiles/default/0005000010110700.ini | 5 +---- bin/gameProfiles/default/0005000010110E00.ini | 1 - bin/gameProfiles/default/0005000010111400.ini | 5 +---- bin/gameProfiles/default/0005000010111600.ini | 8 +------- bin/gameProfiles/default/0005000010112000.ini | 5 +---- bin/gameProfiles/default/0005000010112300.ini | 5 +---- bin/gameProfiles/default/0005000010113000.ini | 5 +---- bin/gameProfiles/default/0005000010113300.ini | 5 +---- bin/gameProfiles/default/0005000010113d00.ini | 5 +---- bin/gameProfiles/default/0005000010115d00.ini | 5 +---- bin/gameProfiles/default/0005000010116100.ini | 1 - bin/gameProfiles/default/0005000010117200.ini | 1 - bin/gameProfiles/default/0005000010118300.ini | 1 - bin/gameProfiles/default/000500001011a600.ini | 8 +------- bin/gameProfiles/default/000500001011af00.ini | 5 +---- bin/gameProfiles/default/000500001011b200.ini | 5 +---- bin/gameProfiles/default/0005000010128600.ini | 3 +-- bin/gameProfiles/default/0005000010129000.ini | 1 - bin/gameProfiles/default/0005000010129200.ini | 1 - bin/gameProfiles/default/000500001012BC00.ini | 4 ---- bin/gameProfiles/default/000500001012BD00.ini | 4 ---- bin/gameProfiles/default/000500001012F000.ini | 3 --- bin/gameProfiles/default/000500001012b200.ini | 1 - bin/gameProfiles/default/000500001012ba00.ini | 1 - bin/gameProfiles/default/000500001012be00.ini | 3 --- bin/gameProfiles/default/000500001012c500.ini | 5 +---- bin/gameProfiles/default/000500001012da00.ini | 8 +------- bin/gameProfiles/default/0005000010131F00.ini | 5 +---- bin/gameProfiles/default/0005000010132c00.ini | 5 +---- bin/gameProfiles/default/0005000010132d00.ini | 5 +---- bin/gameProfiles/default/0005000010133b00.ini | 5 +---- bin/gameProfiles/default/0005000010134e00.ini | 5 +---- bin/gameProfiles/default/0005000010135500.ini | 8 +------- bin/gameProfiles/default/0005000010135e00.ini | 6 +----- bin/gameProfiles/default/0005000010136300.ini | 5 +---- bin/gameProfiles/default/0005000010136c00.ini | 1 - bin/gameProfiles/default/0005000010137F00.ini | 5 +---- bin/gameProfiles/default/0005000010137c00.ini | 1 - bin/gameProfiles/default/0005000010138300.ini | 5 +---- bin/gameProfiles/default/0005000010138a00.ini | 5 +---- bin/gameProfiles/default/0005000010138f00.ini | 1 - bin/gameProfiles/default/0005000010140000.ini | 5 +---- bin/gameProfiles/default/0005000010143200.ini | 1 + bin/gameProfiles/default/0005000010143500.ini | 7 +------ bin/gameProfiles/default/0005000010143600.ini | 7 +------ bin/gameProfiles/default/0005000010144800.ini | 5 +---- bin/gameProfiles/default/0005000010144f00.ini | 1 - bin/gameProfiles/default/0005000010145000.ini | 1 - bin/gameProfiles/default/0005000010145c00.ini | 5 +---- bin/gameProfiles/default/0005000010145d00.ini | 5 +---- bin/gameProfiles/default/0005000010147e00.ini | 5 +---- bin/gameProfiles/default/000500001014c100.ini | 5 +---- bin/gameProfiles/default/000500001014c600.ini | 5 +---- bin/gameProfiles/default/000500001014cb00.ini | 5 +---- bin/gameProfiles/default/000500001014d900.ini | 5 +---- bin/gameProfiles/default/0005000010154600.ini | 1 - bin/gameProfiles/default/000500001015b200.ini | 5 +---- bin/gameProfiles/default/0005000010161a00.ini | 5 +---- bin/gameProfiles/default/0005000010162200.ini | 5 +---- bin/gameProfiles/default/0005000010162a00.ini | 5 +---- bin/gameProfiles/default/0005000010162b00.ini | 7 +------ bin/gameProfiles/default/000500001016a200.ini | 5 +---- bin/gameProfiles/default/000500001016ab00.ini | 5 +---- bin/gameProfiles/default/000500001016b200.ini | 3 --- bin/gameProfiles/default/000500001016d400.ini | 5 +---- bin/gameProfiles/default/000500001016d800.ini | 5 +---- bin/gameProfiles/default/000500001016da00.ini | 3 --- bin/gameProfiles/default/000500001016e000.ini | 5 +---- bin/gameProfiles/default/000500001016e800.ini | 3 --- bin/gameProfiles/default/000500001016ea00.ini | 5 +---- bin/gameProfiles/default/000500001016fc00.ini | 3 +-- bin/gameProfiles/default/0005000010170100.ini | 5 +---- bin/gameProfiles/default/0005000010172900.ini | 3 +-- bin/gameProfiles/default/0005000010173400.ini | 5 +---- bin/gameProfiles/default/0005000010176300.ini | 3 +-- bin/gameProfiles/default/0005000010176900.ini | 7 +------ bin/gameProfiles/default/0005000010176a00.ini | 7 +------ bin/gameProfiles/default/0005000010177000.ini | 5 +---- bin/gameProfiles/default/0005000010177500.ini | 3 --- bin/gameProfiles/default/0005000010177600.ini | 1 - bin/gameProfiles/default/0005000010177700.ini | 1 - bin/gameProfiles/default/000500001017da00.ini | 5 +---- bin/gameProfiles/default/000500001017e400.ini | 5 +---- bin/gameProfiles/default/0005000010180500.ini | 3 --- bin/gameProfiles/default/0005000010180600.ini | 3 --- bin/gameProfiles/default/0005000010180700.ini | 3 --- bin/gameProfiles/default/0005000010183000.ini | 8 +------- bin/gameProfiles/default/0005000010184900.ini | 5 +---- bin/gameProfiles/default/0005000010184E00.ini | 7 +------ bin/gameProfiles/default/0005000010184d00.ini | 7 +------ bin/gameProfiles/default/0005000010185400.ini | 7 +------ bin/gameProfiles/default/0005000010189f00.ini | 3 --- bin/gameProfiles/default/000500001018ab00.ini | 5 +---- bin/gameProfiles/default/000500001018d800.ini | 5 +---- bin/gameProfiles/default/000500001018d900.ini | 3 --- bin/gameProfiles/default/000500001018f100.ini | 5 +---- bin/gameProfiles/default/000500001018f400.ini | 5 +---- bin/gameProfiles/default/000500001018fd00.ini | 5 +---- bin/gameProfiles/default/0005000010190300.ini | 5 +---- bin/gameProfiles/default/0005000010193f00.ini | 5 +---- bin/gameProfiles/default/0005000010195e00.ini | 5 +---- bin/gameProfiles/default/0005000010197300.ini | 5 +---- bin/gameProfiles/default/0005000010197800.ini | 5 +---- bin/gameProfiles/default/0005000010199e00.ini | 5 +---- bin/gameProfiles/default/000500001019ab00.ini | 5 +---- bin/gameProfiles/default/000500001019b000.ini | 5 +---- bin/gameProfiles/default/000500001019b200.ini | 5 +---- bin/gameProfiles/default/000500001019ca00.ini | 5 +---- bin/gameProfiles/default/000500001019e500.ini | 7 +------ bin/gameProfiles/default/000500001019e600.ini | 7 +------ bin/gameProfiles/default/000500001019ea00.ini | 5 +---- bin/gameProfiles/default/000500001019ee00.ini | 5 +---- bin/gameProfiles/default/00050000101C9300.ini | 1 - bin/gameProfiles/default/00050000101E4100.ini | 3 --- bin/gameProfiles/default/00050000101a1200.ini | 6 +----- bin/gameProfiles/default/00050000101a1800.ini | 5 +---- bin/gameProfiles/default/00050000101a3b00.ini | 5 +---- bin/gameProfiles/default/00050000101a4300.ini | 5 +---- bin/gameProfiles/default/00050000101a4800.ini | 3 --- bin/gameProfiles/default/00050000101a4900.ini | 5 +---- bin/gameProfiles/default/00050000101a7f00.ini | 5 +---- bin/gameProfiles/default/00050000101a9c00.ini | 5 +---- bin/gameProfiles/default/00050000101a9e00.ini | 5 +---- bin/gameProfiles/default/00050000101acc00.ini | 5 +---- bin/gameProfiles/default/00050000101b0100.ini | 5 +---- bin/gameProfiles/default/00050000101b4e00.ini | 5 +---- bin/gameProfiles/default/00050000101b9900.ini | 3 --- bin/gameProfiles/default/00050000101bc300.ini | 5 +---- bin/gameProfiles/default/00050000101c3100.ini | 5 +---- bin/gameProfiles/default/00050000101c4200.ini | 5 +---- bin/gameProfiles/default/00050000101c4300.ini | 3 --- bin/gameProfiles/default/00050000101c4c00.ini | 1 - bin/gameProfiles/default/00050000101c4d00.ini | 1 - bin/gameProfiles/default/00050000101c6c00.ini | 5 +---- bin/gameProfiles/default/00050000101c7600.ini | 5 +---- bin/gameProfiles/default/00050000101c7b00.ini | 5 +---- bin/gameProfiles/default/00050000101c7d00.ini | 5 +---- bin/gameProfiles/default/00050000101c9400.ini | 1 - bin/gameProfiles/default/00050000101c9500.ini | 1 - bin/gameProfiles/default/00050000101c9a00.ini | 3 --- bin/gameProfiles/default/00050000101cc900.ini | 5 +---- bin/gameProfiles/default/00050000101ccf00.ini | 5 +---- bin/gameProfiles/default/00050000101ce000.ini | 5 +---- bin/gameProfiles/default/00050000101ce800.ini | 5 +---- bin/gameProfiles/default/00050000101d2100.ini | 3 +-- bin/gameProfiles/default/00050000101d4500.ini | 5 +---- bin/gameProfiles/default/00050000101d4d00.ini | 5 +---- bin/gameProfiles/default/00050000101d5000.ini | 5 +---- bin/gameProfiles/default/00050000101d5500.ini | 5 +---- bin/gameProfiles/default/00050000101d6000.ini | 3 --- bin/gameProfiles/default/00050000101d6d00.ini | 8 +------- bin/gameProfiles/default/00050000101d7500.ini | 3 --- bin/gameProfiles/default/00050000101d8900.ini | 5 +---- bin/gameProfiles/default/00050000101d8d00.ini | 5 +---- bin/gameProfiles/default/00050000101d9600.ini | 5 +---- bin/gameProfiles/default/00050000101d9d00.ini | 3 --- bin/gameProfiles/default/00050000101dac00.ini | 5 +---- bin/gameProfiles/default/00050000101daf00.ini | 5 +---- bin/gameProfiles/default/00050000101db000.ini | 5 +---- bin/gameProfiles/default/00050000101dbb00.ini | 5 +---- bin/gameProfiles/default/00050000101dbc00.ini | 5 +---- bin/gameProfiles/default/00050000101dbe00.ini | 5 +---- bin/gameProfiles/default/00050000101dbf00.ini | 5 +---- bin/gameProfiles/default/00050000101dc000.ini | 5 +---- bin/gameProfiles/default/00050000101dc200.ini | 5 +---- bin/gameProfiles/default/00050000101dd000.ini | 5 +---- bin/gameProfiles/default/00050000101dd600.ini | 5 +---- bin/gameProfiles/default/00050000101dd700.ini | 8 +------- bin/gameProfiles/default/00050000101ddf00.ini | 5 +---- bin/gameProfiles/default/00050000101e0100.ini | 7 +------ bin/gameProfiles/default/00050000101e1800.ini | 5 +---- bin/gameProfiles/default/00050000101e1a00.ini | 5 +---- bin/gameProfiles/default/00050000101e1b00.ini | 3 --- bin/gameProfiles/default/00050000101e3800.ini | 8 +------- bin/gameProfiles/default/00050000101e4200.ini | 3 --- bin/gameProfiles/default/00050000101e5300.ini | 3 --- bin/gameProfiles/default/00050000101e5400.ini | 3 --- bin/gameProfiles/default/00050000101e5e00.ini | 5 +---- bin/gameProfiles/default/00050000101e7300.ini | 5 +---- bin/gameProfiles/default/00050000101e7400.ini | 5 +---- bin/gameProfiles/default/00050000101e9300.ini | 5 +---- bin/gameProfiles/default/00050000101e9400.ini | 5 +---- bin/gameProfiles/default/00050000101eb300.ini | 5 +---- bin/gameProfiles/default/00050000101ec700.ini | 5 +---- bin/gameProfiles/default/00050000101ecf00.ini | 5 +---- bin/gameProfiles/default/00050000101f1300.ini | 5 +---- bin/gameProfiles/default/00050000101f2800.ini | 3 --- bin/gameProfiles/default/00050000101f4a00.ini | 5 +---- bin/gameProfiles/default/00050000101f5700.ini | 5 +---- bin/gameProfiles/default/00050000101f6f00.ini | 5 +---- bin/gameProfiles/default/00050000101f7600.ini | 8 +------- bin/gameProfiles/default/00050000101f9700.ini | 5 +---- bin/gameProfiles/default/00050000101fa600.ini | 5 +---- bin/gameProfiles/default/00050000101fd100.ini | 5 +---- bin/gameProfiles/default/00050000101ff200.ini | 5 +---- bin/gameProfiles/default/00050000101ffc00.ini | 1 - bin/gameProfiles/default/00050000101ffe00.ini | 5 +---- bin/gameProfiles/default/0005000010200300.ini | 5 +---- bin/gameProfiles/default/0005000010200b00.ini | 5 +---- bin/gameProfiles/default/0005000010204a00.ini | 5 +---- bin/gameProfiles/default/0005000010207300.ini | 5 +---- bin/gameProfiles/default/0005000010207500.ini | 5 +---- bin/gameProfiles/default/000500001020a200.ini | 6 +----- bin/gameProfiles/default/000500001020b600.ini | 1 - bin/gameProfiles/default/0005000010211b00.ini | 5 +---- bin/gameProfiles/default/0005000C1012BC00.ini | 4 ---- bin/gameProfiles/default/0005000C1012BD00.ini | 4 ---- bin/gameProfiles/default/0005000C1012BE00.ini | 4 ---- bin/gameProfiles/default/0005000e1019c800.ini | 5 +---- 231 files changed, 174 insertions(+), 889 deletions(-) create mode 100644 bin/gameProfiles/default/0005000010143200.ini diff --git a/bin/gameProfiles/default/0005000010101a00.ini b/bin/gameProfiles/default/0005000010101a00.ini index dc63ccb5..1123343a 100644 --- a/bin/gameProfiles/default/0005000010101a00.ini +++ b/bin/gameProfiles/default/0005000010101a00.ini @@ -1,7 +1 @@ -# LEGO City Undercover (USA) - -[General] -loadSharedLibraries = false - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# LEGO City Undercover (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010101b00.ini b/bin/gameProfiles/default/0005000010101b00.ini index 21aded1d..f3749d47 100644 --- a/bin/gameProfiles/default/0005000010101b00.ini +++ b/bin/gameProfiles/default/0005000010101b00.ini @@ -1,7 +1 @@ -# LEGO City Undercover (EUR) - -[General] -loadSharedLibraries = false - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# LEGO City Undercover (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010104d00.ini b/bin/gameProfiles/default/0005000010104d00.ini index 50a9fa48..dc8bebaf 100644 --- a/bin/gameProfiles/default/0005000010104d00.ini +++ b/bin/gameProfiles/default/0005000010104d00.ini @@ -1,5 +1,4 @@ # Monster Hunter 3(tri-)GHD Ver. (JPN) [Graphics] -GPUBufferCacheAccuracy = 1 streamoutBufferCacheSize = 48 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010106100.ini b/bin/gameProfiles/default/0005000010106100.ini index 77e7c399..9aa38072 100644 --- a/bin/gameProfiles/default/0005000010106100.ini +++ b/bin/gameProfiles/default/0005000010106100.ini @@ -1,7 +1,4 @@ # Super Mario 3D World (JPN) -[CPU] - [Graphics] accurateShaderMul = false -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001010EB00.ini b/bin/gameProfiles/default/000500001010EB00.ini index e4b30b16..281d21db 100644 --- a/bin/gameProfiles/default/000500001010EB00.ini +++ b/bin/gameProfiles/default/000500001010EB00.ini @@ -2,6 +2,3 @@ [CPU] cpuMode = Singlecore-Recompiler - -[Graphics] -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001010F900.ini b/bin/gameProfiles/default/000500001010F900.ini index 4772db75..e22770bf 100644 --- a/bin/gameProfiles/default/000500001010F900.ini +++ b/bin/gameProfiles/default/000500001010F900.ini @@ -1,7 +1 @@ -# Scribblenauts Unlimited (EUR) - -[General] -loadSharedLibraries = true - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Scribblenauts Unlimited (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001010ac00.ini b/bin/gameProfiles/default/000500001010ac00.ini index 7df76e2f..9852538c 100644 --- a/bin/gameProfiles/default/000500001010ac00.ini +++ b/bin/gameProfiles/default/000500001010ac00.ini @@ -1,7 +1 @@ -# Ben 10 Omniverse (USA) - -[General] -loadSharedLibraries = false - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Ben 10 Omniverse (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001010b100.ini b/bin/gameProfiles/default/000500001010b100.ini index e2215587..0741b39f 100644 --- a/bin/gameProfiles/default/000500001010b100.ini +++ b/bin/gameProfiles/default/000500001010b100.ini @@ -1,4 +1 @@ -# Rayman Legends (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Rayman Legends (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001010b200.ini b/bin/gameProfiles/default/000500001010b200.ini index d47b4060..22d51c39 100644 --- a/bin/gameProfiles/default/000500001010b200.ini +++ b/bin/gameProfiles/default/000500001010b200.ini @@ -1,7 +1 @@ -# Scribblenauts Unlimited (US) - -[General] -loadSharedLibraries = true - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Scribblenauts Unlimited (US) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001010dc00.ini b/bin/gameProfiles/default/000500001010dc00.ini index 11d0fd68..ffed75ea 100644 --- a/bin/gameProfiles/default/000500001010dc00.ini +++ b/bin/gameProfiles/default/000500001010dc00.ini @@ -1,4 +1 @@ -# Mass Effect 3 Special Edition (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Mass Effect 3 Special Edition (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001010dd00.ini b/bin/gameProfiles/default/000500001010dd00.ini index 5a0620da..2e74d7e0 100644 --- a/bin/gameProfiles/default/000500001010dd00.ini +++ b/bin/gameProfiles/default/000500001010dd00.ini @@ -1,4 +1 @@ -# ZombiU (US) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# ZombiU (US) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001010e600.ini b/bin/gameProfiles/default/000500001010e600.ini index dc351608..01dc0e7a 100644 --- a/bin/gameProfiles/default/000500001010e600.ini +++ b/bin/gameProfiles/default/000500001010e600.ini @@ -1,4 +1 @@ -# 007 Legends (US) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# 007 Legends (US) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001010e700.ini b/bin/gameProfiles/default/000500001010e700.ini index ea118074..a926c9d4 100644 --- a/bin/gameProfiles/default/000500001010e700.ini +++ b/bin/gameProfiles/default/000500001010e700.ini @@ -1,7 +1 @@ -# Cabela's Dangerous Hunts 2013 (USA) - -[General] -loadSharedLibraries = false - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Cabela's Dangerous Hunts 2013 (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001010ec00.ini b/bin/gameProfiles/default/000500001010ec00.ini index babaf249..697aace3 100644 --- a/bin/gameProfiles/default/000500001010ec00.ini +++ b/bin/gameProfiles/default/000500001010ec00.ini @@ -2,6 +2,3 @@ [CPU] cpuMode = Singlecore-Recompiler - -[Graphics] -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001010ed00.ini b/bin/gameProfiles/default/000500001010ed00.ini index 7935ea37..23e1dffc 100644 --- a/bin/gameProfiles/default/000500001010ed00.ini +++ b/bin/gameProfiles/default/000500001010ed00.ini @@ -2,6 +2,3 @@ [CPU] cpuMode = Singlecore-Recompiler - -[Graphics] -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001010ef00.ini b/bin/gameProfiles/default/000500001010ef00.ini index 005ba84b..dbadd2b1 100644 --- a/bin/gameProfiles/default/000500001010ef00.ini +++ b/bin/gameProfiles/default/000500001010ef00.ini @@ -1,4 +1 @@ -# ZombiU (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# ZombiU (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001010f100.ini b/bin/gameProfiles/default/000500001010f100.ini index c557498c..025e3e70 100644 --- a/bin/gameProfiles/default/000500001010f100.ini +++ b/bin/gameProfiles/default/000500001010f100.ini @@ -1,7 +1 @@ -# Rise of the Guardians: The Video Game (EUR) - -[General] -loadSharedLibraries = false - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Rise of the Guardians: The Video Game (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001010f200.ini b/bin/gameProfiles/default/000500001010f200.ini index 055fda32..507b374b 100644 --- a/bin/gameProfiles/default/000500001010f200.ini +++ b/bin/gameProfiles/default/000500001010f200.ini @@ -1,7 +1 @@ -# Rise of the Guardians: The Video Game (USA) - -[General] -loadSharedLibraries = false - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Rise of the Guardians: The Video Game (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001010f500.ini b/bin/gameProfiles/default/000500001010f500.ini index e1a86417..e598f517 100644 --- a/bin/gameProfiles/default/000500001010f500.ini +++ b/bin/gameProfiles/default/000500001010f500.ini @@ -1,4 +1 @@ -# Mass Effect 3 Special Edition (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Mass Effect 3 Special Edition (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001011000.ini b/bin/gameProfiles/default/000500001011000.ini index 85ebf4c6..8c34cbab 100644 --- a/bin/gameProfiles/default/000500001011000.ini +++ b/bin/gameProfiles/default/000500001011000.ini @@ -1,7 +1 @@ -# Ben 10 Omniverse (EUR) - -[General] -loadSharedLibraries = false - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Ben 10 Omniverse (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010110100.ini b/bin/gameProfiles/default/0005000010110100.ini index 69b988cf..d46267b0 100644 --- a/bin/gameProfiles/default/0005000010110100.ini +++ b/bin/gameProfiles/default/0005000010110100.ini @@ -1,4 +1 @@ -# Nano Assault Neo (USA) - -[Graphics] -GPUBufferCacheAccuracy = 2 \ No newline at end of file +# Nano Assault Neo (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010110600.ini b/bin/gameProfiles/default/0005000010110600.ini index fb19b607..bc0a0552 100644 --- a/bin/gameProfiles/default/0005000010110600.ini +++ b/bin/gameProfiles/default/0005000010110600.ini @@ -1,4 +1 @@ -# Nano Assault Neo (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 2 \ No newline at end of file +# Nano Assault Neo (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010110700.ini b/bin/gameProfiles/default/0005000010110700.ini index e2d361c4..4e88d406 100644 --- a/bin/gameProfiles/default/0005000010110700.ini +++ b/bin/gameProfiles/default/0005000010110700.ini @@ -1,4 +1 @@ -# 007 Legends (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# 007 Legends (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010110E00.ini b/bin/gameProfiles/default/0005000010110E00.ini index 3064350f..6c56910f 100644 --- a/bin/gameProfiles/default/0005000010110E00.ini +++ b/bin/gameProfiles/default/0005000010110E00.ini @@ -5,4 +5,3 @@ cpuMode = Singlecore-Recompiler [Graphics] streamoutBufferCacheSize = 48 -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010111400.ini b/bin/gameProfiles/default/0005000010111400.ini index 7fee0ed1..baa50914 100644 --- a/bin/gameProfiles/default/0005000010111400.ini +++ b/bin/gameProfiles/default/0005000010111400.ini @@ -1,4 +1 @@ -# Rayman Legends (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Rayman Legends (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010111600.ini b/bin/gameProfiles/default/0005000010111600.ini index 6b433160..f1c13f22 100644 --- a/bin/gameProfiles/default/0005000010111600.ini +++ b/bin/gameProfiles/default/0005000010111600.ini @@ -1,7 +1 @@ -# Fast And Furious Showdown (USA) - -[General] -loadSharedLibraries = false - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Fast And Furious Showdown (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010112000.ini b/bin/gameProfiles/default/0005000010112000.ini index 853fc36b..3c7710d9 100644 --- a/bin/gameProfiles/default/0005000010112000.ini +++ b/bin/gameProfiles/default/0005000010112000.ini @@ -1,4 +1 @@ -# The Croods: Prehistoric Party! (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# The Croods: Prehistoric Party! (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010112300.ini b/bin/gameProfiles/default/0005000010112300.ini index 5fe50f24..37d8050f 100644 --- a/bin/gameProfiles/default/0005000010112300.ini +++ b/bin/gameProfiles/default/0005000010112300.ini @@ -1,4 +1 @@ -# ZombiU (JPN) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# ZombiU (JPN) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010113000.ini b/bin/gameProfiles/default/0005000010113000.ini index 30330b89..6c023330 100644 --- a/bin/gameProfiles/default/0005000010113000.ini +++ b/bin/gameProfiles/default/0005000010113000.ini @@ -1,4 +1 @@ -# Mass Effect 3 Special Edition (JPN) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Mass Effect 3 Special Edition (JPN) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010113300.ini b/bin/gameProfiles/default/0005000010113300.ini index af55851d..5118d05f 100644 --- a/bin/gameProfiles/default/0005000010113300.ini +++ b/bin/gameProfiles/default/0005000010113300.ini @@ -1,4 +1 @@ -# The Smurfs 2 (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# The Smurfs 2 (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010113d00.ini b/bin/gameProfiles/default/0005000010113d00.ini index 51369705..afb42bf8 100644 --- a/bin/gameProfiles/default/0005000010113d00.ini +++ b/bin/gameProfiles/default/0005000010113d00.ini @@ -1,4 +1 @@ -# Rapala Pro Bass Fishing (US) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Rapala Pro Bass Fishing (US) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010115d00.ini b/bin/gameProfiles/default/0005000010115d00.ini index 5a00554b..84bb9fad 100644 --- a/bin/gameProfiles/default/0005000010115d00.ini +++ b/bin/gameProfiles/default/0005000010115d00.ini @@ -1,4 +1 @@ -# The Smurfs 2 (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# The Smurfs 2 (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010116100.ini b/bin/gameProfiles/default/0005000010116100.ini index 8f01e5fa..465d3dc3 100644 --- a/bin/gameProfiles/default/0005000010116100.ini +++ b/bin/gameProfiles/default/0005000010116100.ini @@ -5,4 +5,3 @@ cpuMode = Singlecore-Recompiler [Graphics] accurateShaderMul = false -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010117200.ini b/bin/gameProfiles/default/0005000010117200.ini index e802c38e..4000f79f 100644 --- a/bin/gameProfiles/default/0005000010117200.ini +++ b/bin/gameProfiles/default/0005000010117200.ini @@ -1,5 +1,4 @@ # Monster Hunter 3 Ultimate (EUR) [Graphics] -GPUBufferCacheAccuracy = 1 streamoutBufferCacheSize = 48 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010118300.ini b/bin/gameProfiles/default/0005000010118300.ini index 4ba8d937..e69a6b95 100644 --- a/bin/gameProfiles/default/0005000010118300.ini +++ b/bin/gameProfiles/default/0005000010118300.ini @@ -1,5 +1,4 @@ # Monster Hunter 3 Ultimate (USA) [Graphics] -GPUBufferCacheAccuracy = 1 streamoutBufferCacheSize = 48 \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001011a600.ini b/bin/gameProfiles/default/000500001011a600.ini index 7c673d05..454aaf08 100644 --- a/bin/gameProfiles/default/000500001011a600.ini +++ b/bin/gameProfiles/default/000500001011a600.ini @@ -1,7 +1 @@ -# Cabela's Dangerous Hunts 2013 (EUR) - -[General] -loadSharedLibraries = false - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Cabela's Dangerous Hunts 2013 (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001011af00.ini b/bin/gameProfiles/default/000500001011af00.ini index c7bc4826..73c8f567 100644 --- a/bin/gameProfiles/default/000500001011af00.ini +++ b/bin/gameProfiles/default/000500001011af00.ini @@ -1,4 +1 @@ -# BIT.TRIP Presents... Runner2: Future Legend of Rhythm Alien (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# BIT.TRIP Presents... Runner2: Future Legend of Rhythm Alien (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001011b200.ini b/bin/gameProfiles/default/000500001011b200.ini index 64edf0af..4a576434 100644 --- a/bin/gameProfiles/default/000500001011b200.ini +++ b/bin/gameProfiles/default/000500001011b200.ini @@ -1,7 +1,4 @@ # Little Inferno (US) -[CPU] -extendedTextureReadback = true - [Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +extendedTextureReadback = true \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010128600.ini b/bin/gameProfiles/default/0005000010128600.ini index 467f0014..cd6c1b18 100644 --- a/bin/gameProfiles/default/0005000010128600.ini +++ b/bin/gameProfiles/default/0005000010128600.ini @@ -1,5 +1,4 @@ # Little Inferno (EUR) -[CPU] +[Graphics] extendedTextureReadback = true -GPUBufferCacheAccuracy = 0 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010129000.ini b/bin/gameProfiles/default/0005000010129000.ini index e1a9d9c5..93403c3c 100644 --- a/bin/gameProfiles/default/0005000010129000.ini +++ b/bin/gameProfiles/default/0005000010129000.ini @@ -4,5 +4,4 @@ cpuMode = Singlecore-Recompiler [Graphics] -GPUBufferCacheAccuracy = 0 streamoutBufferCacheSize = 48 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010129200.ini b/bin/gameProfiles/default/0005000010129200.ini index 33c2f21a..8705ce00 100644 --- a/bin/gameProfiles/default/0005000010129200.ini +++ b/bin/gameProfiles/default/0005000010129200.ini @@ -4,5 +4,4 @@ cpuMode = Singlecore-Recompiler [Graphics] -GPUBufferCacheAccuracy = 0 streamoutBufferCacheSize = 48 \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001012BC00.ini b/bin/gameProfiles/default/000500001012BC00.ini index c7adb846..0b4f6b6d 100644 --- a/bin/gameProfiles/default/000500001012BC00.ini +++ b/bin/gameProfiles/default/000500001012BC00.ini @@ -1,8 +1,4 @@ # Pikmin 3 (JPN) - - [Graphics] - extendedTextureReadback = true -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001012BD00.ini b/bin/gameProfiles/default/000500001012BD00.ini index 8ab10175..cd27ff77 100644 --- a/bin/gameProfiles/default/000500001012BD00.ini +++ b/bin/gameProfiles/default/000500001012BD00.ini @@ -1,8 +1,4 @@ # Pikmin 3 (USA) - - [Graphics] - extendedTextureReadback = true -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001012F000.ini b/bin/gameProfiles/default/000500001012F000.ini index af1229e6..6b1a3f95 100644 --- a/bin/gameProfiles/default/000500001012F000.ini +++ b/bin/gameProfiles/default/000500001012F000.ini @@ -2,6 +2,3 @@ [CPU] cpuMode = Singlecore-Recompiler - -[Graphics] -GPUBufferCacheAccuracy = 1 \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001012b200.ini b/bin/gameProfiles/default/000500001012b200.ini index b00c1a04..9f3733ff 100644 --- a/bin/gameProfiles/default/000500001012b200.ini +++ b/bin/gameProfiles/default/000500001012b200.ini @@ -4,6 +4,5 @@ loadSharedLibraries = false [Graphics] -GPUBufferCacheAccuracy = 0 extendedTextureReadback = true streamoutBufferCacheSize = 48 \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001012ba00.ini b/bin/gameProfiles/default/000500001012ba00.ini index 7e94a7fd..efab660e 100644 --- a/bin/gameProfiles/default/000500001012ba00.ini +++ b/bin/gameProfiles/default/000500001012ba00.ini @@ -4,6 +4,5 @@ loadSharedLibraries = false [Graphics] -GPUBufferCacheAccuracy = 0 extendedTextureReadback = true streamoutBufferCacheSize = 48 \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001012be00.ini b/bin/gameProfiles/default/000500001012be00.ini index 2987dd1e..64a38b0d 100644 --- a/bin/gameProfiles/default/000500001012be00.ini +++ b/bin/gameProfiles/default/000500001012be00.ini @@ -1,7 +1,4 @@ # Pikmin 3 (EU) - - [Graphics] - extendedTextureReadback = true \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001012c500.ini b/bin/gameProfiles/default/000500001012c500.ini index 9badd49e..e4cc6690 100644 --- a/bin/gameProfiles/default/000500001012c500.ini +++ b/bin/gameProfiles/default/000500001012c500.ini @@ -1,4 +1 @@ -# The Croods: Prehistoric Party! (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# The Croods: Prehistoric Party! (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001012da00.ini b/bin/gameProfiles/default/000500001012da00.ini index 4f478ace..8812e297 100644 --- a/bin/gameProfiles/default/000500001012da00.ini +++ b/bin/gameProfiles/default/000500001012da00.ini @@ -1,7 +1 @@ -# Fast And Furious Showdown (EUR) - -[General] -loadSharedLibraries = false - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Fast And Furious Showdown (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010131F00.ini b/bin/gameProfiles/default/0005000010131F00.ini index 9b2dde57..b484aa82 100644 --- a/bin/gameProfiles/default/0005000010131F00.ini +++ b/bin/gameProfiles/default/0005000010131F00.ini @@ -1,4 +1 @@ -# Yoshi's Woolly World (JPN) - -[Graphics] -GPUBufferCacheAccuracy = 2 \ No newline at end of file +# Yoshi's Woolly World (JPN) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010132c00.ini b/bin/gameProfiles/default/0005000010132c00.ini index 00e83b4c..ff554b6e 100644 --- a/bin/gameProfiles/default/0005000010132c00.ini +++ b/bin/gameProfiles/default/0005000010132c00.ini @@ -1,4 +1 @@ -# Scribblenauts Unmasked: A DC Comics Adventure (US) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Scribblenauts Unmasked: A DC Comics Adventure (US) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010132d00.ini b/bin/gameProfiles/default/0005000010132d00.ini index d516cd8f..3655b674 100644 --- a/bin/gameProfiles/default/0005000010132d00.ini +++ b/bin/gameProfiles/default/0005000010132d00.ini @@ -1,4 +1 @@ -# Scribblenauts Unmasked: A DC Comics Adventure (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Scribblenauts Unmasked: A DC Comics Adventure (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010133b00.ini b/bin/gameProfiles/default/0005000010133b00.ini index 465ef364..ae2c8a48 100644 --- a/bin/gameProfiles/default/0005000010133b00.ini +++ b/bin/gameProfiles/default/0005000010133b00.ini @@ -1,4 +1 @@ -# Sniper Elite V2 (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 1 \ No newline at end of file +# Sniper Elite V2 (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010134e00.ini b/bin/gameProfiles/default/0005000010134e00.ini index e04cdd74..99ff8b41 100644 --- a/bin/gameProfiles/default/0005000010134e00.ini +++ b/bin/gameProfiles/default/0005000010134e00.ini @@ -1,4 +1 @@ -# Sniper Elite V2 (USA) - -[Graphics] -GPUBufferCacheAccuracy = 1 \ No newline at end of file +# Sniper Elite V2 (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010135500.ini b/bin/gameProfiles/default/0005000010135500.ini index cc008930..71f1c152 100644 --- a/bin/gameProfiles/default/0005000010135500.ini +++ b/bin/gameProfiles/default/0005000010135500.ini @@ -1,7 +1 @@ -# LEGO Batman 2: DC Super Heroes (EUR) - -[General] -loadSharedLibraries = false - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# LEGO Batman 2: DC Super Heroes (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010135e00.ini b/bin/gameProfiles/default/0005000010135e00.ini index ea135acc..1319dfe5 100644 --- a/bin/gameProfiles/default/0005000010135e00.ini +++ b/bin/gameProfiles/default/0005000010135e00.ini @@ -1,5 +1 @@ -# LEGO Batman 2: DC Super Heroes (USA) - -[General] -loadSharedLibraries = false -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# LEGO Batman 2: DC Super Heroes (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010136300.ini b/bin/gameProfiles/default/0005000010136300.ini index 19e3176f..b37053e0 100644 --- a/bin/gameProfiles/default/0005000010136300.ini +++ b/bin/gameProfiles/default/0005000010136300.ini @@ -1,4 +1 @@ -# BIT.TRIP Presents... Runner2: Future Legend of Rhythm Alien (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# BIT.TRIP Presents... Runner2: Future Legend of Rhythm Alien (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010136c00.ini b/bin/gameProfiles/default/0005000010136c00.ini index 8d9d5448..427a45fe 100644 --- a/bin/gameProfiles/default/0005000010136c00.ini +++ b/bin/gameProfiles/default/0005000010136c00.ini @@ -4,5 +4,4 @@ cpuMode = Singlecore-Recompiler [Graphics] -GPUBufferCacheAccuracy = 0 streamoutBufferCacheSize = 48 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010137F00.ini b/bin/gameProfiles/default/0005000010137F00.ini index c9cddecc..151d5394 100644 --- a/bin/gameProfiles/default/0005000010137F00.ini +++ b/bin/gameProfiles/default/0005000010137F00.ini @@ -1,4 +1 @@ -# DKC: Tropical Freeze (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# DKC: Tropical Freeze (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010137c00.ini b/bin/gameProfiles/default/0005000010137c00.ini index db36989b..88fff6e7 100644 --- a/bin/gameProfiles/default/0005000010137c00.ini +++ b/bin/gameProfiles/default/0005000010137c00.ini @@ -4,5 +4,4 @@ cpuMode = Singlecore-Recompiler [Graphics] -GPUBufferCacheAccuracy = 0 streamoutBufferCacheSize = 48 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010138300.ini b/bin/gameProfiles/default/0005000010138300.ini index 4385df2e..d992cb24 100644 --- a/bin/gameProfiles/default/0005000010138300.ini +++ b/bin/gameProfiles/default/0005000010138300.ini @@ -1,4 +1 @@ -# DKC: Tropical Freeze (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# DKC: Tropical Freeze (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010138a00.ini b/bin/gameProfiles/default/0005000010138a00.ini index 8a4f621e..26318009 100644 --- a/bin/gameProfiles/default/0005000010138a00.ini +++ b/bin/gameProfiles/default/0005000010138a00.ini @@ -1,4 +1 @@ -# Angry Birds Trilogy (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Angry Birds Trilogy (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010138f00.ini b/bin/gameProfiles/default/0005000010138f00.ini index df1f79ea..1f46d11d 100644 --- a/bin/gameProfiles/default/0005000010138f00.ini +++ b/bin/gameProfiles/default/0005000010138f00.ini @@ -1,5 +1,4 @@ # Devil's Third (JPN) [Graphics] -GPUBufferCacheAccuracy = 0 streamoutBufferCacheSize = 48 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010140000.ini b/bin/gameProfiles/default/0005000010140000.ini index 9afff7f4..347a4481 100644 --- a/bin/gameProfiles/default/0005000010140000.ini +++ b/bin/gameProfiles/default/0005000010140000.ini @@ -1,4 +1 @@ -# Angry Birds Trilogy (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Angry Birds Trilogy (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010143200.ini b/bin/gameProfiles/default/0005000010143200.ini new file mode 100644 index 00000000..7085a558 --- /dev/null +++ b/bin/gameProfiles/default/0005000010143200.ini @@ -0,0 +1 @@ +# Cabela's Big Game Hunter: Pro Hunts (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010143500.ini b/bin/gameProfiles/default/0005000010143500.ini index a9266121..c8b93998 100644 --- a/bin/gameProfiles/default/0005000010143500.ini +++ b/bin/gameProfiles/default/0005000010143500.ini @@ -1,6 +1 @@ -# TLoZ: Wind Waker HD (USA) - -[CPU] - -[Graphics] -GPUBufferCacheAccuracy = 2 \ No newline at end of file +# TLoZ: Wind Waker HD (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010143600.ini b/bin/gameProfiles/default/0005000010143600.ini index 6ba5a4c0..6414c25d 100644 --- a/bin/gameProfiles/default/0005000010143600.ini +++ b/bin/gameProfiles/default/0005000010143600.ini @@ -1,6 +1 @@ -# TLoZ: Wind Waker HD (EUR) - -[CPU] - -[Graphics] -GPUBufferCacheAccuracy = 2 \ No newline at end of file +# TLoZ: Wind Waker HD (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010144800.ini b/bin/gameProfiles/default/0005000010144800.ini index 55a4e962..80721cd0 100644 --- a/bin/gameProfiles/default/0005000010144800.ini +++ b/bin/gameProfiles/default/0005000010144800.ini @@ -1,4 +1 @@ -# DKC: Tropical Freeze (JPN) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# DKC: Tropical Freeze (JPN) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010144f00.ini b/bin/gameProfiles/default/0005000010144f00.ini index 6bbc9e28..c2fce59a 100644 --- a/bin/gameProfiles/default/0005000010144f00.ini +++ b/bin/gameProfiles/default/0005000010144f00.ini @@ -5,4 +5,3 @@ cpuMode = Singlecore-Recompiler [Graphics] streamoutBufferCacheSize = 48 -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010145000.ini b/bin/gameProfiles/default/0005000010145000.ini index f286269f..ace52dba 100644 --- a/bin/gameProfiles/default/0005000010145000.ini +++ b/bin/gameProfiles/default/0005000010145000.ini @@ -5,4 +5,3 @@ cpuMode = Singlecore-Recompiler [Graphics] streamoutBufferCacheSize = 48 -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010145c00.ini b/bin/gameProfiles/default/0005000010145c00.ini index b82867e4..0be65fcc 100644 --- a/bin/gameProfiles/default/0005000010145c00.ini +++ b/bin/gameProfiles/default/0005000010145c00.ini @@ -1,7 +1,4 @@ # Super Mario 3D World (USA) -[CPU] - [Graphics] -accurateShaderMul = false -GPUBufferCacheAccuracy = 2 \ No newline at end of file +accurateShaderMul = false \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010145d00.ini b/bin/gameProfiles/default/0005000010145d00.ini index 99c70dfe..3525edd8 100644 --- a/bin/gameProfiles/default/0005000010145d00.ini +++ b/bin/gameProfiles/default/0005000010145d00.ini @@ -1,7 +1,4 @@ # Super Mario 3D World (EUR) -[CPU] - [Graphics] -accurateShaderMul = false -GPUBufferCacheAccuracy = 2 \ No newline at end of file +accurateShaderMul = false \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010147e00.ini b/bin/gameProfiles/default/0005000010147e00.ini index f5f73b9c..62109cc9 100644 --- a/bin/gameProfiles/default/0005000010147e00.ini +++ b/bin/gameProfiles/default/0005000010147e00.ini @@ -1,4 +1 @@ -# Hello Kitty Kruisers (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Hello Kitty Kruisers (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001014c100.ini b/bin/gameProfiles/default/000500001014c100.ini index da5f4d3a..64add666 100644 --- a/bin/gameProfiles/default/000500001014c100.ini +++ b/bin/gameProfiles/default/000500001014c100.ini @@ -1,4 +1 @@ -# Transformers: Rise of the Dark Spark (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Transformers: Rise of the Dark Spark (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001014c600.ini b/bin/gameProfiles/default/000500001014c600.ini index d83ea1d8..ff80f137 100644 --- a/bin/gameProfiles/default/000500001014c600.ini +++ b/bin/gameProfiles/default/000500001014c600.ini @@ -1,4 +1 @@ -# Giana Sisters Twisted Dreams (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Giana Sisters Twisted Dreams (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001014cb00.ini b/bin/gameProfiles/default/000500001014cb00.ini index 24e6e8d9..3ab3829e 100644 --- a/bin/gameProfiles/default/000500001014cb00.ini +++ b/bin/gameProfiles/default/000500001014cb00.ini @@ -1,4 +1 @@ -# Giana Sisters Twisted Dreams (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Giana Sisters Twisted Dreams (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001014d900.ini b/bin/gameProfiles/default/000500001014d900.ini index b3fae051..d234ff8a 100644 --- a/bin/gameProfiles/default/000500001014d900.ini +++ b/bin/gameProfiles/default/000500001014d900.ini @@ -1,4 +1 @@ -# PUYO PUYO TETRIS (JPN) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# PUYO PUYO TETRIS (JPN) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010154600.ini b/bin/gameProfiles/default/0005000010154600.ini index 8bdd8b98..d5af6dc0 100644 --- a/bin/gameProfiles/default/0005000010154600.ini +++ b/bin/gameProfiles/default/0005000010154600.ini @@ -4,5 +4,4 @@ cpuMode = Singlecore-Recompiler [Graphics] -GPUBufferCacheAccuracy = 0 streamoutBufferCacheSize = 48 \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001015b200.ini b/bin/gameProfiles/default/000500001015b200.ini index fd7aad16..a3872d6f 100644 --- a/bin/gameProfiles/default/000500001015b200.ini +++ b/bin/gameProfiles/default/000500001015b200.ini @@ -1,4 +1 @@ -# Child of Light (USA) - -[Graphics] -GPUBufferCacheAccuracy = 1 \ No newline at end of file +# Child of Light (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010161a00.ini b/bin/gameProfiles/default/0005000010161a00.ini index 8bd28664..88e02890 100644 --- a/bin/gameProfiles/default/0005000010161a00.ini +++ b/bin/gameProfiles/default/0005000010161a00.ini @@ -1,4 +1 @@ -# How to Train Your Dragon 2 (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# How to Train Your Dragon 2 (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010162200.ini b/bin/gameProfiles/default/0005000010162200.ini index 70e5edf4..66363dfe 100644 --- a/bin/gameProfiles/default/0005000010162200.ini +++ b/bin/gameProfiles/default/0005000010162200.ini @@ -1,4 +1 @@ -# Monkey Pirates (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Monkey Pirates (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010162a00.ini b/bin/gameProfiles/default/0005000010162a00.ini index e20011d0..3df87ebb 100644 --- a/bin/gameProfiles/default/0005000010162a00.ini +++ b/bin/gameProfiles/default/0005000010162a00.ini @@ -1,4 +1 @@ -# How to Train Your Dragon 2 (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# How to Train Your Dragon 2 (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010162b00.ini b/bin/gameProfiles/default/0005000010162b00.ini index 9802ca1d..8a50f6f6 100644 --- a/bin/gameProfiles/default/0005000010162b00.ini +++ b/bin/gameProfiles/default/0005000010162b00.ini @@ -1,6 +1 @@ -# Splatoon (JPN) - -[CPU] - -[Graphics] -GPUBufferCacheAccuracy = 2 \ No newline at end of file +# Splatoon (JPN) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001016a200.ini b/bin/gameProfiles/default/000500001016a200.ini index da6a7805..655bc980 100644 --- a/bin/gameProfiles/default/000500001016a200.ini +++ b/bin/gameProfiles/default/000500001016a200.ini @@ -1,4 +1 @@ -# Bombing Bastards (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Bombing Bastards (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001016ab00.ini b/bin/gameProfiles/default/000500001016ab00.ini index 0090ca58..594d783c 100644 --- a/bin/gameProfiles/default/000500001016ab00.ini +++ b/bin/gameProfiles/default/000500001016ab00.ini @@ -1,4 +1 @@ -# Bombing Bastards (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Bombing Bastards (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001016b200.ini b/bin/gameProfiles/default/000500001016b200.ini index 9dd87948..4db2bc79 100644 --- a/bin/gameProfiles/default/000500001016b200.ini +++ b/bin/gameProfiles/default/000500001016b200.ini @@ -6,6 +6,3 @@ useRDTSC = false [CPU] cpuTimer = cycleCounter cpuMode = Singlecore-Interpreter - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001016d400.ini b/bin/gameProfiles/default/000500001016d400.ini index 74f031ec..56c1c0f8 100644 --- a/bin/gameProfiles/default/000500001016d400.ini +++ b/bin/gameProfiles/default/000500001016d400.ini @@ -1,4 +1 @@ -# Stick It to the Man (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Stick It to the Man (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001016d800.ini b/bin/gameProfiles/default/000500001016d800.ini index 3cd2b3d0..6a8a95b8 100644 --- a/bin/gameProfiles/default/000500001016d800.ini +++ b/bin/gameProfiles/default/000500001016d800.ini @@ -1,4 +1 @@ -# Child of Light (JPN) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Child of Light (JPN) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001016da00.ini b/bin/gameProfiles/default/000500001016da00.ini index 105b4406..d54f4f50 100644 --- a/bin/gameProfiles/default/000500001016da00.ini +++ b/bin/gameProfiles/default/000500001016da00.ini @@ -2,6 +2,3 @@ [CPU] cpuMode = Singlecore-Interpreter - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001016e000.ini b/bin/gameProfiles/default/000500001016e000.ini index 871da06e..c9ef12e2 100644 --- a/bin/gameProfiles/default/000500001016e000.ini +++ b/bin/gameProfiles/default/000500001016e000.ini @@ -1,4 +1 @@ -# Stick It to the Man (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Stick It to the Man (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001016e800.ini b/bin/gameProfiles/default/000500001016e800.ini index 13482456..4e578c17 100644 --- a/bin/gameProfiles/default/000500001016e800.ini +++ b/bin/gameProfiles/default/000500001016e800.ini @@ -6,6 +6,3 @@ useRDTSC = false [CPU] cpuTimer = cycleCounter cpuMode = Singlecore-Interpreter - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001016ea00.ini b/bin/gameProfiles/default/000500001016ea00.ini index 6d72c2a2..50723312 100644 --- a/bin/gameProfiles/default/000500001016ea00.ini +++ b/bin/gameProfiles/default/000500001016ea00.ini @@ -1,4 +1 @@ -# Child of Light (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 1 \ No newline at end of file +# Child of Light (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001016fc00.ini b/bin/gameProfiles/default/000500001016fc00.ini index edd44103..9ac3ba4b 100644 --- a/bin/gameProfiles/default/000500001016fc00.ini +++ b/bin/gameProfiles/default/000500001016fc00.ini @@ -1,5 +1,4 @@ # Aqua Moto Racing Utopia (USA) -[GPU] -GPUBufferCacheAccuracy = 0 +[Graphics] streamoutBufferCacheSize = 48 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010170100.ini b/bin/gameProfiles/default/0005000010170100.ini index 848f53ff..3de1dd34 100644 --- a/bin/gameProfiles/default/0005000010170100.ini +++ b/bin/gameProfiles/default/0005000010170100.ini @@ -1,4 +1 @@ -# Monkey Pirates (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Monkey Pirates (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010172900.ini b/bin/gameProfiles/default/0005000010172900.ini index 0414258a..5ed40dd0 100644 --- a/bin/gameProfiles/default/0005000010172900.ini +++ b/bin/gameProfiles/default/0005000010172900.ini @@ -1,5 +1,4 @@ # Aqua Moto Racing Utopia (EUR) -[GPU] -GPUBufferCacheAccuracy = 0 +[Graphics] streamoutBufferCacheSize = 48 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010173400.ini b/bin/gameProfiles/default/0005000010173400.ini index 99995af4..c4062977 100644 --- a/bin/gameProfiles/default/0005000010173400.ini +++ b/bin/gameProfiles/default/0005000010173400.ini @@ -1,4 +1 @@ -# Transformers: Rise of the Dark Spark (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Transformers: Rise of the Dark Spark (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010176300.ini b/bin/gameProfiles/default/0005000010176300.ini index c8e0861c..30305418 100644 --- a/bin/gameProfiles/default/0005000010176300.ini +++ b/bin/gameProfiles/default/0005000010176300.ini @@ -1,5 +1,4 @@ # Little Inferno (JPN) -[CPU] +[Graphics] extendedTextureReadback = true -GPUBufferCacheAccuracy = 0 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010176900.ini b/bin/gameProfiles/default/0005000010176900.ini index d6d36fb8..852c383a 100644 --- a/bin/gameProfiles/default/0005000010176900.ini +++ b/bin/gameProfiles/default/0005000010176900.ini @@ -1,6 +1 @@ -# Splatoon (USA) - -[CPU] - -[Graphics] -GPUBufferCacheAccuracy = 2 \ No newline at end of file +# Splatoon (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010176a00.ini b/bin/gameProfiles/default/0005000010176a00.ini index f52d1658..e1dc631b 100644 --- a/bin/gameProfiles/default/0005000010176a00.ini +++ b/bin/gameProfiles/default/0005000010176a00.ini @@ -1,6 +1 @@ -# Splatoon (EUR) - -[CPU] - -[Graphics] -GPUBufferCacheAccuracy = 2 \ No newline at end of file +# Splatoon (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010177000.ini b/bin/gameProfiles/default/0005000010177000.ini index 1c47b1dc..bc7eebf4 100644 --- a/bin/gameProfiles/default/0005000010177000.ini +++ b/bin/gameProfiles/default/0005000010177000.ini @@ -1,4 +1 @@ -# Hello Kitty Kruisers (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Hello Kitty Kruisers (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010177500.ini b/bin/gameProfiles/default/0005000010177500.ini index 74a0a58e..90be1c63 100644 --- a/bin/gameProfiles/default/0005000010177500.ini +++ b/bin/gameProfiles/default/0005000010177500.ini @@ -2,6 +2,3 @@ [CPU] cpuMode = Singlecore-Interpreter - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010177600.ini b/bin/gameProfiles/default/0005000010177600.ini index 05792513..d1b955f1 100644 --- a/bin/gameProfiles/default/0005000010177600.ini +++ b/bin/gameProfiles/default/0005000010177600.ini @@ -1,5 +1,4 @@ # Devil's Third (USA) [Graphics] -GPUBufferCacheAccuracy = 0 streamoutBufferCacheSize = 48 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010177700.ini b/bin/gameProfiles/default/0005000010177700.ini index 072db711..e311f0ba 100644 --- a/bin/gameProfiles/default/0005000010177700.ini +++ b/bin/gameProfiles/default/0005000010177700.ini @@ -1,5 +1,4 @@ # Devil's Third (EUR) [Graphics] -GPUBufferCacheAccuracy = 0 streamoutBufferCacheSize = 48 \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001017da00.ini b/bin/gameProfiles/default/000500001017da00.ini index 6fc7282a..09b07927 100644 --- a/bin/gameProfiles/default/000500001017da00.ini +++ b/bin/gameProfiles/default/000500001017da00.ini @@ -1,4 +1 @@ -# Costume Quest 2 (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Costume Quest 2 (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001017e400.ini b/bin/gameProfiles/default/000500001017e400.ini index 562aa77a..723a374c 100644 --- a/bin/gameProfiles/default/000500001017e400.ini +++ b/bin/gameProfiles/default/000500001017e400.ini @@ -1,4 +1 @@ -# Chasing Dead (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 +# Chasing Dead (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010180500.ini b/bin/gameProfiles/default/0005000010180500.ini index d9258613..87ea51b5 100644 --- a/bin/gameProfiles/default/0005000010180500.ini +++ b/bin/gameProfiles/default/0005000010180500.ini @@ -1,7 +1,4 @@ # Captain Toad: Treasure Tracker (JPN) -[CPU] - [Graphics] accurateShaderMul = false -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010180600.ini b/bin/gameProfiles/default/0005000010180600.ini index e7d7e325..8b2528e3 100644 --- a/bin/gameProfiles/default/0005000010180600.ini +++ b/bin/gameProfiles/default/0005000010180600.ini @@ -1,7 +1,4 @@ # Captain Toad: Treasure Tracker (USA) -[CPU] - [Graphics] accurateShaderMul = false -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010180700.ini b/bin/gameProfiles/default/0005000010180700.ini index d0b7d792..3a7a8fe6 100644 --- a/bin/gameProfiles/default/0005000010180700.ini +++ b/bin/gameProfiles/default/0005000010180700.ini @@ -1,7 +1,4 @@ # Captain Toad: Treasure Tracker (EUR) -[CPU] - [Graphics] accurateShaderMul = false -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010183000.ini b/bin/gameProfiles/default/0005000010183000.ini index 5b79f8a9..4b1c07cf 100644 --- a/bin/gameProfiles/default/0005000010183000.ini +++ b/bin/gameProfiles/default/0005000010183000.ini @@ -1,7 +1 @@ -# Runbow (USA) - - - -[Graphics] - -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Runbow (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010184900.ini b/bin/gameProfiles/default/0005000010184900.ini index 8c4d5b59..c3aac8e8 100644 --- a/bin/gameProfiles/default/0005000010184900.ini +++ b/bin/gameProfiles/default/0005000010184900.ini @@ -1,4 +1 @@ -# Slender: The Arrival (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Slender: The Arrival (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010184E00.ini b/bin/gameProfiles/default/0005000010184E00.ini index 3ca24eda..4b282824 100644 --- a/bin/gameProfiles/default/0005000010184E00.ini +++ b/bin/gameProfiles/default/0005000010184E00.ini @@ -1,6 +1 @@ -# Yoshi's Woolly World (EUR) - -[CPU] - -[Graphics] -GPUBufferCacheAccuracy = 2 \ No newline at end of file +# Yoshi's Woolly World (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010184d00.ini b/bin/gameProfiles/default/0005000010184d00.ini index 83a64a5e..b1bb76a7 100644 --- a/bin/gameProfiles/default/0005000010184d00.ini +++ b/bin/gameProfiles/default/0005000010184d00.ini @@ -1,6 +1 @@ -# Yoshi's Woolly World (USA) - -[CPU] - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Yoshi's Woolly World (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010185400.ini b/bin/gameProfiles/default/0005000010185400.ini index c69416ee..efd26cbb 100644 --- a/bin/gameProfiles/default/0005000010185400.ini +++ b/bin/gameProfiles/default/0005000010185400.ini @@ -1,6 +1 @@ -# TLoZ: Wind Waker HD (JPN) - -[CPU] - -[Graphics] -GPUBufferCacheAccuracy = 2 \ No newline at end of file +# TLoZ: Wind Waker HD (JPN) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010189f00.ini b/bin/gameProfiles/default/0005000010189f00.ini index a88c6891..ae998084 100644 --- a/bin/gameProfiles/default/0005000010189f00.ini +++ b/bin/gameProfiles/default/0005000010189f00.ini @@ -2,6 +2,3 @@ [CPU] cpuTimer = hostBased - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001018ab00.ini b/bin/gameProfiles/default/000500001018ab00.ini index 9eeef2ff..cd66a1a3 100644 --- a/bin/gameProfiles/default/000500001018ab00.ini +++ b/bin/gameProfiles/default/000500001018ab00.ini @@ -1,4 +1 @@ -# Affordable Space Adventures (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 +# Affordable Space Adventures (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001018d800.ini b/bin/gameProfiles/default/000500001018d800.ini index 0e311245..196b4aba 100644 --- a/bin/gameProfiles/default/000500001018d800.ini +++ b/bin/gameProfiles/default/000500001018d800.ini @@ -1,4 +1 @@ -# SteamWorld Dig (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# SteamWorld Dig (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001018d900.ini b/bin/gameProfiles/default/000500001018d900.ini index 4e8d8ead..779035e1 100644 --- a/bin/gameProfiles/default/000500001018d900.ini +++ b/bin/gameProfiles/default/000500001018d900.ini @@ -2,6 +2,3 @@ [General] loadSharedLibraries = false - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001018f100.ini b/bin/gameProfiles/default/000500001018f100.ini index 1a3c5fba..46ffcf0e 100644 --- a/bin/gameProfiles/default/000500001018f100.ini +++ b/bin/gameProfiles/default/000500001018f100.ini @@ -1,4 +1 @@ -# SteamWorld Dig (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# SteamWorld Dig (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001018f400.ini b/bin/gameProfiles/default/000500001018f400.ini index ecf014cf..a22052e4 100644 --- a/bin/gameProfiles/default/000500001018f400.ini +++ b/bin/gameProfiles/default/000500001018f400.ini @@ -1,4 +1 @@ -# Angry Video Game Nerd Adventures (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Angry Video Game Nerd Adventures (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001018fd00.ini b/bin/gameProfiles/default/000500001018fd00.ini index 119e00c4..eaef68fe 100644 --- a/bin/gameProfiles/default/000500001018fd00.ini +++ b/bin/gameProfiles/default/000500001018fd00.ini @@ -1,4 +1 @@ -# The Penguins of Madagascar (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# The Penguins of Madagascar (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010190300.ini b/bin/gameProfiles/default/0005000010190300.ini index b9a41d4f..fe197295 100644 --- a/bin/gameProfiles/default/0005000010190300.ini +++ b/bin/gameProfiles/default/0005000010190300.ini @@ -5,7 +5,4 @@ useRDTSC = false [CPU] cpuMode = Singlecore-Recompiler -cpuTimer = cycleCounter - -[Graphics] -GPUBufferCacheAccuracy = 1 \ No newline at end of file +cpuTimer = cycleCounter \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010193f00.ini b/bin/gameProfiles/default/0005000010193f00.ini index 9d7f8a63..104a88ca 100644 --- a/bin/gameProfiles/default/0005000010193f00.ini +++ b/bin/gameProfiles/default/0005000010193f00.ini @@ -1,4 +1 @@ -# The Penguins of Madagascar (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# The Penguins of Madagascar (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010195e00.ini b/bin/gameProfiles/default/0005000010195e00.ini index 23d72923..23585737 100644 --- a/bin/gameProfiles/default/0005000010195e00.ini +++ b/bin/gameProfiles/default/0005000010195e00.ini @@ -1,4 +1 @@ -# Rock Zombie (US) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Rock Zombie (US) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010197300.ini b/bin/gameProfiles/default/0005000010197300.ini index e15f1c7d..6f2dfa48 100644 --- a/bin/gameProfiles/default/0005000010197300.ini +++ b/bin/gameProfiles/default/0005000010197300.ini @@ -1,4 +1 @@ -# Electronic Super Joy Groove City (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Electronic Super Joy Groove City (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010197800.ini b/bin/gameProfiles/default/0005000010197800.ini index 92839df2..2af2ef33 100644 --- a/bin/gameProfiles/default/0005000010197800.ini +++ b/bin/gameProfiles/default/0005000010197800.ini @@ -1,4 +1 @@ -# Costume Quest 2 (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Costume Quest 2 (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010199e00.ini b/bin/gameProfiles/default/0005000010199e00.ini index 12c31235..01e1f2d5 100644 --- a/bin/gameProfiles/default/0005000010199e00.ini +++ b/bin/gameProfiles/default/0005000010199e00.ini @@ -1,4 +1 @@ -# High Strangeness (US) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# High Strangeness (US) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001019ab00.ini b/bin/gameProfiles/default/000500001019ab00.ini index 12c31235..01e1f2d5 100644 --- a/bin/gameProfiles/default/000500001019ab00.ini +++ b/bin/gameProfiles/default/000500001019ab00.ini @@ -1,4 +1 @@ -# High Strangeness (US) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# High Strangeness (US) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001019b000.ini b/bin/gameProfiles/default/000500001019b000.ini index 23e77a87..c458883f 100644 --- a/bin/gameProfiles/default/000500001019b000.ini +++ b/bin/gameProfiles/default/000500001019b000.ini @@ -1,4 +1 @@ -# Tachyon Project (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Tachyon Project (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001019b200.ini b/bin/gameProfiles/default/000500001019b200.ini index 6790a314..3e893f12 100644 --- a/bin/gameProfiles/default/000500001019b200.ini +++ b/bin/gameProfiles/default/000500001019b200.ini @@ -1,4 +1 @@ -# Tachyon Project (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Tachyon Project (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001019ca00.ini b/bin/gameProfiles/default/000500001019ca00.ini index 9187ed88..f79dd190 100644 --- a/bin/gameProfiles/default/000500001019ca00.ini +++ b/bin/gameProfiles/default/000500001019ca00.ini @@ -1,4 +1 @@ -# Rock Zombie (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Rock Zombie (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001019e500.ini b/bin/gameProfiles/default/000500001019e500.ini index 0ee89ea1..eec28dc9 100644 --- a/bin/gameProfiles/default/000500001019e500.ini +++ b/bin/gameProfiles/default/000500001019e500.ini @@ -1,6 +1 @@ -# TLoZ: Twilight Princess (US) - -[CPU] - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# TLoZ: Twilight Princess (US) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001019e600.ini b/bin/gameProfiles/default/000500001019e600.ini index 4f3aeecb..47380cbe 100644 --- a/bin/gameProfiles/default/000500001019e600.ini +++ b/bin/gameProfiles/default/000500001019e600.ini @@ -1,6 +1 @@ -# TLoZ: Twilight Princess (EU) - -[CPU] - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# TLoZ: Twilight Princess (EU) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001019ea00.ini b/bin/gameProfiles/default/000500001019ea00.ini index f69c7740..49250a19 100644 --- a/bin/gameProfiles/default/000500001019ea00.ini +++ b/bin/gameProfiles/default/000500001019ea00.ini @@ -1,4 +1 @@ -# Zombeer (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Zombeer (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001019ee00.ini b/bin/gameProfiles/default/000500001019ee00.ini index accf6993..958d2f84 100644 --- a/bin/gameProfiles/default/000500001019ee00.ini +++ b/bin/gameProfiles/default/000500001019ee00.ini @@ -1,4 +1 @@ -# Zombie Defense (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Zombie Defense (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101C9300.ini b/bin/gameProfiles/default/00050000101C9300.ini index f3c94e8f..09f0706d 100644 --- a/bin/gameProfiles/default/00050000101C9300.ini +++ b/bin/gameProfiles/default/00050000101C9300.ini @@ -3,4 +3,3 @@ [Graphics] disableGPUFence = false accurateShaderMul = true -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101E4100.ini b/bin/gameProfiles/default/00050000101E4100.ini index 71176e83..8b8165b1 100644 --- a/bin/gameProfiles/default/00050000101E4100.ini +++ b/bin/gameProfiles/default/00050000101E4100.ini @@ -2,6 +2,3 @@ [CPU] cpuMode = Singlecore-Recompiler - -[Graphics] -GPUBufferCacheAccuracy = 1 \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101a1200.ini b/bin/gameProfiles/default/00050000101a1200.ini index 76b1207a..4ff51799 100644 --- a/bin/gameProfiles/default/00050000101a1200.ini +++ b/bin/gameProfiles/default/00050000101a1200.ini @@ -1,5 +1 @@ -# Affordable Space Adventures (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 - +# Affordable Space Adventures (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101a1800.ini b/bin/gameProfiles/default/00050000101a1800.ini index 94c50e45..56dd856c 100644 --- a/bin/gameProfiles/default/00050000101a1800.ini +++ b/bin/gameProfiles/default/00050000101a1800.ini @@ -1,4 +1 @@ -# Zombie Defense (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Zombie Defense (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101a3b00.ini b/bin/gameProfiles/default/00050000101a3b00.ini index 2efa5016..44ec4fcb 100644 --- a/bin/gameProfiles/default/00050000101a3b00.ini +++ b/bin/gameProfiles/default/00050000101a3b00.ini @@ -1,4 +1 @@ -# Life of Pixel (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Life of Pixel (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101a4300.ini b/bin/gameProfiles/default/00050000101a4300.ini index 1c49a9ab..80c72ed3 100644 --- a/bin/gameProfiles/default/00050000101a4300.ini +++ b/bin/gameProfiles/default/00050000101a4300.ini @@ -1,4 +1 @@ -# Beatbuddy (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Beatbuddy (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101a4800.ini b/bin/gameProfiles/default/00050000101a4800.ini index bb446c86..64e552e9 100644 --- a/bin/gameProfiles/default/00050000101a4800.ini +++ b/bin/gameProfiles/default/00050000101a4800.ini @@ -2,6 +2,3 @@ [General] loadSharedLibraries = false - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101a4900.ini b/bin/gameProfiles/default/00050000101a4900.ini index 74fedef3..3bb54df4 100644 --- a/bin/gameProfiles/default/00050000101a4900.ini +++ b/bin/gameProfiles/default/00050000101a4900.ini @@ -1,4 +1 @@ -# Life of Pixel (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Life of Pixel (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101a7f00.ini b/bin/gameProfiles/default/00050000101a7f00.ini index 35f91d55..be575b52 100644 --- a/bin/gameProfiles/default/00050000101a7f00.ini +++ b/bin/gameProfiles/default/00050000101a7f00.ini @@ -1,4 +1 @@ -# Shiftlings (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Shiftlings (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101a9c00.ini b/bin/gameProfiles/default/00050000101a9c00.ini index 651c8c01..6df3d13a 100644 --- a/bin/gameProfiles/default/00050000101a9c00.ini +++ b/bin/gameProfiles/default/00050000101a9c00.ini @@ -1,4 +1 @@ -# Chompy Chomp Chomp Party (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Chompy Chomp Chomp Party (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101a9e00.ini b/bin/gameProfiles/default/00050000101a9e00.ini index 5fbd157c..a8853119 100644 --- a/bin/gameProfiles/default/00050000101a9e00.ini +++ b/bin/gameProfiles/default/00050000101a9e00.ini @@ -1,4 +1 @@ -# Chompy Chomp Chomp Party (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Chompy Chomp Chomp Party (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101acc00.ini b/bin/gameProfiles/default/00050000101acc00.ini index d94071ac..6dae9607 100644 --- a/bin/gameProfiles/default/00050000101acc00.ini +++ b/bin/gameProfiles/default/00050000101acc00.ini @@ -1,4 +1 @@ -# Shiftlings (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Shiftlings (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101b0100.ini b/bin/gameProfiles/default/00050000101b0100.ini index ed2f7bf9..1599daed 100644 --- a/bin/gameProfiles/default/00050000101b0100.ini +++ b/bin/gameProfiles/default/00050000101b0100.ini @@ -1,4 +1 @@ -# Funk of Titans (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Funk of Titans (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101b4e00.ini b/bin/gameProfiles/default/00050000101b4e00.ini index 38642cbe..a0b90655 100644 --- a/bin/gameProfiles/default/00050000101b4e00.ini +++ b/bin/gameProfiles/default/00050000101b4e00.ini @@ -1,4 +1 @@ -# Funk of Titans (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Funk of Titans (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101b9900.ini b/bin/gameProfiles/default/00050000101b9900.ini index ae410226..2e83fe40 100644 --- a/bin/gameProfiles/default/00050000101b9900.ini +++ b/bin/gameProfiles/default/00050000101b9900.ini @@ -2,6 +2,3 @@ [General] loadSharedLibraries = false - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101bc300.ini b/bin/gameProfiles/default/00050000101bc300.ini index c7ec1da5..e880a0f1 100644 --- a/bin/gameProfiles/default/00050000101bc300.ini +++ b/bin/gameProfiles/default/00050000101bc300.ini @@ -1,4 +1 @@ -# Beatbuddy (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Beatbuddy (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101c3100.ini b/bin/gameProfiles/default/00050000101c3100.ini index d7c2e293..c6f5c569 100644 --- a/bin/gameProfiles/default/00050000101c3100.ini +++ b/bin/gameProfiles/default/00050000101c3100.ini @@ -1,4 +1 @@ -# Freedom Planet (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Freedom Planet (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101c4200.ini b/bin/gameProfiles/default/00050000101c4200.ini index 203ce9a7..c12944a3 100644 --- a/bin/gameProfiles/default/00050000101c4200.ini +++ b/bin/gameProfiles/default/00050000101c4200.ini @@ -1,4 +1 @@ -# The Peanuts Movie: Snoopy's Grand Adventure (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# The Peanuts Movie: Snoopy's Grand Adventure (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101c4300.ini b/bin/gameProfiles/default/00050000101c4300.ini index a2a79f38..549984d4 100644 --- a/bin/gameProfiles/default/00050000101c4300.ini +++ b/bin/gameProfiles/default/00050000101c4300.ini @@ -2,6 +2,3 @@ [CPU] cpuMode = Singlecore-Recompiler - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101c4c00.ini b/bin/gameProfiles/default/00050000101c4c00.ini index 6e8fd71f..08ad5e5b 100644 --- a/bin/gameProfiles/default/00050000101c4c00.ini +++ b/bin/gameProfiles/default/00050000101c4c00.ini @@ -5,4 +5,3 @@ cpuMode = Singlecore-Recompiler [Graphics] accurateShaderMul = false -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101c4d00.ini b/bin/gameProfiles/default/00050000101c4d00.ini index 83301c0d..a1db58b0 100644 --- a/bin/gameProfiles/default/00050000101c4d00.ini +++ b/bin/gameProfiles/default/00050000101c4d00.ini @@ -5,4 +5,3 @@ cpuMode = Singlecore-Recompiler [Graphics] accurateShaderMul = false -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101c6c00.ini b/bin/gameProfiles/default/00050000101c6c00.ini index da9dd712..ff16d037 100644 --- a/bin/gameProfiles/default/00050000101c6c00.ini +++ b/bin/gameProfiles/default/00050000101c6c00.ini @@ -1,4 +1 @@ -# Typoman (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Typoman (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101c7600.ini b/bin/gameProfiles/default/00050000101c7600.ini index 14128c7b..599520e4 100644 --- a/bin/gameProfiles/default/00050000101c7600.ini +++ b/bin/gameProfiles/default/00050000101c7600.ini @@ -1,4 +1 @@ -# Pumped BMX + (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Pumped BMX + (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101c7b00.ini b/bin/gameProfiles/default/00050000101c7b00.ini index 7350d0b5..9b764f96 100644 --- a/bin/gameProfiles/default/00050000101c7b00.ini +++ b/bin/gameProfiles/default/00050000101c7b00.ini @@ -1,4 +1 @@ -# Chronicles of Teddy: Harmony of Exidus (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Chronicles of Teddy: Harmony of Exidus (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101c7d00.ini b/bin/gameProfiles/default/00050000101c7d00.ini index 45ce4448..60a0dd23 100644 --- a/bin/gameProfiles/default/00050000101c7d00.ini +++ b/bin/gameProfiles/default/00050000101c7d00.ini @@ -1,4 +1 @@ -# Pumped BMX + (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Pumped BMX + (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101c9400.ini b/bin/gameProfiles/default/00050000101c9400.ini index 86c1b839..585a9e6f 100644 --- a/bin/gameProfiles/default/00050000101c9400.ini +++ b/bin/gameProfiles/default/00050000101c9400.ini @@ -3,4 +3,3 @@ [Graphics] disableGPUFence = false accurateShaderMul = true -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101c9500.ini b/bin/gameProfiles/default/00050000101c9500.ini index 4f326b32..71485002 100644 --- a/bin/gameProfiles/default/00050000101c9500.ini +++ b/bin/gameProfiles/default/00050000101c9500.ini @@ -3,4 +3,3 @@ [Graphics] disableGPUFence = false accurateShaderMul = true -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101c9a00.ini b/bin/gameProfiles/default/00050000101c9a00.ini index 0cc7525b..026e593f 100644 --- a/bin/gameProfiles/default/00050000101c9a00.ini +++ b/bin/gameProfiles/default/00050000101c9a00.ini @@ -2,6 +2,3 @@ [CPU] cpuMode = Singlecore-Recompiler - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101cc900.ini b/bin/gameProfiles/default/00050000101cc900.ini index dded48b2..ceaa7e67 100644 --- a/bin/gameProfiles/default/00050000101cc900.ini +++ b/bin/gameProfiles/default/00050000101cc900.ini @@ -1,4 +1 @@ -# Freedom Planet (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Freedom Planet (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101ccf00.ini b/bin/gameProfiles/default/00050000101ccf00.ini index e71dfd8f..d3d497f1 100644 --- a/bin/gameProfiles/default/00050000101ccf00.ini +++ b/bin/gameProfiles/default/00050000101ccf00.ini @@ -1,4 +1 @@ -# Never Alone (Kisima Ingitchuna) (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Never Alone (Kisima Ingitchuna) (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101ce000.ini b/bin/gameProfiles/default/00050000101ce000.ini index 69aa9dae..30a271e8 100644 --- a/bin/gameProfiles/default/00050000101ce000.ini +++ b/bin/gameProfiles/default/00050000101ce000.ini @@ -1,4 +1 @@ -# Typoman (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Typoman (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101ce800.ini b/bin/gameProfiles/default/00050000101ce800.ini index 2cc75f8e..902e8e23 100644 --- a/bin/gameProfiles/default/00050000101ce800.ini +++ b/bin/gameProfiles/default/00050000101ce800.ini @@ -1,4 +1 @@ -# Never Alone (Kisima Ingitchuna) (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Never Alone (Kisima Ingitchuna) (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101d2100.ini b/bin/gameProfiles/default/00050000101d2100.ini index caac5cdc..830f5f81 100644 --- a/bin/gameProfiles/default/00050000101d2100.ini +++ b/bin/gameProfiles/default/00050000101d2100.ini @@ -1,5 +1,4 @@ # Little Inferno (US) -[CPU] +[Graphics] extendedTextureReadback = true -GPUBufferCacheAccuracy = 0 \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101d4500.ini b/bin/gameProfiles/default/00050000101d4500.ini index 8ae9fc73..c8244324 100644 --- a/bin/gameProfiles/default/00050000101d4500.ini +++ b/bin/gameProfiles/default/00050000101d4500.ini @@ -1,4 +1 @@ -# Grumpy Reaper (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Grumpy Reaper (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101d4d00.ini b/bin/gameProfiles/default/00050000101d4d00.ini index 5df2ddbb..23887c1f 100644 --- a/bin/gameProfiles/default/00050000101d4d00.ini +++ b/bin/gameProfiles/default/00050000101d4d00.ini @@ -1,4 +1 @@ -# Joe's Diner (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Joe's Diner (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101d5000.ini b/bin/gameProfiles/default/00050000101d5000.ini index 2e1b8356..4cff3852 100644 --- a/bin/gameProfiles/default/00050000101d5000.ini +++ b/bin/gameProfiles/default/00050000101d5000.ini @@ -1,4 +1 @@ -# The Peanuts Movie: Snoopy's Grand Adventure (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# The Peanuts Movie: Snoopy's Grand Adventure (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101d5500.ini b/bin/gameProfiles/default/00050000101d5500.ini index 81bcef30..2e1f7542 100644 --- a/bin/gameProfiles/default/00050000101d5500.ini +++ b/bin/gameProfiles/default/00050000101d5500.ini @@ -1,4 +1 @@ -# Joe's Diner (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Joe's Diner (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101d6000.ini b/bin/gameProfiles/default/00050000101d6000.ini index 129477c2..09ff9c3c 100644 --- a/bin/gameProfiles/default/00050000101d6000.ini +++ b/bin/gameProfiles/default/00050000101d6000.ini @@ -2,6 +2,3 @@ [CPU] cpuMode = Singlecore-Recompiler - -[Graphics] -GPUBufferCacheAccuracy = 1 \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101d6d00.ini b/bin/gameProfiles/default/00050000101d6d00.ini index 0bf677ba..a04e39c7 100644 --- a/bin/gameProfiles/default/00050000101d6d00.ini +++ b/bin/gameProfiles/default/00050000101d6d00.ini @@ -1,7 +1 @@ -# Runbow (EUR) - - - -[Graphics] - -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Runbow (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101d7500.ini b/bin/gameProfiles/default/00050000101d7500.ini index 8707efcd..992b00cc 100644 --- a/bin/gameProfiles/default/00050000101d7500.ini +++ b/bin/gameProfiles/default/00050000101d7500.ini @@ -2,6 +2,3 @@ [General] loadSharedLibraries = false - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101d8900.ini b/bin/gameProfiles/default/00050000101d8900.ini index 29b9698f..aaf7d122 100644 --- a/bin/gameProfiles/default/00050000101d8900.ini +++ b/bin/gameProfiles/default/00050000101d8900.ini @@ -1,4 +1 @@ -# Slender: The Arrival (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Slender: The Arrival (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101d8d00.ini b/bin/gameProfiles/default/00050000101d8d00.ini index 864c446b..0b61b998 100644 --- a/bin/gameProfiles/default/00050000101d8d00.ini +++ b/bin/gameProfiles/default/00050000101d8d00.ini @@ -1,4 +1 @@ -# Rock 'N Racing Off Road DX (USA) - -[Graphics] -GPUBufferCacheAccuracy = 1 \ No newline at end of file +# Rock 'N Racing Off Road DX (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101d9600.ini b/bin/gameProfiles/default/00050000101d9600.ini index c2c62b6a..aa4f9a10 100644 --- a/bin/gameProfiles/default/00050000101d9600.ini +++ b/bin/gameProfiles/default/00050000101d9600.ini @@ -1,4 +1 @@ -# Rock 'N Racing Off Road DX (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 1 \ No newline at end of file +# Rock 'N Racing Off Road DX (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101d9d00.ini b/bin/gameProfiles/default/00050000101d9d00.ini index dc54dea2..30d07833 100644 --- a/bin/gameProfiles/default/00050000101d9d00.ini +++ b/bin/gameProfiles/default/00050000101d9d00.ini @@ -2,6 +2,3 @@ [General] loadSharedLibraries = false - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101dac00.ini b/bin/gameProfiles/default/00050000101dac00.ini index 1780613f..bb988dac 100644 --- a/bin/gameProfiles/default/00050000101dac00.ini +++ b/bin/gameProfiles/default/00050000101dac00.ini @@ -1,4 +1 @@ -# Swap Fire - -[GPU] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Swap Fire \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101daf00.ini b/bin/gameProfiles/default/00050000101daf00.ini index 2c8bef7b..3ebc182e 100644 --- a/bin/gameProfiles/default/00050000101daf00.ini +++ b/bin/gameProfiles/default/00050000101daf00.ini @@ -1,4 +1 @@ -# Chronicles of Teddy: Harmony of Exidus (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Chronicles of Teddy: Harmony of Exidus (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101db000.ini b/bin/gameProfiles/default/00050000101db000.ini index 94736dbb..fafb6333 100644 --- a/bin/gameProfiles/default/00050000101db000.ini +++ b/bin/gameProfiles/default/00050000101db000.ini @@ -1,4 +1 @@ -# Oddworld New 'n' Tasty (US) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Oddworld New 'n' Tasty (US) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101dbb00.ini b/bin/gameProfiles/default/00050000101dbb00.ini index 092b54ec..7e08923d 100644 --- a/bin/gameProfiles/default/00050000101dbb00.ini +++ b/bin/gameProfiles/default/00050000101dbb00.ini @@ -1,4 +1 @@ -# Oddworld New 'n' Tasty (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Oddworld New 'n' Tasty (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101dbc00.ini b/bin/gameProfiles/default/00050000101dbc00.ini index 98c41e22..94808fc1 100644 --- a/bin/gameProfiles/default/00050000101dbc00.ini +++ b/bin/gameProfiles/default/00050000101dbc00.ini @@ -1,4 +1 @@ -# Star Ghost (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Star Ghost (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101dbe00.ini b/bin/gameProfiles/default/00050000101dbe00.ini index 66ec5de9..0145c649 100644 --- a/bin/gameProfiles/default/00050000101dbe00.ini +++ b/bin/gameProfiles/default/00050000101dbe00.ini @@ -1,7 +1,4 @@ # Minecraft: Wii U Edition (JPN) [General] -loadSharedLibraries = false - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +loadSharedLibraries = false \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101dbf00.ini b/bin/gameProfiles/default/00050000101dbf00.ini index 6f32b59d..6cab37ad 100644 --- a/bin/gameProfiles/default/00050000101dbf00.ini +++ b/bin/gameProfiles/default/00050000101dbf00.ini @@ -1,4 +1 @@ -# Angry Video Game Nerd Adventures (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Angry Video Game Nerd Adventures (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101dc000.ini b/bin/gameProfiles/default/00050000101dc000.ini index b4b8717c..8734af28 100644 --- a/bin/gameProfiles/default/00050000101dc000.ini +++ b/bin/gameProfiles/default/00050000101dc000.ini @@ -1,4 +1 @@ -# Vektor Wars (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Vektor Wars (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101dc200.ini b/bin/gameProfiles/default/00050000101dc200.ini index 0cec8b6d..eebe3ac5 100644 --- a/bin/gameProfiles/default/00050000101dc200.ini +++ b/bin/gameProfiles/default/00050000101dc200.ini @@ -1,4 +1 @@ -# Vektor Wars (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Vektor Wars (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101dd000.ini b/bin/gameProfiles/default/00050000101dd000.ini index 8f275d8c..33caf468 100644 --- a/bin/gameProfiles/default/00050000101dd000.ini +++ b/bin/gameProfiles/default/00050000101dd000.ini @@ -1,4 +1 @@ -# Star Ghost (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Star Ghost (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101dd600.ini b/bin/gameProfiles/default/00050000101dd600.ini index 7b180555..1a17ec4b 100644 --- a/bin/gameProfiles/default/00050000101dd600.ini +++ b/bin/gameProfiles/default/00050000101dd600.ini @@ -1,4 +1 @@ -# BIT.TRIP Presents... Runner2: Future Legend of Rhythm Alien (JPN) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# BIT.TRIP Presents... Runner2: Future Legend of Rhythm Alien (JPN) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101dd700.ini b/bin/gameProfiles/default/00050000101dd700.ini index 57b3a897..fbefda0e 100644 --- a/bin/gameProfiles/default/00050000101dd700.ini +++ b/bin/gameProfiles/default/00050000101dd700.ini @@ -1,7 +1 @@ -# Runbow (JPN) - - - -[Graphics] - -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Runbow (JPN) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101ddf00.ini b/bin/gameProfiles/default/00050000101ddf00.ini index a3adf2fc..5c802484 100644 --- a/bin/gameProfiles/default/00050000101ddf00.ini +++ b/bin/gameProfiles/default/00050000101ddf00.ini @@ -1,4 +1 @@ -# Hive Jump (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Hive Jump (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101e0100.ini b/bin/gameProfiles/default/00050000101e0100.ini index 13f74180..738553d8 100644 --- a/bin/gameProfiles/default/00050000101e0100.ini +++ b/bin/gameProfiles/default/00050000101e0100.ini @@ -1,6 +1 @@ -# Minecraft: Story Mode (USA) - - - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Minecraft: Story Mode (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101e1800.ini b/bin/gameProfiles/default/00050000101e1800.ini index bf35ca87..a372c127 100644 --- a/bin/gameProfiles/default/00050000101e1800.ini +++ b/bin/gameProfiles/default/00050000101e1800.ini @@ -1,4 +1 @@ -# Human Resource Machine (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Human Resource Machine (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101e1a00.ini b/bin/gameProfiles/default/00050000101e1a00.ini index 23dca6a5..a90125c4 100644 --- a/bin/gameProfiles/default/00050000101e1a00.ini +++ b/bin/gameProfiles/default/00050000101e1a00.ini @@ -1,4 +1 @@ -# Human Resource Machine (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Human Resource Machine (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101e1b00.ini b/bin/gameProfiles/default/00050000101e1b00.ini index 5675969a..88e9b456 100644 --- a/bin/gameProfiles/default/00050000101e1b00.ini +++ b/bin/gameProfiles/default/00050000101e1b00.ini @@ -2,6 +2,3 @@ [CPU] cpuTimer = hostBased - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101e3800.ini b/bin/gameProfiles/default/00050000101e3800.ini index 4e6f4859..fdfa2cdf 100644 --- a/bin/gameProfiles/default/00050000101e3800.ini +++ b/bin/gameProfiles/default/00050000101e3800.ini @@ -1,7 +1 @@ -# Dual Core (USA) - -[General] -loadSharedLibraries = false - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Dual Core (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101e4200.ini b/bin/gameProfiles/default/00050000101e4200.ini index 9e6a983b..ef94b291 100644 --- a/bin/gameProfiles/default/00050000101e4200.ini +++ b/bin/gameProfiles/default/00050000101e4200.ini @@ -6,6 +6,3 @@ useRDTSC = false [CPU] cpuTimer = cycleCounter cpuMode = Singlecore-Interpreter - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101e5300.ini b/bin/gameProfiles/default/00050000101e5300.ini index c6f6674a..c04cd6b6 100644 --- a/bin/gameProfiles/default/00050000101e5300.ini +++ b/bin/gameProfiles/default/00050000101e5300.ini @@ -6,6 +6,3 @@ useRDTSC = false [CPU] cpuMode = Singlecore-Recompiler cpuTimer = cycleCounter - -[Graphics] -GPUBufferCacheAccuracy = 1 \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101e5400.ini b/bin/gameProfiles/default/00050000101e5400.ini index 299c9e40..221a23ed 100644 --- a/bin/gameProfiles/default/00050000101e5400.ini +++ b/bin/gameProfiles/default/00050000101e5400.ini @@ -6,6 +6,3 @@ useRDTSC = false [CPU] cpuMode = Singlecore-Recompiler cpuTimer = cycleCounter - -[Graphics] -GPUBufferCacheAccuracy = 1 \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101e5e00.ini b/bin/gameProfiles/default/00050000101e5e00.ini index 2736143b..ab9fc348 100644 --- a/bin/gameProfiles/default/00050000101e5e00.ini +++ b/bin/gameProfiles/default/00050000101e5e00.ini @@ -1,4 +1 @@ -# Chasing Dead (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 +# Chasing Dead (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101e7300.ini b/bin/gameProfiles/default/00050000101e7300.ini index de2d9ace..092720b8 100644 --- a/bin/gameProfiles/default/00050000101e7300.ini +++ b/bin/gameProfiles/default/00050000101e7300.ini @@ -1,4 +1 @@ -# The Deer God (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# The Deer God (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101e7400.ini b/bin/gameProfiles/default/00050000101e7400.ini index 38a64064..e8644aa7 100644 --- a/bin/gameProfiles/default/00050000101e7400.ini +++ b/bin/gameProfiles/default/00050000101e7400.ini @@ -1,4 +1 @@ -# Grumpy Reaper (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Grumpy Reaper (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101e9300.ini b/bin/gameProfiles/default/00050000101e9300.ini index a88c29bc..24c0dad8 100644 --- a/bin/gameProfiles/default/00050000101e9300.ini +++ b/bin/gameProfiles/default/00050000101e9300.ini @@ -1,4 +1 @@ -# Gear Gauntlet (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Gear Gauntlet (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101e9400.ini b/bin/gameProfiles/default/00050000101e9400.ini index 6f914578..6b78201e 100644 --- a/bin/gameProfiles/default/00050000101e9400.ini +++ b/bin/gameProfiles/default/00050000101e9400.ini @@ -1,4 +1 @@ -# Gear Gauntlet (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Gear Gauntlet (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101eb300.ini b/bin/gameProfiles/default/00050000101eb300.ini index e5b62760..66c8dcb4 100644 --- a/bin/gameProfiles/default/00050000101eb300.ini +++ b/bin/gameProfiles/default/00050000101eb300.ini @@ -1,4 +1 @@ -# The Beggar's Ride (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# The Beggar's Ride (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101ec700.ini b/bin/gameProfiles/default/00050000101ec700.ini index 27e0f560..7dd85123 100644 --- a/bin/gameProfiles/default/00050000101ec700.ini +++ b/bin/gameProfiles/default/00050000101ec700.ini @@ -1,4 +1 @@ -# The Beggar's Ride (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# The Beggar's Ride (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101ecf00.ini b/bin/gameProfiles/default/00050000101ecf00.ini index 3191607c..e3b80181 100644 --- a/bin/gameProfiles/default/00050000101ecf00.ini +++ b/bin/gameProfiles/default/00050000101ecf00.ini @@ -1,4 +1 @@ -# Buddy & Me Dream Edition (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Buddy & Me Dream Edition (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101f1300.ini b/bin/gameProfiles/default/00050000101f1300.ini index 3b17b3a5..85683631 100644 --- a/bin/gameProfiles/default/00050000101f1300.ini +++ b/bin/gameProfiles/default/00050000101f1300.ini @@ -1,4 +1 @@ -# Armikrog (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Armikrog (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101f2800.ini b/bin/gameProfiles/default/00050000101f2800.ini index 65c08d62..a8d455dd 100644 --- a/bin/gameProfiles/default/00050000101f2800.ini +++ b/bin/gameProfiles/default/00050000101f2800.ini @@ -1,7 +1,4 @@ # 8Bit Hero (USA) -[Graphics] -GPUBufferCacheAccuracy = 0 - [CPU] cpuMode = Singlecore-Interpreter \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101f4a00.ini b/bin/gameProfiles/default/00050000101f4a00.ini index 2d9432da..e0ca60b7 100644 --- a/bin/gameProfiles/default/00050000101f4a00.ini +++ b/bin/gameProfiles/default/00050000101f4a00.ini @@ -1,4 +1 @@ -# Buddy & Me Dream Edition (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Buddy & Me Dream Edition (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101f5700.ini b/bin/gameProfiles/default/00050000101f5700.ini index a2d5119f..c0c98988 100644 --- a/bin/gameProfiles/default/00050000101f5700.ini +++ b/bin/gameProfiles/default/00050000101f5700.ini @@ -1,4 +1 @@ -# Jotun Valhalla Edition (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Jotun Valhalla Edition (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101f6f00.ini b/bin/gameProfiles/default/00050000101f6f00.ini index 92bf8b9e..4a590788 100644 --- a/bin/gameProfiles/default/00050000101f6f00.ini +++ b/bin/gameProfiles/default/00050000101f6f00.ini @@ -1,4 +1 @@ -# Jotun Valhalla Edition (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Jotun Valhalla Edition (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101f7600.ini b/bin/gameProfiles/default/00050000101f7600.ini index 88e55d2a..bbe32dff 100644 --- a/bin/gameProfiles/default/00050000101f7600.ini +++ b/bin/gameProfiles/default/00050000101f7600.ini @@ -1,7 +1 @@ -# Dual Core (EUR) - -[General] -loadSharedLibraries = false - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Dual Core (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101f9700.ini b/bin/gameProfiles/default/00050000101f9700.ini index 34c46581..a36f67a1 100644 --- a/bin/gameProfiles/default/00050000101f9700.ini +++ b/bin/gameProfiles/default/00050000101f9700.ini @@ -1,4 +1 @@ -# Darksiders Warmastered Edition (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Darksiders Warmastered Edition (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101fa600.ini b/bin/gameProfiles/default/00050000101fa600.ini index 89ab8ade..653531f9 100644 --- a/bin/gameProfiles/default/00050000101fa600.ini +++ b/bin/gameProfiles/default/00050000101fa600.ini @@ -1,4 +1 @@ -# Darksiders Warmastered Edition (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Darksiders Warmastered Edition (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101fd100.ini b/bin/gameProfiles/default/00050000101fd100.ini index 6ce5a608..a0f02db2 100644 --- a/bin/gameProfiles/default/00050000101fd100.ini +++ b/bin/gameProfiles/default/00050000101fd100.ini @@ -1,4 +1 @@ -# Grumpy Reaper (JPN) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Grumpy Reaper (JPN) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101ff200.ini b/bin/gameProfiles/default/00050000101ff200.ini index 54e13b65..13b5470b 100644 --- a/bin/gameProfiles/default/00050000101ff200.ini +++ b/bin/gameProfiles/default/00050000101ff200.ini @@ -1,4 +1 @@ -# Exile's End (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Exile's End (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101ffc00.ini b/bin/gameProfiles/default/00050000101ffc00.ini index e01c407c..4d6850ae 100644 --- a/bin/gameProfiles/default/00050000101ffc00.ini +++ b/bin/gameProfiles/default/00050000101ffc00.ini @@ -1,5 +1,4 @@ # Ghost Blade HD (USA) [Graphics] -GPUBufferCacheAccuracy = 0 streamoutBufferCacheSize = 48 \ No newline at end of file diff --git a/bin/gameProfiles/default/00050000101ffe00.ini b/bin/gameProfiles/default/00050000101ffe00.ini index 9225f393..123cc4b9 100644 --- a/bin/gameProfiles/default/00050000101ffe00.ini +++ b/bin/gameProfiles/default/00050000101ffe00.ini @@ -1,4 +1 @@ -# Tetrimos (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Tetrimos (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010200300.ini b/bin/gameProfiles/default/0005000010200300.ini index 47600923..b62d3c32 100644 --- a/bin/gameProfiles/default/0005000010200300.ini +++ b/bin/gameProfiles/default/0005000010200300.ini @@ -1,4 +1 @@ -# Armikrog (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Armikrog (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010200b00.ini b/bin/gameProfiles/default/0005000010200b00.ini index 8fc9da4b..7a6469b5 100644 --- a/bin/gameProfiles/default/0005000010200b00.ini +++ b/bin/gameProfiles/default/0005000010200b00.ini @@ -1,4 +1 @@ -# Tetrimos (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Tetrimos (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010204a00.ini b/bin/gameProfiles/default/0005000010204a00.ini index 8a0285c3..e0d9db5a 100644 --- a/bin/gameProfiles/default/0005000010204a00.ini +++ b/bin/gameProfiles/default/0005000010204a00.ini @@ -1,4 +1 @@ -# Exile's End (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Exile's End (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010207300.ini b/bin/gameProfiles/default/0005000010207300.ini index c678e7d6..56d9a42f 100644 --- a/bin/gameProfiles/default/0005000010207300.ini +++ b/bin/gameProfiles/default/0005000010207300.ini @@ -1,4 +1 @@ -# Koi DX (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Koi DX (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010207500.ini b/bin/gameProfiles/default/0005000010207500.ini index 090be3f2..1a4ab56d 100644 --- a/bin/gameProfiles/default/0005000010207500.ini +++ b/bin/gameProfiles/default/0005000010207500.ini @@ -1,4 +1 @@ -# Koi DX (USA) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Koi DX (USA) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001020a200.ini b/bin/gameProfiles/default/000500001020a200.ini index bef0c82e..6960a967 100644 --- a/bin/gameProfiles/default/000500001020a200.ini +++ b/bin/gameProfiles/default/000500001020a200.ini @@ -1,5 +1 @@ -# Minecraft: Story Mode (EUR) - - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Minecraft: Story Mode (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/000500001020b600.ini b/bin/gameProfiles/default/000500001020b600.ini index 74528c38..46e4c8da 100644 --- a/bin/gameProfiles/default/000500001020b600.ini +++ b/bin/gameProfiles/default/000500001020b600.ini @@ -1,5 +1,4 @@ # Ghost Blade HD (EUR) [Graphics] -GPUBufferCacheAccuracy = 0 streamoutBufferCacheSize = 48 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000010211b00.ini b/bin/gameProfiles/default/0005000010211b00.ini index 69534194..7e82f408 100644 --- a/bin/gameProfiles/default/0005000010211b00.ini +++ b/bin/gameProfiles/default/0005000010211b00.ini @@ -1,4 +1 @@ -# Sphere Slice (EUR) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# Sphere Slice (EUR) \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000C1012BC00.ini b/bin/gameProfiles/default/0005000C1012BC00.ini index 5c2c09d4..5f699eb6 100644 --- a/bin/gameProfiles/default/0005000C1012BC00.ini +++ b/bin/gameProfiles/default/0005000C1012BC00.ini @@ -1,8 +1,4 @@ # Pikmin 3 (JAP) - - [Graphics] - extendedTextureReadback = true -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000C1012BD00.ini b/bin/gameProfiles/default/0005000C1012BD00.ini index 8ab10175..cd27ff77 100644 --- a/bin/gameProfiles/default/0005000C1012BD00.ini +++ b/bin/gameProfiles/default/0005000C1012BD00.ini @@ -1,8 +1,4 @@ # Pikmin 3 (USA) - - [Graphics] - extendedTextureReadback = true -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000C1012BE00.ini b/bin/gameProfiles/default/0005000C1012BE00.ini index e7226ef4..6be6a929 100644 --- a/bin/gameProfiles/default/0005000C1012BE00.ini +++ b/bin/gameProfiles/default/0005000C1012BE00.ini @@ -1,8 +1,4 @@ # Pikmin 3 (EU) - - [Graphics] - extendedTextureReadback = true -GPUBufferCacheAccuracy = 2 \ No newline at end of file diff --git a/bin/gameProfiles/default/0005000e1019c800.ini b/bin/gameProfiles/default/0005000e1019c800.ini index 8c47898c..81adc4c9 100644 --- a/bin/gameProfiles/default/0005000e1019c800.ini +++ b/bin/gameProfiles/default/0005000e1019c800.ini @@ -1,4 +1 @@ -# TLoZ: Twilight Princess (JPN) - -[Graphics] -GPUBufferCacheAccuracy = 0 \ No newline at end of file +# TLoZ: Twilight Princess (JPN) \ No newline at end of file From 1bcc064593545ff3603521f24e7d32c8b8adabab Mon Sep 17 00:00:00 2001 From: Tillsunset <35825944+Tillsunset@users.noreply.github.com> Date: Sun, 23 Oct 2022 06:06:20 -0500 Subject: [PATCH 042/616] Add check for "." in FSC path (#402) --- src/Cafe/Filesystem/FST/fstUtil.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Cafe/Filesystem/FST/fstUtil.h b/src/Cafe/Filesystem/FST/fstUtil.h index 4ea9465d..01283684 100644 --- a/src/Cafe/Filesystem/FST/fstUtil.h +++ b/src/Cafe/Filesystem/FST/fstUtil.h @@ -28,6 +28,8 @@ class FSCPath { if (m_names.size() > 0xFFFF) return; + if (nameLen == 1 && *name == '.') + return; m_nodes.emplace_back((uint16)m_names.size(), nameLen); m_names.insert(m_names.end(), name, name + nameLen); } @@ -297,6 +299,12 @@ static void FSTPathUnitTest() cemu_assert_debug(p6.GetNodeCount() == 0); p6 = FSCPath("/////////////"); cemu_assert_debug(p6.GetNodeCount() == 0); + // test 7 - periods in path + FSCPath p7("/vol/content/./.."); + cemu_assert_debug(p7.GetNodeCount() == 3); + cemu_assert_debug(p7.MatchNodeName(0, "vol")); + cemu_assert_debug(p7.MatchNodeName(1, "content")); + cemu_assert_debug(p7.MatchNodeName(2, "..")); } From c40466f3a8d12a34ca1a82b596af18dd56a3ec2b Mon Sep 17 00:00:00 2001 From: Jeremy Kescher Date: Sun, 23 Oct 2022 12:03:51 +0000 Subject: [PATCH 043/616] Fix incorrect title ID (00050000-1011000? -> 00050000-10111000) (#404) --- .../default/{000500001011000.ini => 0005000010111000.ini} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename bin/gameProfiles/default/{000500001011000.ini => 0005000010111000.ini} (100%) diff --git a/bin/gameProfiles/default/000500001011000.ini b/bin/gameProfiles/default/0005000010111000.ini similarity index 100% rename from bin/gameProfiles/default/000500001011000.ini rename to bin/gameProfiles/default/0005000010111000.ini From 028b3f79926a280b1a6fc5abbd438cc875079b89 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Sun, 23 Oct 2022 15:47:42 +0200 Subject: [PATCH 044/616] Make controller button code thread-safe (#405) * Refactor spinlock to meet Lockable requirements * Input: Refactor button code and make it thread-safe --- .../HW/Espresso/Debugger/DebugSymbolStorage.h | 14 +-- src/Cafe/HW/Espresso/PPCTimer.cpp | 4 +- .../HW/Espresso/Recompiler/PPCRecompiler.cpp | 36 +++--- src/Cafe/HW/Latte/Core/FetchShader.cpp | 10 +- src/Cafe/HW/Latte/Core/LatteBufferCache.cpp | 8 +- .../HW/Latte/Renderer/Vulkan/CachedFBOVk.h | 8 +- .../Latte/Renderer/Vulkan/RendererShaderVk.h | 8 +- .../Vulkan/VulkanPipelineStableCache.cpp | 24 ++-- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 12 +- src/Cafe/IOSU/kernel/iosu_kernel.cpp | 14 +-- .../OS/libs/coreinit/coreinit_Callbacks.cpp | 18 +-- .../OS/libs/coreinit/coreinit_MPQueue.cpp | 8 +- .../coreinit/coreinit_Synchronization.cpp | 4 +- src/Cafe/OS/libs/snd_core/ax_ist.cpp | 4 +- src/Cafe/OS/libs/snd_core/ax_voice.cpp | 12 +- src/gui/guiWrapper.h | 14 +-- src/gui/input/InputSettings2.cpp | 2 +- src/gui/input/panels/InputPanel.cpp | 110 ++++++++--------- src/input/api/Controller.cpp | 24 ++-- src/input/api/ControllerState.h | 111 +++++++++++++++++- src/input/api/DSU/DSUController.cpp | 6 +- .../api/DirectInput/DirectInputController.cpp | 23 ++-- src/input/api/Keyboard/KeyboardController.cpp | 7 +- src/input/api/SDL/SDLController.cpp | 4 +- .../api/Wiimote/NativeWiimoteController.cpp | 13 +- src/input/api/XInput/XInputController.cpp | 2 +- src/input/emulated/EmulatedController.cpp | 8 +- src/util/helpers/fspinlock.h | 23 ++-- 28 files changed, 311 insertions(+), 220 deletions(-) diff --git a/src/Cafe/HW/Espresso/Debugger/DebugSymbolStorage.h b/src/Cafe/HW/Espresso/Debugger/DebugSymbolStorage.h index 0d7b7d49..aba6a9b5 100644 --- a/src/Cafe/HW/Espresso/Debugger/DebugSymbolStorage.h +++ b/src/Cafe/HW/Espresso/Debugger/DebugSymbolStorage.h @@ -24,28 +24,28 @@ class DebugSymbolStorage public: static void StoreDataType(MPTR address, DEBUG_SYMBOL_TYPE type) { - s_lock.acquire(); + s_lock.lock(); s_typeStorage[address] = type; - s_lock.release(); + s_lock.unlock(); } static DEBUG_SYMBOL_TYPE GetDataType(MPTR address) { - s_lock.acquire(); + s_lock.lock(); auto itr = s_typeStorage.find(address); if (itr == s_typeStorage.end()) { - s_lock.release(); + s_lock.unlock(); return DEBUG_SYMBOL_TYPE::UNDEFINED; } DEBUG_SYMBOL_TYPE t = itr->second; - s_lock.release(); + s_lock.unlock(); return t; } static void ClearRange(MPTR address, uint32 length) { - s_lock.acquire(); + s_lock.lock(); while (length > 0) { auto itr = s_typeStorage.find(address); @@ -54,7 +54,7 @@ public: address += 4; length -= 4; } - s_lock.release(); + s_lock.unlock(); } private: diff --git a/src/Cafe/HW/Espresso/PPCTimer.cpp b/src/Cafe/HW/Espresso/PPCTimer.cpp index 36198dac..153458d8 100644 --- a/src/Cafe/HW/Espresso/PPCTimer.cpp +++ b/src/Cafe/HW/Espresso/PPCTimer.cpp @@ -129,7 +129,7 @@ FSpinlock sTimerSpinlock; // thread safe uint64 PPCTimer_getFromRDTSC() { - sTimerSpinlock.acquire(); + sTimerSpinlock.lock(); _mm_mfence(); uint64 rdtscCurrentMeasure = __rdtsc(); uint64 rdtscDif = rdtscCurrentMeasure - _rdtscLastMeasure; @@ -165,6 +165,6 @@ uint64 PPCTimer_getFromRDTSC() _tickSummary += elapsedTick; - sTimerSpinlock.release(); + sTimerSpinlock.unlock(); return _tickSummary; } diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCRecompiler.cpp b/src/Cafe/HW/Espresso/Recompiler/PPCRecompiler.cpp index 98263ff3..588e5397 100644 --- a/src/Cafe/HW/Espresso/Recompiler/PPCRecompiler.cpp +++ b/src/Cafe/HW/Espresso/Recompiler/PPCRecompiler.cpp @@ -47,20 +47,20 @@ void PPCRecompiler_visitAddressNoBlock(uint32 enterAddress) if (ppcRecompilerInstanceData->ppcRecompilerDirectJumpTable[enterAddress / 4] != PPCRecompiler_leaveRecompilerCode_unvisited) return; // try to acquire lock - if (!PPCRecompilerState.recompilerSpinlock.tryAcquire()) + if (!PPCRecompilerState.recompilerSpinlock.try_lock()) return; auto funcPtr = ppcRecompilerInstanceData->ppcRecompilerDirectJumpTable[enterAddress / 4]; if (funcPtr != PPCRecompiler_leaveRecompilerCode_unvisited) { // was visited since previous check - PPCRecompilerState.recompilerSpinlock.release(); + PPCRecompilerState.recompilerSpinlock.unlock(); return; } // add to recompilation queue and flag as visited PPCRecompilerState.targetQueue.emplace(enterAddress); ppcRecompilerInstanceData->ppcRecompilerDirectJumpTable[enterAddress / 4] = PPCRecompiler_leaveRecompilerCode_visited; - PPCRecompilerState.recompilerSpinlock.release(); + PPCRecompilerState.recompilerSpinlock.unlock(); } void PPCRecompiler_recompileIfUnvisited(uint32 enterAddress) @@ -193,13 +193,13 @@ PPCRecFunction_t* PPCRecompiler_recompileFunction(PPCFunctionBoundaryTracker::PP bool PPCRecompiler_makeRecompiledFunctionActive(uint32 initialEntryPoint, PPCFunctionBoundaryTracker::PPCRange_t& range, PPCRecFunction_t* ppcRecFunc, std::vector>& entryPoints) { // update jump table - PPCRecompilerState.recompilerSpinlock.acquire(); + PPCRecompilerState.recompilerSpinlock.lock(); // check if the initial entrypoint is still flagged for recompilation // its possible that the range has been invalidated during the time it took to translate the function if (ppcRecompilerInstanceData->ppcRecompilerDirectJumpTable[initialEntryPoint / 4] != PPCRecompiler_leaveRecompilerCode_visited) { - PPCRecompilerState.recompilerSpinlock.release(); + PPCRecompilerState.recompilerSpinlock.unlock(); return false; } @@ -221,7 +221,7 @@ bool PPCRecompiler_makeRecompiledFunctionActive(uint32 initialEntryPoint, PPCFun PPCRecompilerState.invalidationRanges.clear(); if (isInvalidated) { - PPCRecompilerState.recompilerSpinlock.release(); + PPCRecompilerState.recompilerSpinlock.unlock(); return false; } @@ -249,7 +249,7 @@ bool PPCRecompiler_makeRecompiledFunctionActive(uint32 initialEntryPoint, PPCFun { r.storedRange = rangeStore_ppcRanges.storeRange(ppcRecFunc, r.ppcAddress, r.ppcAddress + r.ppcSize); } - PPCRecompilerState.recompilerSpinlock.release(); + PPCRecompilerState.recompilerSpinlock.unlock(); return true; @@ -272,13 +272,13 @@ void PPCRecompiler_recompileAtAddress(uint32 address) // todo - use info from previously compiled ranges to determine full size of this function (and merge all the entryAddresses) // collect all currently known entry points for this range - PPCRecompilerState.recompilerSpinlock.acquire(); + PPCRecompilerState.recompilerSpinlock.lock(); std::set entryAddresses; entryAddresses.emplace(address); - PPCRecompilerState.recompilerSpinlock.release(); + PPCRecompilerState.recompilerSpinlock.unlock(); std::vector> functionEntryPoints; auto func = PPCRecompiler_recompileFunction(range, entryAddresses, functionEntryPoints); @@ -302,10 +302,10 @@ void PPCRecompiler_thread() // 3) if yes -> calculate size, gather all entry points, recompile and update jump table while (true) { - PPCRecompilerState.recompilerSpinlock.acquire(); + PPCRecompilerState.recompilerSpinlock.lock(); if (PPCRecompilerState.targetQueue.empty()) { - PPCRecompilerState.recompilerSpinlock.release(); + PPCRecompilerState.recompilerSpinlock.unlock(); break; } auto enterAddress = PPCRecompilerState.targetQueue.front(); @@ -315,10 +315,10 @@ void PPCRecompiler_thread() if (funcPtr != PPCRecompiler_leaveRecompilerCode_visited) { // only recompile functions if marked as visited - PPCRecompilerState.recompilerSpinlock.release(); + PPCRecompilerState.recompilerSpinlock.unlock(); continue; } - PPCRecompilerState.recompilerSpinlock.release(); + PPCRecompilerState.recompilerSpinlock.unlock(); PPCRecompiler_recompileAtAddress(enterAddress); } @@ -376,7 +376,7 @@ struct ppcRecompilerFuncRange_t bool PPCRecompiler_findFuncRanges(uint32 addr, ppcRecompilerFuncRange_t* rangesOut, size_t* countInOut) { - PPCRecompilerState.recompilerSpinlock.acquire(); + PPCRecompilerState.recompilerSpinlock.lock(); size_t countIn = *countInOut; size_t countOut = 0; @@ -392,7 +392,7 @@ bool PPCRecompiler_findFuncRanges(uint32 addr, ppcRecompilerFuncRange_t* rangesO countOut++; } ); - PPCRecompilerState.recompilerSpinlock.release(); + PPCRecompilerState.recompilerSpinlock.unlock(); *countInOut = countOut; if (countOut > countIn) return false; @@ -420,7 +420,7 @@ void PPCRecompiler_invalidateTableRange(uint32 offset, uint32 size) void PPCRecompiler_deleteFunction(PPCRecFunction_t* func) { // assumes PPCRecompilerState.recompilerSpinlock is already held - cemu_assert_debug(PPCRecompilerState.recompilerSpinlock.isHolding()); + cemu_assert_debug(PPCRecompilerState.recompilerSpinlock.is_locked()); for (auto& r : func->list_ranges) { PPCRecompiler_invalidateTableRange(r.ppcAddress, r.ppcSize); @@ -439,7 +439,7 @@ void PPCRecompiler_invalidateRange(uint32 startAddr, uint32 endAddr) return; cemu_assert_debug(endAddr >= startAddr); - PPCRecompilerState.recompilerSpinlock.acquire(); + PPCRecompilerState.recompilerSpinlock.lock(); uint32 rStart; uint32 rEnd; @@ -458,7 +458,7 @@ void PPCRecompiler_invalidateRange(uint32 startAddr, uint32 endAddr) PPCRecompiler_deleteFunction(rFunc); } - PPCRecompilerState.recompilerSpinlock.release(); + PPCRecompilerState.recompilerSpinlock.unlock(); } void PPCRecompiler_init() diff --git a/src/Cafe/HW/Latte/Core/FetchShader.cpp b/src/Cafe/HW/Latte/Core/FetchShader.cpp index c6756f4e..b4beba4e 100644 --- a/src/Cafe/HW/Latte/Core/FetchShader.cpp +++ b/src/Cafe/HW/Latte/Core/FetchShader.cpp @@ -516,16 +516,16 @@ FSpinlock s_spinlockFetchShaderCache; LatteFetchShader* LatteFetchShader::RegisterInCache(CacheHash fsHash) { - s_spinlockFetchShaderCache.acquire(); + s_spinlockFetchShaderCache.lock(); auto itr = s_fetchShaderByHash.find(fsHash); if (itr != s_fetchShaderByHash.end()) { LatteFetchShader* fs = itr->second; - s_spinlockFetchShaderCache.release(); + s_spinlockFetchShaderCache.unlock(); return fs; } s_fetchShaderByHash.emplace(fsHash, this); - s_spinlockFetchShaderCache.release(); + s_spinlockFetchShaderCache.unlock(); return nullptr; } @@ -533,11 +533,11 @@ void LatteFetchShader::UnregisterInCache() { if (!m_isRegistered) return; - s_spinlockFetchShaderCache.acquire(); + s_spinlockFetchShaderCache.lock(); auto itr = s_fetchShaderByHash.find(m_cacheHash); cemu_assert(itr == s_fetchShaderByHash.end()); s_fetchShaderByHash.erase(itr); - s_spinlockFetchShaderCache.release(); + s_spinlockFetchShaderCache.unlock(); } std::unordered_map LatteFetchShader::s_fetchShaderByHash; diff --git a/src/Cafe/HW/Latte/Core/LatteBufferCache.cpp b/src/Cafe/HW/Latte/Core/LatteBufferCache.cpp index 1e2c43b1..b0917895 100644 --- a/src/Cafe/HW/Latte/Core/LatteBufferCache.cpp +++ b/src/Cafe/HW/Latte/Core/LatteBufferCache.cpp @@ -1074,19 +1074,19 @@ void LatteBufferCache_notifyDCFlush(MPTR address, uint32 size) uint32 firstPage = address / CACHE_PAGE_SIZE; uint32 lastPage = (address + size - 1) / CACHE_PAGE_SIZE; - g_spinlockDCFlushQueue.acquire(); + g_spinlockDCFlushQueue.lock(); for (uint32 i = firstPage; i <= lastPage; i++) s_DCFlushQueue->Set(i); - g_spinlockDCFlushQueue.release(); + g_spinlockDCFlushQueue.unlock(); } void LatteBufferCache_processDCFlushQueue() { if (s_DCFlushQueue->Empty()) // quick check to avoid locking if there is no work to do return; - g_spinlockDCFlushQueue.acquire(); + g_spinlockDCFlushQueue.lock(); std::swap(s_DCFlushQueue, s_DCFlushQueueAlternate); - g_spinlockDCFlushQueue.release(); + g_spinlockDCFlushQueue.unlock(); s_DCFlushQueueAlternate->ForAllAndClear([](uint32 index) {LatteBufferCache_invalidatePage(index * CACHE_PAGE_SIZE); }); } diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/CachedFBOVk.h b/src/Cafe/HW/Latte/Renderer/Vulkan/CachedFBOVk.h index b83bd96c..4e6be012 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/CachedFBOVk.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/CachedFBOVk.h @@ -37,16 +37,16 @@ public: void TrackDependency(class PipelineInfo* pipelineInfo) { - s_spinlockDependency.acquire(); + s_spinlockDependency.lock(); m_usedByPipelines.emplace_back(pipelineInfo); - s_spinlockDependency.release(); + s_spinlockDependency.unlock(); } void RemoveDependency(class PipelineInfo* pipelineInfo) { - s_spinlockDependency.acquire(); + s_spinlockDependency.lock(); vectorRemoveByValue(m_usedByPipelines, pipelineInfo); - s_spinlockDependency.release(); + s_spinlockDependency.unlock(); } [[nodiscard]] const VkExtent2D& GetExtend() const { return m_extend;} diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h index 5be9ce1f..b1883c3d 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h @@ -37,16 +37,16 @@ public: void TrackDependency(class PipelineInfo* p) { - s_dependencyLock.acquire(); + s_dependencyLock.lock(); list_pipelineInfo.emplace_back(p); - s_dependencyLock.release(); + s_dependencyLock.unlock(); } void RemoveDependency(class PipelineInfo* p) { - s_dependencyLock.acquire(); + s_dependencyLock.lock(); vectorRemoveByValue(list_pipelineInfo, p); - s_dependencyLock.release(); + s_dependencyLock.unlock(); } void PreponeCompilation(bool isRenderThread) override; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp index 38f7c882..0d3ff771 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp @@ -206,18 +206,18 @@ void VulkanPipelineStableCache::LoadPipelineFromCache(std::span fileData) // deserialize file LatteContextRegister* lcr = new LatteContextRegister(); - s_spinlockSharedInternal.acquire(); + s_spinlockSharedInternal.lock(); CachedPipeline* cachedPipeline = new CachedPipeline(); - s_spinlockSharedInternal.release(); + s_spinlockSharedInternal.unlock(); MemStreamReader streamReader(fileData.data(), fileData.size()); if (!DeserializePipeline(streamReader, *cachedPipeline)) { // failed to deserialize - s_spinlockSharedInternal.acquire(); + s_spinlockSharedInternal.lock(); delete lcr; delete cachedPipeline; - s_spinlockSharedInternal.release(); + s_spinlockSharedInternal.unlock(); return; } // restored register view from compacted state @@ -264,18 +264,18 @@ void VulkanPipelineStableCache::LoadPipelineFromCache(std::span fileData) } auto renderPass = __CreateTemporaryRenderPass(pixelShader, *lcr); // create pipeline info - m_pipelineIsCachedLock.acquire(); + m_pipelineIsCachedLock.lock(); PipelineInfo* pipelineInfo = new PipelineInfo(0, 0, vertexShader->compatibleFetchShader, vertexShader, pixelShader, geometryShader); - m_pipelineIsCachedLock.release(); + m_pipelineIsCachedLock.unlock(); // compile { PipelineCompiler pp; if (!pp.InitFromCurrentGPUState(pipelineInfo, *lcr, renderPass)) { - s_spinlockSharedInternal.acquire(); + s_spinlockSharedInternal.lock(); delete lcr; delete cachedPipeline; - s_spinlockSharedInternal.release(); + s_spinlockSharedInternal.unlock(); return; } pp.Compile(true, true, false); @@ -284,16 +284,16 @@ void VulkanPipelineStableCache::LoadPipelineFromCache(std::span fileData) // on success, calculate pipeline hash and flag as present in cache uint64 pipelineBaseHash = vertexShader->baseHash; uint64 pipelineStateHash = VulkanRenderer::draw_calculateGraphicsPipelineHash(vertexShader->compatibleFetchShader, vertexShader, geometryShader, pixelShader, renderPass, *lcr); - m_pipelineIsCachedLock.acquire(); + m_pipelineIsCachedLock.lock(); m_pipelineIsCached.emplace(pipelineBaseHash, pipelineStateHash); - m_pipelineIsCachedLock.release(); + m_pipelineIsCachedLock.unlock(); // clean up - s_spinlockSharedInternal.acquire(); + s_spinlockSharedInternal.lock(); delete pipelineInfo; delete lcr; delete cachedPipeline; VulkanRenderer::GetInstance()->releaseDestructibleObject(renderPass); - s_spinlockSharedInternal.release(); + s_spinlockSharedInternal.unlock(); } bool VulkanPipelineStableCache::HasPipelineCached(uint64 baseHash, uint64 pipelineStateHash) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 5cbf7f94..35d81446 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -3447,14 +3447,14 @@ void VulkanRenderer::releaseDestructibleObject(VKRDestructibleObject* destructib return; } // otherwise put on queue - m_spinlockDestructionQueue.acquire(); + m_spinlockDestructionQueue.lock(); m_destructionQueue.emplace_back(destructibleObject); - m_spinlockDestructionQueue.release(); + m_spinlockDestructionQueue.unlock(); } void VulkanRenderer::ProcessDestructionQueue2() { - m_spinlockDestructionQueue.acquire(); + m_spinlockDestructionQueue.lock(); for (auto it = m_destructionQueue.begin(); it != m_destructionQueue.end();) { if ((*it)->canDestroy()) @@ -3465,7 +3465,7 @@ void VulkanRenderer::ProcessDestructionQueue2() } ++it; } - m_spinlockDestructionQueue.release(); + m_spinlockDestructionQueue.unlock(); } VkDescriptorSetInfo::~VkDescriptorSetInfo() @@ -4010,9 +4010,9 @@ void VulkanRenderer::AppendOverlayDebugInfo() ImGui::Text("ImageView %u", performanceMonitor.vk.numImageViews.get()); ImGui::Text("RenderPass %u", performanceMonitor.vk.numRenderPass.get()); ImGui::Text("Framebuffer %u", performanceMonitor.vk.numFramebuffer.get()); - m_spinlockDestructionQueue.acquire(); + m_spinlockDestructionQueue.lock(); ImGui::Text("DestructionQ %u", (unsigned int)m_destructionQueue.size()); - m_spinlockDestructionQueue.release(); + m_spinlockDestructionQueue.unlock(); ImGui::Text("BeginRP/f %u", performanceMonitor.vk.numBeginRenderpassPerFrame.get()); diff --git a/src/Cafe/IOSU/kernel/iosu_kernel.cpp b/src/Cafe/IOSU/kernel/iosu_kernel.cpp index 1a642028..680170bc 100644 --- a/src/Cafe/IOSU/kernel/iosu_kernel.cpp +++ b/src/Cafe/IOSU/kernel/iosu_kernel.cpp @@ -234,38 +234,38 @@ namespace iosu void _IPCInitDispatchablePool() { - sIPCDispatchableCommandPoolLock.acquire(); + sIPCDispatchableCommandPoolLock.lock(); while (!sIPCFreeDispatchableCommands.empty()) sIPCFreeDispatchableCommands.pop(); for (size_t i = 0; i < sIPCDispatchableCommandPool.GetCount(); i++) sIPCFreeDispatchableCommands.push(sIPCDispatchableCommandPool.GetPtr()+i); - sIPCDispatchableCommandPoolLock.release(); + sIPCDispatchableCommandPoolLock.unlock(); } IOSDispatchableCommand* _IPCAllocateDispatchableCommand() { - sIPCDispatchableCommandPoolLock.acquire(); + sIPCDispatchableCommandPoolLock.lock(); if (sIPCFreeDispatchableCommands.empty()) { cemuLog_log(LogType::Force, "IOS: Exhausted pool of dispatchable commands"); - sIPCDispatchableCommandPoolLock.release(); + sIPCDispatchableCommandPoolLock.unlock(); return nullptr; } IOSDispatchableCommand* cmd = sIPCFreeDispatchableCommands.front(); sIPCFreeDispatchableCommands.pop(); cemu_assert_debug(!cmd->isAllocated); cmd->isAllocated = true; - sIPCDispatchableCommandPoolLock.release(); + sIPCDispatchableCommandPoolLock.unlock(); return cmd; } void _IPCReleaseDispatchableCommand(IOSDispatchableCommand* cmd) { - sIPCDispatchableCommandPoolLock.acquire(); + sIPCDispatchableCommandPoolLock.lock(); cemu_assert_debug(cmd->isAllocated); cmd->isAllocated = false; sIPCFreeDispatchableCommands.push(cmd); - sIPCDispatchableCommandPoolLock.release(); + sIPCDispatchableCommandPoolLock.unlock(); } static constexpr size_t MAX_NUM_ACTIVE_DEV_HANDLES = 96; // per process diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Callbacks.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Callbacks.cpp index 403bec61..aef7e5aa 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Callbacks.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Callbacks.cpp @@ -8,27 +8,27 @@ struct CoreinitAsyncCallback static void queue(MPTR functionMPTR, uint32 numParameters, uint32 r3, uint32 r4, uint32 r5, uint32 r6, uint32 r7, uint32 r8, uint32 r9, uint32 r10) { - s_asyncCallbackSpinlock.acquire(); + s_asyncCallbackSpinlock.lock(); s_asyncCallbackQueue.emplace_back(allocateAndInitFromPool(functionMPTR, numParameters, r3, r4, r5, r6, r7, r8, r9, r10)); - s_asyncCallbackSpinlock.release(); + s_asyncCallbackSpinlock.unlock(); } static void callNextFromQueue() { - s_asyncCallbackSpinlock.acquire(); + s_asyncCallbackSpinlock.lock(); if (s_asyncCallbackQueue.empty()) { cemuLog_log(LogType::Force, "AsyncCallbackQueue is empty. Unexpected behavior"); - s_asyncCallbackSpinlock.release(); + s_asyncCallbackSpinlock.unlock(); return; } CoreinitAsyncCallback* cb = s_asyncCallbackQueue[0]; s_asyncCallbackQueue.erase(s_asyncCallbackQueue.begin()); - s_asyncCallbackSpinlock.release(); + s_asyncCallbackSpinlock.unlock(); cb->doCall(); - s_asyncCallbackSpinlock.acquire(); + s_asyncCallbackSpinlock.lock(); releaseToPool(cb); - s_asyncCallbackSpinlock.release(); + s_asyncCallbackSpinlock.unlock(); } private: @@ -39,7 +39,7 @@ private: static CoreinitAsyncCallback* allocateAndInitFromPool(MPTR functionMPTR, uint32 numParameters, uint32 r3, uint32 r4, uint32 r5, uint32 r6, uint32 r7, uint32 r8, uint32 r9, uint32 r10) { - cemu_assert_debug(s_asyncCallbackSpinlock.isHolding()); + cemu_assert_debug(s_asyncCallbackSpinlock.is_locked()); if (s_asyncCallbackPool.empty()) { CoreinitAsyncCallback* cb = new CoreinitAsyncCallback(functionMPTR, numParameters, r3, r4, r5, r6, r7, r8, r9, r10); @@ -54,7 +54,7 @@ private: static void releaseToPool(CoreinitAsyncCallback* cb) { - cemu_assert_debug(s_asyncCallbackSpinlock.isHolding()); + cemu_assert_debug(s_asyncCallbackSpinlock.is_locked()); s_asyncCallbackPool.emplace_back(cb); } diff --git a/src/Cafe/OS/libs/coreinit/coreinit_MPQueue.cpp b/src/Cafe/OS/libs/coreinit/coreinit_MPQueue.cpp index 8fc5a935..1816ef24 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_MPQueue.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_MPQueue.cpp @@ -6,8 +6,8 @@ // titles that utilize MP task queue: Yoshi's Woolly World, Fast Racing Neo, Tokyo Mirage Sessions, Mii Maker -#define AcquireMPQLock() s_workaroundSpinlock.acquire() -#define ReleaseMPQLock() s_workaroundSpinlock.release() +#define AcquireMPQLock() s_workaroundSpinlock.lock() +#define ReleaseMPQLock() s_workaroundSpinlock.unlock() namespace coreinit { @@ -35,7 +35,7 @@ namespace coreinit void MPInitTask(MPTask* task, void* func, void* data, uint32 size) { - s_workaroundSpinlock.acquire(); + s_workaroundSpinlock.lock(); task->thisptr = task; task->coreIndex = PPC_CORE_COUNT; @@ -48,7 +48,7 @@ namespace coreinit task->userdata = nullptr; task->runtime = 0; - s_workaroundSpinlock.release(); + s_workaroundSpinlock.unlock(); } bool MPTermTask(MPTask* task) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp index 84f7c5cf..bdcc531d 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp @@ -465,12 +465,12 @@ namespace coreinit void _OSFastMutex_AcquireContention(OSFastMutex* fastMutex) { - g_fastMutexSpinlock.acquire(); + g_fastMutexSpinlock.lock(); } void _OSFastMutex_ReleaseContention(OSFastMutex* fastMutex) { - g_fastMutexSpinlock.release(); + g_fastMutexSpinlock.unlock(); } void OSFastMutex_LockInternal(OSFastMutex* fastMutex) diff --git a/src/Cafe/OS/libs/snd_core/ax_ist.cpp b/src/Cafe/OS/libs/snd_core/ax_ist.cpp index 26f975af..528a8302 100644 --- a/src/Cafe/OS/libs/snd_core/ax_ist.cpp +++ b/src/Cafe/OS/libs/snd_core/ax_ist.cpp @@ -778,7 +778,7 @@ namespace snd_core void AXIst_SyncVPB(AXVPBInternal_t** lastProcessedDSPShadowCopy, AXVPBInternal_t** lastProcessedPPCShadowCopy) { - __AXVoiceListSpinlock.acquire(); + __AXVoiceListSpinlock.lock(); AXVPBInternal_t* previousInternalDSP = nullptr; AXVPBInternal_t* previousInternalPPC = nullptr; @@ -869,7 +869,7 @@ namespace snd_core else *lastProcessedPPCShadowCopy = nullptr; } - __AXVoiceListSpinlock.release(); + __AXVoiceListSpinlock.unlock(); } void AXIst_HandleFrameCallbacks() diff --git a/src/Cafe/OS/libs/snd_core/ax_voice.cpp b/src/Cafe/OS/libs/snd_core/ax_voice.cpp index 746f04d3..6a599c8b 100644 --- a/src/Cafe/OS/libs/snd_core/ax_voice.cpp +++ b/src/Cafe/OS/libs/snd_core/ax_voice.cpp @@ -393,7 +393,7 @@ namespace snd_core AXVPB* AXAcquireVoiceEx(uint32 priority, MPTR callbackEx, MPTR userParam) { cemu_assert(priority != AX_PRIORITY_FREE && priority < AX_PRIORITY_MAX); - __AXVoiceListSpinlock.acquire(); + __AXVoiceListSpinlock.lock(); AXVPB* vpb = AXVoiceList_GetFreeVoice(); if (vpb != nullptr) { @@ -410,7 +410,7 @@ namespace snd_core if (droppedVoice == nullptr) { // no voice available - __AXVoiceListSpinlock.release(); + __AXVoiceListSpinlock.unlock(); return nullptr; } vpb->userParam = userParam; @@ -418,18 +418,18 @@ namespace snd_core vpb->callbackEx = callbackEx; AXVPB_SetVoiceDefault(vpb); } - __AXVoiceListSpinlock.release(); + __AXVoiceListSpinlock.unlock(); return vpb; } void AXFreeVoice(AXVPB* vpb) { cemu_assert(vpb != nullptr); - __AXVoiceListSpinlock.acquire(); + __AXVoiceListSpinlock.lock(); if (vpb->priority == (uint32be)AX_PRIORITY_FREE) { forceLog_printf("AXFreeVoice() called on free voice\n"); - __AXVoiceListSpinlock.release(); + __AXVoiceListSpinlock.unlock(); return; } AXVoiceProtection_Release(vpb); @@ -442,7 +442,7 @@ namespace snd_core vpb->callback = MPTR_NULL; vpb->callbackEx = MPTR_NULL; AXVoiceList_AddFreeVoice(vpb); - __AXVoiceListSpinlock.release(); + __AXVoiceListSpinlock.unlock(); } void AXVPBInit() diff --git a/src/gui/guiWrapper.h b/src/gui/guiWrapper.h index ce041efb..4f46f8b4 100644 --- a/src/gui/guiWrapper.h +++ b/src/gui/guiWrapper.h @@ -45,7 +45,8 @@ struct WindowInfo { const std::lock_guard lock(keycode_mutex); m_keydown[keycode] = state; - }; + } + bool get_keystate(uint32 keycode) { const std::lock_guard lock(keycode_mutex); @@ -54,25 +55,20 @@ struct WindowInfo return false; return result->second; } - void get_keystates(std::unordered_map& buttons_out) - { - const std::lock_guard lock(keycode_mutex); - for (auto&& button : m_keydown) - { - buttons_out[button.first] = button.second; - } - } + void set_keystatesdown() { const std::lock_guard lock(keycode_mutex); std::for_each(m_keydown.begin(), m_keydown.end(), [](std::pair& el){ el.second = false; }); } + template void iter_keystates(fn f) { const std::lock_guard lock(keycode_mutex); std::for_each(m_keydown.cbegin(), m_keydown.cend(), f); } + WindowHandleInfo window_main; WindowHandleInfo window_pad; diff --git a/src/gui/input/InputSettings2.cpp b/src/gui/input/InputSettings2.cpp index 095aa52a..7757a2ae 100644 --- a/src/gui/input/InputSettings2.cpp +++ b/src/gui/input/InputSettings2.cpp @@ -111,7 +111,7 @@ InputSettings2::InputSettings2(wxWindow* parent) Bind(wxEVT_TIMER, &InputSettings2::on_timer, this); m_timer = new wxTimer(this); - m_timer->Start(100); + m_timer->Start(25); m_controller_changed = EventService::instance().connect(&InputSettings2::on_controller_changed, this); } diff --git a/src/gui/input/panels/InputPanel.cpp b/src/gui/input/panels/InputPanel.cpp index b01157c8..984b46d7 100644 --- a/src/gui/input/panels/InputPanel.cpp +++ b/src/gui/input/panels/InputPanel.cpp @@ -41,77 +41,69 @@ void InputPanel::on_timer(const EmulatedControllerPtr& emulated_controller, cons } static bool s_was_idle = true; - if (!std::any_of(state.buttons.begin(), state.buttons.end(), [](auto el){ return el.second; })) { + if (state.buttons.IsIdle()) + { s_was_idle = true; return; } - if (!s_was_idle) { + if (!s_was_idle) + { return; } - auto get_button_state = [&](uint32 key_id) - { - auto result = state.buttons.find(key_id); - if (result == state.buttons.end()) - return false; - return result->second; - }; s_was_idle = false; - for(auto && button : state.buttons) + for(const auto& id : state.buttons.GetButtonList()) { - if (button.second) + if (controller->has_axis()) { - auto id=button.first; - if (controller->has_axis()) { - // test if one axis direction is pressed more than the other - if ((id == kAxisXP || id == kAxisXN) && (get_button_state(kAxisYP) || get_button_state(kAxisYN))) - { - if (std::abs(state.axis.y) > std::abs(state.axis.x)) - continue; - } - else if ((id == kAxisYP || id == kAxisYN) && (get_button_state(kAxisXP) || get_button_state(kAxisXN))) - { - if (std::abs(state.axis.x) > std::abs(state.axis.y)) - continue; - } - else if ((id == kRotationXP || id == kRotationXN) && (get_button_state(kRotationYP) || get_button_state(kRotationYN))) - { - if (std::abs(state.rotation.y) > std::abs(state.rotation.x)) - continue; - } - else if ((id == kRotationYP || id == kRotationYN) && (get_button_state(kRotationXP) || get_button_state(kRotationXN))) - { - if (std::abs(state.rotation.x) > std::abs(state.rotation.y)) - continue; - } - else if ((id == kTriggerXP || id == kTriggerXN) && (get_button_state(kTriggerYP) || get_button_state(kTriggerYN))) - { - if (std::abs(state.trigger.y) > std::abs(state.trigger.x)) - continue; - } - else if ((id == kTriggerYP || id == kTriggerYN) && (get_button_state(kTriggerXP) || get_button_state(kTriggerXN))) - { - if (std::abs(state.trigger.x) > std::abs(state.trigger.y)) - continue; - } - - // ignore too low button values on configuration - if (id >= kButtonAxisStart) - { - if (controller->get_axis_value(id) < 0.33f) { - forceLogDebug_printf("skipping since value too low %f", controller->get_axis_value(id)); - s_was_idle = true; - return; - } - } + // test if one axis direction is pressed more than the other + if ((id == kAxisXP || id == kAxisXN) && (state.buttons.GetButtonState(kAxisYP) || state.buttons.GetButtonState(kAxisYN))) + { + if (std::abs(state.axis.y) > std::abs(state.axis.x)) + continue; + } + else if ((id == kAxisYP || id == kAxisYN) && (state.buttons.GetButtonState(kAxisXP) || state.buttons.GetButtonState(kAxisXN))) + { + if (std::abs(state.axis.x) > std::abs(state.axis.y)) + continue; + } + else if ((id == kRotationXP || id == kRotationXN) && (state.buttons.GetButtonState(kRotationYP) || state.buttons.GetButtonState(kRotationYN))) + { + if (std::abs(state.rotation.y) > std::abs(state.rotation.x)) + continue; + } + else if ((id == kRotationYP || id == kRotationYN) && (state.buttons.GetButtonState(kRotationXP) || state.buttons.GetButtonState(kRotationXN))) + { + if (std::abs(state.rotation.x) > std::abs(state.rotation.y)) + continue; + } + else if ((id == kTriggerXP || id == kTriggerXN) && (state.buttons.GetButtonState(kTriggerYP) || state.buttons.GetButtonState(kTriggerYN))) + { + if (std::abs(state.trigger.y) > std::abs(state.trigger.x)) + continue; + } + else if ((id == kTriggerYP || id == kTriggerYN) && (state.buttons.GetButtonState(kTriggerXP) || state.buttons.GetButtonState(kTriggerXN))) + { + if (std::abs(state.trigger.x) > std::abs(state.trigger.y)) + continue; } - emulated_controller->set_mapping(mapping, controller, id); - element->SetValue(controller->get_button_name(id)); - element->SetBackgroundColour(kKeyColourNormalMode); - m_color_backup[element->GetId()] = kKeyColourNormalMode; - break; + // ignore too low button values on configuration + if (id >= kButtonAxisStart) + { + if (controller->get_axis_value(id) < 0.33f) { + forceLogDebug_printf("skipping since value too low %f", controller->get_axis_value(id)); + s_was_idle = true; + return; + } + } } + + emulated_controller->set_mapping(mapping, controller, id); + element->SetValue(controller->get_button_name(id)); + element->SetBackgroundColour(kKeyColourNormalMode); + m_color_backup[element->GetId()] = kKeyColourNormalMode; + break; } if (const auto sibling = get_next_sibling(element)) diff --git a/src/input/api/Controller.cpp b/src/input/api/Controller.cpp index a75cdf57..b7831def 100644 --- a/src/input/api/Controller.cpp +++ b/src/input/api/Controller.cpp @@ -15,10 +15,7 @@ const ControllerState& ControllerBase::update_state() ControllerState result = raw_state(); // ignore default buttons - for (auto&& el : m_default_state.buttons) - { - result.buttons[el.first] = result.buttons[el.first] && !el.second; - } + result.buttons.UnsetButtons(m_default_state.buttons); // apply deadzone and range and ignore default axis values apply_axis_setting(result.axis, m_default_state.axis, m_settings.axis); apply_axis_setting(result.rotation, m_default_state.rotation, m_settings.rotation); @@ -26,22 +23,22 @@ const ControllerState& ControllerBase::update_state() #define APPLY_AXIS_BUTTON(_axis_, _flag_) \ if (result._axis_.x < -ControllerState::kAxisThreshold) \ - result.buttons[(_flag_) + (kAxisXN - kAxisXP)]=true; \ + result.buttons.SetButtonState((_flag_) + (kAxisXN - kAxisXP), true); \ else if (result._axis_.x > ControllerState::kAxisThreshold) \ - result.buttons[(_flag_)]=true; \ + result.buttons.SetButtonState((_flag_), true); \ if (result._axis_.y < -ControllerState::kAxisThreshold) \ - result.buttons[(_flag_) + 1 + (kAxisXN - kAxisXP)]=true; \ + result.buttons.SetButtonState((_flag_) + 1 + (kAxisXN - kAxisXP), true); \ else if (result._axis_.y > ControllerState::kAxisThreshold) \ - result.buttons[(_flag_) + 1]=true; + result.buttons.SetButtonState((_flag_) + 1, true); if (result.axis.x < -ControllerState::kAxisThreshold) - result.buttons[(kAxisXP) + (kAxisXN - kAxisXP)]=true; + result.buttons.SetButtonState((kAxisXP) + (kAxisXN - kAxisXP), true); else if (result.axis.x > ControllerState::kAxisThreshold) - result.buttons[(kAxisXP)]=true; + result.buttons.SetButtonState((kAxisXP), true); if (result.axis.y < -ControllerState::kAxisThreshold) - result.buttons[(kAxisXP) + 1 + (kAxisXN - kAxisXP)]=true; + result.buttons.SetButtonState((kAxisXP) + 1 + (kAxisXN - kAxisXP), true); else if (result.axis.y > ControllerState::kAxisThreshold) - result.buttons[(kAxisXP) + 1]=true; + result.buttons.SetButtonState((kAxisXP) + 1, true); APPLY_AXIS_BUTTON(rotation, kRotationXP); APPLY_AXIS_BUTTON(trigger, kTriggerXP); @@ -129,8 +126,7 @@ bool ControllerBase::operator==(const ControllerBase& c) const float ControllerBase::get_axis_value(uint64 button) const { - auto buttonState=m_last_state.buttons.find(button); - if (buttonState!=m_last_state.buttons.end() && buttonState->second) + if (m_last_state.buttons.GetButtonState(button)) { if (button <= kButtonNoneAxisMAX || !has_axis()) return 1.0f; diff --git a/src/input/api/ControllerState.h b/src/input/api/ControllerState.h index a8ff6628..3e6149ab 100644 --- a/src/input/api/ControllerState.h +++ b/src/input/api/ControllerState.h @@ -1,6 +1,115 @@ #pragma once #include +#include "util/helpers/fspinlock.h" + +// helper class for storing and managing button press states in a thread-safe manner +struct ControllerButtonState +{ + ControllerButtonState() = default; + ControllerButtonState(const ControllerButtonState& other) + { + this->m_pressedButtons = other.m_pressedButtons; + } + + ControllerButtonState(ControllerButtonState&& other) + { + this->m_pressedButtons = std::move(other.m_pressedButtons); + } + + void SetButtonState(uint32 buttonId, bool isPressed) + { + std::lock_guard _l(this->m_spinlock); + if (isPressed) + { + if (std::find(m_pressedButtons.cbegin(), m_pressedButtons.cend(), buttonId) != m_pressedButtons.end()) + return; + m_pressedButtons.emplace_back(buttonId); + } + else + { + std::erase(m_pressedButtons, buttonId); + } + } + + // set multiple buttons at once within a single lock interval + void SetPressedButtons(std::span buttonList) + { + std::lock_guard _l(this->m_spinlock); + for (auto& buttonId : buttonList) + { + if (std::find(m_pressedButtons.cbegin(), m_pressedButtons.cend(), buttonId) == m_pressedButtons.end()) + m_pressedButtons.emplace_back(buttonId); + } + } + + // returns true if pressed + bool GetButtonState(uint32 buttonId) const + { + std::lock_guard _l(this->m_spinlock); + bool r = std::find(m_pressedButtons.cbegin(), m_pressedButtons.cend(), buttonId) != m_pressedButtons.cend(); + return r; + } + + // remove pressed state for all pressed buttons in buttonsToUnset + void UnsetButtons(const ControllerButtonState& buttonsToUnset) + { + std::scoped_lock _l(this->m_spinlock, buttonsToUnset.m_spinlock); + for (auto it = m_pressedButtons.begin(); it != m_pressedButtons.end();) + { + if (std::find(buttonsToUnset.m_pressedButtons.cbegin(), buttonsToUnset.m_pressedButtons.cend(), *it) == buttonsToUnset.m_pressedButtons.cend()) + { + ++it; + continue; + } + it = m_pressedButtons.erase(it); + } + } + + // returns true if no buttons are pressed + bool IsIdle() const + { + std::lock_guard _l(this->m_spinlock); + const bool r = m_pressedButtons.empty(); + return r; + } + + std::vector GetButtonList() const + { + std::lock_guard _l(this->m_spinlock); + std::vector copy = m_pressedButtons; + return copy; + } + + bool operator==(const ControllerButtonState& other) const + { + std::scoped_lock _l(this->m_spinlock, other.m_spinlock); + auto& otherButtons = other.m_pressedButtons; + if (m_pressedButtons.size() != otherButtons.size()) + { + return false; + } + for (auto& buttonId : m_pressedButtons) + { + if (std::find(otherButtons.cbegin(), otherButtons.cend(), buttonId) == otherButtons.cend()) + { + return false; + } + } + return true; + } + + ControllerButtonState& operator=(ControllerButtonState&& other) + { + cemu_assert_debug(!other.m_spinlock.is_locked()); + this->m_pressedButtons = std::move(other.m_pressedButtons); + return *this; + } + +private: + std::vector m_pressedButtons; // since only very few buttons are pressed at a time, using a vector with linear scan is more efficient than a set/map + mutable FSpinlock m_spinlock; +}; struct ControllerState { @@ -17,7 +126,7 @@ struct ControllerState glm::vec2 rotation{ }; glm::vec2 trigger{ }; - std::unordered_map buttons{}; + ControllerButtonState buttons{}; uint64 last_state = 0; diff --git a/src/input/api/DSU/DSUController.cpp b/src/input/api/DSU/DSUController.cpp index c04c8453..f134440c 100644 --- a/src/input/api/DSU/DSUController.cpp +++ b/src/input/api/DSU/DSUController.cpp @@ -137,7 +137,7 @@ ControllerState DSUController::raw_state() { if (HAS_BIT(state.data.state1, i)) { - result.buttons[bitindex]=true; + result.buttons.SetButtonState(bitindex, true); } } @@ -145,12 +145,12 @@ ControllerState DSUController::raw_state() { if (HAS_BIT(state.data.state2, i)) { - result.buttons[bitindex]=true; + result.buttons.SetButtonState(bitindex, true); } } if (state.data.touch) - result.buttons[kButton16]=true; + result.buttons.SetButtonState(kButton16, true); result.axis.x = (float)state.data.lx / std::numeric_limits::max(); result.axis.x = (result.axis.x * 2.0f) - 1.0f; diff --git a/src/input/api/DirectInput/DirectInputController.cpp b/src/input/api/DirectInput/DirectInputController.cpp index 87bd3685..dbb7c80c 100644 --- a/src/input/api/DirectInput/DirectInputController.cpp +++ b/src/input/api/DirectInput/DirectInputController.cpp @@ -245,7 +245,6 @@ ControllerState DirectInputController::raw_state() ControllerState result{}; if (!is_connected()) return result; - HRESULT hr = m_device->Poll(); if (FAILED(hr)) { @@ -277,9 +276,7 @@ ControllerState DirectInputController::raw_state() for (size_t i = 0; i < std::size(state.rgbButtons); ++i) { if (HAS_BIT(state.rgbButtons[i], 7)) - { - result.buttons[i]=true; - } + result.buttons.SetButtonState(i, true); } // axis @@ -316,19 +313,19 @@ ControllerState DirectInputController::raw_state() { switch (pov) { - case 0: result.buttons[kButtonUp]=true; + case 0: result.buttons.SetButtonState(kButtonUp, true); break; - case 4500: result.buttons[kButtonUp]=true; // up + right - case 9000: result.buttons[kButtonRight]=true; + case 4500: result.buttons.SetButtonState(kButtonUp, true); // up + right + case 9000: result.buttons.SetButtonState(kButtonRight, true); break; - case 13500: result.buttons[kButtonRight] = true; // right + down - case 18000: result.buttons[kButtonDown] = true; + case 13500: result.buttons.SetButtonState(kButtonRight, true); // right + down + case 18000: result.buttons.SetButtonState(kButtonDown, true); break; - case 22500: result.buttons[kButtonDown] = true; // down + left - case 27000: result.buttons[kButtonLeft] = true; + case 22500: result.buttons.SetButtonState(kButtonDown, true); // down + left + case 27000: result.buttons.SetButtonState(kButtonLeft, true); break; - case 31500: result.buttons[kButtonLeft] = true; // left + up - result.buttons[kButtonUp] = true; // left + up + case 31500: result.buttons.SetButtonState(kButtonLeft, true); // left + up + result.buttons.SetButtonState(kButtonUp, true); // left + up break; } } diff --git a/src/input/api/Keyboard/KeyboardController.cpp b/src/input/api/Keyboard/KeyboardController.cpp index 0d41fe1a..9cd31b4b 100644 --- a/src/input/api/Keyboard/KeyboardController.cpp +++ b/src/input/api/Keyboard/KeyboardController.cpp @@ -1,5 +1,6 @@ -#include "input/api/Keyboard/KeyboardController.h" +#include +#include "input/api/Keyboard/KeyboardController.h" #include "gui/guiWrapper.h" KeyboardController::KeyboardController() @@ -51,7 +52,9 @@ ControllerState KeyboardController::raw_state() ControllerState result{}; if (g_window_info.app_active) { - g_window_info.get_keystates(result.buttons); + boost::container::small_vector pressedKeys; + g_window_info.iter_keystates([&pressedKeys](const std::pair& keyState) { if (keyState.second) pressedKeys.emplace_back(keyState.first); }); + result.buttons.SetPressedButtons(pressedKeys); } return result; } diff --git a/src/input/api/SDL/SDLController.cpp b/src/input/api/SDL/SDLController.cpp index 63f7f4d3..a3e7eece 100644 --- a/src/input/api/SDL/SDLController.cpp +++ b/src/input/api/SDL/SDLController.cpp @@ -146,9 +146,7 @@ ControllerState SDLController::raw_state() for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) { if (m_buttons[i] && SDL_GameControllerGetButton(m_controller, (SDL_GameControllerButton)i)) - { - result.buttons[i]=true; - } + result.buttons.SetButtonState(i, true); } if (m_axis[SDL_CONTROLLER_AXIS_LEFTX]) diff --git a/src/input/api/Wiimote/NativeWiimoteController.cpp b/src/input/api/Wiimote/NativeWiimoteController.cpp index c5059f7c..3f9e82a5 100644 --- a/src/input/api/Wiimote/NativeWiimoteController.cpp +++ b/src/input/api/Wiimote/NativeWiimoteController.cpp @@ -207,16 +207,16 @@ ControllerState NativeWiimoteController::raw_state() const auto state = m_provider->get_state(m_index); for (int i = 0; i < std::numeric_limits::digits; i++) - result.buttons[i] = state.buttons & (1<(state.m_extension)) { const auto nunchuck = std::get(state.m_extension); if (nunchuck.c) - result.buttons[kWiimoteButton_C]=true; + result.buttons.SetButtonState(kWiimoteButton_C, true); if (nunchuck.z) - result.buttons[kWiimoteButton_Z]=true; + result.buttons.SetButtonState(kWiimoteButton_Z, true); result.axis = nunchuck.axis; } @@ -225,8 +225,11 @@ ControllerState NativeWiimoteController::raw_state() const auto classic = std::get(state.m_extension); uint64 buttons = (uint64)classic.buttons << kHighestWiimote; for (int i = 0; i < std::numeric_limits::digits; i++) - result.buttons[i] = result.buttons[i] || (buttons & (1 << i)); - + { + // OR with base buttons + if((buttons & (1 << i))) + result.buttons.SetButtonState(i, true); + } result.axis = classic.left_axis; result.rotation = classic.right_axis; result.trigger = classic.trigger; diff --git a/src/input/api/XInput/XInputController.cpp b/src/input/api/XInput/XInputController.cpp index e2be29b6..c3ab67cf 100644 --- a/src/input/api/XInput/XInputController.cpp +++ b/src/input/api/XInput/XInputController.cpp @@ -121,7 +121,7 @@ ControllerState XInputController::raw_state() // Buttons for(int i=0;i::digits;i++) - result.buttons[i] = state.Gamepad.wButtons & (1< 0) result.axis.x = (float)state.Gamepad.sThumbLX / std::numeric_limits::max(); diff --git a/src/input/emulated/EmulatedController.cpp b/src/input/emulated/EmulatedController.cpp index 0028db58..b7a4743c 100644 --- a/src/input/emulated/EmulatedController.cpp +++ b/src/input/emulated/EmulatedController.cpp @@ -279,13 +279,9 @@ bool EmulatedController::is_mapping_down(uint64 mapping) const const auto it = m_mappings.find(mapping); if (it != m_mappings.cend()) { - if (const auto controller = it->second.controller.lock()) { - auto& buttons=controller->get_state().buttons; - auto buttonState=buttons.find(it->second.button); - return buttonState!=buttons.end() && buttonState->second; - } + if (const auto controller = it->second.controller.lock()) + return controller->get_state().buttons.GetButtonState(it->second.button); } - return false; } diff --git a/src/util/helpers/fspinlock.h b/src/util/helpers/fspinlock.h index 04f761e7..4fa642f4 100644 --- a/src/util/helpers/fspinlock.h +++ b/src/util/helpers/fspinlock.h @@ -7,32 +7,33 @@ class FSpinlock { public: - void acquire() + bool is_locked() const { - while( true ) + return m_lockBool.load(std::memory_order_relaxed); + } + + // implement BasicLockable and Lockable + void lock() const + { + while (true) { - if (!m_lockBool.exchange(true, std::memory_order_acquire)) + if (!m_lockBool.exchange(true, std::memory_order_acquire)) break; while (m_lockBool.load(std::memory_order_relaxed)) _mm_pause(); } } - bool tryAcquire() + bool try_lock() const { return !m_lockBool.exchange(true, std::memory_order_acquire); } - void release() + void unlock() const { m_lockBool.store(false, std::memory_order_release); } - bool isHolding() const - { - return m_lockBool.load(std::memory_order_relaxed); - } - private: - std::atomic m_lockBool = false; + mutable std::atomic m_lockBool = false; }; \ No newline at end of file From 8f674933d2b507ce91c17b69fef2c38c6f1fbcaf Mon Sep 17 00:00:00 2001 From: emiyl Date: Sun, 23 Oct 2022 15:58:28 +0100 Subject: [PATCH 045/616] Create Cemu .app bundle for macOS (#364) --- .github/workflows/build.yml | 17 +++++++-- .../workflows/deploy_experimental_release.yml | 8 +--- .github/workflows/deploy_stable_release.yml | 8 +--- CMakeLists.txt | 1 + src/CMakeLists.txt | 29 +++++++++++++- src/gui/CemuApp.cpp | 3 ++ src/resource/MacOSXBundleInfo.plist.in | 36 ++++++++++++++++++ src/resource/cemu.icns | Bin 0 -> 817145 bytes 8 files changed, 84 insertions(+), 18 deletions(-) create mode 100644 src/resource/MacOSXBundleInfo.plist.in create mode 100644 src/resource/cemu.icns diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ac010c8f..5fc56aec 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -162,6 +162,8 @@ jobs: with: name: cemu-bin-windows-x64 path: ./bin/Cemu.exe + + build-macos: runs-on: macos-12 steps: @@ -213,7 +215,7 @@ jobs: run: | mkdir build cd build - cmake .. ${{ env.BUILD_FLAGS }} -DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} -DCMAKE_C_COMPILER=/usr/local/opt/llvm@14/bin/clang -DCMAKE_CXX_COMPILER=/usr/local/opt/llvm@14/bin/clang++ -G Ninja + cmake .. ${{ env.BUILD_FLAGS }} -DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} -DPORTABLE=OFF -DMACOS_BUNDLE=ON -DCMAKE_C_COMPILER=/usr/local/opt/llvm@14/bin/clang -DCMAKE_CXX_COMPILER=/usr/local/opt/llvm@14/bin/clang++ -G Ninja - name: "Build Cemu" run: | @@ -221,11 +223,20 @@ jobs: - name: Prepare artifact if: ${{ inputs.deploymode == 'release' }} - run: chmod a+x bin/Cemu_release && mv bin/Cemu_release bin/Cemu + run: | + mkdir bin/Cemu_app + mv bin/Cemu_release.app bin/Cemu_app/Cemu.app + mv bin/Cemu_app/Cemu.app/Contents/MacOS/Cemu_release bin/Cemu_app/Cemu.app/Contents/MacOS/Cemu + sed -i '' 's/Cemu_release/Cemu/g' bin/Cemu_app/Cemu.app/Contents/Info.plist + chmod a+x bin/Cemu_app/Cemu.app/Contents/MacOS/Cemu + ln -s /Applications bin/Cemu_app/Applications + hdiutil create ./bin/tmp.dmg -ov -volname "Cemu" -fs HFS+ -srcfolder "./bin/Cemu_app" + hdiutil convert ./bin/tmp.dmg -format UDZO -o bin/Cemu.dmg + rm bin/tmp.dmg - name: Upload artifact uses: actions/upload-artifact@v3 if: ${{ inputs.deploymode == 'release' }} with: name: cemu-bin-macos-x64 - path: ./bin/Cemu + path: ./bin/Cemu.dmg \ No newline at end of file diff --git a/.github/workflows/deploy_experimental_release.yml b/.github/workflows/deploy_experimental_release.yml index 31a661e2..8a5ee0e9 100644 --- a/.github/workflows/deploy_experimental_release.yml +++ b/.github/workflows/deploy_experimental_release.yml @@ -64,13 +64,7 @@ jobs: rm -r ./${{ env.CEMU_FOLDER_NAME }} - name: Create release from macos-bin - run: | - ls ./ - ls ./bin/ - cp -R ./bin ./${{ env.CEMU_FOLDER_NAME }} - mv cemu-bin-macos-x64/Cemu ./${{ env.CEMU_FOLDER_NAME }}/Cemu - zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-macos-12-x64.zip ${{ env.CEMU_FOLDER_NAME }} - rm -r ./${{ env.CEMU_FOLDER_NAME }} + run: cp cemu-bin-macos-x64/Cemu.dmg upload/cemu-${{ env.CEMU_VERSION }}-macos-12-x64.dmg - name: Create release run: | diff --git a/.github/workflows/deploy_stable_release.yml b/.github/workflows/deploy_stable_release.yml index e0a7ac3d..9e880218 100644 --- a/.github/workflows/deploy_stable_release.yml +++ b/.github/workflows/deploy_stable_release.yml @@ -68,13 +68,7 @@ jobs: rm -r ./${{ env.CEMU_FOLDER_NAME }} - name: Create release from macos-bin - run: | - ls ./ - ls ./bin/ - cp -R ./bin ./${{ env.CEMU_FOLDER_NAME }} - mv cemu-bin-macos-x64/Cemu ./${{ env.CEMU_FOLDER_NAME }}/Cemu - zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-macos-12-x64.zip ${{ env.CEMU_FOLDER_NAME }} - rm -r ./${{ env.CEMU_FOLDER_NAME }} + run: cp cemu-bin-macos-x64/Cemu.dmg upload/cemu-${{ env.CEMU_VERSION }}-macos-12-x64.dmg - name: Create release run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index b973a3f5..ce4d444e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.21.1) option(ENABLE_VCPKG "Enable the vcpkg package manager" ON) option(PORTABLE "All data created and maintained by Cemu will be in the directory where the executable file is located" ON) +option(MACOS_BUNDLE "The executable when built on macOS will be created as an application bundle" OFF) set(EXPERIMENTAL_VERSION "" CACHE STRING "") # used by CI script to set experimental version if (EXPERIMENTAL_VERSION) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 024432d0..1679623f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -67,12 +67,39 @@ endif() set_property(TARGET CemuBin PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") set_property(TARGET CemuBin PROPERTY WIN32_EXECUTABLE $>) +set(OUTPUT_NAME "Cemu_$>") + +if (MACOS_BUNDLE) + set_property(TARGET CemuBin PROPERTY MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/resource/MacOSXBundleInfo.plist.in") + + set(RESOURCE_FILES "${CMAKE_SOURCE_DIR}/src/resource/cemu.icns") + target_sources(CemuBin PRIVATE "${RESOURCE_FILES}") + + set(MACOSX_BUNDLE_CATEGORY "public.app-category.games") + + set_target_properties(CemuBin PROPERTIES + MACOSX_BUNDLE true + RESOURCE "${RESOURCE_FILES}" + ) + + set(FOLDERS gameProfiles resources) + foreach(folder ${FOLDERS}) + add_custom_command (TARGET CemuBin POST_BUILD + COMMAND ${CMAKE_COMMAND} ARGS -E copy_directory "${CMAKE_SOURCE_DIR}/bin/${folder}" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/SharedSupport/${folder}") + endforeach(folder) + + add_custom_command (TARGET CemuBin POST_BUILD + COMMAND ${CMAKE_COMMAND} ARGS -E copy "/usr/local/lib/libMoltenVK.dylib" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libMoltenVK.dylib") + + add_custom_command (TARGET CemuBin POST_BUILD + COMMAND bash -c "install_name_tool -add_rpath @executable_path/../Frameworks ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}") +endif() set_target_properties(CemuBin PROPERTIES # multi-configuration generators will add a config subdirectory to RUNTIME_OUTPUT_DIRECTORY if no generator expression is used # to get the same behavior everywhere we append an empty generator expression RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../bin/$<1:>" - OUTPUT_NAME "Cemu_$>" + OUTPUT_NAME "${OUTPUT_NAME}" ) target_link_libraries(CemuBin PRIVATE diff --git a/src/gui/CemuApp.cpp b/src/gui/CemuApp.cpp index c502e8d2..1de007fb 100644 --- a/src/gui/CemuApp.cpp +++ b/src/gui/CemuApp.cpp @@ -77,6 +77,9 @@ bool CemuApp::OnInit() auto standardPaths = wxStandardPaths::Get(); #ifdef PORTABLE fs::path exePath(standardPaths.GetExecutablePath().ToStdString()); +#if MACOS_BUNDLE + exePath = exePath.parent_path().parent_path().parent_path(); +#endif user_data_path = config_path = cache_path = data_path = exePath.parent_path(); #else SetAppName("Cemu"); diff --git a/src/resource/MacOSXBundleInfo.plist.in b/src/resource/MacOSXBundleInfo.plist.in new file mode 100644 index 00000000..73ff737b --- /dev/null +++ b/src/resource/MacOSXBundleInfo.plist.in @@ -0,0 +1,36 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + ${MACOSX_BUNDLE_LONG_VERSION_STRING} + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleSignature + ???? + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CSResourcesFileMapped + + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + LSApplicationCategoryType + ${MACOSX_BUNDLE_CATEGORY} + + \ No newline at end of file diff --git a/src/resource/cemu.icns b/src/resource/cemu.icns new file mode 100644 index 0000000000000000000000000000000000000000..c6d8c5b5088fc39792a697af8f771de09b5b61c5 GIT binary patch literal 817145 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