summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dist/license.md3
-rw-r--r--dist/qt_themes/colorful/icons/48x48/star.pngbin0 -> 1248 bytes
-rw-r--r--dist/qt_themes/colorful/style.qrc1
-rw-r--r--dist/qt_themes/default/default.qrc1
-rw-r--r--dist/qt_themes/default/icons/48x48/star.pngbin0 -> 686 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/icons/48x48/star.pngbin0 -> 725 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/style.qrc1
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/star.pngbin0 -> 725 bytes
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/style.qrc1
-rw-r--r--license.txt1
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/service/am/am.cpp10
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/glue/ectx.cpp22
-rw-r--r--src/core/hle/service/glue/ectx.h21
-rw-r--r--src/core/hle/service/glue/glue.cpp4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp12
-rw-r--r--src/video_core/gpu.cpp4
-rw-r--r--src/video_core/gpu.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp25
-rw-r--r--src/yuzu/configuration/config.cpp33
-rw-r--r--src/yuzu/game_list.cpp103
-rw-r--r--src/yuzu/game_list.h5
-rw-r--r--src/yuzu/game_list_p.h26
-rw-r--r--src/yuzu/uisettings.h1
26 files changed, 256 insertions, 31 deletions
diff --git a/dist/license.md b/dist/license.md
index e9bc87656..7bdebfec1 100644
--- a/dist/license.md
+++ b/dist/license.md
@@ -12,6 +12,7 @@ qt_themes/default/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
qt_themes/default/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
+qt_themes/default/icons/48x48/star.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io
qt_themes/qdarkstyle/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
@@ -20,6 +21,7 @@ qt_themes/qdarkstyle/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
qt_themes/qdarkstyle/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
+qt_themes/qdarkstyle/icons/48x48/star.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io
qt_themes/colorful/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
@@ -28,5 +30,6 @@ qt_themes/colorful/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/48x48/plus.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
+qt_themes/colorful/icons/48x48/star.png | CC BY-ND 3.0 | https://icons8.com
<!-- TODO: Add the license of the yuzu icon --> \ No newline at end of file
diff --git a/dist/qt_themes/colorful/icons/48x48/star.png b/dist/qt_themes/colorful/icons/48x48/star.png
new file mode 100644
index 000000000..43b5d52ed
--- /dev/null
+++ b/dist/qt_themes/colorful/icons/48x48/star.png
Binary files differ
diff --git a/dist/qt_themes/colorful/style.qrc b/dist/qt_themes/colorful/style.qrc
index 36735519a..18b10869e 100644
--- a/dist/qt_themes/colorful/style.qrc
+++ b/dist/qt_themes/colorful/style.qrc
@@ -7,6 +7,7 @@
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
<file alias="48x48/plus.png">icons/48x48/plus.png</file>
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
+ <file alias="48x48/star.png">icons/48x48/star.png</file>
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
</qresource>
<qresource prefix="colorful">
diff --git a/dist/qt_themes/default/default.qrc b/dist/qt_themes/default/default.qrc
index 2182f33f3..b195747a3 100644
--- a/dist/qt_themes/default/default.qrc
+++ b/dist/qt_themes/default/default.qrc
@@ -10,6 +10,7 @@
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
<file alias="48x48/plus.png">icons/48x48/plus.png</file>
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
+ <file alias="48x48/star.png">icons/48x48/star.png</file>
<file alias="256x256/yuzu.png">icons/256x256/yuzu.png</file>
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
</qresource>
diff --git a/dist/qt_themes/default/icons/48x48/star.png b/dist/qt_themes/default/icons/48x48/star.png
new file mode 100644
index 000000000..740f7f3e7
--- /dev/null
+++ b/dist/qt_themes/default/icons/48x48/star.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/icons/48x48/star.png b/dist/qt_themes/qdarkstyle/icons/48x48/star.png
new file mode 100644
index 000000000..90d423a1d
--- /dev/null
+++ b/dist/qt_themes/qdarkstyle/icons/48x48/star.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/style.qrc b/dist/qt_themes/qdarkstyle/style.qrc
index 2b91204f3..34e872d25 100644
--- a/dist/qt_themes/qdarkstyle/style.qrc
+++ b/dist/qt_themes/qdarkstyle/style.qrc
@@ -8,6 +8,7 @@
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
<file alias="48x48/plus.png">icons/48x48/plus.png</file>
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
+ <file alias="48x48/star.png">icons/48x48/star.png</file>
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
</qresource>
<qresource prefix="qss_icons">
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/star.png b/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/star.png
new file mode 100644
index 000000000..90d423a1d
--- /dev/null
+++ b/dist/qt_themes/qdarkstyle_midnight_blue/icons/48x48/star.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc b/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc
index 579e73ece..142dd3288 100644
--- a/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc
+++ b/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc
@@ -8,6 +8,7 @@
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
<file alias="48x48/plus.png">icons/48x48/plus.png</file>
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
+ <file alias="48x48/star.png">icons/48x48/star.png</file>
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
</qresource>
<qresource prefix="qss_icons">
diff --git a/license.txt b/license.txt
index 86e7b3c1b..495f3e676 100644
--- a/license.txt
+++ b/license.txt
@@ -358,6 +358,7 @@ chip.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
plus.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
sd_card.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
+star.png | CC BY-ND 3.0 | https://icons8.com
Note:
Some icons are different in different themes, and they are separately listed
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 532e418b0..04cf3f5b9 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -377,6 +377,8 @@ add_library(core STATIC
hle/service/glue/arp.h
hle/service/glue/bgtc.cpp
hle/service/glue/bgtc.h
+ hle/service/glue/ectx.cpp
+ hle/service/glue/ectx.h
hle/service/glue/errors.h
hle/service/glue/glue.cpp
hle/service/glue/glue.h
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 4c8216b47..58c7f2930 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -687,7 +687,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
{501, nullptr, "SuppressDisablingSleepTemporarily"},
{502, nullptr, "IsSleepEnabled"},
{503, nullptr, "IsDisablingSleepSuppressed"},
- {900, nullptr, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"},
+ {900, &ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"},
};
// clang-format on
@@ -817,6 +817,14 @@ void ICommonStateGetter::SetCpuBoostMode(Kernel::HLERequestContext& ctx) {
apm_sys->SetCpuBoostMode(ctx);
}
+void ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(
+ Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
IStorageImpl::~IStorageImpl() = default;
class StorageDataImpl final : public IStorageImpl {
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 756434716..5d302e155 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -196,6 +196,7 @@ private:
void EndVrModeEx(Kernel::HLERequestContext& ctx);
void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
void SetCpuBoostMode(Kernel::HLERequestContext& ctx);
+ void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(Kernel::HLERequestContext& ctx);
std::shared_ptr<AppletMessageQueue> msg_queue;
bool vr_mode_state{};
diff --git a/src/core/hle/service/glue/ectx.cpp b/src/core/hle/service/glue/ectx.cpp
new file mode 100644
index 000000000..249c6f003
--- /dev/null
+++ b/src/core/hle/service/glue/ectx.cpp
@@ -0,0 +1,22 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/glue/ectx.h"
+
+namespace Service::Glue {
+
+ECTX_AW::ECTX_AW(Core::System& system_) : ServiceFramework{system_, "ectx:aw"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "CreateContextRegistrar"},
+ {1, nullptr, "CommitContext"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+ECTX_AW::~ECTX_AW() = default;
+
+} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/ectx.h b/src/core/hle/service/glue/ectx.h
new file mode 100644
index 000000000..b275e808a
--- /dev/null
+++ b/src/core/hle/service/glue/ectx.h
@@ -0,0 +1,21 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::Glue {
+
+class ECTX_AW final : public ServiceFramework<ECTX_AW> {
+public:
+ explicit ECTX_AW(Core::System& system_);
+ ~ECTX_AW() override;
+};
+
+} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp
index 4eafbe5fa..a08dc9758 100644
--- a/src/core/hle/service/glue/glue.cpp
+++ b/src/core/hle/service/glue/glue.cpp
@@ -6,6 +6,7 @@
#include "core/core.h"
#include "core/hle/service/glue/arp.h"
#include "core/hle/service/glue/bgtc.h"
+#include "core/hle/service/glue/ectx.h"
#include "core/hle/service/glue/glue.h"
namespace Service::Glue {
@@ -20,6 +21,9 @@ void InstallInterfaces(Core::System& system) {
// BackGround Task Controller
std::make_shared<BGTC_T>(system)->InstallAsService(system.ServiceManager());
std::make_shared<BGTC_SC>(system)->InstallAsService(system.ServiceManager());
+
+ // Error Context
+ std::make_shared<ECTX_AW>(system)->InstallAsService(system.ServiceManager());
}
} // namespace Service::Glue
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index 4e58b9b80..e2f671d8e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -31,9 +31,8 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
return SetSubmitTimeout(input, output);
case 0x9:
return MapBuffer(input, output);
- case 0xa: {
+ case 0xa:
return UnmapBuffer(input, output);
- }
default:
break;
}
@@ -67,7 +66,8 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>&
void nvhost_nvdec::OnOpen(DeviceFD fd) {}
void nvhost_nvdec::OnClose(DeviceFD fd) {
- system.GPU().ClearCommandBuffer();
+ LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
+ system.GPU().ClearCdmaInstance();
}
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index 0421fb956..301efe8a1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -29,13 +29,8 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& i
return GetWaitbase(input, output);
case 0x9:
return MapBuffer(input, output);
- case 0xa: {
- if (command.length == 0x1c) {
- Tegra::ChCommandHeaderList cmdlist{{0xDEADB33F}};
- system.GPU().PushCommandBuffer(cmdlist);
- }
+ case 0xa:
return UnmapBuffer(input, output);
- }
default:
break;
}
@@ -69,6 +64,9 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& i
}
void nvhost_vic::OnOpen(DeviceFD fd) {}
-void nvhost_vic::OnClose(DeviceFD fd) {}
+
+void nvhost_vic::OnClose(DeviceFD fd) {
+ system.GPU().ClearCdmaInstance();
+}
} // namespace Service::Nvidia::Devices
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 7c42f1177..a38024242 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -492,10 +492,8 @@ void GPU::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) {
cdma_pusher->ProcessEntries(std::move(entries));
}
-void GPU::ClearCommandBuffer() {
- // This condition fires when a video stream ends, clear all intermediary data
+void GPU::ClearCdmaInstance() {
cdma_pusher.reset();
- LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
}
void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index b1960ea86..8669e9940 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -324,8 +324,8 @@ public:
/// Push GPU command buffer entries to be processed
void PushCommandBuffer(Tegra::ChCommandHeaderList& entries);
- /// Frees the CDMAPusher to free up resources
- void ClearCommandBuffer();
+ /// Frees the CDMAPusher instance to free up resources
+ void ClearCdmaInstance();
/// Swap buffers (render frame)
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index bc2a53841..017348e05 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -417,7 +417,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
};
}
-[[nodiscard]] constexpr SwizzleSource ConvertGreenRed(SwizzleSource value) {
+[[nodiscard]] SwizzleSource ConvertGreenRed(SwizzleSource value) {
switch (value) {
case SwizzleSource::G:
return SwizzleSource::R;
@@ -426,6 +426,17 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
}
}
+[[nodiscard]] SwizzleSource SwapBlueRed(SwizzleSource value) {
+ switch (value) {
+ case SwizzleSource::R:
+ return SwizzleSource::B;
+ case SwizzleSource::B:
+ return SwizzleSource::R;
+ default:
+ return value;
+ }
+}
+
void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage image,
VkImageAspectFlags aspect_mask, bool is_initialized,
std::span<const VkBufferImageCopy> copies) {
@@ -543,6 +554,15 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
};
}
+[[nodiscard]] bool IsFormatFlipped(PixelFormat format) {
+ switch (format) {
+ case PixelFormat::A1B5G5R5_UNORM:
+ return true;
+ default:
+ return false;
+ }
+}
+
struct RangedBarrierRange {
u32 min_mip = std::numeric_limits<u32>::max();
u32 max_mip = std::numeric_limits<u32>::min();
@@ -948,6 +968,9 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
};
if (!info.IsRenderTarget()) {
swizzle = info.Swizzle();
+ if (IsFormatFlipped(format)) {
+ std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
+ }
if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed);
}
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 851246233..d1b8c4fc9 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -29,9 +29,10 @@ Config::~Config() {
}
const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
- Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q,
- Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T,
- Qt::Key_H, Qt::Key_G, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V,
+ Qt::Key_C, Qt::Key_X, Qt::Key_V, Qt::Key_Z, Qt::Key_F,
+ Qt::Key_G, Qt::Key_Q, Qt::Key_E, Qt::Key_R, Qt::Key_T,
+ Qt::Key_M, Qt::Key_N, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right,
+ Qt::Key_Down, Qt::Key_Q, Qt::Key_E, 0, 0,
};
const std::array<int, Settings::NativeMotion::NumMotions> Config::default_motions = {
@@ -41,10 +42,10 @@ const std::array<int, Settings::NativeMotion::NumMotions> Config::default_motion
const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
{
- Qt::Key_Up,
- Qt::Key_Down,
- Qt::Key_Left,
- Qt::Key_Right,
+ Qt::Key_W,
+ Qt::Key_S,
+ Qt::Key_A,
+ Qt::Key_D,
},
{
Qt::Key_I,
@@ -55,8 +56,8 @@ const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config:
}};
const std::array<int, 2> Config::default_stick_mod = {
- Qt::Key_E,
- Qt::Key_R,
+ Qt::Key_Shift,
+ 0,
};
const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons =
@@ -936,6 +937,13 @@ void Config::ReadUIGamelistValues() {
UISettings::values.row_2_text_id = ReadSetting(QStringLiteral("row_2_text_id"), 2).toUInt();
UISettings::values.cache_game_list =
ReadSetting(QStringLiteral("cache_game_list"), true).toBool();
+ const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites"));
+ for (int i = 0; i < favorites_size; i++) {
+ qt_config->setArrayIndex(i);
+ UISettings::values.favorited_ids.append(
+ ReadSetting(QStringLiteral("program_id")).toULongLong());
+ }
+ qt_config->endArray();
qt_config->endGroup();
}
@@ -1479,6 +1487,13 @@ void Config::SaveUIGamelistValues() {
WriteSetting(QStringLiteral("row_1_text_id"), UISettings::values.row_1_text_id, 3);
WriteSetting(QStringLiteral("row_2_text_id"), UISettings::values.row_2_text_id, 2);
WriteSetting(QStringLiteral("cache_game_list"), UISettings::values.cache_game_list, true);
+ qt_config->beginWriteArray(QStringLiteral("favorites"));
+ for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
+ qt_config->setArrayIndex(i);
+ WriteSetting(QStringLiteral("program_id"),
+ QVariant::fromValue(UISettings::values.favorited_ids[i]));
+ }
+ qt_config->endArray();
qt_config->endGroup();
}
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 9afd5b45f..48b78d65f 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -11,6 +11,7 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QKeyEvent>
+#include <QList>
#include <QMenu>
#include <QThreadPool>
#include <fmt/format.h>
@@ -84,6 +85,10 @@ void GameListSearchField::setFilterResult(int visible, int total) {
label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible));
}
+bool GameListSearchField::isEmpty() const {
+ return edit_filter->text().isEmpty();
+}
+
QString GameList::GetLastFilterResultItem() const {
QString file_path;
const int folder_count = item_model->rowCount();
@@ -187,7 +192,9 @@ void GameList::OnTextChanged(const QString& new_text) {
// If the searchfield is empty every item is visible
// Otherwise the filter gets applied
if (edit_filter_text.isEmpty()) {
- for (int i = 0; i < folder_count; ++i) {
+ tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(),
+ UISettings::values.favorited_ids.size() == 0);
+ for (int i = 1; i < folder_count; ++i) {
folder = item_model->item(i, 0);
const QModelIndex folder_index = folder->index();
const int children_count = folder->rowCount();
@@ -198,8 +205,9 @@ void GameList::OnTextChanged(const QString& new_text) {
}
search_field->setFilterResult(children_total, children_total);
} else {
+ tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), true);
int result_count = 0;
- for (int i = 0; i < folder_count; ++i) {
+ for (int i = 1; i < folder_count; ++i) {
folder = item_model->item(i, 0);
const QModelIndex folder_index = folder->index();
const int children_count = folder->rowCount();
@@ -280,6 +288,13 @@ void GameList::OnUpdateThemedIcons() {
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
Qt::DecorationRole);
break;
+ case GameListItemType::Favorites:
+ child->setData(
+ QIcon::fromTheme(QStringLiteral("star"))
+ .pixmap(icon_size)
+ .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
+ Qt::DecorationRole);
+ break;
default:
break;
}
@@ -427,6 +442,13 @@ void GameList::DonePopulating(const QStringList& watch_list) {
emit ShowList(!IsEmpty());
item_model->invisibleRootItem()->appendRow(new GameListAddDir());
+ item_model->invisibleRootItem()->insertRow(0, new GameListFavorites());
+ tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(),
+ UISettings::values.favorited_ids.size() == 0);
+ tree_view->expand(item_model->invisibleRootItem()->child(0)->index());
+ for (const auto id : UISettings::values.favorited_ids) {
+ AddFavorite(id);
+ }
// Clear out the old directories to watch for changes and add the new ones
auto watch_dirs = watcher->directories();
@@ -446,7 +468,7 @@ void GameList::DonePopulating(const QStringList& watch_list) {
tree_view->setEnabled(true);
const int folder_count = tree_view->model()->rowCount();
int children_total = 0;
- for (int i = 0; i < folder_count; ++i) {
+ for (int i = 1; i < folder_count; ++i) {
children_total += item_model->item(i, 0)->rowCount();
}
search_field->setFilterResult(children_total, children_total);
@@ -478,6 +500,9 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
case GameListItemType::SysNandDir:
AddPermDirPopup(context_menu, selected);
break;
+ case GameListItemType::Favorites:
+ AddFavoritesPopup(context_menu);
+ break;
default:
break;
}
@@ -485,6 +510,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
}
void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::string& path) {
+ QAction* favorite = context_menu.addAction(tr("Favorite"));
+ context_menu.addSeparator();
QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location"));
QAction* open_transferable_shader_cache =
@@ -503,6 +530,9 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
context_menu.addSeparator();
QAction* properties = context_menu.addAction(tr("Properties"));
+ favorite->setVisible(program_id != 0);
+ favorite->setCheckable(true);
+ favorite->setChecked(UISettings::values.favorited_ids.contains(program_id));
open_save_location->setVisible(program_id != 0);
open_mod_location->setVisible(program_id != 0);
open_transferable_shader_cache->setVisible(program_id != 0);
@@ -513,6 +543,7 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0);
+ connect(favorite, &QAction::triggered, [this, program_id]() { ToggleFavorite(program_id); });
connect(open_save_location, &QAction::triggered, [this, program_id, path]() {
emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path);
});
@@ -576,7 +607,7 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) {
const int row = selected.row();
- move_up->setEnabled(row > 0);
+ move_up->setEnabled(row > 1);
move_down->setEnabled(row < item_model->rowCount() - 2);
connect(move_up, &QAction::triggered, [this, selected, row, game_dir_index] {
@@ -614,6 +645,18 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) {
});
}
+void GameList::AddFavoritesPopup(QMenu& context_menu) {
+ QAction* clear_all = context_menu.addAction(tr("Clear"));
+
+ connect(clear_all, &QAction::triggered, [this] {
+ for (const auto id : UISettings::values.favorited_ids) {
+ RemoveFavorite(id);
+ }
+ UISettings::values.favorited_ids.clear();
+ tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), true);
+ });
+}
+
void GameList::LoadCompatibilityList() {
QFile compat_list{QStringLiteral(":compatibility_list/compatibility_list.json")};
@@ -728,6 +771,58 @@ void GameList::RefreshGameDirectory() {
}
}
+void GameList::ToggleFavorite(u64 program_id) {
+ if (!UISettings::values.favorited_ids.contains(program_id)) {
+ tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(),
+ !search_field->isEmpty());
+ UISettings::values.favorited_ids.append(program_id);
+ AddFavorite(program_id);
+ item_model->sort(tree_view->header()->sortIndicatorSection(),
+ tree_view->header()->sortIndicatorOrder());
+ } else {
+ UISettings::values.favorited_ids.removeOne(program_id);
+ RemoveFavorite(program_id);
+ if (UISettings::values.favorited_ids.size() == 0) {
+ tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), true);
+ }
+ }
+}
+
+void GameList::AddFavorite(u64 program_id) {
+ auto* favorites_row = item_model->item(0);
+
+ for (int i = 1; i < item_model->rowCount() - 1; i++) {
+ const auto* folder = item_model->item(i);
+ for (int j = 0; j < folder->rowCount(); j++) {
+ if (folder->child(j)->data(GameListItemPath::ProgramIdRole).toULongLong() ==
+ program_id) {
+ QList<QStandardItem*> list;
+ for (int k = 0; k < item_model->columnCount(); k++) {
+ list.append(folder->child(j, k)->clone());
+ }
+ list[0]->setData(folder->child(j)->data(GameListItem::SortRole),
+ GameListItem::SortRole);
+ list[0]->setText(folder->child(j)->data(Qt::DisplayRole).toString());
+
+ favorites_row->appendRow(list);
+ return;
+ }
+ }
+ }
+}
+
+void GameList::RemoveFavorite(u64 program_id) {
+ auto* favorites_row = item_model->item(0);
+
+ for (int i = 0; i < favorites_row->rowCount(); i++) {
+ const auto* game = favorites_row->child(i);
+ if (game->data(GameListItemPath::ProgramIdRole).toULongLong() == program_id) {
+ favorites_row->removeRow(i);
+ return;
+ }
+ }
+}
+
GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent} {
connect(parent, &GMainWindow::UpdateThemedIcons, this,
&GameListPlaceholder::onUpdateThemedIcons);
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 58059a3c4..9c0a1a482 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -112,10 +112,15 @@ private:
void RefreshGameDirectory();
+ void ToggleFavorite(u64 program_id);
+ void AddFavorite(u64 program_id);
+ void RemoveFavorite(u64 program_id);
+
void PopupContextMenu(const QPoint& menu_location);
void AddGamePopup(QMenu& context_menu, u64 program_id, const std::string& path);
void AddCustomDirPopup(QMenu& context_menu, QModelIndex selected);
void AddPermDirPopup(QMenu& context_menu, QModelIndex selected);
+ void AddFavoritesPopup(QMenu& context_menu);
std::shared_ptr<FileSys::VfsFilesystem> vfs;
FileSys::ManualContentProvider* provider;
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index f25445f18..7ca8ece23 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -29,7 +29,8 @@ enum class GameListItemType {
SdmcDir = QStandardItem::UserType + 3,
UserNandDir = QStandardItem::UserType + 4,
SysNandDir = QStandardItem::UserType + 5,
- AddDir = QStandardItem::UserType + 6
+ AddDir = QStandardItem::UserType + 6,
+ Favorites = QStandardItem::UserType + 7,
};
Q_DECLARE_METATYPE(GameListItemType);
@@ -310,6 +311,28 @@ public:
}
};
+class GameListFavorites : public GameListItem {
+public:
+ explicit GameListFavorites() {
+ setData(type(), TypeRole);
+
+ const int icon_size = std::min(static_cast<int>(UISettings::values.icon_size), 64);
+ setData(QIcon::fromTheme(QStringLiteral("star"))
+ .pixmap(icon_size)
+ .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
+ Qt::DecorationRole);
+ setData(QObject::tr("Favorites"), Qt::DisplayRole);
+ }
+
+ int type() const override {
+ return static_cast<int>(GameListItemType::Favorites);
+ }
+
+ bool operator<(const QStandardItem& other) const override {
+ return false;
+ }
+};
+
class GameList;
class QHBoxLayout;
class QTreeView;
@@ -324,6 +347,7 @@ public:
explicit GameListSearchField(GameList* parent = nullptr);
void setFilterResult(int visible, int total);
+ bool isEmpty() const;
void clear();
void setFocus();
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index ce3945485..5ba00b8c8 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -74,6 +74,7 @@ struct Values {
QString game_dir_deprecated;
bool game_dir_deprecated_deepscan;
QVector<UISettings::GameDir> game_dirs;
+ QVector<u64> favorited_ids;
QStringList recent_files;
QString language;