Compare commits
7 commits
master
...
feature/da
Author | SHA1 | Date | |
---|---|---|---|
4b0268aea4 | |||
7112a1f061 | |||
cbea2ff397 | |||
3836b16beb | |||
2071c2acb0 | |||
6397a9b410 | |||
970c7c1681 |
45
.reuse/dep5
|
@ -13,12 +13,13 @@ Copyright: yuzu Emulator Project
|
||||||
License: GPL-2.0-or-later
|
License: GPL-2.0-or-later
|
||||||
|
|
||||||
Files: dist/qt_themes/default/icons/256x256/eden.png
|
Files: dist/qt_themes/default/icons/256x256/eden.png
|
||||||
|
dist/qt_themes/default/icons/256x256/eden_named.png
|
||||||
dist/yuzu.bmp
|
dist/yuzu.bmp
|
||||||
dist/yuzu.icns
|
dist/eden.icns
|
||||||
dist/eden.ico
|
dist/eden.ico
|
||||||
dist/eden.svg
|
dist/dev.eden_emu.eden.svg
|
||||||
Copyright: yuzu Emulator Project
|
Copyright: 2025 Eden Emulator Project
|
||||||
License: GPL-2.0-or-later
|
License: GPL-3.0-or-later
|
||||||
|
|
||||||
Files: dist/qt_themes/qdarkstyle*/LICENSE.*
|
Files: dist/qt_themes/qdarkstyle*/LICENSE.*
|
||||||
dist/qt_themes/qdarkstyle*/style.qrc
|
dist/qt_themes/qdarkstyle*/style.qrc
|
||||||
|
@ -155,3 +156,39 @@ License: BSD-3-Clause
|
||||||
Files: src/android/app/debug.keystore
|
Files: src/android/app/debug.keystore
|
||||||
Copyright: 2023 yuzu Emulator Project
|
Copyright: 2023 yuzu Emulator Project
|
||||||
License: GPL-3.0-or-later
|
License: GPL-3.0-or-later
|
||||||
|
|
||||||
|
Files: dist/qt_themes/colorful/icons/48x48/user-trash.png
|
||||||
|
dist/qt_themes/colorful/icons/48x48/upload.png
|
||||||
|
dist/qt_themes/colorful/icons/48x48/download.png
|
||||||
|
Copyright: 2014 Uri Herrera
|
||||||
|
1996-2025 KDE Software Foundation
|
||||||
|
License: LGPL-2.0-or-later
|
||||||
|
|
||||||
|
Files: dist/qt_themes/default/icons/48x48/user-trash.png
|
||||||
|
dist/qt_themes/default/icons/48x48/upload.png
|
||||||
|
dist/qt_themes/default/icons/48x48/download.png
|
||||||
|
dist/qt_themes/default_dark/icons/48x48/user-trash.png
|
||||||
|
dist/qt_themes/default_dark/icons/48x48/upload.png
|
||||||
|
dist/qt_themes/default_dark/icons/48x48/download.png
|
||||||
|
Copyright: 2025 Fonticons, Inc.
|
||||||
|
License: CC-BY-4.0
|
||||||
|
Comment: All of these icons have been modified by crueter <crueter@crueter.xyz>
|
||||||
|
|
||||||
|
Files: CMakeModules/CPM.cmake
|
||||||
|
Copyright: 2019-2023 Lars Melchior
|
||||||
|
License: MIT
|
||||||
|
|
||||||
|
Files: CMakeModules/CPMUtil.cmake
|
||||||
|
CMakeModules/CPM.cmake
|
||||||
|
CMakeModules/GetSCMRev.cmake
|
||||||
|
CMakeModules/DetectArchitecture.cmake
|
||||||
|
tools/cpm/*
|
||||||
|
tools/update-cpm.sh
|
||||||
|
tools/shellcheck.sh
|
||||||
|
docs/CPMUtil.md
|
||||||
|
**cpmfile.json
|
||||||
|
Copyright: 2025 crueter <crueter@crueter.xyz>
|
||||||
|
License: GPL-3.0-or-later
|
||||||
|
Comment: CPM.cmake has had additional modifications from crueter to better work with CPMUtil
|
||||||
|
https://git.crueter.xyz/CMake/CPMUtil
|
||||||
|
https://git.crueter.xyz/CMake/Modules
|
||||||
|
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
BIN
dist/qt_themes/colorful/icons/48x48/download.png
vendored
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
dist/qt_themes/colorful/icons/48x48/upload.png
vendored
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
dist/qt_themes/colorful/icons/48x48/user-trash.png
vendored
Normal file
After Width: | Height: | Size: 1.4 KiB |
3
dist/qt_themes/colorful/style.qrc
vendored
|
@ -18,6 +18,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
|
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
|
||||||
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
|
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
|
||||||
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
|
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
|
||||||
|
<file alias="48x48/user-trash.png">icons/48x48/user-trash.png</file>
|
||||||
|
<file alias="48x48/download.png">icons/48x48/download.png</file>
|
||||||
|
<file alias="48x48/upload.png">icons/48x48/upload.png</file>
|
||||||
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
|
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
|
||||||
<file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
|
<file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
|
||||||
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
|
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
|
||||||
|
|
|
@ -11,6 +11,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
|
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
|
||||||
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
|
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
|
||||||
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
|
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
|
||||||
|
<file alias="48x48/user-trash.png">../colorful/icons/48x48/user-trash.png</file>
|
||||||
|
<file alias="48x48/download.png">../colorful/icons/48x48/download.png</file>
|
||||||
|
<file alias="48x48/upload.png">../colorful/icons/48x48/upload.png</file>
|
||||||
<file alias="48x48/list-add.png">../colorful/icons/48x48/list-add.png</file>
|
<file alias="48x48/list-add.png">../colorful/icons/48x48/list-add.png</file>
|
||||||
<file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file>
|
<file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file>
|
||||||
<file alias="256x256/plus_folder.png">../colorful/icons/256x256/plus_folder.png</file>
|
<file alias="256x256/plus_folder.png">../colorful/icons/256x256/plus_folder.png</file>
|
||||||
|
|
3
dist/qt_themes/default/default.qrc
vendored
|
@ -14,6 +14,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
|
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
|
||||||
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
|
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
|
||||||
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
|
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
|
||||||
|
<file alias="48x48/user-trash.png">icons/48x48/user-trash.png</file>
|
||||||
|
<file alias="48x48/download.png">icons/48x48/download.png</file>
|
||||||
|
<file alias="48x48/upload.png">icons/48x48/upload.png</file>
|
||||||
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
|
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
|
||||||
<file alias="48x48/sd_card.png">icons/48x48/sd_card.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="48x48/star.png">icons/48x48/star.png</file>
|
||||||
|
|
BIN
dist/qt_themes/default/icons/256x256/eden.png
vendored
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
BIN
dist/qt_themes/default/icons/48x48/download.png
vendored
Normal file
After Width: | Height: | Size: 853 B |
BIN
dist/qt_themes/default/icons/48x48/upload.png
vendored
Normal file
After Width: | Height: | Size: 820 B |
BIN
dist/qt_themes/default/icons/48x48/user-trash.png
vendored
Normal file
After Width: | Height: | Size: 584 B |
3
dist/qt_themes/default_dark/style.qrc
vendored
|
@ -13,6 +13,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
|
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
|
||||||
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
|
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
|
||||||
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
|
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
|
||||||
|
<file alias="48x48/user-trash.png">../colorful/icons/48x48/user-trash.png</file>
|
||||||
|
<file alias="48x48/download.png">../colorful/icons/48x48/download.png</file>
|
||||||
|
<file alias="48x48/upload.png">../colorful/icons/48x48/upload.png</file>
|
||||||
<file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file>
|
<file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file>
|
||||||
<file alias="48x48/list-add.png">../colorful/icons/48x48/list-add.png</file>
|
<file alias="48x48/list-add.png">../colorful/icons/48x48/list-add.png</file>
|
||||||
<file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file>
|
<file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file>
|
||||||
|
|
BIN
dist/qt_themes/qdarkstyle/icons/48x48/download.png
vendored
Normal file
After Width: | Height: | Size: 883 B |
BIN
dist/qt_themes/qdarkstyle/icons/48x48/upload.png
vendored
Normal file
After Width: | Height: | Size: 853 B |
BIN
dist/qt_themes/qdarkstyle/icons/48x48/user-trash.png
vendored
Normal file
After Width: | Height: | Size: 584 B |
3
dist/qt_themes/qdarkstyle/style.qrc
vendored
|
@ -9,6 +9,9 @@
|
||||||
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
|
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
|
||||||
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
|
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
|
||||||
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
|
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
|
||||||
|
<file alias="48x48/user-trash.png">icons/48x48/user-trash.png</file>
|
||||||
|
<file alias="48x48/download.png">icons/48x48/download.png</file>
|
||||||
|
<file alias="48x48/upload.png">icons/48x48/upload.png</file>
|
||||||
<file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
|
<file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
|
||||||
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
|
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
|
||||||
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
|
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
<file alias="48x48/bad_folder.png">../qdarkstyle/icons/48x48/bad_folder.png</file>
|
<file alias="48x48/bad_folder.png">../qdarkstyle/icons/48x48/bad_folder.png</file>
|
||||||
<file alias="48x48/chip.png">../qdarkstyle/icons/48x48/chip.png</file>
|
<file alias="48x48/chip.png">../qdarkstyle/icons/48x48/chip.png</file>
|
||||||
<file alias="48x48/folder.png">../qdarkstyle/icons/48x48/folder.png</file>
|
<file alias="48x48/folder.png">../qdarkstyle/icons/48x48/folder.png</file>
|
||||||
|
<file alias="48x48/user-trash.png">../qdarkstyle/icons/48x48/user-trash.png</file>
|
||||||
|
<file alias="48x48/download.png">../qdarkstyle/icons/48x48/download.png</file>
|
||||||
|
<file alias="48x48/upload.png">../qdarkstyle/icons/48x48/upload.png</file>
|
||||||
<file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file>
|
<file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file>
|
||||||
<file alias="48x48/list-add.png">../qdarkstyle/icons/48x48/list-add.png</file>
|
<file alias="48x48/list-add.png">../qdarkstyle/icons/48x48/list-add.png</file>
|
||||||
<file alias="48x48/sd_card.png">../qdarkstyle/icons/48x48/sd_card.png</file>
|
<file alias="48x48/sd_card.png">../qdarkstyle/icons/48x48/sd_card.png</file>
|
||||||
|
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 438 KiB After Width: | Height: | Size: 244 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
|
@ -161,7 +161,7 @@ struct Values {
|
||||||
Category::LibraryApplet};
|
Category::LibraryApplet};
|
||||||
Setting<AppletMode> photo_viewer_applet_mode{
|
Setting<AppletMode> photo_viewer_applet_mode{
|
||||||
linkage, AppletMode::LLE, "photo_viewer_applet_mode", Category::LibraryApplet};
|
linkage, AppletMode::LLE, "photo_viewer_applet_mode", Category::LibraryApplet};
|
||||||
Setting<AppletMode> offline_web_applet_mode{linkage, AppletMode::LLE, "offline_web_applet_mode",
|
Setting<AppletMode> offline_web_applet_mode{linkage, AppletMode::HLE, "offline_web_applet_mode",
|
||||||
Category::LibraryApplet};
|
Category::LibraryApplet};
|
||||||
Setting<AppletMode> login_share_applet_mode{linkage, AppletMode::HLE, "login_share_applet_mode",
|
Setting<AppletMode> login_share_applet_mode{linkage, AppletMode::HLE, "login_share_applet_mode",
|
||||||
Category::LibraryApplet};
|
Category::LibraryApplet};
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -71,6 +68,42 @@ std::string ResolveURL(const std::string& url) {
|
||||||
return url.substr(0, index) + "lp1" + url.substr(index + 1);
|
return url.substr(0, index) + "lp1" + url.substr(index + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WebArgInputTLVMap ReadWebArgs(const std::vector<u8>& web_arg, WebArgHeader& web_arg_header) {
|
||||||
|
std::memcpy(&web_arg_header, web_arg.data(), sizeof(WebArgHeader));
|
||||||
|
|
||||||
|
if (web_arg.size() == sizeof(WebArgHeader)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
WebArgInputTLVMap input_tlv_map;
|
||||||
|
|
||||||
|
u64 current_offset = sizeof(WebArgHeader);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < web_arg_header.total_tlv_entries; ++i) {
|
||||||
|
if (web_arg.size() < current_offset + sizeof(WebArgInputTLV)) {
|
||||||
|
return input_tlv_map;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebArgInputTLV input_tlv;
|
||||||
|
std::memcpy(&input_tlv, web_arg.data() + current_offset, sizeof(WebArgInputTLV));
|
||||||
|
|
||||||
|
current_offset += sizeof(WebArgInputTLV);
|
||||||
|
|
||||||
|
if (web_arg.size() < current_offset + input_tlv.arg_data_size) {
|
||||||
|
return input_tlv_map;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> data(input_tlv.arg_data_size);
|
||||||
|
std::memcpy(data.data(), web_arg.data() + current_offset, input_tlv.arg_data_size);
|
||||||
|
|
||||||
|
current_offset += input_tlv.arg_data_size;
|
||||||
|
|
||||||
|
input_tlv_map.insert_or_assign(input_tlv.input_tlv_type, std::move(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
return input_tlv_map;
|
||||||
|
}
|
||||||
|
|
||||||
FileSys::VirtualFile GetOfflineRomFS(Core::System& system, u64 title_id,
|
FileSys::VirtualFile GetOfflineRomFS(Core::System& system, u64 title_id,
|
||||||
FileSys::ContentRecordType nca_type) {
|
FileSys::ContentRecordType nca_type) {
|
||||||
if (nca_type == FileSys::ContentRecordType::Data) {
|
if (nca_type == FileSys::ContentRecordType::Data) {
|
||||||
|
@ -111,43 +144,6 @@ FileSys::VirtualFile GetOfflineRomFS(Core::System& system, u64 title_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef YUZU_USE_QT_WEB_ENGINE
|
|
||||||
WebArgInputTLVMap ReadWebArgs(const std::vector<u8>& web_arg, WebArgHeader& web_arg_header) {
|
|
||||||
std::memcpy(&web_arg_header, web_arg.data(), sizeof(WebArgHeader));
|
|
||||||
|
|
||||||
if (web_arg.size() == sizeof(WebArgHeader)) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
WebArgInputTLVMap input_tlv_map;
|
|
||||||
|
|
||||||
u64 current_offset = sizeof(WebArgHeader);
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < web_arg_header.total_tlv_entries; ++i) {
|
|
||||||
if (web_arg.size() < current_offset + sizeof(WebArgInputTLV)) {
|
|
||||||
return input_tlv_map;
|
|
||||||
}
|
|
||||||
|
|
||||||
WebArgInputTLV input_tlv;
|
|
||||||
std::memcpy(&input_tlv, web_arg.data() + current_offset, sizeof(WebArgInputTLV));
|
|
||||||
|
|
||||||
current_offset += sizeof(WebArgInputTLV);
|
|
||||||
|
|
||||||
if (web_arg.size() < current_offset + input_tlv.arg_data_size) {
|
|
||||||
return input_tlv_map;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<u8> data(input_tlv.arg_data_size);
|
|
||||||
std::memcpy(data.data(), web_arg.data() + current_offset, input_tlv.arg_data_size);
|
|
||||||
|
|
||||||
current_offset += input_tlv.arg_data_size;
|
|
||||||
|
|
||||||
input_tlv_map.insert_or_assign(input_tlv.input_tlv_type, std::move(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
return input_tlv_map;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExtractSharedFonts(Core::System& system) {
|
void ExtractSharedFonts(Core::System& system) {
|
||||||
static constexpr std::array<const char*, 7> DECRYPTED_SHARED_FONTS{
|
static constexpr std::array<const char*, 7> DECRYPTED_SHARED_FONTS{
|
||||||
"FontStandard.ttf",
|
"FontStandard.ttf",
|
||||||
|
@ -225,7 +221,6 @@ void ExtractSharedFonts(Core::System& system) {
|
||||||
FileSys::VfsRawCopy(decrypted_font, out_file);
|
FileSys::VfsRawCopy(decrypted_font, out_file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -237,7 +232,6 @@ WebBrowser::WebBrowser(Core::System& system_, std::shared_ptr<Applet> applet_,
|
||||||
WebBrowser::~WebBrowser() = default;
|
WebBrowser::~WebBrowser() = default;
|
||||||
|
|
||||||
void WebBrowser::Initialize() {
|
void WebBrowser::Initialize() {
|
||||||
#ifdef YUZU_USE_QT_WEB_ENGINE
|
|
||||||
FrontendApplet::Initialize();
|
FrontendApplet::Initialize();
|
||||||
|
|
||||||
LOG_INFO(Service_AM, "Initializing Web Browser Applet.");
|
LOG_INFO(Service_AM, "Initializing Web Browser Applet.");
|
||||||
|
@ -290,7 +284,6 @@ void WebBrowser::Initialize() {
|
||||||
ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind);
|
ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result WebBrowser::GetStatus() const {
|
Result WebBrowser::GetStatus() const {
|
||||||
|
@ -302,7 +295,6 @@ void WebBrowser::ExecuteInteractive() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebBrowser::Execute() {
|
void WebBrowser::Execute() {
|
||||||
#ifdef YUZU_USE_QT_WEB_ENGINE
|
|
||||||
switch (web_arg_header.shim_kind) {
|
switch (web_arg_header.shim_kind) {
|
||||||
case ShimKind::Shop:
|
case ShimKind::Shop:
|
||||||
ExecuteShop();
|
ExecuteShop();
|
||||||
|
@ -330,10 +322,6 @@ void WebBrowser::Execute() {
|
||||||
WebBrowserExit(WebExitReason::EndButtonPressed);
|
WebBrowserExit(WebExitReason::EndButtonPressed);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
LOG_INFO(Service_AM, "Web Browser Applet disabled, skipping.");
|
|
||||||
WebBrowserExit(WebExitReason::EndButtonPressed);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebBrowser::ExtractOfflineRomFS() {
|
void WebBrowser::ExtractOfflineRomFS() {
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -7,6 +10,7 @@ add_library(frontend_common STATIC
|
||||||
content_manager.h
|
content_manager.h
|
||||||
firmware_manager.h
|
firmware_manager.h
|
||||||
firmware_manager.cpp
|
firmware_manager.cpp
|
||||||
|
data_manager.h data_manager.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
create_target_directory_groups(frontend_common)
|
create_target_directory_groups(frontend_common)
|
||||||
|
|
77
src/frontend_common/data_manager.cpp
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "data_manager.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/fs/path_util.h"
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
namespace FrontendCommon::DataManager {
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
const std::string GetDataDir(DataDir dir, const std::string &user_id)
|
||||||
|
{
|
||||||
|
const fs::path nand_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir);
|
||||||
|
|
||||||
|
switch (dir) {
|
||||||
|
case DataDir::Saves:
|
||||||
|
return (nand_dir / "user" / "save" / "0000000000000000" / user_id).string();
|
||||||
|
case DataDir::UserNand:
|
||||||
|
return (nand_dir / "user" / "Contents" / "registered").string();
|
||||||
|
case DataDir::SysNand:
|
||||||
|
// NB: do NOT delete save
|
||||||
|
// that contains profile data and other stuff
|
||||||
|
return (nand_dir / "system" / "Contents" / "registered").string();
|
||||||
|
case DataDir::Mods:
|
||||||
|
return Common::FS::GetEdenPathString(Common::FS::EdenPath::LoadDir);
|
||||||
|
case DataDir::Shaders:
|
||||||
|
return Common::FS::GetEdenPathString(Common::FS::EdenPath::ShaderDir);
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 ClearDir(DataDir dir, const std::string &user_id)
|
||||||
|
{
|
||||||
|
fs::path data_dir = GetDataDir(dir, user_id);
|
||||||
|
u64 result = fs::remove_all(data_dir);
|
||||||
|
|
||||||
|
// mkpath at the end just so it actually exists
|
||||||
|
fs::create_directories(data_dir);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string ReadableBytesSize(u64 size)
|
||||||
|
{
|
||||||
|
static constexpr std::array units{"B", "KiB", "MiB", "GiB", "TiB", "PiB"};
|
||||||
|
if (size == 0) {
|
||||||
|
return "0 B";
|
||||||
|
}
|
||||||
|
|
||||||
|
const int digit_groups = (std::min) (static_cast<int>(std::log10(size) / std::log10(1024)),
|
||||||
|
static_cast<int>(units.size()));
|
||||||
|
return fmt::format("{:.1f} {}", size / std::pow(1024, digit_groups), units[digit_groups]);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 DataDirSize(DataDir dir)
|
||||||
|
{
|
||||||
|
fs::path data_dir = GetDataDir(dir);
|
||||||
|
u64 size = 0;
|
||||||
|
|
||||||
|
if (!fs::exists(data_dir))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (const auto& entry : fs::recursive_directory_iterator(data_dir)) {
|
||||||
|
if (!entry.is_directory()) {
|
||||||
|
size += entry.file_size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
24
src/frontend_common/data_manager.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#ifndef DATA_MANAGER_H
|
||||||
|
#define DATA_MANAGER_H
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace FrontendCommon::DataManager {
|
||||||
|
|
||||||
|
enum class DataDir { Saves, UserNand, SysNand, Mods, Shaders };
|
||||||
|
|
||||||
|
const std::string GetDataDir(DataDir dir, const std::string &user_id = "");
|
||||||
|
|
||||||
|
u64 ClearDir(DataDir dir, const std::string &user_id = "");
|
||||||
|
|
||||||
|
const std::string ReadableBytesSize(u64 size);
|
||||||
|
|
||||||
|
u64 DataDirSize(DataDir dir);
|
||||||
|
|
||||||
|
}; // namespace FrontendCommon::DataManager
|
||||||
|
|
||||||
|
#endif // DATA_MANAGER_H
|
|
@ -4,9 +4,6 @@
|
||||||
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Core)
|
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Core)
|
|
||||||
|
|
||||||
add_library(qt_common STATIC
|
add_library(qt_common STATIC
|
||||||
qt_common.h
|
qt_common.h
|
||||||
qt_common.cpp
|
qt_common.cpp
|
||||||
|
@ -27,6 +24,8 @@ add_library(qt_common STATIC
|
||||||
qt_rom_util.h qt_rom_util.cpp
|
qt_rom_util.h qt_rom_util.cpp
|
||||||
qt_applet_util.h qt_applet_util.cpp
|
qt_applet_util.h qt_applet_util.cpp
|
||||||
qt_progress_dialog.h qt_progress_dialog.cpp
|
qt_progress_dialog.h qt_progress_dialog.cpp
|
||||||
|
qt_string_lookup.h
|
||||||
|
qt_compress.h qt_compress.cpp
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -37,9 +36,29 @@ if (ENABLE_QT)
|
||||||
target_link_libraries(qt_common PRIVATE Qt6::Widgets)
|
target_link_libraries(qt_common PRIVATE Qt6::Widgets)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
target_compile_definitions(qt_common PUBLIC
|
||||||
|
# Use QStringBuilder for string concatenation to reduce
|
||||||
|
# the overall number of temporary strings created.
|
||||||
|
QT_USE_QSTRINGBUILDER
|
||||||
|
|
||||||
|
# Disable implicit conversions from/to C strings
|
||||||
|
QT_NO_CAST_FROM_ASCII
|
||||||
|
QT_NO_CAST_TO_ASCII
|
||||||
|
|
||||||
|
# Disable implicit type narrowing in signal/slot connect() calls.
|
||||||
|
QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
|
||||||
|
|
||||||
|
# Disable unsafe overloads of QProcess' start() function.
|
||||||
|
QT_NO_PROCESS_COMBINED_ARGUMENT_START
|
||||||
|
|
||||||
|
# Disable implicit QString->QUrl conversions to enforce use of proper resolving functions.
|
||||||
|
QT_NO_URL_CAST_FROM_STRING
|
||||||
|
)
|
||||||
|
|
||||||
add_subdirectory(externals)
|
add_subdirectory(externals)
|
||||||
|
|
||||||
target_link_libraries(qt_common PRIVATE core Qt6::Core SimpleIni::SimpleIni QuaZip::QuaZip)
|
target_link_libraries(qt_common PRIVATE core Qt6::Core Qt6::Concurrent SimpleIni::SimpleIni QuaZip::QuaZip)
|
||||||
|
target_link_libraries(qt_common PUBLIC frozen::frozen)
|
||||||
|
|
||||||
if (NOT APPLE AND ENABLE_OPENGL)
|
if (NOT APPLE AND ENABLE_OPENGL)
|
||||||
target_compile_definitions(qt_common PUBLIC HAS_OPENGL)
|
target_compile_definitions(qt_common PUBLIC HAS_OPENGL)
|
||||||
|
|
3
src/qt_common/externals/CMakeLists.txt
vendored
|
@ -16,5 +16,4 @@ set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON)
|
||||||
AddJsonPackage(quazip)
|
AddJsonPackage(quazip)
|
||||||
|
|
||||||
# frozen
|
# frozen
|
||||||
# TODO(crueter): Qt String Lookup
|
AddJsonPackage(frozen)
|
||||||
# AddJsonPackage(frozen)
|
|
||||||
|
|
354
src/qt_common/qt_compress.cpp
Normal file
|
@ -0,0 +1,354 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "qt_compress.h"
|
||||||
|
#include "quazipfileinfo.h"
|
||||||
|
|
||||||
|
#include <QDirIterator>
|
||||||
|
|
||||||
|
/** This is a modified version of JlCompress **/
|
||||||
|
namespace QtCommon::Compress {
|
||||||
|
|
||||||
|
bool compressDir(QString fileCompressed,
|
||||||
|
QString dir,
|
||||||
|
const JlCompress::Options &options,
|
||||||
|
QtCommon::QtProgressCallback callback)
|
||||||
|
{
|
||||||
|
// Create zip
|
||||||
|
QuaZip zip(fileCompressed);
|
||||||
|
QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
|
||||||
|
if (!zip.open(QuaZip::mdCreate)) {
|
||||||
|
QFile::remove(fileCompressed);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See how big the overall fs structure is
|
||||||
|
// good approx. of total progress
|
||||||
|
// TODO(crueter): QDirListing impl
|
||||||
|
QDirIterator iter(dir,
|
||||||
|
QDir::NoDotAndDotDot | QDir::Hidden | QDir::Files,
|
||||||
|
QDirIterator::Subdirectories);
|
||||||
|
|
||||||
|
std::size_t total = 0;
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
total += iter.nextFileInfo().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t progress = 0;
|
||||||
|
callback(total, progress);
|
||||||
|
|
||||||
|
// Add the files and subdirectories
|
||||||
|
if (!compressSubDir(&zip, dir, dir, options, total, progress, callback)) {
|
||||||
|
QFile::remove(fileCompressed);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close zip
|
||||||
|
zip.close();
|
||||||
|
if (zip.getZipError() != 0) {
|
||||||
|
QFile::remove(fileCompressed);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compressSubDir(QuaZip *zip,
|
||||||
|
QString dir,
|
||||||
|
QString origDir,
|
||||||
|
const JlCompress::Options &options,
|
||||||
|
std::size_t total,
|
||||||
|
std::size_t &progress,
|
||||||
|
QtProgressCallback callback)
|
||||||
|
{
|
||||||
|
// zip: object where to add the file
|
||||||
|
// dir: current real directory
|
||||||
|
// origDir: original real directory
|
||||||
|
// (path(dir)-path(origDir)) = path inside the zip object
|
||||||
|
|
||||||
|
if (!zip)
|
||||||
|
return false;
|
||||||
|
if (zip->getMode() != QuaZip::mdCreate && zip->getMode() != QuaZip::mdAppend
|
||||||
|
&& zip->getMode() != QuaZip::mdAdd)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
QDir directory(dir);
|
||||||
|
if (!directory.exists())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
QDir origDirectory(origDir);
|
||||||
|
if (dir != origDir) {
|
||||||
|
QuaZipFile dirZipFile(zip);
|
||||||
|
std::unique_ptr<QuaZipNewInfo> qzni;
|
||||||
|
if (options.getDateTime().isNull()) {
|
||||||
|
qzni = std::make_unique<QuaZipNewInfo>(origDirectory.relativeFilePath(dir)
|
||||||
|
+ QLatin1String("/"),
|
||||||
|
dir);
|
||||||
|
} else {
|
||||||
|
qzni = std::make_unique<QuaZipNewInfo>(origDirectory.relativeFilePath(dir)
|
||||||
|
+ QLatin1String("/"),
|
||||||
|
dir,
|
||||||
|
options.getDateTime());
|
||||||
|
}
|
||||||
|
if (!dirZipFile.open(QIODevice::WriteOnly, *qzni, nullptr, 0, 0)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
dirZipFile.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each subfolder
|
||||||
|
QFileInfoList subfiles = directory.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot
|
||||||
|
| QDir::Hidden | QDir::Dirs);
|
||||||
|
for (const auto &file : std::as_const(subfiles)) {
|
||||||
|
if (!compressSubDir(
|
||||||
|
zip, file.absoluteFilePath(), origDir, options, total, progress, callback)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each file in directory
|
||||||
|
QFileInfoList files = directory.entryInfoList(QDir::Hidden | QDir::Files);
|
||||||
|
for (const auto &file : std::as_const(files)) {
|
||||||
|
// If it's not a file or it's the compressed file being created
|
||||||
|
if (!file.isFile() || file.absoluteFilePath() == zip->getZipName())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Create relative name for the compressed file
|
||||||
|
QString filename = origDirectory.relativeFilePath(file.absoluteFilePath());
|
||||||
|
|
||||||
|
// Compress the file
|
||||||
|
if (!compressFile(zip, file.absoluteFilePath(), filename, options, total, progress, callback)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compressFile(QuaZip *zip,
|
||||||
|
QString fileName,
|
||||||
|
QString fileDest,
|
||||||
|
const JlCompress::Options &options,
|
||||||
|
std::size_t total,
|
||||||
|
std::size_t &progress,
|
||||||
|
QtCommon::QtProgressCallback callback)
|
||||||
|
{
|
||||||
|
// zip: object where to add the file
|
||||||
|
// fileName: real file name
|
||||||
|
// fileDest: file name inside the zip object
|
||||||
|
|
||||||
|
if (!zip)
|
||||||
|
return false;
|
||||||
|
if (zip->getMode() != QuaZip::mdCreate && zip->getMode() != QuaZip::mdAppend
|
||||||
|
&& zip->getMode() != QuaZip::mdAdd)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
QuaZipFile outFile(zip);
|
||||||
|
if (options.getDateTime().isNull()) {
|
||||||
|
if (!outFile.open(QIODevice::WriteOnly,
|
||||||
|
QuaZipNewInfo(fileDest, fileName),
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
options.getCompressionMethod(),
|
||||||
|
options.getCompressionLevel()))
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (!outFile.open(QIODevice::WriteOnly,
|
||||||
|
QuaZipNewInfo(fileDest, fileName, options.getDateTime()),
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
options.getCompressionMethod(),
|
||||||
|
options.getCompressionLevel()))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfo input(fileName);
|
||||||
|
if (quazip_is_symlink(input)) {
|
||||||
|
// Not sure if we should use any specialized codecs here.
|
||||||
|
// After all, a symlink IS just a byte array. And
|
||||||
|
// this is mostly for Linux, where UTF-8 is ubiquitous these days.
|
||||||
|
QString path = quazip_symlink_target(input);
|
||||||
|
QString relativePath = input.dir().relativeFilePath(path);
|
||||||
|
outFile.write(QFile::encodeName(relativePath));
|
||||||
|
} else {
|
||||||
|
QFile inFile;
|
||||||
|
inFile.setFileName(fileName);
|
||||||
|
if (!inFile.open(QIODevice::ReadOnly)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!copyData(inFile, outFile, total, progress, callback) || outFile.getZipError() != UNZ_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inFile.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
outFile.close();
|
||||||
|
return outFile.getZipError() == UNZ_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool copyData(QIODevice &inFile,
|
||||||
|
QIODevice &outFile,
|
||||||
|
std::size_t total,
|
||||||
|
std::size_t &progress,
|
||||||
|
QtProgressCallback callback)
|
||||||
|
{
|
||||||
|
while (!inFile.atEnd()) {
|
||||||
|
char buf[4096];
|
||||||
|
qint64 readLen = inFile.read(buf, 4096);
|
||||||
|
if (readLen <= 0)
|
||||||
|
return false;
|
||||||
|
if (outFile.write(buf, readLen) != readLen)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
progress += readLen;
|
||||||
|
if (!callback(total, progress)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList extractDir(QString fileCompressed, QString dir, QtCommon::QtProgressCallback callback)
|
||||||
|
{
|
||||||
|
// Open zip
|
||||||
|
QuaZip zip(fileCompressed);
|
||||||
|
return extractDir(zip, dir, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList extractDir(QuaZip &zip, const QString &dir, QtCommon::QtProgressCallback callback)
|
||||||
|
{
|
||||||
|
if (!zip.open(QuaZip::mdUnzip)) {
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
QString cleanDir = QDir::cleanPath(dir);
|
||||||
|
QDir directory(cleanDir);
|
||||||
|
QString absCleanDir = directory.absolutePath();
|
||||||
|
if (!absCleanDir.endsWith(QLatin1Char('/'))) // It only ends with / if it's the FS root.
|
||||||
|
absCleanDir += QLatin1Char('/');
|
||||||
|
QStringList extracted;
|
||||||
|
if (!zip.goToFirstFile()) {
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t total = 0;
|
||||||
|
for (const QuaZipFileInfo64 &info : zip.getFileInfoList64()) {
|
||||||
|
total += info.uncompressedSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t progress = 0;
|
||||||
|
callback(total, progress);
|
||||||
|
|
||||||
|
do {
|
||||||
|
QString name = zip.getCurrentFileName();
|
||||||
|
QString absFilePath = directory.absoluteFilePath(name);
|
||||||
|
QString absCleanPath = QDir::cleanPath(absFilePath);
|
||||||
|
if (!absCleanPath.startsWith(absCleanDir))
|
||||||
|
continue;
|
||||||
|
if (!extractFile(&zip, QLatin1String(""), absFilePath, total, progress, callback)) {
|
||||||
|
removeFile(extracted);
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
extracted.append(absFilePath);
|
||||||
|
} while (zip.goToNextFile());
|
||||||
|
|
||||||
|
// Close zip
|
||||||
|
zip.close();
|
||||||
|
if (zip.getZipError() != 0) {
|
||||||
|
removeFile(extracted);
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return extracted;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool extractFile(QuaZip *zip,
|
||||||
|
QString fileName,
|
||||||
|
QString fileDest,
|
||||||
|
std::size_t total,
|
||||||
|
std::size_t &progress,
|
||||||
|
QtCommon::QtProgressCallback callback)
|
||||||
|
{
|
||||||
|
// zip: object where to add the file
|
||||||
|
// filename: real file name
|
||||||
|
// fileincompress: file name of the compressed file
|
||||||
|
|
||||||
|
if (!zip)
|
||||||
|
return false;
|
||||||
|
if (zip->getMode() != QuaZip::mdUnzip)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!fileName.isEmpty())
|
||||||
|
zip->setCurrentFile(fileName);
|
||||||
|
QuaZipFile inFile(zip);
|
||||||
|
if (!inFile.open(QIODevice::ReadOnly) || inFile.getZipError() != UNZ_OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check existence of resulting file
|
||||||
|
QDir curDir;
|
||||||
|
if (fileDest.endsWith(QLatin1String("/"))) {
|
||||||
|
if (!curDir.mkpath(fileDest)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!curDir.mkpath(QFileInfo(fileDest).absolutePath())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QuaZipFileInfo64 info;
|
||||||
|
if (!zip->getCurrentFileInfo(&info))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
QFile::Permissions srcPerm = info.getPermissions();
|
||||||
|
if (fileDest.endsWith(QLatin1String("/")) && QFileInfo(fileDest).isDir()) {
|
||||||
|
if (srcPerm != 0) {
|
||||||
|
QFile(fileDest).setPermissions(srcPerm);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.isSymbolicLink()) {
|
||||||
|
QString target = QFile::decodeName(inFile.readAll());
|
||||||
|
return QFile::link(target, fileDest);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open resulting file
|
||||||
|
QFile outFile;
|
||||||
|
outFile.setFileName(fileDest);
|
||||||
|
if (!outFile.open(QIODevice::WriteOnly))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Copy data
|
||||||
|
if (!copyData(inFile, outFile, total, progress, callback) || inFile.getZipError() != UNZ_OK) {
|
||||||
|
outFile.close();
|
||||||
|
removeFile(QStringList(fileDest));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outFile.close();
|
||||||
|
|
||||||
|
// Close file
|
||||||
|
inFile.close();
|
||||||
|
if (inFile.getZipError() != UNZ_OK) {
|
||||||
|
removeFile(QStringList(fileDest));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srcPerm != 0) {
|
||||||
|
outFile.setPermissions(srcPerm);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool removeFile(QStringList listFile)
|
||||||
|
{
|
||||||
|
bool ret = true;
|
||||||
|
// For each file
|
||||||
|
for (int i = 0; i < listFile.count(); i++) {
|
||||||
|
// Remove
|
||||||
|
ret = ret && QFile::remove(listFile.at(i));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QtCommon::Compress
|
71
src/qt_common/qt_compress.h
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QString>
|
||||||
|
#include <JlCompress.h>
|
||||||
|
#include "qt_common/qt_common.h"
|
||||||
|
|
||||||
|
/** This is a modified version of JlCompress **/
|
||||||
|
namespace QtCommon::Compress {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compress an entire directory and report its progress.
|
||||||
|
* @param fileCompressed Destination file
|
||||||
|
* @param dir The directory to compress
|
||||||
|
* @param options Compression level, etc
|
||||||
|
* @param callback Callback that takes in two std::size_t (total, progress) and returns false if the current operation should be cancelled.
|
||||||
|
*/
|
||||||
|
bool compressDir(QString fileCompressed,
|
||||||
|
QString dir,
|
||||||
|
const JlCompress::Options& options = JlCompress::Options(),
|
||||||
|
QtCommon::QtProgressCallback callback = {});
|
||||||
|
|
||||||
|
// Internal //
|
||||||
|
bool compressSubDir(QuaZip *zip,
|
||||||
|
QString dir,
|
||||||
|
QString origDir,
|
||||||
|
const JlCompress::Options &options,
|
||||||
|
std::size_t total,
|
||||||
|
std::size_t &progress,
|
||||||
|
QtCommon::QtProgressCallback callback);
|
||||||
|
|
||||||
|
bool compressFile(QuaZip *zip,
|
||||||
|
QString fileName,
|
||||||
|
QString fileDest,
|
||||||
|
const JlCompress::Options &options,
|
||||||
|
std::size_t total,
|
||||||
|
std::size_t &progress,
|
||||||
|
QtCommon::QtProgressCallback callback);
|
||||||
|
|
||||||
|
bool copyData(QIODevice &inFile,
|
||||||
|
QIODevice &outFile,
|
||||||
|
std::size_t total,
|
||||||
|
std::size_t &progress,
|
||||||
|
QtCommon::QtProgressCallback callback);
|
||||||
|
|
||||||
|
// Extract //
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Extract a zip file and report its progress.
|
||||||
|
* @param fileCompressed Compressed file
|
||||||
|
* @param dir The directory to push the results to
|
||||||
|
* @param callback Callback that takes in two std::size_t (total, progress) and returns false if the current operation should be cancelled.
|
||||||
|
*/
|
||||||
|
QStringList extractDir(QString fileCompressed, QString dir, QtCommon::QtProgressCallback callback = {});
|
||||||
|
|
||||||
|
// Internal //
|
||||||
|
QStringList extractDir(QuaZip &zip, const QString &dir, QtCommon::QtProgressCallback callback);
|
||||||
|
|
||||||
|
bool extractFile(QuaZip *zip,
|
||||||
|
QString fileName,
|
||||||
|
QString fileDest,
|
||||||
|
std::size_t total,
|
||||||
|
std::size_t &progress,
|
||||||
|
QtCommon::QtProgressCallback callback);
|
||||||
|
|
||||||
|
bool removeFile(QStringList listFile);
|
||||||
|
|
||||||
|
}
|
|
@ -1,17 +1,22 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#include "qt_common/qt_game_util.h"
|
|
||||||
#include "qt_content_util.h"
|
#include "qt_content_util.h"
|
||||||
#include "common/fs/fs.h"
|
#include "common/fs/fs.h"
|
||||||
#include "core/hle/service/acc/profile_manager.h"
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
#include "frontend_common/content_manager.h"
|
#include "frontend_common/content_manager.h"
|
||||||
|
#include "frontend_common/data_manager.h"
|
||||||
#include "frontend_common/firmware_manager.h"
|
#include "frontend_common/firmware_manager.h"
|
||||||
#include "qt_common/qt_common.h"
|
#include "qt_common/qt_common.h"
|
||||||
|
#include "qt_common/qt_compress.h"
|
||||||
|
#include "qt_common/qt_game_util.h"
|
||||||
#include "qt_common/qt_progress_dialog.h"
|
#include "qt_common/qt_progress_dialog.h"
|
||||||
#include "qt_frontend_util.h"
|
#include "qt_frontend_util.h"
|
||||||
|
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QtConcurrentRun>
|
||||||
#include <JlCompress.h>
|
#include <JlCompress.h>
|
||||||
|
#include <qfuturewatcher.h>
|
||||||
|
|
||||||
namespace QtCommon::Content {
|
namespace QtCommon::Content {
|
||||||
|
|
||||||
|
@ -21,10 +26,10 @@ bool CheckGameFirmware(u64 program_id, QObject* parent)
|
||||||
&& !FirmwareManager::CheckFirmwarePresence(*system)) {
|
&& !FirmwareManager::CheckFirmwarePresence(*system)) {
|
||||||
auto result = QtCommon::Frontend::ShowMessage(
|
auto result = QtCommon::Frontend::ShowMessage(
|
||||||
QMessageBox::Warning,
|
QMessageBox::Warning,
|
||||||
"Game Requires Firmware",
|
tr("Game Requires Firmware"),
|
||||||
"The game you are trying to launch requires firmware to boot or to get past the "
|
tr("The game you are trying to launch requires firmware to boot or to get past the "
|
||||||
"opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>"
|
"opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>"
|
||||||
"dump and install firmware</a>, or press \"OK\" to launch anyways.",
|
"dump and install firmware</a>, or press \"OK\" to launch anyways."),
|
||||||
QMessageBox::Ok | QMessageBox::Cancel,
|
QMessageBox::Ok | QMessageBox::Cancel,
|
||||||
parent);
|
parent);
|
||||||
|
|
||||||
|
@ -60,8 +65,8 @@ void InstallFirmware(const QString& location, bool recursive)
|
||||||
|
|
||||||
const auto ShowMessage = [&]() {
|
const auto ShowMessage = [&]() {
|
||||||
QtCommon::Frontend::ShowMessage(icon,
|
QtCommon::Frontend::ShowMessage(icon,
|
||||||
failedTitle,
|
tr(failedTitle),
|
||||||
GetFirmwareInstallResultString(result));
|
tr(GetFirmwareInstallResultString(result)));
|
||||||
};
|
};
|
||||||
|
|
||||||
LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString());
|
LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString());
|
||||||
|
@ -125,8 +130,8 @@ void InstallFirmware(const QString& location, bool recursive)
|
||||||
i++;
|
i++;
|
||||||
auto firmware_src_vfile = vfs->OpenFile(firmware_src_path.generic_string(),
|
auto firmware_src_vfile = vfs->OpenFile(firmware_src_path.generic_string(),
|
||||||
FileSys::OpenMode::Read);
|
FileSys::OpenMode::Read);
|
||||||
auto firmware_dst_vfile = firmware_vdir
|
auto firmware_dst_vfile = firmware_vdir->CreateFileRelative(
|
||||||
->CreateFileRelative(firmware_src_path.filename().string());
|
firmware_src_path.filename().string());
|
||||||
|
|
||||||
if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) {
|
if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) {
|
||||||
LOG_ERROR(Frontend,
|
LOG_ERROR(Frontend,
|
||||||
|
@ -168,9 +173,9 @@ void InstallFirmware(const QString& location, bool recursive)
|
||||||
const auto failed_names = QString::fromStdString(
|
const auto failed_names = QString::fromStdString(
|
||||||
fmt::format("{}", fmt::join(results, "\n")));
|
fmt::format("{}", fmt::join(results, "\n")));
|
||||||
progress.close();
|
progress.close();
|
||||||
QtCommon::Frontend::Critical(tr("Firmware integrity verification failed!"),
|
QtCommon::Frontend::Critical(
|
||||||
tr("Verification failed for the following files:\n\n%1")
|
tr("Firmware integrity verification failed!"),
|
||||||
.arg(failed_names));
|
tr("Verification failed for the following files:\n\n%1").arg(failed_names));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,10 +186,10 @@ void InstallFirmware(const QString& location, bool recursive)
|
||||||
const std::string display_version(firmware_data.display_version.data());
|
const std::string display_version(firmware_data.display_version.data());
|
||||||
|
|
||||||
result = FirmwareInstallResult::Success;
|
result = FirmwareInstallResult::Success;
|
||||||
QtCommon::Frontend::Information(rootObject,
|
QtCommon::Frontend::Information(
|
||||||
|
rootObject,
|
||||||
tr(successTitle),
|
tr(successTitle),
|
||||||
tr(GetFirmwareInstallResultString(result))
|
tr(GetFirmwareInstallResultString(result)).arg(QString::fromStdString(display_version)));
|
||||||
.arg(QString::fromStdString(display_version)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString UnzipFirmwareToTmp(const QString& location)
|
QString UnzipFirmwareToTmp(const QString& location)
|
||||||
|
@ -193,7 +198,7 @@ QString UnzipFirmwareToTmp(const QString& location)
|
||||||
fs::path tmp{fs::temp_directory_path()};
|
fs::path tmp{fs::temp_directory_path()};
|
||||||
|
|
||||||
if (!fs::create_directories(tmp / "eden" / "firmware")) {
|
if (!fs::create_directories(tmp / "eden" / "firmware")) {
|
||||||
return "";
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp /= "eden";
|
tmp /= "eden";
|
||||||
|
@ -205,7 +210,7 @@ QString UnzipFirmwareToTmp(const QString& location)
|
||||||
|
|
||||||
QStringList result = JlCompress::extractDir(&zip, qCacheDir);
|
QStringList result = JlCompress::extractDir(&zip, qCacheDir);
|
||||||
if (result.isEmpty()) {
|
if (result.isEmpty()) {
|
||||||
return "";
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
return qCacheDir;
|
return qCacheDir;
|
||||||
|
@ -264,9 +269,8 @@ void InstallKeys()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FirmwareManager::KeyInstallResult result = FirmwareManager::InstallKeys(key_source_location
|
FirmwareManager::KeyInstallResult result
|
||||||
.toStdString(),
|
= FirmwareManager::InstallKeys(key_source_location.toStdString(), "keys");
|
||||||
"keys");
|
|
||||||
|
|
||||||
system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
|
system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
|
||||||
|
|
||||||
|
@ -282,9 +286,14 @@ void InstallKeys()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VerifyInstalledContents() {
|
void VerifyInstalledContents()
|
||||||
|
{
|
||||||
// Initialize a progress dialog.
|
// Initialize a progress dialog.
|
||||||
QtCommon::Frontend::QtProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, QtCommon::rootObject);
|
QtCommon::Frontend::QtProgressDialog progress(tr("Verifying integrity..."),
|
||||||
|
tr("Cancel"),
|
||||||
|
0,
|
||||||
|
100,
|
||||||
|
QtCommon::rootObject);
|
||||||
progress.setWindowModality(Qt::WindowModal);
|
progress.setWindowModality(Qt::WindowModal);
|
||||||
progress.setMinimumDuration(100);
|
progress.setMinimumDuration(100);
|
||||||
progress.setAutoClose(false);
|
progress.setAutoClose(false);
|
||||||
|
@ -296,16 +305,17 @@ void VerifyInstalledContents() {
|
||||||
return progress.wasCanceled();
|
return progress.wasCanceled();
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::vector<std::string> result =
|
const std::vector<std::string> result
|
||||||
ContentManager::VerifyInstalledContents(*QtCommon::system, *QtCommon::provider, QtProgressCallback);
|
= ContentManager::VerifyInstalledContents(*QtCommon::system,
|
||||||
|
*QtCommon::provider,
|
||||||
|
QtProgressCallback);
|
||||||
progress.close();
|
progress.close();
|
||||||
|
|
||||||
if (result.empty()) {
|
if (result.empty()) {
|
||||||
QtCommon::Frontend::Information(tr("Integrity verification succeeded!"),
|
QtCommon::Frontend::Information(tr("Integrity verification succeeded!"),
|
||||||
tr("The operation completed successfully."));
|
tr("The operation completed successfully."));
|
||||||
} else {
|
} else {
|
||||||
const auto failed_names =
|
const auto failed_names = QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
|
||||||
QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
|
|
||||||
QtCommon::Frontend::Critical(
|
QtCommon::Frontend::Critical(
|
||||||
tr("Integrity verification failed!"),
|
tr("Integrity verification failed!"),
|
||||||
tr("Verification failed for the following files:\n\n%1").arg(failed_names));
|
tr("Verification failed for the following files:\n\n%1").arg(failed_names));
|
||||||
|
@ -332,7 +342,7 @@ void FixProfiles()
|
||||||
qorphaned.reserve(8 * 33);
|
qorphaned.reserve(8 * 33);
|
||||||
|
|
||||||
for (const std::string& s : orphaned) {
|
for (const std::string& s : orphaned) {
|
||||||
qorphaned += "\n" + QString::fromStdString(s);
|
qorphaned = qorphaned % QStringLiteral("\n") % QString::fromStdString(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
QtCommon::Frontend::Critical(
|
QtCommon::Frontend::Critical(
|
||||||
|
@ -348,4 +358,183 @@ void FixProfiles()
|
||||||
QtCommon::Game::OpenSaveFolder();
|
QtCommon::Game::OpenSaveFolder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClearDataDir(FrontendCommon::DataManager::DataDir dir, const std::string& user_id)
|
||||||
|
{
|
||||||
|
auto result = QtCommon::Frontend::Warning(tr("Really clear data?"),
|
||||||
|
tr("Important data may be lost!"),
|
||||||
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
|
|
||||||
|
if (result != QMessageBox::Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
result = QtCommon::Frontend::Warning(
|
||||||
|
tr("Are you REALLY sure?"),
|
||||||
|
tr("Once deleted, your data will NOT come back!\n"
|
||||||
|
"Only do this if you're 100% sure you want to delete this data."),
|
||||||
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
|
|
||||||
|
if (result != QMessageBox::Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QtCommon::Frontend::QtProgressDialog dialog(tr("Clearing..."), QString(), 0, 0);
|
||||||
|
dialog.show();
|
||||||
|
|
||||||
|
FrontendCommon::DataManager::ClearDir(dir, user_id);
|
||||||
|
|
||||||
|
dialog.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExportDataDir(FrontendCommon::DataManager::DataDir data_dir,
|
||||||
|
const std::string& user_id,
|
||||||
|
std::function<void()> callback)
|
||||||
|
{
|
||||||
|
using namespace QtCommon::Frontend;
|
||||||
|
const std::string dir = FrontendCommon::DataManager::GetDataDir(data_dir, user_id);
|
||||||
|
|
||||||
|
const QString zip_dump_location = GetSaveFileName(tr("Select Export Location"),
|
||||||
|
QStringLiteral("export.zip"),
|
||||||
|
tr("Zipped Archives (*.zip)"));
|
||||||
|
|
||||||
|
if (zip_dump_location.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QtProgressDialog* progress = new QtProgressDialog(
|
||||||
|
tr("Exporting data. This may take a while..."), tr("Cancel"), 0, 100, rootObject);
|
||||||
|
|
||||||
|
progress->setWindowTitle(tr("Exporting"));
|
||||||
|
progress->setWindowModality(Qt::WindowModal);
|
||||||
|
progress->setMinimumDuration(100);
|
||||||
|
progress->setAutoClose(false);
|
||||||
|
progress->setAutoReset(false);
|
||||||
|
progress->show();
|
||||||
|
|
||||||
|
QGuiApplication::processEvents();
|
||||||
|
|
||||||
|
auto progress_callback = [=](size_t total_size, size_t processed_size) {
|
||||||
|
QMetaObject::invokeMethod(progress,
|
||||||
|
&QtProgressDialog::setValue,
|
||||||
|
static_cast<int>((processed_size * 100) / total_size));
|
||||||
|
|
||||||
|
return !progress->wasCanceled();
|
||||||
|
};
|
||||||
|
|
||||||
|
QFuture<bool> future = QtConcurrent::run([=]() {
|
||||||
|
return QtCommon::Compress::compressDir(zip_dump_location,
|
||||||
|
QString::fromStdString(dir),
|
||||||
|
JlCompress::Options(),
|
||||||
|
progress_callback);
|
||||||
|
});
|
||||||
|
|
||||||
|
QFutureWatcher<bool>* watcher = new QFutureWatcher<bool>(rootObject);
|
||||||
|
|
||||||
|
QObject::connect(watcher, &QFutureWatcher<bool>::finished, rootObject, [=]() {
|
||||||
|
progress->close();
|
||||||
|
|
||||||
|
if (watcher->result()) {
|
||||||
|
Information(tr("Exported Successfully"), tr("Data was exported successfully."));
|
||||||
|
} else if (progress->wasCanceled()) {
|
||||||
|
Information(tr("Export Cancelled"), tr("Export was cancelled by the user."));
|
||||||
|
} else {
|
||||||
|
Critical(
|
||||||
|
tr("Export Failed"),
|
||||||
|
tr("Ensure you have write permissions on the targeted directory and try again."));
|
||||||
|
}
|
||||||
|
|
||||||
|
progress->deleteLater();
|
||||||
|
watcher->deleteLater();
|
||||||
|
if (callback)
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
|
||||||
|
watcher->setFuture(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImportDataDir(FrontendCommon::DataManager::DataDir data_dir,
|
||||||
|
const std::string& user_id,
|
||||||
|
std::function<void()> callback)
|
||||||
|
{
|
||||||
|
const std::string dir = FrontendCommon::DataManager::GetDataDir(data_dir, user_id);
|
||||||
|
|
||||||
|
using namespace QtCommon::Frontend;
|
||||||
|
|
||||||
|
const QString zip_dump_location = GetOpenFileName(tr("Select Import Location"),
|
||||||
|
{},
|
||||||
|
tr("Zipped Archives (*.zip)"));
|
||||||
|
|
||||||
|
if (zip_dump_location.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
StandardButton button = Warning(
|
||||||
|
tr("Import Warning"),
|
||||||
|
tr("All previous data in this directory will be deleted. Are you sure you wish to "
|
||||||
|
"proceed?"),
|
||||||
|
StandardButton::Yes | StandardButton::No);
|
||||||
|
|
||||||
|
if (button != QMessageBox::Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QtProgressDialog* progress = new QtProgressDialog(
|
||||||
|
tr("Importing data. This may take a while..."), tr("Cancel"), 0, 100, rootObject);
|
||||||
|
|
||||||
|
progress->setWindowTitle(tr("Importing"));
|
||||||
|
progress->setWindowModality(Qt::WindowModal);
|
||||||
|
progress->setMinimumDuration(100);
|
||||||
|
progress->setAutoClose(false);
|
||||||
|
progress->setAutoReset(false);
|
||||||
|
progress->show();
|
||||||
|
progress->setValue(0);
|
||||||
|
|
||||||
|
QGuiApplication::processEvents();
|
||||||
|
|
||||||
|
// to prevent GUI mangling we have to run this in a thread as well
|
||||||
|
QFuture<bool> delete_future = QtConcurrent::run([=]() {
|
||||||
|
FrontendCommon::DataManager::ClearDir(data_dir, user_id);
|
||||||
|
return !progress->wasCanceled();
|
||||||
|
});
|
||||||
|
|
||||||
|
QFutureWatcher<bool>* delete_watcher = new QFutureWatcher<bool>(rootObject);
|
||||||
|
delete_watcher->setFuture(delete_future);
|
||||||
|
|
||||||
|
QObject::connect(delete_watcher, &QFutureWatcher<bool>::finished, rootObject, [=]() {
|
||||||
|
auto progress_callback = [=](size_t total_size, size_t processed_size) {
|
||||||
|
QMetaObject::invokeMethod(progress,
|
||||||
|
&QtProgressDialog::setValue,
|
||||||
|
static_cast<int>((processed_size * 100) / total_size));
|
||||||
|
|
||||||
|
return !progress->wasCanceled();
|
||||||
|
};
|
||||||
|
|
||||||
|
QFuture<bool> future = QtConcurrent::run([=]() {
|
||||||
|
return !QtCommon::Compress::extractDir(zip_dump_location,
|
||||||
|
QString::fromStdString(dir),
|
||||||
|
progress_callback)
|
||||||
|
.empty();
|
||||||
|
});
|
||||||
|
|
||||||
|
QFutureWatcher<bool>* watcher = new QFutureWatcher<bool>(rootObject);
|
||||||
|
|
||||||
|
QObject::connect(watcher, &QFutureWatcher<bool>::finished, rootObject, [=]() {
|
||||||
|
progress->close();
|
||||||
|
|
||||||
|
// this sucks
|
||||||
|
if (watcher->result()) {
|
||||||
|
Information(tr("Imported Successfully"), tr("Data was imported successfully."));
|
||||||
|
} else if (progress->wasCanceled()) {
|
||||||
|
Information(tr("Import Cancelled"), tr("Import was cancelled by the user."));
|
||||||
|
} else {
|
||||||
|
Critical(tr("Import Failed"),
|
||||||
|
tr("Ensure you have read permissions on the targeted directory and try "
|
||||||
|
"again."));
|
||||||
|
}
|
||||||
|
|
||||||
|
progress->deleteLater();
|
||||||
|
watcher->deleteLater();
|
||||||
|
if (callback)
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
|
||||||
|
watcher->setFuture(future);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QtCommon::Content
|
} // namespace QtCommon::Content
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "frontend_common/data_manager.h"
|
||||||
|
|
||||||
namespace QtCommon::Content {
|
namespace QtCommon::Content {
|
||||||
|
|
||||||
|
@ -46,6 +47,10 @@ void InstallKeys();
|
||||||
void VerifyGameContents(const std::string &game_path);
|
void VerifyGameContents(const std::string &game_path);
|
||||||
void VerifyInstalledContents();
|
void VerifyInstalledContents();
|
||||||
|
|
||||||
|
void ClearDataDir(FrontendCommon::DataManager::DataDir dir, const std::string &user_id = "");
|
||||||
|
void ExportDataDir(FrontendCommon::DataManager::DataDir dir, const std::string &user_id = "", std::function<void()> callback = {});
|
||||||
|
void ImportDataDir(FrontendCommon::DataManager::DataDir dir, const std::string &user_id = "", std::function<void()> callback = {});
|
||||||
|
|
||||||
// Profiles //
|
// Profiles //
|
||||||
void FixProfiles();
|
void FixProfiles();
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,4 +32,15 @@ const QString GetOpenFileName(const QString &title,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QString GetSaveFileName(const QString &title,
|
||||||
|
const QString &dir,
|
||||||
|
const QString &filter,
|
||||||
|
QString *selectedFilter,
|
||||||
|
Options options)
|
||||||
|
{
|
||||||
|
#ifdef YUZU_QT_WIDGETS
|
||||||
|
return QFileDialog::getSaveFileName((QWidget *) rootObject, title, dir, filter, selectedFilter, options);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QtCommon::Frontend
|
} // namespace QtCommon::Frontend
|
||||||
|
|
|
@ -110,21 +110,6 @@ StandardButton ShowMessage(Icon icon,
|
||||||
{ \
|
{ \
|
||||||
return ShowMessage(Icon::level, title, text, buttons, parent); \
|
return ShowMessage(Icon::level, title, text, buttons, parent); \
|
||||||
} \
|
} \
|
||||||
inline StandardButton level(QObject *parent, \
|
|
||||||
const char *title, \
|
|
||||||
const char *text, \
|
|
||||||
StandardButtons buttons \
|
|
||||||
= StandardButton::Ok) \
|
|
||||||
{ \
|
|
||||||
return ShowMessage(Icon::level, tr(title), tr(text), buttons, parent); \
|
|
||||||
} \
|
|
||||||
inline StandardButton level(const char *title, \
|
|
||||||
const char *text, \
|
|
||||||
StandardButtons buttons \
|
|
||||||
= StandardButton::Ok) \
|
|
||||||
{ \
|
|
||||||
return ShowMessage(Icon::level, tr(title), tr(text), buttons, rootObject); \
|
|
||||||
} \
|
|
||||||
inline StandardButton level(const QString title, \
|
inline StandardButton level(const QString title, \
|
||||||
const QString &text, \
|
const QString &text, \
|
||||||
StandardButtons buttons \
|
StandardButtons buttons \
|
||||||
|
@ -144,5 +129,11 @@ const QString GetOpenFileName(const QString &title,
|
||||||
QString *selectedFilter = nullptr,
|
QString *selectedFilter = nullptr,
|
||||||
Options options = Options());
|
Options options = Options());
|
||||||
|
|
||||||
|
const QString GetSaveFileName(const QString &title,
|
||||||
|
const QString &dir,
|
||||||
|
const QString &filter,
|
||||||
|
QString *selectedFilter = nullptr,
|
||||||
|
Options options = Options());
|
||||||
|
|
||||||
} // namespace QtCommon::Frontend
|
} // namespace QtCommon::Frontend
|
||||||
#endif // QT_FRONTEND_UTIL_H
|
#endif // QT_FRONTEND_UTIL_H
|
||||||
|
|
|
@ -220,8 +220,8 @@ void RemoveBaseContent(u64 program_id, InstalledEntryType type)
|
||||||
program_id);
|
program_id);
|
||||||
if (res) {
|
if (res) {
|
||||||
QtCommon::Frontend::Information(rootObject,
|
QtCommon::Frontend::Information(rootObject,
|
||||||
"Successfully Removed",
|
tr("Successfully Removed"),
|
||||||
"Successfully removed the installed base game.");
|
tr("Successfully removed the installed base game."));
|
||||||
} else {
|
} else {
|
||||||
QtCommon::Frontend::Warning(
|
QtCommon::Frontend::Warning(
|
||||||
rootObject,
|
rootObject,
|
||||||
|
@ -235,8 +235,8 @@ void RemoveUpdateContent(u64 program_id, InstalledEntryType type)
|
||||||
const auto res = ContentManager::RemoveUpdate(system->GetFileSystemController(), program_id);
|
const auto res = ContentManager::RemoveUpdate(system->GetFileSystemController(), program_id);
|
||||||
if (res) {
|
if (res) {
|
||||||
QtCommon::Frontend::Information(rootObject,
|
QtCommon::Frontend::Information(rootObject,
|
||||||
"Successfully Removed",
|
tr("Successfully Removed"),
|
||||||
"Successfully removed the installed update.");
|
tr("Successfully removed the installed update."));
|
||||||
} else {
|
} else {
|
||||||
QtCommon::Frontend::Warning(rootObject,
|
QtCommon::Frontend::Warning(rootObject,
|
||||||
GetGameListErrorRemoving(type),
|
GetGameListErrorRemoving(type),
|
||||||
|
|
|
@ -17,7 +17,12 @@ bool OpenShaderCache(u64 program_id, QObject *parent)
|
||||||
const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
|
const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
|
||||||
const auto shader_cache_folder_path{shader_cache_dir / fmt::format("{:016x}", program_id)};
|
const auto shader_cache_folder_path{shader_cache_dir / fmt::format("{:016x}", program_id)};
|
||||||
if (!Common::FS::CreateDirs(shader_cache_folder_path)) {
|
if (!Common::FS::CreateDirs(shader_cache_folder_path)) {
|
||||||
QtCommon::Frontend::ShowMessage(QMessageBox::Warning, "Error Opening Shader Cache", "Failed to create or open shader cache for this title, ensure your app data directory has write permissions.", QMessageBox::Ok, parent);
|
QtCommon::Frontend::ShowMessage(QMessageBox::Warning,
|
||||||
|
tr("Error Opening Shader Cache"),
|
||||||
|
tr("Failed to create or open shader cache for this title, "
|
||||||
|
"ensure your app data directory has write permissions."),
|
||||||
|
QMessageBox::Ok,
|
||||||
|
parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto shader_path_string{Common::FS::PathToUTF8String(shader_cache_folder_path)};
|
const auto shader_path_string{Common::FS::PathToUTF8String(shader_cache_folder_path)};
|
||||||
|
|
38
src/qt_common/qt_string_lookup.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <frozen/string.h>
|
||||||
|
#include <frozen/unordered_map.h>
|
||||||
|
#include <qobjectdefs.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
|
namespace QtCommon::StringLookup {
|
||||||
|
|
||||||
|
Q_NAMESPACE
|
||||||
|
|
||||||
|
// TODO(crueter): QML interface
|
||||||
|
enum StringKey {
|
||||||
|
SavesTooltip,
|
||||||
|
ShadersTooltip,
|
||||||
|
UserNandTooltip,
|
||||||
|
SysNandTooltip,
|
||||||
|
ModsTooltip,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr const frozen::unordered_map<StringKey, frozen::string, 5> strings = {
|
||||||
|
{SavesTooltip, "Contains game save data. DO NOT REMOVE UNLESS YOU KNOW WHAT YOU'RE DOING!"},
|
||||||
|
{ShadersTooltip, "Contains Vulkan and OpenGL pipeline caches. Generally safe to remove."},
|
||||||
|
{UserNandTooltip, "Contains updates and DLC for games."},
|
||||||
|
{SysNandTooltip, "Contains firmware and applet data."},
|
||||||
|
{ModsTooltip, "Contains game mods, patches, and cheats."},
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline const QString Lookup(StringKey key)
|
||||||
|
{
|
||||||
|
return QString::fromStdString(strings.at(key).data());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -386,9 +386,11 @@ void BufferCache<P>::BindHostComputeBuffers() {
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::SetUniformBuffersState(const std::array<u32, NUM_STAGES>& mask,
|
void BufferCache<P>::SetUniformBuffersState(const std::array<u32, NUM_STAGES>& mask,
|
||||||
const UniformBufferSizes* sizes) {
|
const UniformBufferSizes* sizes) {
|
||||||
if (channel_state->enabled_uniform_buffer_masks != mask) {
|
|
||||||
channel_state->fast_bound_uniform_buffers.fill(0);
|
|
||||||
if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
|
if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
|
||||||
|
if (channel_state->enabled_uniform_buffer_masks != mask) {
|
||||||
|
if constexpr (IS_OPENGL) {
|
||||||
|
channel_state->fast_bound_uniform_buffers.fill(0);
|
||||||
|
}
|
||||||
channel_state->dirty_uniform_buffers.fill(~u32{0});
|
channel_state->dirty_uniform_buffers.fill(~u32{0});
|
||||||
channel_state->uniform_buffer_binding_sizes.fill({});
|
channel_state->uniform_buffer_binding_sizes.fill({});
|
||||||
}
|
}
|
||||||
|
@ -804,7 +806,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||||
channel_state->uniform_buffer_binding_sizes[stage][binding_index] != size;
|
channel_state->uniform_buffer_binding_sizes[stage][binding_index] != size;
|
||||||
if (should_fast_bind) {
|
if (should_fast_bind) {
|
||||||
// We only have to bind when the currently bound buffer is not the fast version
|
// We only have to bind when the currently bound buffer is not the fast version
|
||||||
channel_state->fast_bound_uniform_buffers[stage] |= 1u << binding_index;
|
channel_state->fast_bound_uniform_buffers[stage] |= 1U << binding_index;
|
||||||
channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size;
|
channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size;
|
||||||
runtime.BindFastUniformBuffer(stage, binding_index, size);
|
runtime.BindFastUniformBuffer(stage, binding_index, size);
|
||||||
}
|
}
|
||||||
|
@ -813,8 +815,10 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
channel_state->fast_bound_uniform_buffers[stage] |= 1u << binding_index;
|
if constexpr (IS_OPENGL) {
|
||||||
|
channel_state->fast_bound_uniform_buffers[stage] |= 1U << binding_index;
|
||||||
channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size;
|
channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size;
|
||||||
|
}
|
||||||
// Stream buffer path to avoid stalling on non-Nvidia drivers or Vulkan
|
// Stream buffer path to avoid stalling on non-Nvidia drivers or Vulkan
|
||||||
const std::span<u8> span = runtime.BindMappedUniformBuffer(stage, binding_index, size);
|
const std::span<u8> span = runtime.BindMappedUniformBuffer(stage, binding_index, size);
|
||||||
device_memory.ReadBlockUnsafe(device_addr, span.data(), size);
|
device_memory.ReadBlockUnsafe(device_addr, span.data(), size);
|
||||||
|
@ -835,6 +839,9 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||||
}
|
}
|
||||||
const u32 offset = buffer.Offset(device_addr);
|
const u32 offset = buffer.Offset(device_addr);
|
||||||
if constexpr (IS_OPENGL) {
|
if constexpr (IS_OPENGL) {
|
||||||
|
// Fast buffer will be unbound
|
||||||
|
channel_state->fast_bound_uniform_buffers[stage] &= ~(1U << binding_index);
|
||||||
|
|
||||||
// Mark the index as dirty if offset doesn't match
|
// Mark the index as dirty if offset doesn't match
|
||||||
const bool is_copy_bind = offset != 0 && !runtime.SupportsNonZeroUniformOffset();
|
const bool is_copy_bind = offset != 0 && !runtime.SupportsNonZeroUniformOffset();
|
||||||
channel_state->dirty_uniform_buffers[stage] |= (is_copy_bind ? 1U : 0U) << index;
|
channel_state->dirty_uniform_buffers[stage] |= (is_copy_bind ? 1U : 0U) << index;
|
||||||
|
@ -848,7 +855,6 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||||
} else {
|
} else {
|
||||||
runtime.BindUniformBuffer(buffer, offset, size);
|
runtime.BindUniformBuffer(buffer, offset, size);
|
||||||
}
|
}
|
||||||
channel_state->fast_bound_uniform_buffers[stage] &= ~(1u << binding_index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
|
@ -1783,7 +1789,12 @@ std::span<u8> BufferCache<P>::ImmediateBuffer(size_t wanted_capacity) {
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
bool BufferCache<P>::HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept {
|
bool BufferCache<P>::HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept {
|
||||||
return ((channel_state->fast_bound_uniform_buffers[stage] >> binding_index) & 1u) != 0;
|
if constexpr (IS_OPENGL) {
|
||||||
|
return ((channel_state->fast_bound_uniform_buffers[stage] >> binding_index) & 1) != 0;
|
||||||
|
} else {
|
||||||
|
// Only OpenGL has fast uniform buffers
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
|
|
|
@ -54,8 +54,6 @@ constexpr u32 NUM_STORAGE_BUFFERS = 16;
|
||||||
constexpr u32 NUM_TEXTURE_BUFFERS = 32;
|
constexpr u32 NUM_TEXTURE_BUFFERS = 32;
|
||||||
constexpr u32 NUM_STAGES = 5;
|
constexpr u32 NUM_STAGES = 5;
|
||||||
|
|
||||||
static_assert(NUM_GRAPHICS_UNIFORM_BUFFERS <= 32, "fast bitmask must fit u32");
|
|
||||||
|
|
||||||
using UniformBufferSizes = std::array<std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS>, NUM_STAGES>;
|
using UniformBufferSizes = std::array<std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS>, NUM_STAGES>;
|
||||||
using ComputeUniformBufferSizes = std::array<u32, NUM_COMPUTE_UNIFORM_BUFFERS>;
|
using ComputeUniformBufferSizes = std::array<u32, NUM_COMPUTE_UNIFORM_BUFFERS>;
|
||||||
|
|
||||||
|
@ -139,8 +137,8 @@ public:
|
||||||
u32 written_compute_texture_buffers = 0;
|
u32 written_compute_texture_buffers = 0;
|
||||||
u32 image_compute_texture_buffers = 0;
|
u32 image_compute_texture_buffers = 0;
|
||||||
|
|
||||||
std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS> uniform_cache_hits{};
|
std::array<u32, 16> uniform_cache_hits{};
|
||||||
std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS> uniform_cache_shots{};
|
std::array<u32, 16> uniform_cache_shots{};
|
||||||
|
|
||||||
u32 uniform_buffer_skip_cache_size = DEFAULT_SKIP_CACHE_SIZE;
|
u32 uniform_buffer_skip_cache_size = DEFAULT_SKIP_CACHE_SIZE;
|
||||||
|
|
||||||
|
|
|
@ -234,6 +234,9 @@ add_executable(yuzu
|
||||||
deps_dialog.cpp
|
deps_dialog.cpp
|
||||||
deps_dialog.h
|
deps_dialog.h
|
||||||
deps_dialog.ui
|
deps_dialog.ui
|
||||||
|
|
||||||
|
data_dialog.h data_dialog.cpp data_dialog.ui
|
||||||
|
data_widget.ui
|
||||||
)
|
)
|
||||||
|
|
||||||
set_target_properties(yuzu PROPERTIES OUTPUT_NAME "eden")
|
set_target_properties(yuzu PROPERTIES OUTPUT_NAME "eden")
|
||||||
|
@ -390,32 +393,13 @@ endif()
|
||||||
|
|
||||||
target_link_libraries(yuzu PRIVATE nlohmann_json::nlohmann_json)
|
target_link_libraries(yuzu PRIVATE nlohmann_json::nlohmann_json)
|
||||||
target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core qt_common)
|
target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core qt_common)
|
||||||
target_link_libraries(yuzu PRIVATE Boost::headers glad Qt6::Widgets)
|
target_link_libraries(yuzu PRIVATE Boost::headers glad Qt6::Widgets Qt6::Concurrent)
|
||||||
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||||
|
|
||||||
if (UNIX AND NOT APPLE)
|
if (UNIX AND NOT APPLE)
|
||||||
target_link_libraries(yuzu PRIVATE Qt6::DBus)
|
target_link_libraries(yuzu PRIVATE Qt6::DBus)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_compile_definitions(yuzu PRIVATE
|
|
||||||
# Use QStringBuilder for string concatenation to reduce
|
|
||||||
# the overall number of temporary strings created.
|
|
||||||
QT_USE_QSTRINGBUILDER
|
|
||||||
|
|
||||||
# Disable implicit conversions from/to C strings
|
|
||||||
QT_NO_CAST_FROM_ASCII
|
|
||||||
QT_NO_CAST_TO_ASCII
|
|
||||||
|
|
||||||
# Disable implicit type narrowing in signal/slot connect() calls.
|
|
||||||
QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
|
|
||||||
|
|
||||||
# Disable unsafe overloads of QProcess' start() function.
|
|
||||||
QT_NO_PROCESS_COMBINED_ARGUMENT_START
|
|
||||||
|
|
||||||
# Disable implicit QString->QUrl conversions to enforce use of proper resolving functions.
|
|
||||||
QT_NO_URL_CAST_FROM_STRING
|
|
||||||
)
|
|
||||||
|
|
||||||
if (YUZU_ENABLE_COMPATIBILITY_REPORTING)
|
if (YUZU_ENABLE_COMPATIBILITY_REPORTING)
|
||||||
target_compile_definitions(yuzu PRIVATE YUZU_ENABLE_COMPATIBILITY_REPORTING)
|
target_compile_definitions(yuzu PRIVATE YUZU_ENABLE_COMPATIBILITY_REPORTING)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <QButtonGroup>
|
#include <QButtonGroup>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QtConcurrent/qtconcurrentrun.h>
|
#include <qtconcurrentrun.h>
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "ui_compatdb.h"
|
#include "ui_compatdb.h"
|
||||||
#include "yuzu/compatdb.h"
|
#include "yuzu/compatdb.h"
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#include <QRegExpValidator>
|
#include <QRegExpValidator>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <QtConcurrent/QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "ui_configure_web.h"
|
#include "ui_configure_web.h"
|
||||||
#include "qt_common/uisettings.h"
|
#include "qt_common/uisettings.h"
|
||||||
|
|
165
src/yuzu/data_dialog.cpp
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "data_dialog.h"
|
||||||
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
|
#include "frontend_common/data_manager.h"
|
||||||
|
#include "qt_common/qt_content_util.h"
|
||||||
|
#include "qt_common/qt_frontend_util.h"
|
||||||
|
#include "qt_common/qt_progress_dialog.h"
|
||||||
|
#include "qt_common/qt_string_lookup.h"
|
||||||
|
#include "ui_data_dialog.h"
|
||||||
|
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QFutureWatcher>
|
||||||
|
#include <QProgressDialog>
|
||||||
|
#include <QtConcurrentRun>
|
||||||
|
|
||||||
|
#include <core/frontend/applets/profile_select.h>
|
||||||
|
|
||||||
|
#include <applets/qt_profile_select.h>
|
||||||
|
|
||||||
|
DataDialog::DataDialog(QWidget *parent)
|
||||||
|
: QDialog(parent)
|
||||||
|
, ui(std::make_unique<Ui::DataDialog>())
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
// TODO: Should we make this a single widget that pulls data from a model?
|
||||||
|
#define WIDGET(name) \
|
||||||
|
ui->page->addWidget(new DataWidget(FrontendCommon::DataManager::DataDir::name, \
|
||||||
|
QtCommon::StringLookup::name##Tooltip, \
|
||||||
|
this));
|
||||||
|
|
||||||
|
WIDGET(Saves)
|
||||||
|
WIDGET(Shaders)
|
||||||
|
WIDGET(UserNand)
|
||||||
|
WIDGET(SysNand)
|
||||||
|
WIDGET(Mods)
|
||||||
|
|
||||||
|
#undef WIDGET
|
||||||
|
|
||||||
|
connect(ui->labels, &QListWidget::itemSelectionChanged, this, [this]() {
|
||||||
|
const auto items = ui->labels->selectedItems();
|
||||||
|
if (items.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->page->setCurrentIndex(ui->labels->row(items[0]));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
DataDialog::~DataDialog() = default;
|
||||||
|
|
||||||
|
DataWidget::DataWidget(FrontendCommon::DataManager::DataDir data_dir,
|
||||||
|
QtCommon::StringLookup::StringKey tooltip,
|
||||||
|
QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, ui(std::make_unique<Ui::DataWidget>())
|
||||||
|
, m_dir(data_dir)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
ui->tooltip->setText(QtCommon::StringLookup::Lookup(tooltip));
|
||||||
|
|
||||||
|
ui->clear->setIcon(QIcon::fromTheme(QStringLiteral("user-trash")));
|
||||||
|
ui->open->setIcon(QIcon::fromTheme(QStringLiteral("folder")));
|
||||||
|
ui->upload->setIcon(QIcon::fromTheme(QStringLiteral("upload")));
|
||||||
|
ui->download->setIcon(QIcon::fromTheme(QStringLiteral("download")));
|
||||||
|
|
||||||
|
connect(ui->clear, &QPushButton::clicked, this, &DataWidget::clear);
|
||||||
|
connect(ui->open, &QPushButton::clicked, this, &DataWidget::open);
|
||||||
|
connect(ui->upload, &QPushButton::clicked, this, &DataWidget::upload);
|
||||||
|
connect(ui->download, &QPushButton::clicked, this, &DataWidget::download);
|
||||||
|
|
||||||
|
scan();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataWidget::clear()
|
||||||
|
{
|
||||||
|
std::string user_id{};
|
||||||
|
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
|
||||||
|
user_id = selectProfile();
|
||||||
|
}
|
||||||
|
QtCommon::Content::ClearDataDir(m_dir, user_id);
|
||||||
|
scan();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataWidget::open()
|
||||||
|
{
|
||||||
|
std::string user_id{};
|
||||||
|
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
|
||||||
|
user_id = selectProfile();
|
||||||
|
}
|
||||||
|
QDesktopServices::openUrl(QUrl::fromLocalFile(
|
||||||
|
QString::fromStdString(FrontendCommon::DataManager::GetDataDir(m_dir, user_id))));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataWidget::upload()
|
||||||
|
{
|
||||||
|
std::string user_id{};
|
||||||
|
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
|
||||||
|
user_id = selectProfile();
|
||||||
|
}
|
||||||
|
QtCommon::Content::ExportDataDir(m_dir, user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataWidget::download()
|
||||||
|
{
|
||||||
|
std::string user_id{};
|
||||||
|
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
|
||||||
|
user_id = selectProfile();
|
||||||
|
}
|
||||||
|
QtCommon::Content::ImportDataDir(m_dir, user_id, std::bind(&DataWidget::scan, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataWidget::scan() {
|
||||||
|
ui->size->setText(tr("Calculating..."));
|
||||||
|
|
||||||
|
QFutureWatcher<u64> *watcher = new QFutureWatcher<u64>(this);
|
||||||
|
|
||||||
|
connect(watcher, &QFutureWatcher<u64>::finished, this, [=, this]() {
|
||||||
|
u64 size = watcher->result();
|
||||||
|
ui->size->setText(
|
||||||
|
QString::fromStdString(FrontendCommon::DataManager::ReadableBytesSize(size)));
|
||||||
|
watcher->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
|
watcher->setFuture(
|
||||||
|
QtConcurrent::run([this]() { return FrontendCommon::DataManager::DataDirSize(m_dir); }));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DataWidget::selectProfile()
|
||||||
|
{
|
||||||
|
const auto select_profile = [this] {
|
||||||
|
const Core::Frontend::ProfileSelectParameters parameters{
|
||||||
|
.mode = Service::AM::Frontend::UiMode::UserSelector,
|
||||||
|
.invalid_uid_list = {},
|
||||||
|
.display_options = {},
|
||||||
|
.purpose = Service::AM::Frontend::UserSelectionPurpose::General,
|
||||||
|
};
|
||||||
|
QtProfileSelectionDialog dialog(*QtCommon::system, this, parameters);
|
||||||
|
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint
|
||||||
|
| Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
|
||||||
|
dialog.setWindowModality(Qt::WindowModal);
|
||||||
|
|
||||||
|
if (dialog.exec() == QDialog::Rejected) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dialog.GetIndex();
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto index = select_profile();
|
||||||
|
if (index == -1) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto uuid = QtCommon::system->GetProfileManager().GetUser(static_cast<std::size_t>(index));
|
||||||
|
ASSERT(uuid);
|
||||||
|
|
||||||
|
const auto user_id = uuid->AsU128();
|
||||||
|
|
||||||
|
return fmt::format("{:016X}{:016X}", user_id[1], user_id[0]);
|
||||||
|
}
|
52
src/yuzu/data_dialog.h
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#ifndef DATA_DIALOG_H
|
||||||
|
#define DATA_DIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include "frontend_common/data_manager.h"
|
||||||
|
#include "qt_common/qt_string_lookup.h"
|
||||||
|
|
||||||
|
#include "ui_data_widget.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class DataDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DataDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DataDialog(QWidget *parent = nullptr);
|
||||||
|
~DataDialog();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Ui::DataDialog> ui;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DataWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit DataWidget(FrontendCommon::DataManager::DataDir data_dir,
|
||||||
|
QtCommon::StringLookup::StringKey tooltip,
|
||||||
|
QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void clear();
|
||||||
|
void open();
|
||||||
|
void upload();
|
||||||
|
void download();
|
||||||
|
|
||||||
|
void scan();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Ui::DataWidget> ui;
|
||||||
|
FrontendCommon::DataManager::DataDir m_dir;
|
||||||
|
|
||||||
|
std::string selectProfile();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DATA_DIALOG_H
|
148
src/yuzu/data_dialog.ui
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>DataDialog</class>
|
||||||
|
<widget class="QDialog" name="DataDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>480</width>
|
||||||
|
<height>320</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>300</width>
|
||||||
|
<height>320</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Data Manager</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1">
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="labels">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Saves</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Shaders</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>UserNAND</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>SysNAND</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Mods</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QStackedWidget" name="page">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>275</width>
|
||||||
|
<height>200</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>-1</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Deleting ANY data is IRREVERSABLE!</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::StandardButton::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>DataDialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>DataDialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
205
src/yuzu/data_widget.ui
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>DataWidget</class>
|
||||||
|
<widget class="QWidget" name="DataWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>275</width>
|
||||||
|
<height>200</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout" stretch="3,2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="tooltip">
|
||||||
|
<property name="text">
|
||||||
|
<string>Tooltip</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="size">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">Size</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="open">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>1</horstretch>
|
||||||
|
<verstretch>1</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>52</width>
|
||||||
|
<height>47</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Open with your system file manager</string>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">QPushButton {
|
||||||
|
max-width: 50px;
|
||||||
|
max-height: 45px;
|
||||||
|
min-width: 50px;
|
||||||
|
min-height: 45px;
|
||||||
|
}</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>28</width>
|
||||||
|
<height>28</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="clear">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>1</horstretch>
|
||||||
|
<verstretch>1</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>52</width>
|
||||||
|
<height>47</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Delete all data in this directory. THIS IS 100% IRREVERSABLE!</string>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">QPushButton {
|
||||||
|
max-width: 50px;
|
||||||
|
max-height: 45px;
|
||||||
|
min-width: 50px;
|
||||||
|
min-height: 45px;
|
||||||
|
}</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>28</width>
|
||||||
|
<height>28</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="upload">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>1</horstretch>
|
||||||
|
<verstretch>1</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>52</width>
|
||||||
|
<height>47</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Export all data in this directory. This may take a while!</string>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">QPushButton {
|
||||||
|
max-width: 50px;
|
||||||
|
max-height: 45px;
|
||||||
|
min-width: 50px;
|
||||||
|
min-height: 45px;
|
||||||
|
}</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>28</width>
|
||||||
|
<height>28</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="download">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>1</horstretch>
|
||||||
|
<verstretch>1</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>52</width>
|
||||||
|
<height>47</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Import data for this directory. This may take a while, and will delete ALL EXISTING DATA!</string>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">QPushButton {
|
||||||
|
max-width: 50px;
|
||||||
|
max-height: 45px;
|
||||||
|
min-width: 50px;
|
||||||
|
min-height: 45px;
|
||||||
|
}</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>28</width>
|
||||||
|
<height>28</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -156,6 +156,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||||
#include "yuzu/debugger/console.h"
|
#include "yuzu/debugger/console.h"
|
||||||
#include "yuzu/debugger/controller.h"
|
#include "yuzu/debugger/controller.h"
|
||||||
#include "yuzu/debugger/wait_tree.h"
|
#include "yuzu/debugger/wait_tree.h"
|
||||||
|
#include "yuzu/data_dialog.h"
|
||||||
#include "yuzu/deps_dialog.h"
|
#include "yuzu/deps_dialog.h"
|
||||||
#include "yuzu/discord.h"
|
#include "yuzu/discord.h"
|
||||||
#include "yuzu/game_list.h"
|
#include "yuzu/game_list.h"
|
||||||
|
@ -1705,6 +1706,7 @@ void GMainWindow::ConnectMenuEvents() {
|
||||||
connect_menu(ui->action_Install_Keys, &GMainWindow::OnInstallDecryptionKeys);
|
connect_menu(ui->action_Install_Keys, &GMainWindow::OnInstallDecryptionKeys);
|
||||||
connect_menu(ui->action_About, &GMainWindow::OnAbout);
|
connect_menu(ui->action_About, &GMainWindow::OnAbout);
|
||||||
connect_menu(ui->action_Eden_Dependencies, &GMainWindow::OnEdenDependencies);
|
connect_menu(ui->action_Eden_Dependencies, &GMainWindow::OnEdenDependencies);
|
||||||
|
connect_menu(ui->action_Data_Manager, &GMainWindow::OnDataDialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::UpdateMenuState() {
|
void GMainWindow::UpdateMenuState() {
|
||||||
|
@ -2406,7 +2408,6 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
|
||||||
|
|
||||||
ASSERT_MSG(has_user_save != has_device_save, "Game uses both user and device savedata?");
|
ASSERT_MSG(has_user_save != has_device_save, "Game uses both user and device savedata?");
|
||||||
|
|
||||||
// TODO(alekpop): It returns the wrong user
|
|
||||||
switch (target) {
|
switch (target) {
|
||||||
case GameListOpenTarget::SaveData: {
|
case GameListOpenTarget::SaveData: {
|
||||||
open_target = tr("Save Data");
|
open_target = tr("Save Data");
|
||||||
|
@ -2792,6 +2793,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
|
void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
|
||||||
|
// TODO(crueter): QtCommon
|
||||||
std::filesystem::path fs_path;
|
std::filesystem::path fs_path;
|
||||||
if (directory == QStringLiteral("SDMC")) {
|
if (directory == QStringLiteral("SDMC")) {
|
||||||
fs_path =
|
fs_path =
|
||||||
|
@ -3934,6 +3936,15 @@ void GMainWindow::OnEdenDependencies() {
|
||||||
depsDialog.exec();
|
depsDialog.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMainWindow::OnDataDialog() {
|
||||||
|
DataDialog dataDialog(this);
|
||||||
|
dataDialog.exec();
|
||||||
|
|
||||||
|
// refresh stuff in case it was cleared
|
||||||
|
OnGameListRefresh();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void GMainWindow::OnToggleFilterBar() {
|
void GMainWindow::OnToggleFilterBar() {
|
||||||
game_list->SetFilterVisible(ui->action_Show_Filter_Bar->isChecked());
|
game_list->SetFilterVisible(ui->action_Show_Filter_Bar->isChecked());
|
||||||
if (ui->action_Show_Filter_Bar->isChecked()) {
|
if (ui->action_Show_Filter_Bar->isChecked()) {
|
||||||
|
@ -4474,11 +4485,15 @@ void GMainWindow::SetFirmwareVersion() {
|
||||||
|
|
||||||
if (result.IsError() || !CheckFirmwarePresence()) {
|
if (result.IsError() || !CheckFirmwarePresence()) {
|
||||||
LOG_INFO(Frontend, "Installed firmware: No firmware available");
|
LOG_INFO(Frontend, "Installed firmware: No firmware available");
|
||||||
|
ui->menu_Applets->setEnabled(false);
|
||||||
|
ui->menu_Create_Shortcuts->setEnabled(false);
|
||||||
firmware_label->setVisible(false);
|
firmware_label->setVisible(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
firmware_label->setVisible(true);
|
firmware_label->setVisible(true);
|
||||||
|
ui->menu_Applets->setEnabled(true);
|
||||||
|
ui->menu_Create_Shortcuts->setEnabled(true);
|
||||||
|
|
||||||
const std::string display_version(firmware_data.display_version.data());
|
const std::string display_version(firmware_data.display_version.data());
|
||||||
const std::string display_title(firmware_data.display_title.data());
|
const std::string display_title(firmware_data.display_title.data());
|
||||||
|
|
|
@ -387,6 +387,7 @@ private slots:
|
||||||
void OnInstallDecryptionKeys();
|
void OnInstallDecryptionKeys();
|
||||||
void OnAbout();
|
void OnAbout();
|
||||||
void OnEdenDependencies();
|
void OnEdenDependencies();
|
||||||
|
void OnDataDialog();
|
||||||
void OnToggleFilterBar();
|
void OnToggleFilterBar();
|
||||||
void OnToggleStatusBar();
|
void OnToggleStatusBar();
|
||||||
void OnGameListRefresh();
|
void OnGameListRefresh();
|
||||||
|
|
|
@ -158,13 +158,23 @@
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menu_cabinet_applet">
|
<widget class="QMenu" name="menu_cabinet_applet">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>&Amiibo</string>
|
<string>Am&iibo</string>
|
||||||
</property>
|
</property>
|
||||||
<addaction name="action_Load_Cabinet_Nickname_Owner"/>
|
<addaction name="action_Load_Cabinet_Nickname_Owner"/>
|
||||||
<addaction name="action_Load_Cabinet_Eraser"/>
|
<addaction name="action_Load_Cabinet_Eraser"/>
|
||||||
<addaction name="action_Load_Cabinet_Restorer"/>
|
<addaction name="action_Load_Cabinet_Restorer"/>
|
||||||
<addaction name="action_Load_Cabinet_Formatter"/>
|
<addaction name="action_Load_Cabinet_Formatter"/>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QMenu" name="menu_Applets">
|
||||||
|
<property name="title">
|
||||||
|
<string>&Applets</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="action_Load_Home_Menu"/>
|
||||||
|
<addaction name="action_Load_Album"/>
|
||||||
|
<addaction name="action_Load_Mii_Edit"/>
|
||||||
|
<addaction name="action_Open_Controller_Menu"/>
|
||||||
|
<addaction name="action_Open_Setup"/>
|
||||||
|
</widget>
|
||||||
<widget class="QMenu" name="menuTAS">
|
<widget class="QMenu" name="menuTAS">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>&TAS</string>
|
<string>&TAS</string>
|
||||||
|
@ -184,7 +194,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menuInstall_Firmware">
|
<widget class="QMenu" name="menuInstall_Firmware">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Install Firmware</string>
|
<string>Install &Firmware</string>
|
||||||
</property>
|
</property>
|
||||||
<addaction name="action_Firmware_From_Folder"/>
|
<addaction name="action_Firmware_From_Folder"/>
|
||||||
<addaction name="action_Firmware_From_ZIP"/>
|
<addaction name="action_Firmware_From_ZIP"/>
|
||||||
|
@ -192,13 +202,10 @@
|
||||||
<addaction name="action_Install_Keys"/>
|
<addaction name="action_Install_Keys"/>
|
||||||
<addaction name="menuInstall_Firmware"/>
|
<addaction name="menuInstall_Firmware"/>
|
||||||
<addaction name="action_Verify_installed_contents"/>
|
<addaction name="action_Verify_installed_contents"/>
|
||||||
|
<addaction name="action_Data_Manager"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="menu_cabinet_applet"/>
|
<addaction name="menu_cabinet_applet"/>
|
||||||
<addaction name="action_Load_Album"/>
|
<addaction name="menu_Applets"/>
|
||||||
<addaction name="action_Load_Mii_Edit"/>
|
|
||||||
<addaction name="action_Open_Controller_Menu"/>
|
|
||||||
<addaction name="action_Load_Home_Menu"/>
|
|
||||||
<addaction name="action_Open_Setup"/>
|
|
||||||
<addaction name="menu_Create_Shortcuts"/>
|
<addaction name="menu_Create_Shortcuts"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="action_Capture_Screenshot"/>
|
<addaction name="action_Capture_Screenshot"/>
|
||||||
|
@ -497,12 +504,12 @@
|
||||||
</action>
|
</action>
|
||||||
<action name="action_Install_Keys">
|
<action name="action_Install_Keys">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Install Decryption Keys</string>
|
<string>Install Decryption &Keys</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="action_Load_Home_Menu">
|
<action name="action_Load_Home_Menu">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Open Home Menu</string>
|
<string>Open &Home Menu</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="action_Discord">
|
<action name="action_Discord">
|
||||||
|
@ -593,6 +600,11 @@
|
||||||
<string>&Eden Dependencies</string>
|
<string>&Eden Dependencies</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="action_Data_Manager">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Data Manager</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="yuzu.qrc"/>
|
<include location="yuzu.qrc"/>
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QtConcurrent/QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "network/announce_multiplayer_session.h"
|
#include "network/announce_multiplayer_session.h"
|
||||||
#include "ui_chat_room.h"
|
#include "ui_chat_room.h"
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include <QLocale>
|
#include <QLocale>
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <QtConcurrent/QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "network/announce_multiplayer_session.h"
|
#include "network/announce_multiplayer_session.h"
|
||||||
#include "ui_client_room.h"
|
#include "ui_client_room.h"
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include <QIntValidator>
|
#include <QIntValidator>
|
||||||
#include <QRegularExpressionValidator>
|
#include <QRegularExpressionValidator>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QtConcurrent/QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/internal_network/network_interface.h"
|
#include "core/internal_network/network_interface.h"
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <QtConcurrent/QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QtConcurrent/QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
|