Compare commits
10 Commits
vulkan-thi
...
selfhost0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4da9c6303b | ||
|
|
5478ddde87 | ||
|
|
8f1d895932 | ||
|
|
42863027e2 | ||
|
|
08f3639c80 | ||
|
|
e13c7ef3f8 | ||
|
|
89dd133a2f | ||
|
|
86e9c32800 | ||
|
|
f1cf30bc2a | ||
|
|
5af5214451 |
@@ -586,7 +586,7 @@ else()
|
||||
find_package(zstd 1.5 REQUIRED MODULE)
|
||||
|
||||
# wow
|
||||
find_package(Boost 1.57.0 CONFIG REQUIRED OPTIONAL_COMPONENTS headers context system fiber)
|
||||
find_package(Boost 1.57.0 CONFIG REQUIRED OPTIONAL_COMPONENTS headers context system fiber filesystem)
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR ANDROID)
|
||||
find_package(gamemode 1.7 MODULE)
|
||||
|
||||
@@ -13,6 +13,11 @@ option(CPMUTIL_FORCE_BUNDLED
|
||||
option(CPMUTIL_FORCE_SYSTEM
|
||||
"Force system packages for all CPM dependencies (NOT RECOMMENDED)" OFF)
|
||||
|
||||
set(CPMUTIL_DEFAULT_HOST github.com CACHE STRING "Sets the default host when 'git_host' isn't defined")
|
||||
|
||||
option(CPMUTIL_FORCE_HOST
|
||||
"Force host CPMUTIL_DEFAULT_HOST to be used for all CPM dependencies even when 'git_host' is defined" OFF)
|
||||
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
include(CPM)
|
||||
|
||||
@@ -293,8 +298,8 @@ function(AddPackage)
|
||||
option(${PKG_ARGS_NAME}_FORCE_SYSTEM "Force the system package for ${PKG_ARGS_NAME}")
|
||||
option(${PKG_ARGS_NAME}_FORCE_BUNDLED "Force the bundled package for ${PKG_ARGS_NAME}")
|
||||
|
||||
if (NOT DEFINED PKG_ARGS_GIT_HOST)
|
||||
set(git_host github.com)
|
||||
if (CPMUTIL_FORCE_HOST OR NOT DEFINED PKG_ARGS_GIT_HOST)
|
||||
set(git_host ${CPMUTIL_DEFAULT_HOST})
|
||||
else()
|
||||
set(git_host ${PKG_ARGS_GIT_HOST})
|
||||
endif()
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"hash": "4fb7f6fde92762305aad8754d7643cd918dd1f3f67e104e9ab385b18c73178d72a17321354eb203b790b6702f2cf6d725a5d6e2dfbc63b1e35f9eb59fb42ece9",
|
||||
"git_version": "1.89.0",
|
||||
"version": "1.57",
|
||||
"find_args": "CONFIG",
|
||||
"find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem",
|
||||
"patches": [
|
||||
"0001-clang-cl.patch",
|
||||
"0002-use-marmasm.patch",
|
||||
|
||||
0
dist/dev.eden_emu.eden.desktop
vendored
Normal file → Executable file
107
dist/dev.eden_emu.eden.svg
vendored
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 9.2 KiB |
BIN
dist/eden.icns
vendored
BIN
dist/eden.ico
vendored
|
Before Width: | Height: | Size: 403 KiB After Width: | Height: | Size: 315 KiB |
83
dist/eden_named.svg
vendored
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 37 KiB |
BIN
dist/qt_themes/default/icons/256x256/eden.png
vendored
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 17 KiB |
BIN
dist/qt_themes/default/icons/256x256/eden_named.png
vendored
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 29 KiB |
25
dist/qt_themes/qdarkstyle/style.qss
vendored
@@ -821,31 +821,6 @@ QTabBar QToolButton::left-arrow:disabled {
|
||||
image: url(:/qss_icons/rc/left_arrow_disabled.png);
|
||||
}
|
||||
|
||||
QDockWidget {
|
||||
background: #31363b;
|
||||
border: 1px solid #403F3F;
|
||||
titlebar-close-icon: url(:/qss_icons/rc/close.png);
|
||||
titlebar-normal-icon: url(:/qss_icons/rc/undock.png);
|
||||
}
|
||||
|
||||
QDockWidget::close-button,
|
||||
QDockWidget::float-button {
|
||||
border: 1px solid transparent;
|
||||
border-radius: 2px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QDockWidget::close-button:hover,
|
||||
QDockWidget::float-button:hover {
|
||||
background: rgba(255, 255, 255, 10);
|
||||
}
|
||||
|
||||
QDockWidget::close-button:pressed,
|
||||
QDockWidget::float-button:pressed {
|
||||
padding: 1px -1px -1px 1px;
|
||||
background: rgba(255, 255, 255, 10);
|
||||
}
|
||||
|
||||
QTreeView,
|
||||
QListView {
|
||||
border: 1px solid #54575B;
|
||||
|
||||
@@ -1685,54 +1685,6 @@ QTabBar QToolButton::right-arrow:disabled {
|
||||
image: url(":/qss_icons/rc/arrow_right_disabled.png");
|
||||
}
|
||||
|
||||
/* QDockWiget -------------------------------------------------------------
|
||||
|
||||
--------------------------------------------------------------------------- */
|
||||
QDockWidget {
|
||||
outline: 1px solid #32414B;
|
||||
background-color: #19232D;
|
||||
border: 1px solid #32414B;
|
||||
border-radius: 4px;
|
||||
titlebar-close-icon: url(":/qss_icons/rc/window_close.png");
|
||||
titlebar-normal-icon: url(":/qss_icons/rc/window_undock.png");
|
||||
}
|
||||
|
||||
QDockWidget::title {
|
||||
/* Better size for title bar */
|
||||
padding: 6px;
|
||||
spacing: 4px;
|
||||
border: none;
|
||||
background-color: #32414B;
|
||||
}
|
||||
|
||||
QDockWidget::close-button {
|
||||
background-color: #32414B;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
QDockWidget::close-button:hover {
|
||||
image: url(":/qss_icons/rc/window_close_focus.png");
|
||||
}
|
||||
|
||||
QDockWidget::close-button:pressed {
|
||||
image: url(":/qss_icons/rc/window_close_pressed.png");
|
||||
}
|
||||
|
||||
QDockWidget::float-button {
|
||||
background-color: #32414B;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
QDockWidget::float-button:hover {
|
||||
image: url(":/qss_icons/rc/window_undock_focus.png");
|
||||
}
|
||||
|
||||
QDockWidget::float-button:pressed {
|
||||
image: url(":/qss_icons/rc/window_undock_pressed.png");
|
||||
}
|
||||
|
||||
/* QTreeView QListView QTableView -----------------------------------------
|
||||
|
||||
https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtreeview
|
||||
|
||||
BIN
dist/yuzu.bmp
vendored
|
Before Width: | Height: | Size: 256 KiB After Width: | Height: | Size: 256 KiB |
BIN
dist/yuzu.icns
vendored
@@ -7,6 +7,8 @@ Global Options:
|
||||
- `CPMUTIL_FORCE_SYSTEM` (default `OFF`): Require all CPM dependencies to use system packages. NOT RECOMMENDED!
|
||||
* You may optionally override this (section)
|
||||
- `CPMUTIL_FORCE_BUNDLED` (default `ON` on MSVC and Android, `OFF` elsewhere): Require all CPM dependencies to use bundled packages.
|
||||
- `CPMUTIL_DEFAULT_HOST`: (default `github.com`): Sets the default `git_host`.
|
||||
- `CPMUTIL_FORCE_HOST`: (default `OFF`): Forces all CPM packages to use `CPMUTIL_DEFAULT_HOST` instead, even if they have `git_host` defined.
|
||||
|
||||
You are highly encouraged to read AddPackage first, even if you plan to only interact with CPMUtil via `AddJsonPackage`.
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 65 KiB |
@@ -252,11 +252,13 @@ if(CXX_CLANG)
|
||||
endif()
|
||||
|
||||
if (BOOST_NO_HEADERS)
|
||||
target_link_libraries(common PUBLIC Boost::algorithm Boost::icl Boost::pool)
|
||||
target_link_libraries(common PUBLIC Boost::algorithm Boost::icl Boost::pool Boost::filesystem)
|
||||
else()
|
||||
target_link_libraries(common PUBLIC Boost::headers)
|
||||
endif()
|
||||
|
||||
target_link_libraries(common PUBLIC Boost::filesystem)
|
||||
|
||||
if (lz4_ADDED)
|
||||
target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib)
|
||||
endif()
|
||||
|
||||
@@ -14,16 +14,29 @@ namespace fs = std::filesystem;
|
||||
|
||||
fs::path GetKvdbPath()
|
||||
{
|
||||
return GetLegacyPath(EmuPath::RyujinxDir) / "bis" / "system" / "save" / "8000000000000000" / "0"
|
||||
return GetKvdbPath(GetLegacyPath(EmuPath::RyujinxDir));
|
||||
}
|
||||
|
||||
fs::path GetKvdbPath(const fs::path& path) {
|
||||
return path / "bis" / "system" / "save" / "8000000000000000" / "0"
|
||||
/ "imkvdb.arc";
|
||||
}
|
||||
|
||||
fs::path GetRyuPathFromSavePath(const fs::path& path) {
|
||||
// This is a horrible hack, but I cba to find something better
|
||||
return path.parent_path().parent_path().parent_path().parent_path().parent_path();
|
||||
}
|
||||
|
||||
fs::path GetRyuSavePath(const u64 &save_id)
|
||||
{
|
||||
return GetRyuSavePath(GetLegacyPath(EmuPath::RyujinxDir), save_id);
|
||||
}
|
||||
|
||||
std::filesystem::path GetRyuSavePath(const std::filesystem::path& path, const u64& save_id) {
|
||||
std::string hex = fmt::format("{:016x}", save_id);
|
||||
|
||||
// TODO: what's the difference between 0 and 1?
|
||||
return GetLegacyPath(EmuPath::RyujinxDir) / "bis" / "user" / "save" / hex / "0";
|
||||
// TODO: what's the difference between 0 and 1?
|
||||
return path / "bis" / "user" / "save" / hex / "0";
|
||||
}
|
||||
|
||||
IMENReadResult ReadKvdb(const fs::path &path, std::vector<IMEN> &imens)
|
||||
|
||||
@@ -7,16 +7,17 @@
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
constexpr const char IMEN_MAGIC[4] = {0x49, 0x4d, 0x45, 0x4e};
|
||||
constexpr const char IMKV_MAGIC[4] = {0x49, 0x4d, 0x4b, 0x56};
|
||||
constexpr const u8 IMEN_SIZE = 0x8c;
|
||||
|
||||
fs::path GetKvdbPath();
|
||||
fs::path GetRyuSavePath(const u64 &program_id);
|
||||
std::filesystem::path GetKvdbPath();
|
||||
std::filesystem::path GetKvdbPath(const std::filesystem::path &path);
|
||||
std::filesystem::path GetRyuPathFromSavePath(const std::filesystem::path &path);
|
||||
std::filesystem::path GetRyuSavePath(const u64 &save_id);
|
||||
std::filesystem::path GetRyuSavePath(const std::filesystem::path &path, const u64 &save_id);
|
||||
|
||||
enum class IMENReadResult {
|
||||
Nonexistent, // ryujinx not found
|
||||
@@ -35,6 +36,6 @@ struct IMEN
|
||||
|
||||
static_assert(sizeof(IMEN) == 0x10, "IMEN has incorrect size.");
|
||||
|
||||
IMENReadResult ReadKvdb(const fs::path &path, std::vector<IMEN> &imens);
|
||||
IMENReadResult ReadKvdb(const std::filesystem::path &path, std::vector<IMEN> &imens);
|
||||
|
||||
} // namespace Common::FS
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
#include "symlink.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <fmt/format.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// The sole purpose of this file is to treat symlinks like symlinks on POSIX,
|
||||
@@ -15,29 +17,40 @@ namespace fs = std::filesystem;
|
||||
// This is because, for some inexplicable reason, Microsoft has locked symbolic
|
||||
// links behind a "security policy", whereas directory junctions--functionally identical
|
||||
// for directories, by the way--are not. Why? I don't know.
|
||||
// And no, they do NOT provide a standard API for this (at least to my knowledge).
|
||||
// CreateSymbolicLink, even when EXPLICITLY TOLD to create a junction, still fails
|
||||
// because of their security policy.
|
||||
// I don't know what kind of drugs the Windows developers have been on since NT started.
|
||||
|
||||
// Microsoft still has not implemented any of this in their std::filesystem implemenation,
|
||||
// which ALSO means that it DOES NOT FOLLOW ANY DIRECTORY JUNCTIONS... AT ALL.
|
||||
// Nor does any of their command line utilities or APIs. So you're quite literally
|
||||
// on your own.
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
bool CreateSymlink(const fs::path &from, const fs::path &to)
|
||||
bool CreateSymlink(fs::path from, fs::path to)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const std::string command = fmt::format("mklink /J {} {}", to.string(), from.string());
|
||||
return system(command.c_str()) == 0;
|
||||
#else
|
||||
from.make_preferred();
|
||||
to.make_preferred();
|
||||
|
||||
std::error_code ec;
|
||||
fs::create_directory_symlink(from, to, ec);
|
||||
return !ec;
|
||||
#ifdef _WIN32
|
||||
if (ec) {
|
||||
const std::string command = fmt::format("mklink /J \"{}\" \"{}\"",
|
||||
to.string(),
|
||||
from.string());
|
||||
return system(command.c_str()) == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return !ec;
|
||||
}
|
||||
|
||||
bool IsSymlink(const fs::path &path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
auto attributes = GetFileAttributesW(path.wstring().c_str());
|
||||
return attributes & FILE_ATTRIBUTE_REPARSE_POINT;
|
||||
#else
|
||||
return fs::is_symlink(path);
|
||||
#endif
|
||||
return boost::filesystem::is_symlink(boost::filesystem::path{path});
|
||||
}
|
||||
|
||||
} // namespace Common::FS
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <filesystem>
|
||||
namespace Common::FS {
|
||||
|
||||
bool CreateSymlink(const std::filesystem::path &from, const std::filesystem::path &to);
|
||||
bool CreateSymlink(std::filesystem::path from, std::filesystem::path to);
|
||||
bool IsSymlink(const std::filesystem::path &path);
|
||||
|
||||
} // namespace Common::FS
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include "common/heap_tracker.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/assert.h"
|
||||
@@ -37,8 +36,6 @@ HeapTracker::~HeapTracker() = default;
|
||||
|
||||
void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
||||
MemoryPermission perm, bool is_separate_heap) {
|
||||
bool rebuild_required = false;
|
||||
|
||||
// When mapping other memory, map pages immediately.
|
||||
if (!is_separate_heap) {
|
||||
m_buffer.Map(virtual_offset, host_offset, length, perm, false);
|
||||
@@ -60,29 +57,11 @@ void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
||||
|
||||
// Insert into mappings.
|
||||
m_map_count++;
|
||||
const auto it = m_mappings.insert(*map);
|
||||
|
||||
// Update tick before possible rebuild.
|
||||
it->tick = m_tick++;
|
||||
|
||||
// Check if we need to rebuild.
|
||||
if (m_resident_map_count >= m_max_resident_map_count) {
|
||||
rebuild_required = true;
|
||||
}
|
||||
|
||||
// Map the area.
|
||||
m_buffer.Map(it->vaddr, it->paddr, it->size, it->perm, false);
|
||||
|
||||
// This map is now resident.
|
||||
it->is_resident = true;
|
||||
m_resident_map_count++;
|
||||
m_resident_mappings.insert(*it);
|
||||
m_mappings.insert(*map);
|
||||
}
|
||||
|
||||
if (rebuild_required) {
|
||||
// A rebuild was required, so perform it now.
|
||||
this->RebuildSeparateHeapAddressSpace();
|
||||
}
|
||||
// Finally, map.
|
||||
this->DeferredMapSeparateHeap(virtual_offset);
|
||||
}
|
||||
|
||||
void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_heap) {
|
||||
@@ -169,7 +148,6 @@ void HeapTracker::Protect(size_t virtual_offset, size_t size, MemoryPermission p
|
||||
|
||||
// Clamp to end.
|
||||
next = (std::min)(next, end);
|
||||
|
||||
// Reprotect, if we need to.
|
||||
if (should_protect) {
|
||||
m_buffer.Protect(cur, next - cur, perm);
|
||||
@@ -180,6 +158,51 @@ void HeapTracker::Protect(size_t virtual_offset, size_t size, MemoryPermission p
|
||||
}
|
||||
}
|
||||
|
||||
bool HeapTracker::DeferredMapSeparateHeap(u8* fault_address) {
|
||||
if (m_buffer.IsInVirtualRange(fault_address)) {
|
||||
return this->DeferredMapSeparateHeap(fault_address - m_buffer.VirtualBasePointer());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) {
|
||||
bool rebuild_required = false;
|
||||
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
// Check to ensure this was a non-resident separate heap mapping.
|
||||
const auto it = this->GetNearestHeapMapLocked(virtual_offset);
|
||||
if (it == m_mappings.end() || it->is_resident) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update tick before possible rebuild.
|
||||
it->tick = m_tick++;
|
||||
|
||||
// Check if we need to rebuild.
|
||||
if (m_resident_map_count > m_max_resident_map_count) {
|
||||
rebuild_required = true;
|
||||
}
|
||||
|
||||
// Map the area.
|
||||
m_buffer.Map(it->vaddr, it->paddr, it->size, it->perm, false);
|
||||
|
||||
// This map is now resident.
|
||||
it->is_resident = true;
|
||||
m_resident_map_count++;
|
||||
m_resident_mappings.insert(*it);
|
||||
}
|
||||
|
||||
if (rebuild_required) {
|
||||
// A rebuild was required, so perform it now.
|
||||
this->RebuildSeparateHeapAddressSpace();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void HeapTracker::RebuildSeparateHeapAddressSpace() {
|
||||
std::scoped_lock lk{m_rebuild_lock, m_lock};
|
||||
|
||||
@@ -190,8 +213,8 @@ void HeapTracker::RebuildSeparateHeapAddressSpace() {
|
||||
// Despite being worse in theory, this has proven to be better in practice than more
|
||||
// regularly dumping a smaller amount, because it significantly reduces average case
|
||||
// lock contention.
|
||||
const size_t desired_count = (std::min)(m_resident_map_count, m_max_resident_map_count) / 2;
|
||||
const size_t evict_count = m_resident_map_count - desired_count;
|
||||
std::size_t const desired_count = (std::min)(m_resident_map_count, m_max_resident_map_count) / 2;
|
||||
std::size_t const evict_count = m_resident_map_count - desired_count;
|
||||
auto it = m_resident_mappings.begin();
|
||||
|
||||
for (size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) {
|
||||
|
||||
@@ -1231,6 +1231,7 @@ endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||
target_sources(core PRIVATE
|
||||
arm/dynarmic/arm_dynarmic.cpp
|
||||
arm/dynarmic/arm_dynarmic.h
|
||||
arm/dynarmic/arm_dynarmic_64.cpp
|
||||
arm/dynarmic/arm_dynarmic_64.h
|
||||
|
||||
52
src/core/arm/dynarmic/arm_dynarmic.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include "common/signal_chain.h"
|
||||
|
||||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
namespace {
|
||||
|
||||
thread_local Core::Memory::Memory* g_current_memory{};
|
||||
std::once_flag g_registered{};
|
||||
struct sigaction g_old_segv {};
|
||||
|
||||
void HandleSigSegv(int sig, siginfo_t* info, void* ctx) {
|
||||
if (g_current_memory && g_current_memory->InvalidateSeparateHeap(info->si_addr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return g_old_segv.sa_sigaction(sig, info, ctx);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ScopedJitExecution::ScopedJitExecution(Kernel::KProcess* process) {
|
||||
g_current_memory = std::addressof(process->GetMemory());
|
||||
}
|
||||
|
||||
ScopedJitExecution::~ScopedJitExecution() {
|
||||
g_current_memory = nullptr;
|
||||
}
|
||||
|
||||
void ScopedJitExecution::RegisterHandler() {
|
||||
std::call_once(g_registered, [] {
|
||||
struct sigaction sa {};
|
||||
sa.sa_sigaction = &HandleSigSegv;
|
||||
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
Common::SigAction(SIGSEGV, std::addressof(sa), std::addressof(g_old_segv));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
#endif
|
||||
@@ -29,4 +29,24 @@ constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
|
||||
return static_cast<HaltReason>(hr);
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
class ScopedJitExecution {
|
||||
public:
|
||||
explicit ScopedJitExecution(Kernel::KProcess* process);
|
||||
~ScopedJitExecution();
|
||||
static void RegisterHandler();
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
class ScopedJitExecution {
|
||||
public:
|
||||
explicit ScopedJitExecution(Kernel::KProcess* process) {}
|
||||
~ScopedJitExecution() {}
|
||||
static void RegisterHandler() {}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -341,11 +341,15 @@ bool ArmDynarmic32::IsInThumbMode() const {
|
||||
}
|
||||
|
||||
HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) {
|
||||
ScopedJitExecution sj(thread->GetOwnerProcess());
|
||||
|
||||
m_jit->ClearExclusiveState();
|
||||
return TranslateHaltReason(m_jit->Run());
|
||||
}
|
||||
|
||||
HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) {
|
||||
ScopedJitExecution sj(thread->GetOwnerProcess());
|
||||
|
||||
m_jit->ClearExclusiveState();
|
||||
return TranslateHaltReason(m_jit->Step());
|
||||
}
|
||||
@@ -387,6 +391,7 @@ ArmDynarmic32::ArmDynarmic32(System& system, bool uses_wall_clock, Kernel::KProc
|
||||
m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} {
|
||||
auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl();
|
||||
m_jit = MakeJit(&page_table_impl);
|
||||
ScopedJitExecution::RegisterHandler();
|
||||
}
|
||||
|
||||
ArmDynarmic32::~ArmDynarmic32() = default;
|
||||
|
||||
@@ -372,11 +372,15 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
|
||||
}
|
||||
|
||||
HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) {
|
||||
ScopedJitExecution sj(thread->GetOwnerProcess());
|
||||
|
||||
m_jit->ClearExclusiveState();
|
||||
return TranslateHaltReason(m_jit->Run());
|
||||
}
|
||||
|
||||
HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) {
|
||||
ScopedJitExecution sj(thread->GetOwnerProcess());
|
||||
|
||||
m_jit->ClearExclusiveState();
|
||||
return TranslateHaltReason(m_jit->Step());
|
||||
}
|
||||
@@ -416,6 +420,7 @@ ArmDynarmic64::ArmDynarmic64(System& system, bool uses_wall_clock, Kernel::KProc
|
||||
auto& page_table = process->GetPageTable().GetBasePageTable();
|
||||
auto& page_table_impl = page_table.GetImpl();
|
||||
m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth());
|
||||
ScopedJitExecution::RegisterHandler();
|
||||
}
|
||||
|
||||
ArmDynarmic64::~ArmDynarmic64() = default;
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <boost/algorithm/string/find.hpp>
|
||||
@@ -18,11 +16,11 @@
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/fs_types.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/fs/symlink.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include <ranges>
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
@@ -492,6 +490,32 @@ void ProfileManager::ResetUserSaveFile()
|
||||
ParseUserSaveFile();
|
||||
}
|
||||
|
||||
std::vector<UUID> ProfileManager::FindExistingProfileUUIDs()
|
||||
{
|
||||
std::vector<UUID> uuids;
|
||||
for (const ProfileInfo& p : profiles) {
|
||||
auto uuid = p.user_uuid;
|
||||
if (!uuid.IsInvalid()) {
|
||||
uuids.emplace_back(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
return uuids;
|
||||
}
|
||||
|
||||
std::vector<std::string> ProfileManager::FindExistingProfileStrings()
|
||||
{
|
||||
std::vector<UUID> uuids = FindExistingProfileUUIDs();
|
||||
std::vector<std::string> uuid_strings;
|
||||
|
||||
for (const UUID &uuid : uuids) {
|
||||
auto user_id = uuid.AsU128();
|
||||
uuid_strings.emplace_back(fmt::format("{:016X}{:016X}", user_id[1], user_id[0]));
|
||||
}
|
||||
|
||||
return uuid_strings;
|
||||
}
|
||||
|
||||
std::vector<std::string> ProfileManager::FindGoodProfiles()
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
@@ -501,31 +525,17 @@ std::vector<std::string> ProfileManager::FindGoodProfiles()
|
||||
const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir)
|
||||
/ "user/save/0000000000000000";
|
||||
|
||||
// some exceptions because certain games just LOVE TO CAUSE ISSUES
|
||||
static constexpr const std::array<const char* const, 2> EXCEPTION_UUIDS
|
||||
= {"5755CC2A545A87128500000000000000", "00000000000000000000000000000000"};
|
||||
// some exceptions, e.g. the "system" profile
|
||||
static constexpr const std::array<const char* const, 1> EXCEPTION_UUIDS
|
||||
= {"00000000000000000000000000000000"};
|
||||
|
||||
for (const char *const uuid : EXCEPTION_UUIDS) {
|
||||
if (fs::exists(path / uuid))
|
||||
good_uuids.emplace_back(uuid);
|
||||
}
|
||||
|
||||
for (const ProfileInfo& p : profiles) {
|
||||
std::string uuid_string = [p]() -> std::string {
|
||||
auto uuid = p.user_uuid;
|
||||
|
||||
// "ignore" invalid uuids
|
||||
if (uuid.IsInvalid()) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
auto user_id = uuid.AsU128();
|
||||
|
||||
return fmt::format("{:016X}{:016X}", user_id[1], user_id[0]);
|
||||
}();
|
||||
|
||||
if (uuid_string != "0") good_uuids.emplace_back(uuid_string);
|
||||
}
|
||||
auto existing = FindExistingProfileStrings();
|
||||
good_uuids.insert(good_uuids.end(), existing.begin(), existing.end());
|
||||
|
||||
return good_uuids;
|
||||
}
|
||||
@@ -562,7 +572,8 @@ std::vector<std::string> ProfileManager::FindOrphanedProfiles()
|
||||
override = true;
|
||||
|
||||
// if there are any regular files (NOT directories) there, do NOT delete it :p
|
||||
if (file.is_regular_file())
|
||||
// Also: check for symlinks
|
||||
if (file.is_regular_file() || Common::FS::IsSymlink(file.path()))
|
||||
return false;
|
||||
}
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
|
||||
@@ -105,6 +105,8 @@ public:
|
||||
|
||||
void ResetUserSaveFile();
|
||||
|
||||
std::vector<Common::UUID> FindExistingProfileUUIDs();
|
||||
std::vector<std::string> FindExistingProfileStrings();
|
||||
std::vector<std::string> FindGoodProfiles();
|
||||
std::vector<std::string> FindOrphanedProfiles();
|
||||
|
||||
|
||||
@@ -1230,7 +1230,22 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
|
||||
if (rasterizer) {
|
||||
impl->InvalidateGPUMemory(ptr, size);
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
if (!rasterizer && mapped) {
|
||||
impl->buffer->DeferredMapSeparateHeap(GetInteger(vaddr));
|
||||
}
|
||||
#endif
|
||||
|
||||
return mapped && ptr != nullptr;
|
||||
}
|
||||
|
||||
bool Memory::InvalidateSeparateHeap(void* fault_address) {
|
||||
#ifdef __linux__
|
||||
return impl->buffer->DeferredMapSeparateHeap(static_cast<u8*>(fault_address));
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Core::Memory
|
||||
|
||||
@@ -495,6 +495,8 @@ public:
|
||||
|
||||
bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size);
|
||||
|
||||
bool InvalidateSeparateHeap(void* fault_address);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
|
||||
|
||||
@@ -125,6 +125,10 @@ struct Jit::Impl final {
|
||||
current_state.exclusive_state = false;
|
||||
}
|
||||
|
||||
std::string Disassemble() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
void PerformRequestedCacheInvalidation(HaltReason hr) {
|
||||
if (Has(hr, HaltReason::CacheInvalidation)) {
|
||||
@@ -231,4 +235,8 @@ void Jit::ClearExclusiveState() {
|
||||
impl->ClearExclusiveState();
|
||||
}
|
||||
|
||||
std::string Jit::Disassemble() const {
|
||||
return impl->Disassemble();
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::A32
|
||||
|
||||
@@ -152,7 +152,7 @@ struct Jit::Impl final {
|
||||
}
|
||||
|
||||
std::string Disassemble() const {
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -28,7 +28,7 @@ const QString GetOpenFileName(const QString &title,
|
||||
Options options)
|
||||
{
|
||||
#ifdef YUZU_QT_WIDGETS
|
||||
return QFileDialog::getOpenFileName((QWidget *) rootObject, title, dir, filter, selectedFilter, options);
|
||||
return QFileDialog::getOpenFileName(rootObject, title, dir, filter, selectedFilter, options);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -39,7 +39,14 @@ const QString GetSaveFileName(const QString &title,
|
||||
Options options)
|
||||
{
|
||||
#ifdef YUZU_QT_WIDGETS
|
||||
return QFileDialog::getSaveFileName((QWidget *) rootObject, title, dir, filter, selectedFilter, options);
|
||||
return QFileDialog::getSaveFileName(rootObject, title, dir, filter, selectedFilter, options);
|
||||
#endif
|
||||
}
|
||||
|
||||
const QString GetExistingDirectory(const QString& caption, const QString& dir,
|
||||
Options options) {
|
||||
#ifdef YUZU_QT_WIDGETS
|
||||
return QFileDialog::getExistingDirectory(rootObject, caption, dir, options);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -135,5 +135,9 @@ const QString GetSaveFileName(const QString &title,
|
||||
QString *selectedFilter = nullptr,
|
||||
Options options = Options());
|
||||
|
||||
const QString GetExistingDirectory(const QString &caption = QString(),
|
||||
const QString &dir = QString(),
|
||||
Options options = Option::ShowDirsOnly);
|
||||
|
||||
} // namespace QtCommon::Frontend
|
||||
#endif // FRONTEND_H
|
||||
|
||||
@@ -294,6 +294,17 @@ void QtConfig::ReadUIGamelistValues() {
|
||||
}
|
||||
EndArray();
|
||||
|
||||
const int linked_size = BeginArray("ryujinx_linked");
|
||||
for (int i = 0; i < linked_size; ++i) {
|
||||
SetArrayIndex(i);
|
||||
|
||||
QDir ryu_dir = QString::fromStdString(ReadStringSetting("ryujinx_path"));
|
||||
u64 program_id = ReadUnsignedIntegerSetting("program_id");
|
||||
|
||||
UISettings::values.ryujinx_link_paths.insert(program_id, ryu_dir);
|
||||
}
|
||||
EndArray();
|
||||
|
||||
EndGroup();
|
||||
}
|
||||
|
||||
@@ -499,6 +510,21 @@ void QtConfig::SaveUIGamelistValues() {
|
||||
}
|
||||
EndArray(); // favorites
|
||||
|
||||
BeginArray(std::string("ryujinx_linked"));
|
||||
int i = 0;
|
||||
QMapIterator iter(UISettings::values.ryujinx_link_paths);
|
||||
|
||||
while (iter.hasNext()) {
|
||||
iter.next();
|
||||
|
||||
SetArrayIndex(i);
|
||||
WriteIntegerSetting("program_id", iter.key());
|
||||
WriteStringSetting("ryujinx_path", iter.value().absolutePath().toStdString());
|
||||
++i;
|
||||
}
|
||||
|
||||
EndArray(); // ryujinx
|
||||
|
||||
EndGroup();
|
||||
}
|
||||
|
||||
|
||||
@@ -28,54 +28,54 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject *parent);
|
||||
std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent);
|
||||
|
||||
static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map = {
|
||||
{Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))},
|
||||
{Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FXAA"))},
|
||||
{Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SMAA"))},
|
||||
{Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "None"))},
|
||||
{Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "FXAA"))},
|
||||
{Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "SMAA"))},
|
||||
};
|
||||
|
||||
static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map = {
|
||||
{Settings::ScalingFilter::NearestNeighbor,
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Nearest"))},
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Nearest"))},
|
||||
{Settings::ScalingFilter::Bilinear,
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))},
|
||||
{Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))},
|
||||
{Settings::ScalingFilter::ZeroTangent, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Zero-Tangent"))},
|
||||
{Settings::ScalingFilter::BSpline, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "B-Spline"))},
|
||||
{Settings::ScalingFilter::Mitchell, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Mitchell"))},
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Bilinear"))},
|
||||
{Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Bicubic"))},
|
||||
{Settings::ScalingFilter::ZeroTangent, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Zero-Tangent"))},
|
||||
{Settings::ScalingFilter::BSpline, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "B-Spline"))},
|
||||
{Settings::ScalingFilter::Mitchell, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Mitchell"))},
|
||||
{Settings::ScalingFilter::Spline1,
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Spline-1"))},
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Spline-1"))},
|
||||
{Settings::ScalingFilter::Gaussian,
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))},
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Gaussian"))},
|
||||
{Settings::ScalingFilter::Lanczos,
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Lanczos"))},
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Lanczos"))},
|
||||
{Settings::ScalingFilter::ScaleForce,
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))},
|
||||
{Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))},
|
||||
{Settings::ScalingFilter::Area, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Area"))},
|
||||
{Settings::ScalingFilter::Mmpx, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "MMPX"))},
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "ScaleForce"))},
|
||||
{Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "FSR"))},
|
||||
{Settings::ScalingFilter::Area, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Area"))},
|
||||
{Settings::ScalingFilter::Mmpx, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "MMPX"))},
|
||||
};
|
||||
|
||||
static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map = {
|
||||
{Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))},
|
||||
{Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))},
|
||||
{Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Docked"))},
|
||||
{Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Handheld"))},
|
||||
};
|
||||
|
||||
static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map = {
|
||||
{Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))},
|
||||
{Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))},
|
||||
{Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))},
|
||||
{Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Normal"))},
|
||||
{Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "High"))},
|
||||
{Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Extreme"))},
|
||||
};
|
||||
|
||||
static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map = {
|
||||
{Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Vulkan"))},
|
||||
{Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))},
|
||||
{Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))},
|
||||
{Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Vulkan"))},
|
||||
{Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "OpenGL"))},
|
||||
{Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Null"))},
|
||||
};
|
||||
|
||||
static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map = {
|
||||
{Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))},
|
||||
{Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))},
|
||||
{Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))},
|
||||
{Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "GLSL"))},
|
||||
{Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "GLASM"))},
|
||||
{Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "SPIRV"))},
|
||||
};
|
||||
|
||||
} // namespace ConfigurationShared
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QVector>
|
||||
#include <qdir.h>
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
@@ -201,6 +202,7 @@ struct Values {
|
||||
Setting<bool> cache_game_list{linkage, true, "cache_game_list", Category::UiGameList};
|
||||
Setting<bool> favorites_expanded{linkage, true, "favorites_expanded", Category::UiGameList};
|
||||
QVector<u64> favorited_ids;
|
||||
QMap<u64, QDir> ryujinx_link_paths;
|
||||
|
||||
// Compatibility List
|
||||
Setting<bool> show_compat{linkage, true, "show_compat", Category::UiGameList};
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
#include "frozen/map.h"
|
||||
#include "frozen/string.h"
|
||||
|
||||
/// Small helper to look up enums.
|
||||
/// res = the result code
|
||||
/// base = the base matching value in the StringKey table
|
||||
#define LOOKUP_ENUM(res, base) StringLookup::Lookup( \
|
||||
static_cast<StringLookup::StringKey>((int) res + (int) StringLookup::base))
|
||||
|
||||
namespace QtCommon::StringLookup {
|
||||
|
||||
Q_NAMESPACE
|
||||
|
||||
@@ -25,8 +25,7 @@ enum class FirmwareInstallResult {
|
||||
|
||||
inline const QString GetFirmwareInstallResultString(FirmwareInstallResult result)
|
||||
{
|
||||
return QtCommon::StringLookup::Lookup(static_cast<StringLookup::StringKey>(
|
||||
(int) result + (int) QtCommon::StringLookup::FwInstallSuccess));
|
||||
return LOOKUP_ENUM(result, FwInstallSuccess);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,9 +35,7 @@ inline const QString GetFirmwareInstallResultString(FirmwareInstallResult result
|
||||
*/
|
||||
inline const QString GetKeyInstallResultString(FirmwareManager::KeyInstallResult result)
|
||||
{
|
||||
// this can probably be made into a common function of sorts
|
||||
return QtCommon::StringLookup::Lookup(static_cast<StringLookup::StringKey>(
|
||||
(int) result + (int) QtCommon::StringLookup::KeyInstallSuccess));
|
||||
return LOOKUP_ENUM(result, KeyInstallSuccess);
|
||||
}
|
||||
|
||||
void InstallFirmware(const QString &location, bool recursive);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include "fs.h"
|
||||
#include "common/fs/ryujinx_compat.h"
|
||||
#include "common/fs/symlink.h"
|
||||
#include "frontend_common/data_manager.h"
|
||||
#include "fs.h"
|
||||
#include "qt_common/abstract/frontend.h"
|
||||
#include "qt_common/qt_string_lookup.h"
|
||||
|
||||
@@ -56,6 +56,9 @@ bool CheckUnlink(const fs::path &eden_dir, const fs::path &ryu_dir)
|
||||
orig = eden_dir;
|
||||
}
|
||||
|
||||
linked.make_preferred();
|
||||
orig.make_preferred();
|
||||
|
||||
// first cleanup the symlink/junction,
|
||||
try {
|
||||
// NB: do NOT use remove_all, as Windows treats this as a remove_all to the target,
|
||||
@@ -84,47 +87,64 @@ bool CheckUnlink(const fs::path &eden_dir, const fs::path &ryu_dir)
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 GetRyujinxSaveID(const u64 &program_id)
|
||||
const fs::path GetRyujinxSavePath(const fs::path &path_hint, const u64 &program_id)
|
||||
{
|
||||
auto path = Common::FS::GetKvdbPath();
|
||||
auto ryu_path = path_hint;
|
||||
|
||||
auto kvdb_path = Common::FS::GetKvdbPath(ryu_path);
|
||||
|
||||
if (!fs::exists(kvdb_path)) {
|
||||
using namespace QtCommon::Frontend;
|
||||
auto res = Warning(
|
||||
tr("Could not find Ryujinx installation"),
|
||||
tr("Could not find a valid Ryujinx installation. This may typically occur if you are "
|
||||
"using Ryujinx in portable mode.\n\nWould you like to manually select a portable "
|
||||
"folder to use?"), StandardButton::Yes | StandardButton::No);
|
||||
|
||||
if (res == StandardButton::Yes) {
|
||||
auto selected_path = GetExistingDirectory(tr("Ryujinx Portable Location"), QDir::homePath()).toStdString();
|
||||
if (selected_path.empty())
|
||||
return fs::path{};
|
||||
|
||||
ryu_path = selected_path;
|
||||
|
||||
// In case the user selects the actual ryujinx installation dir INSTEAD OF
|
||||
// the portable dir
|
||||
if (fs::exists(ryu_path / "portable")) {
|
||||
ryu_path = ryu_path / "portable";
|
||||
}
|
||||
|
||||
kvdb_path = Common::FS::GetKvdbPath(ryu_path);
|
||||
|
||||
if (!fs::exists(kvdb_path)) {
|
||||
QtCommon::Frontend::Critical(
|
||||
tr("Not a valid Ryujinx directory"),
|
||||
tr("The specified directory does not contain valid Ryujinx data."));
|
||||
return fs::path{};
|
||||
}
|
||||
} else {
|
||||
return fs::path{};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Common::FS::IMEN> imens;
|
||||
Common::FS::IMENReadResult res = Common::FS::ReadKvdb(path, imens);
|
||||
Common::FS::IMENReadResult res = Common::FS::ReadKvdb(kvdb_path, imens);
|
||||
|
||||
if (res == Common::FS::IMENReadResult::Success) {
|
||||
// TODO: this can probably be done with std::find_if but I'm lazy
|
||||
for (const Common::FS::IMEN &imen : imens) {
|
||||
if (imen.title_id == program_id)
|
||||
return imen.save_id;
|
||||
return Common::FS::GetRyuSavePath(ryu_path, imen.save_id);
|
||||
}
|
||||
|
||||
QtCommon::Frontend::Critical(
|
||||
tr("Could not find Ryujinx save data"),
|
||||
StringLookup::Lookup(StringLookup::RyujinxNoSaveId).arg(program_id, 0, 16));
|
||||
} else {
|
||||
// TODO: make this long thing a function or something
|
||||
QString caption = StringLookup::Lookup(
|
||||
static_cast<StringLookup::StringKey>((int) res + (int) StringLookup::KvdbNonexistent));
|
||||
QString caption = LOOKUP_ENUM(res, KvdbNonexistent);
|
||||
QtCommon::Frontend::Critical(tr("Could not find Ryujinx save data"), caption);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::optional<std::pair<fs::path, fs::path> > GetEmuPaths(
|
||||
const u64 program_id, const u64 save_id, const std::string &user_id)
|
||||
{
|
||||
fs::path ryu_dir = Common::FS::GetRyuSavePath(save_id);
|
||||
|
||||
if (user_id.empty())
|
||||
return std::nullopt;
|
||||
|
||||
std::string hex_program = fmt::format("{:016X}", program_id);
|
||||
fs::path eden_dir
|
||||
= FrontendCommon::DataManager::GetDataDir(FrontendCommon::DataManager::DataDir::Saves,
|
||||
user_id)
|
||||
/ hex_program;
|
||||
|
||||
return std::make_pair(eden_dir, ryu_dir);
|
||||
return fs::path{};
|
||||
}
|
||||
|
||||
} // namespace QtCommon::FS
|
||||
|
||||
@@ -10,13 +10,10 @@
|
||||
namespace QtCommon::FS {
|
||||
|
||||
void LinkRyujinx(std::filesystem::path &from, std::filesystem::path &to);
|
||||
u64 GetRyujinxSaveID(const u64 &program_id);
|
||||
|
||||
/// @brief {eden, ryu}
|
||||
std::optional<std::pair<std::filesystem::path, std::filesystem::path>> GetEmuPaths(
|
||||
const u64 program_id, const u64 save_id, const std::string &user_id);
|
||||
const std::filesystem::path GetRyujinxSavePath(const std::filesystem::path &path_hint, const u64 &program_id);
|
||||
|
||||
/// returns FALSE if the dirs are NOT linked
|
||||
bool CheckUnlink(const std::filesystem::path &eden_dir, const std::filesystem::path &ryu_dir);
|
||||
bool CheckUnlink(const std::filesystem::path& eden_dir,
|
||||
const std::filesystem::path& ryu_dir);
|
||||
|
||||
} // namespace QtCommon::FS
|
||||
|
||||
@@ -775,65 +775,20 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
if (extensions.memory_budget) {
|
||||
flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT;
|
||||
}
|
||||
|
||||
// Runtime sanity check: some drivers (notably Qualcomm and Turnip) advertise
|
||||
// VK_EXT_custom_border_color but mis-implement it. Try creating a sampler
|
||||
// with a formatless custom border color and disable the feature if it fails.
|
||||
if (extensions.custom_border_color) {
|
||||
VkSamplerCustomBorderColorCreateInfoEXT border_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT,
|
||||
.pNext = nullptr,
|
||||
.customBorderColor = VkClearColorValue{{1.0f, 0.0f, 0.0f, 1.0f}},
|
||||
.format = VK_FORMAT_UNDEFINED,
|
||||
};
|
||||
VkSamplerCreateInfo sampler_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.pNext = &border_ci,
|
||||
.flags = 0,
|
||||
.magFilter = VK_FILTER_NEAREST,
|
||||
.minFilter = VK_FILTER_NEAREST,
|
||||
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
|
||||
.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
|
||||
.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
|
||||
.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
|
||||
.mipLodBias = 0.0f,
|
||||
.anisotropyEnable = VK_FALSE,
|
||||
.maxAnisotropy = 1.0f,
|
||||
.compareEnable = VK_FALSE,
|
||||
.compareOp = VK_COMPARE_OP_ALWAYS,
|
||||
.minLod = 0.0f,
|
||||
.maxLod = 0.0f,
|
||||
.borderColor = VK_BORDER_COLOR_FLOAT_CUSTOM_EXT,
|
||||
.unnormalizedCoordinates = VK_FALSE,
|
||||
};
|
||||
try {
|
||||
const auto test_sampler = logical.CreateSampler(sampler_ci);
|
||||
// dson't allow to optimize away
|
||||
(void)test_sampler;
|
||||
// Destroy immediately; this is just a capability test.
|
||||
test_sampler.~Handle();
|
||||
LOG_INFO(Render_Vulkan, "VK_EXT_custom_border_color runtime test passed");
|
||||
} catch (const vk::Exception& e) {
|
||||
LOG_WARNING(Render_Vulkan, "VK_EXT_custom_border_color advertised but sampler create failed: {}. Disabling feature.", e.what());
|
||||
// Disable the extension feature so the runtime falls back to fixed border colors.
|
||||
RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
const VmaAllocatorCreateInfo allocator_info{
|
||||
.flags = flags,
|
||||
.physicalDevice = physical,
|
||||
.device = *logical,
|
||||
.preferredLargeHeapBlockSize = is_integrated
|
||||
? (64u * 1024u * 1024u)
|
||||
: (256u * 1024u * 1024u),
|
||||
.pAllocationCallbacks = nullptr,
|
||||
.pDeviceMemoryCallbacks = nullptr,
|
||||
.pHeapSizeLimit = nullptr,
|
||||
.pVulkanFunctions = &functions,
|
||||
.instance = instance,
|
||||
.vulkanApiVersion = ApiVersion(),
|
||||
.pTypeExternalMemoryHandleTypes = nullptr,
|
||||
.flags = flags,
|
||||
.physicalDevice = physical,
|
||||
.device = *logical,
|
||||
.preferredLargeHeapBlockSize = is_integrated
|
||||
? (64u * 1024u * 1024u)
|
||||
: (256u * 1024u * 1024u),
|
||||
.pAllocationCallbacks = nullptr,
|
||||
.pDeviceMemoryCallbacks = nullptr,
|
||||
.pHeapSizeLimit = nullptr,
|
||||
.pVulkanFunctions = &functions,
|
||||
.instance = instance,
|
||||
.vulkanApiVersion = ApiVersion(),
|
||||
.pTypeExternalMemoryHandleTypes = nullptr,
|
||||
};
|
||||
|
||||
vk::Check(vmaCreateAllocator(&allocator_info, &allocator));
|
||||
|
||||
@@ -12,6 +12,8 @@ if (YUZU_USE_BUNDLED_QT AND PLATFORM_LINUX)
|
||||
set(CMAKE_BUILD_RPATH "${CMAKE_BINARY_DIR}/bin/lib/")
|
||||
endif()
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets)
|
||||
|
||||
add_executable(yuzu
|
||||
Info.plist
|
||||
about_dialog.cpp
|
||||
@@ -156,8 +158,6 @@ add_executable(yuzu
|
||||
debugger/console.h
|
||||
debugger/controller.cpp
|
||||
debugger/controller.h
|
||||
debugger/wait_tree.cpp
|
||||
debugger/wait_tree.h
|
||||
|
||||
game_list.cpp
|
||||
game_list.h
|
||||
@@ -171,9 +171,9 @@ add_executable(yuzu
|
||||
loading_screen.cpp
|
||||
loading_screen.h
|
||||
loading_screen.ui
|
||||
|
||||
main.cpp
|
||||
main.h
|
||||
main.ui
|
||||
|
||||
multiplayer/chat_room.cpp
|
||||
multiplayer/chat_room.h
|
||||
multiplayer/chat_room.ui
|
||||
@@ -237,6 +237,7 @@ add_executable(yuzu
|
||||
data_dialog.h data_dialog.cpp data_dialog.ui
|
||||
data_widget.ui
|
||||
ryujinx_dialog.h ryujinx_dialog.cpp ryujinx_dialog.ui
|
||||
main_window.h main_window.cpp
|
||||
)
|
||||
|
||||
set_target_properties(yuzu PROPERTIES OUTPUT_NAME "eden")
|
||||
@@ -443,6 +444,7 @@ endif()
|
||||
|
||||
if (YUZU_ROOM)
|
||||
target_link_libraries(yuzu PRIVATE yuzu-room)
|
||||
target_link_libraries(yuzu PRIVATE Qt6::Widgets)
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(yuzu)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -18,7 +21,7 @@
|
||||
#include "web_service/web_backend.h"
|
||||
#endif
|
||||
#include "yuzu/applets/qt_amiibo_settings.h"
|
||||
#include "yuzu/main.h"
|
||||
#include "yuzu/main_window.h"
|
||||
|
||||
QtAmiiboSettingsDialog::QtAmiiboSettingsDialog(QWidget* parent,
|
||||
Core::Frontend::CabinetParameters parameters_,
|
||||
@@ -244,12 +247,12 @@ void QtAmiiboSettingsDialog::SetSettingsDescription() {
|
||||
}
|
||||
}
|
||||
|
||||
QtAmiiboSettings::QtAmiiboSettings(GMainWindow& parent) {
|
||||
QtAmiiboSettings::QtAmiiboSettings(MainWindow& parent) {
|
||||
connect(this, &QtAmiiboSettings::MainWindowShowAmiiboSettings, &parent,
|
||||
&GMainWindow::AmiiboSettingsShowDialog, Qt::QueuedConnection);
|
||||
&MainWindow::AmiiboSettingsShowDialog, Qt::QueuedConnection);
|
||||
connect(this, &QtAmiiboSettings::MainWindowRequestExit, &parent,
|
||||
&GMainWindow::AmiiboSettingsRequestExit, Qt::QueuedConnection);
|
||||
connect(&parent, &GMainWindow::AmiiboSettingsFinished, this,
|
||||
&MainWindow::AmiiboSettingsRequestExit, Qt::QueuedConnection);
|
||||
connect(&parent, &MainWindow::AmiiboSettingsFinished, this,
|
||||
&QtAmiiboSettings::MainWindowFinished, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -8,7 +11,7 @@
|
||||
#include <QDialog>
|
||||
#include "core/frontend/applets/cabinet.h"
|
||||
|
||||
class GMainWindow;
|
||||
class MainWindow;
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QDialogButtonBox;
|
||||
@@ -65,7 +68,7 @@ class QtAmiiboSettings final : public QObject, public Core::Frontend::CabinetApp
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtAmiiboSettings(GMainWindow& parent);
|
||||
explicit QtAmiiboSettings(MainWindow& parent);
|
||||
~QtAmiiboSettings() override;
|
||||
|
||||
void Close() const override;
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "yuzu/configuration/configure_motion_touch.h"
|
||||
#include "yuzu/configuration/configure_vibration.h"
|
||||
#include "yuzu/configuration/input_profiles.h"
|
||||
#include "yuzu/main.h"
|
||||
#include "yuzu/main_window.h"
|
||||
#include "yuzu/util/controller_navigation.h"
|
||||
|
||||
namespace {
|
||||
@@ -753,12 +753,12 @@ void QtControllerSelectorDialog::DisableUnsupportedPlayers() {
|
||||
}
|
||||
}
|
||||
|
||||
QtControllerSelector::QtControllerSelector(GMainWindow& parent) {
|
||||
QtControllerSelector::QtControllerSelector(MainWindow& parent) {
|
||||
connect(this, &QtControllerSelector::MainWindowReconfigureControllers, &parent,
|
||||
&GMainWindow::ControllerSelectorReconfigureControllers, Qt::QueuedConnection);
|
||||
&MainWindow::ControllerSelectorReconfigureControllers, Qt::QueuedConnection);
|
||||
connect(this, &QtControllerSelector::MainWindowRequestExit, &parent,
|
||||
&GMainWindow::ControllerSelectorRequestExit, Qt::QueuedConnection);
|
||||
connect(&parent, &GMainWindow::ControllerSelectorReconfigureFinished, this,
|
||||
&MainWindow::ControllerSelectorRequestExit, Qt::QueuedConnection);
|
||||
connect(&parent, &MainWindow::ControllerSelectorReconfigureFinished, this,
|
||||
&QtControllerSelector::MainWindowReconfigureFinished, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -8,7 +11,7 @@
|
||||
#include <QDialog>
|
||||
#include "core/frontend/applets/controller.h"
|
||||
|
||||
class GMainWindow;
|
||||
class MainWindow;
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QDialogButtonBox;
|
||||
@@ -163,7 +166,7 @@ class QtControllerSelector final : public QObject, public Core::Frontend::Contro
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtControllerSelector(GMainWindow& parent);
|
||||
explicit QtControllerSelector(MainWindow& parent);
|
||||
~QtControllerSelector() override;
|
||||
|
||||
void Close() const override;
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <QDateTime>
|
||||
#include "yuzu/applets/qt_error.h"
|
||||
#include "yuzu/main.h"
|
||||
#include "yuzu/main_window.h"
|
||||
|
||||
QtErrorDisplay::QtErrorDisplay(GMainWindow& parent) {
|
||||
QtErrorDisplay::QtErrorDisplay(MainWindow& parent) {
|
||||
connect(this, &QtErrorDisplay::MainWindowDisplayError, &parent,
|
||||
&GMainWindow::ErrorDisplayDisplayError, Qt::QueuedConnection);
|
||||
&MainWindow::ErrorDisplayDisplayError, Qt::QueuedConnection);
|
||||
connect(this, &QtErrorDisplay::MainWindowRequestExit, &parent,
|
||||
&GMainWindow::ErrorDisplayRequestExit, Qt::QueuedConnection);
|
||||
connect(&parent, &GMainWindow::ErrorDisplayFinished, this,
|
||||
&MainWindow::ErrorDisplayRequestExit, Qt::QueuedConnection);
|
||||
connect(&parent, &MainWindow::ErrorDisplayFinished, this,
|
||||
&QtErrorDisplay::MainWindowFinishedError, Qt::DirectConnection);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -7,13 +10,13 @@
|
||||
|
||||
#include "core/frontend/applets/error.h"
|
||||
|
||||
class GMainWindow;
|
||||
class MainWindow;
|
||||
|
||||
class QtErrorDisplay final : public QObject, public Core::Frontend::ErrorApplet {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtErrorDisplay(GMainWindow& parent);
|
||||
explicit QtErrorDisplay(MainWindow& parent);
|
||||
~QtErrorDisplay() override;
|
||||
|
||||
void Close() const override;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "yuzu/applets/qt_profile_select.h"
|
||||
#include "yuzu/main.h"
|
||||
#include "yuzu/main_window.h"
|
||||
#include "yuzu/util/controller_navigation.h"
|
||||
|
||||
namespace {
|
||||
@@ -230,12 +230,12 @@ void QtProfileSelectionDialog::SetDialogPurpose(
|
||||
}
|
||||
}
|
||||
|
||||
QtProfileSelector::QtProfileSelector(GMainWindow& parent) {
|
||||
QtProfileSelector::QtProfileSelector(MainWindow& parent) {
|
||||
connect(this, &QtProfileSelector::MainWindowSelectProfile, &parent,
|
||||
&GMainWindow::ProfileSelectorSelectProfile, Qt::QueuedConnection);
|
||||
&MainWindow::ProfileSelectorSelectProfile, Qt::QueuedConnection);
|
||||
connect(this, &QtProfileSelector::MainWindowRequestExit, &parent,
|
||||
&GMainWindow::ProfileSelectorRequestExit, Qt::QueuedConnection);
|
||||
connect(&parent, &GMainWindow::ProfileSelectorFinishedSelection, this,
|
||||
&MainWindow::ProfileSelectorRequestExit, Qt::QueuedConnection);
|
||||
connect(&parent, &MainWindow::ProfileSelectorFinishedSelection, this,
|
||||
&QtProfileSelector::MainWindowFinishedSelection, Qt::DirectConnection);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -9,7 +12,7 @@
|
||||
#include "core/frontend/applets/profile_select.h"
|
||||
|
||||
class ControllerNavigation;
|
||||
class GMainWindow;
|
||||
class MainWindow;
|
||||
class QDialogButtonBox;
|
||||
class QGraphicsScene;
|
||||
class QLabel;
|
||||
@@ -69,7 +72,7 @@ class QtProfileSelector final : public QObject, public Core::Frontend::ProfileSe
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtProfileSelector(GMainWindow& parent);
|
||||
explicit QtProfileSelector(MainWindow& parent);
|
||||
~QtProfileSelector() override;
|
||||
|
||||
void Close() const override;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -15,7 +18,7 @@
|
||||
#include "hid_core/hid_types.h"
|
||||
#include "ui_qt_software_keyboard.h"
|
||||
#include "yuzu/applets/qt_software_keyboard.h"
|
||||
#include "yuzu/main.h"
|
||||
#include "yuzu/main_window.h"
|
||||
#include "yuzu/util/overlay_dialog.h"
|
||||
|
||||
namespace {
|
||||
@@ -1541,24 +1544,24 @@ void QtSoftwareKeyboardDialog::InputThread() {
|
||||
}
|
||||
}
|
||||
|
||||
QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) {
|
||||
QtSoftwareKeyboard::QtSoftwareKeyboard(MainWindow& main_window) {
|
||||
connect(this, &QtSoftwareKeyboard::MainWindowInitializeKeyboard, &main_window,
|
||||
&GMainWindow::SoftwareKeyboardInitialize, Qt::QueuedConnection);
|
||||
&MainWindow::SoftwareKeyboardInitialize, Qt::QueuedConnection);
|
||||
connect(this, &QtSoftwareKeyboard::MainWindowShowNormalKeyboard, &main_window,
|
||||
&GMainWindow::SoftwareKeyboardShowNormal, Qt::QueuedConnection);
|
||||
&MainWindow::SoftwareKeyboardShowNormal, Qt::QueuedConnection);
|
||||
connect(this, &QtSoftwareKeyboard::MainWindowShowTextCheckDialog, &main_window,
|
||||
&GMainWindow::SoftwareKeyboardShowTextCheck, Qt::QueuedConnection);
|
||||
&MainWindow::SoftwareKeyboardShowTextCheck, Qt::QueuedConnection);
|
||||
connect(this, &QtSoftwareKeyboard::MainWindowShowInlineKeyboard, &main_window,
|
||||
&GMainWindow::SoftwareKeyboardShowInline, Qt::QueuedConnection);
|
||||
&MainWindow::SoftwareKeyboardShowInline, Qt::QueuedConnection);
|
||||
connect(this, &QtSoftwareKeyboard::MainWindowHideInlineKeyboard, &main_window,
|
||||
&GMainWindow::SoftwareKeyboardHideInline, Qt::QueuedConnection);
|
||||
&MainWindow::SoftwareKeyboardHideInline, Qt::QueuedConnection);
|
||||
connect(this, &QtSoftwareKeyboard::MainWindowInlineTextChanged, &main_window,
|
||||
&GMainWindow::SoftwareKeyboardInlineTextChanged, Qt::QueuedConnection);
|
||||
&MainWindow::SoftwareKeyboardInlineTextChanged, Qt::QueuedConnection);
|
||||
connect(this, &QtSoftwareKeyboard::MainWindowExitKeyboard, &main_window,
|
||||
&GMainWindow::SoftwareKeyboardExit, Qt::QueuedConnection);
|
||||
connect(&main_window, &GMainWindow::SoftwareKeyboardSubmitNormalText, this,
|
||||
&MainWindow::SoftwareKeyboardExit, Qt::QueuedConnection);
|
||||
connect(&main_window, &MainWindow::SoftwareKeyboardSubmitNormalText, this,
|
||||
&QtSoftwareKeyboard::SubmitNormalText, Qt::QueuedConnection);
|
||||
connect(&main_window, &GMainWindow::SoftwareKeyboardSubmitInlineText, this,
|
||||
connect(&main_window, &MainWindow::SoftwareKeyboardSubmitInlineText, this,
|
||||
&QtSoftwareKeyboard::SubmitInlineText, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -27,7 +30,7 @@ namespace Ui {
|
||||
class QtSoftwareKeyboardDialog;
|
||||
}
|
||||
|
||||
class GMainWindow;
|
||||
class MainWindow;
|
||||
|
||||
class QtSoftwareKeyboardDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
@@ -230,7 +233,7 @@ class QtSoftwareKeyboard final : public QObject, public Core::Frontend::Software
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtSoftwareKeyboard(GMainWindow& parent);
|
||||
explicit QtSoftwareKeyboard(MainWindow& parent);
|
||||
~QtSoftwareKeyboard() override;
|
||||
|
||||
void Close() const override {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -18,7 +21,7 @@
|
||||
#endif
|
||||
|
||||
#include "yuzu/applets/qt_web_browser.h"
|
||||
#include "yuzu/main.h"
|
||||
#include "yuzu/main_window.h"
|
||||
|
||||
#ifdef YUZU_USE_QT_WEB_ENGINE
|
||||
|
||||
@@ -391,14 +394,14 @@ void QtNXWebEngineView::FocusFirstLinkElement() {
|
||||
|
||||
#endif
|
||||
|
||||
QtWebBrowser::QtWebBrowser(GMainWindow& main_window) {
|
||||
QtWebBrowser::QtWebBrowser(MainWindow& main_window) {
|
||||
connect(this, &QtWebBrowser::MainWindowOpenWebPage, &main_window,
|
||||
&GMainWindow::WebBrowserOpenWebPage, Qt::QueuedConnection);
|
||||
&MainWindow::WebBrowserOpenWebPage, Qt::QueuedConnection);
|
||||
connect(this, &QtWebBrowser::MainWindowRequestExit, &main_window,
|
||||
&GMainWindow::WebBrowserRequestExit, Qt::QueuedConnection);
|
||||
connect(&main_window, &GMainWindow::WebBrowserExtractOfflineRomFS, this,
|
||||
&MainWindow::WebBrowserRequestExit, Qt::QueuedConnection);
|
||||
connect(&main_window, &MainWindow::WebBrowserExtractOfflineRomFS, this,
|
||||
&QtWebBrowser::MainWindowExtractOfflineRomFS, Qt::QueuedConnection);
|
||||
connect(&main_window, &GMainWindow::WebBrowserClosed, this,
|
||||
connect(&main_window, &MainWindow::WebBrowserClosed, this,
|
||||
&QtWebBrowser::MainWindowWebBrowserClosed, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -14,7 +17,7 @@
|
||||
|
||||
#include "core/frontend/applets/web_browser.h"
|
||||
|
||||
class GMainWindow;
|
||||
class MainWindow;
|
||||
class InputInterpreter;
|
||||
class UrlRequestInterceptor;
|
||||
|
||||
@@ -193,7 +196,7 @@ class QtWebBrowser final : public QObject, public Core::Frontend::WebBrowserAppl
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtWebBrowser(GMainWindow& parent);
|
||||
explicit QtWebBrowser(MainWindow& parent);
|
||||
~QtWebBrowser() override;
|
||||
|
||||
void Close() const override;
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "yuzu/bootmanager.h"
|
||||
#include "yuzu/main.h"
|
||||
#include "yuzu/main_window.h"
|
||||
#include "qt_common/qt_common.h"
|
||||
|
||||
class QObject;
|
||||
@@ -272,7 +272,7 @@ struct NullRenderWidget : public RenderWidget {
|
||||
explicit NullRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {}
|
||||
};
|
||||
|
||||
GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
|
||||
GRenderWindow::GRenderWindow(MainWindow* parent, EmuThread* emu_thread_,
|
||||
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
|
||||
Core::System& system_)
|
||||
: QWidget(parent),
|
||||
@@ -290,11 +290,11 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
|
||||
strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland") ||
|
||||
QGuiApplication::platformName() == QStringLiteral("wayland-egl");
|
||||
|
||||
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
|
||||
connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram,
|
||||
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &MainWindow::OnLoadComplete);
|
||||
connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &MainWindow::OnExecuteProgram,
|
||||
Qt::QueuedConnection);
|
||||
connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection);
|
||||
connect(this, &GRenderWindow::TasPlaybackStateChanged, parent, &GMainWindow::OnTasStateChanged);
|
||||
connect(this, &GRenderWindow::ExitSignal, parent, &MainWindow::OnExit, Qt::QueuedConnection);
|
||||
connect(this, &GRenderWindow::TasPlaybackStateChanged, parent, &MainWindow::OnTasStateChanged);
|
||||
|
||||
mouse_constrain_timer.setInterval(default_mouse_constrain_timeout);
|
||||
connect(&mouse_constrain_timer, &QTimer::timeout, this, &GRenderWindow::ConstrainMouse);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -29,7 +32,7 @@
|
||||
#include "common/thread.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
|
||||
class GMainWindow;
|
||||
class MainWindow;
|
||||
class QCamera;
|
||||
class QCameraImageCapture;
|
||||
class QCloseEvent;
|
||||
@@ -146,7 +149,7 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
|
||||
explicit GRenderWindow(MainWindow* parent, EmuThread* emu_thread_,
|
||||
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
|
||||
Core::System& system_);
|
||||
~GRenderWindow() override;
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
// 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_common.h"
|
||||
#include "qt_common/util/content.h"
|
||||
#include "qt_common/qt_string_lookup.h"
|
||||
#include "ui_data_dialog.h"
|
||||
#include "util/util.h"
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QFileDialog>
|
||||
@@ -26,17 +25,18 @@ DataDialog::DataDialog(QWidget *parent)
|
||||
ui->setupUi(this);
|
||||
|
||||
// TODO: Should we make this a single widget that pulls data from a model?
|
||||
#define WIDGET(name) \
|
||||
#define WIDGET(label, name) \
|
||||
ui->page->addWidget(new DataWidget(FrontendCommon::DataManager::DataDir::name, \
|
||||
QtCommon::StringLookup::name##Tooltip, \
|
||||
QStringLiteral(#name), \
|
||||
this));
|
||||
this)); \
|
||||
ui->labels->addItem(label);
|
||||
|
||||
WIDGET(Saves)
|
||||
WIDGET(Shaders)
|
||||
WIDGET(UserNand)
|
||||
WIDGET(SysNand)
|
||||
WIDGET(Mods)
|
||||
WIDGET(tr("Shaders"), Shaders)
|
||||
WIDGET(tr("UserNAND"), UserNand)
|
||||
WIDGET(tr("SysNAND"), SysNand)
|
||||
WIDGET(tr("Mods"), Mods)
|
||||
WIDGET(tr("Saves"), Saves)
|
||||
|
||||
#undef WIDGET
|
||||
|
||||
@@ -82,7 +82,7 @@ void DataWidget::clear()
|
||||
{
|
||||
std::string user_id{};
|
||||
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
|
||||
user_id = selectProfile();
|
||||
user_id = GetProfileIDString();
|
||||
}
|
||||
QtCommon::Content::ClearDataDir(m_dir, user_id);
|
||||
scan();
|
||||
@@ -92,7 +92,7 @@ void DataWidget::open()
|
||||
{
|
||||
std::string user_id{};
|
||||
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
|
||||
user_id = selectProfile();
|
||||
user_id = GetProfileIDString();
|
||||
}
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(
|
||||
QString::fromStdString(FrontendCommon::DataManager::GetDataDirString(m_dir, user_id))));
|
||||
@@ -102,7 +102,7 @@ void DataWidget::upload()
|
||||
{
|
||||
std::string user_id{};
|
||||
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
|
||||
user_id = selectProfile();
|
||||
user_id = GetProfileIDString();
|
||||
}
|
||||
QtCommon::Content::ExportDataDir(m_dir, user_id, m_exportName);
|
||||
}
|
||||
@@ -111,7 +111,7 @@ void DataWidget::download()
|
||||
{
|
||||
std::string user_id{};
|
||||
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
|
||||
user_id = selectProfile();
|
||||
user_id = GetProfileIDString();
|
||||
}
|
||||
QtCommon::Content::ImportDataDir(m_dir, user_id, std::bind(&DataWidget::scan, this));
|
||||
}
|
||||
@@ -131,37 +131,3 @@ void DataWidget::scan() {
|
||||
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]);
|
||||
}
|
||||
|
||||
@@ -36,31 +36,6 @@
|
||||
<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>
|
||||
|
||||
@@ -1,434 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
#include "yuzu/debugger/wait_tree.h"
|
||||
#include "qt_common/config/uisettings.h"
|
||||
|
||||
#include "core/arm/debug.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_class_token.h"
|
||||
#include "core/hle/kernel/k_handle_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/svc_common.h"
|
||||
#include "core/hle/kernel/svc_types.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::array<std::array<Qt::GlobalColor, 2>, 10> WaitTreeColors{{
|
||||
{Qt::GlobalColor::darkGreen, Qt::GlobalColor::green},
|
||||
{Qt::GlobalColor::darkBlue, Qt::GlobalColor::cyan},
|
||||
{Qt::GlobalColor::lightGray, Qt::GlobalColor::lightGray},
|
||||
{Qt::GlobalColor::lightGray, Qt::GlobalColor::lightGray},
|
||||
{Qt::GlobalColor::darkRed, Qt::GlobalColor::red},
|
||||
{Qt::GlobalColor::darkYellow, Qt::GlobalColor::yellow},
|
||||
{Qt::GlobalColor::red, Qt::GlobalColor::red},
|
||||
{Qt::GlobalColor::darkCyan, Qt::GlobalColor::cyan},
|
||||
{Qt::GlobalColor::gray, Qt::GlobalColor::gray},
|
||||
}};
|
||||
|
||||
bool IsDarkTheme() {
|
||||
const auto& theme = UISettings::values.theme;
|
||||
return theme == std::string("qdarkstyle") || theme == std::string("qdarkstyle_midnight_blue") ||
|
||||
theme == std::string("colorful_dark") || theme == std::string("colorful_midnight_blue");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
WaitTreeItem::WaitTreeItem() = default;
|
||||
WaitTreeItem::~WaitTreeItem() = default;
|
||||
|
||||
QColor WaitTreeItem::GetColor() const {
|
||||
if (IsDarkTheme()) {
|
||||
return QColor(Qt::GlobalColor::white);
|
||||
} else {
|
||||
return QColor(Qt::GlobalColor::black);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeItem::GetChildren() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
void WaitTreeItem::Expand() {
|
||||
if (IsExpandable() && !expanded) {
|
||||
children = GetChildren();
|
||||
for (std::size_t i = 0; i < children.size(); ++i) {
|
||||
children[i]->parent = this;
|
||||
children[i]->row = i;
|
||||
}
|
||||
expanded = true;
|
||||
}
|
||||
}
|
||||
|
||||
WaitTreeItem* WaitTreeItem::Parent() const {
|
||||
return parent;
|
||||
}
|
||||
|
||||
const std::vector<std::unique_ptr<WaitTreeItem>>& WaitTreeItem::Children() const {
|
||||
return children;
|
||||
}
|
||||
|
||||
bool WaitTreeItem::IsExpandable() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t WaitTreeItem::Row() const {
|
||||
return row;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList(
|
||||
Core::System& system) {
|
||||
std::vector<std::unique_ptr<WaitTreeThread>> item_list;
|
||||
std::size_t row = 0;
|
||||
auto add_threads = [&](const std::vector<Kernel::KThread*>& threads) {
|
||||
for (std::size_t i = 0; i < threads.size(); ++i) {
|
||||
if (threads[i]->GetThreadType() == Kernel::ThreadType::User) {
|
||||
item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i], system));
|
||||
item_list.back()->row = row;
|
||||
}
|
||||
++row;
|
||||
}
|
||||
};
|
||||
|
||||
add_threads(system.GlobalSchedulerContext().GetThreadList());
|
||||
|
||||
return item_list;
|
||||
}
|
||||
|
||||
WaitTreeText::WaitTreeText(QString t) : text(std::move(t)) {}
|
||||
WaitTreeText::~WaitTreeText() = default;
|
||||
|
||||
QString WaitTreeText::GetText() const {
|
||||
return text;
|
||||
}
|
||||
|
||||
WaitTreeCallstack::WaitTreeCallstack(const Kernel::KThread& thread_, Core::System& system_)
|
||||
: thread{thread_}, system{system_} {}
|
||||
WaitTreeCallstack::~WaitTreeCallstack() = default;
|
||||
|
||||
QString WaitTreeCallstack::GetText() const {
|
||||
return tr("Call stack");
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() const {
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> list;
|
||||
|
||||
if (thread.GetThreadType() != Kernel::ThreadType::User) {
|
||||
return list;
|
||||
}
|
||||
|
||||
if (thread.GetOwnerProcess() == nullptr || !thread.GetOwnerProcess()->Is64Bit()) {
|
||||
return list;
|
||||
}
|
||||
|
||||
auto backtrace = Core::GetBacktraceFromContext(thread.GetOwnerProcess(), thread.GetContext());
|
||||
|
||||
for (auto& entry : backtrace) {
|
||||
std::string s = fmt::format("{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address,
|
||||
entry.original_address, entry.offset, entry.name);
|
||||
list.push_back(std::make_unique<WaitTreeText>(QString::fromStdString(s)));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
WaitTreeSynchronizationObject::WaitTreeSynchronizationObject(
|
||||
const Kernel::KSynchronizationObject& object_, Core::System& system_)
|
||||
: object{object_}, system{system_} {}
|
||||
WaitTreeSynchronizationObject::~WaitTreeSynchronizationObject() = default;
|
||||
|
||||
WaitTreeExpandableItem::WaitTreeExpandableItem() = default;
|
||||
WaitTreeExpandableItem::~WaitTreeExpandableItem() = default;
|
||||
|
||||
bool WaitTreeExpandableItem::IsExpandable() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
QString WaitTreeSynchronizationObject::GetText() const {
|
||||
return tr("[%1] %2")
|
||||
.arg(object.GetId())
|
||||
.arg(QString::fromStdString(object.GetTypeObj().GetName()));
|
||||
}
|
||||
|
||||
std::unique_ptr<WaitTreeSynchronizationObject> WaitTreeSynchronizationObject::make(
|
||||
const Kernel::KSynchronizationObject& object, Core::System& system) {
|
||||
const auto type =
|
||||
static_cast<Kernel::KClassTokenGenerator::ObjectType>(object.GetTypeObj().GetClassToken());
|
||||
switch (type) {
|
||||
case Kernel::KClassTokenGenerator::ObjectType::KReadableEvent:
|
||||
return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::KReadableEvent&>(object),
|
||||
system);
|
||||
case Kernel::KClassTokenGenerator::ObjectType::KThread:
|
||||
return std::make_unique<WaitTreeThread>(static_cast<const Kernel::KThread&>(object),
|
||||
system);
|
||||
default:
|
||||
return std::make_unique<WaitTreeSynchronizationObject>(object, system);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeSynchronizationObject::GetChildren() const {
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> list;
|
||||
|
||||
auto threads = object.GetWaitingThreadsForDebugging();
|
||||
if (threads.empty()) {
|
||||
list.push_back(std::make_unique<WaitTreeText>(tr("waited by no thread")));
|
||||
} else {
|
||||
list.push_back(std::make_unique<WaitTreeThreadList>(std::move(threads), system));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
WaitTreeThread::WaitTreeThread(const Kernel::KThread& thread, Core::System& system_)
|
||||
: WaitTreeSynchronizationObject(thread, system_), system{system_} {}
|
||||
WaitTreeThread::~WaitTreeThread() = default;
|
||||
|
||||
QString WaitTreeThread::GetText() const {
|
||||
const auto& thread = static_cast<const Kernel::KThread&>(object);
|
||||
QString status;
|
||||
switch (thread.GetState()) {
|
||||
case Kernel::ThreadState::Runnable:
|
||||
if (!thread.IsSuspended()) {
|
||||
status = tr("runnable");
|
||||
} else {
|
||||
status = tr("paused");
|
||||
}
|
||||
break;
|
||||
case Kernel::ThreadState::Waiting:
|
||||
switch (thread.GetWaitReasonForDebugging()) {
|
||||
case Kernel::ThreadWaitReasonForDebugging::Sleep:
|
||||
status = tr("sleeping");
|
||||
break;
|
||||
case Kernel::ThreadWaitReasonForDebugging::IPC:
|
||||
status = tr("waiting for IPC reply");
|
||||
break;
|
||||
case Kernel::ThreadWaitReasonForDebugging::Synchronization:
|
||||
status = tr("waiting for objects");
|
||||
break;
|
||||
case Kernel::ThreadWaitReasonForDebugging::ConditionVar:
|
||||
status = tr("waiting for condition variable");
|
||||
break;
|
||||
case Kernel::ThreadWaitReasonForDebugging::Arbitration:
|
||||
status = tr("waiting for address arbiter");
|
||||
break;
|
||||
case Kernel::ThreadWaitReasonForDebugging::Suspended:
|
||||
status = tr("waiting for suspend resume");
|
||||
break;
|
||||
default:
|
||||
status = tr("waiting");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Kernel::ThreadState::Initialized:
|
||||
status = tr("initialized");
|
||||
break;
|
||||
case Kernel::ThreadState::Terminated:
|
||||
status = tr("terminated");
|
||||
break;
|
||||
default:
|
||||
status = tr("unknown");
|
||||
break;
|
||||
}
|
||||
|
||||
const auto& context = thread.GetContext();
|
||||
const QString pc_info = tr(" PC = 0x%1 LR = 0x%2")
|
||||
.arg(context.pc, 8, 16, QLatin1Char{'0'})
|
||||
.arg(context.lr, 8, 16, QLatin1Char{'0'});
|
||||
return QStringLiteral("%1%2 (%3) ")
|
||||
.arg(WaitTreeSynchronizationObject::GetText(), pc_info, status);
|
||||
}
|
||||
|
||||
QColor WaitTreeThread::GetColor() const {
|
||||
const std::size_t color_index = IsDarkTheme() ? 1 : 0;
|
||||
|
||||
const auto& thread = static_cast<const Kernel::KThread&>(object);
|
||||
switch (thread.GetState()) {
|
||||
case Kernel::ThreadState::Runnable:
|
||||
if (!thread.IsSuspended()) {
|
||||
return QColor(WaitTreeColors[0][color_index]);
|
||||
} else {
|
||||
return QColor(WaitTreeColors[2][color_index]);
|
||||
}
|
||||
case Kernel::ThreadState::Waiting:
|
||||
switch (thread.GetWaitReasonForDebugging()) {
|
||||
case Kernel::ThreadWaitReasonForDebugging::IPC:
|
||||
return QColor(WaitTreeColors[4][color_index]);
|
||||
case Kernel::ThreadWaitReasonForDebugging::Sleep:
|
||||
return QColor(WaitTreeColors[5][color_index]);
|
||||
case Kernel::ThreadWaitReasonForDebugging::Synchronization:
|
||||
case Kernel::ThreadWaitReasonForDebugging::ConditionVar:
|
||||
case Kernel::ThreadWaitReasonForDebugging::Arbitration:
|
||||
case Kernel::ThreadWaitReasonForDebugging::Suspended:
|
||||
return QColor(WaitTreeColors[6][color_index]);
|
||||
break;
|
||||
default:
|
||||
return QColor(WaitTreeColors[3][color_index]);
|
||||
}
|
||||
case Kernel::ThreadState::Initialized:
|
||||
return QColor(WaitTreeColors[7][color_index]);
|
||||
case Kernel::ThreadState::Terminated:
|
||||
return QColor(WaitTreeColors[8][color_index]);
|
||||
default:
|
||||
return WaitTreeItem::GetColor();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeSynchronizationObject::GetChildren());
|
||||
|
||||
const auto& thread = static_cast<const Kernel::KThread&>(object);
|
||||
|
||||
QString processor;
|
||||
switch (thread.GetActiveCore()) {
|
||||
case Kernel::Svc::IdealCoreUseProcessValue:
|
||||
processor = tr("ideal");
|
||||
break;
|
||||
default:
|
||||
processor = tr("core %1").arg(thread.GetActiveCore());
|
||||
break;
|
||||
}
|
||||
|
||||
list.push_back(std::make_unique<WaitTreeText>(tr("processor = %1").arg(processor)));
|
||||
list.push_back(std::make_unique<WaitTreeText>(
|
||||
tr("affinity mask = %1").arg(thread.GetAffinityMask().GetAffinityMask())));
|
||||
list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadId())));
|
||||
list.push_back(std::make_unique<WaitTreeText>(tr("priority = %1(current) / %2(normal)")
|
||||
.arg(thread.GetPriority())
|
||||
.arg(thread.GetBasePriority())));
|
||||
list.push_back(std::make_unique<WaitTreeText>(
|
||||
tr("last running ticks = %1").arg(thread.GetLastScheduledTick())));
|
||||
|
||||
list.push_back(std::make_unique<WaitTreeCallstack>(thread, system));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
WaitTreeEvent::WaitTreeEvent(const Kernel::KReadableEvent& object_, Core::System& system_)
|
||||
: WaitTreeSynchronizationObject(object_, system_) {}
|
||||
WaitTreeEvent::~WaitTreeEvent() = default;
|
||||
|
||||
WaitTreeThreadList::WaitTreeThreadList(std::vector<Kernel::KThread*>&& list, Core::System& system_)
|
||||
: thread_list(std::move(list)), system{system_} {}
|
||||
WaitTreeThreadList::~WaitTreeThreadList() = default;
|
||||
|
||||
QString WaitTreeThreadList::GetText() const {
|
||||
return tr("waited by thread");
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThreadList::GetChildren() const {
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> list(thread_list.size());
|
||||
std::transform(thread_list.begin(), thread_list.end(), list.begin(),
|
||||
[this](const auto& t) { return std::make_unique<WaitTreeThread>(*t, system); });
|
||||
return list;
|
||||
}
|
||||
|
||||
WaitTreeModel::WaitTreeModel(Core::System& system_, QObject* parent)
|
||||
: QAbstractItemModel(parent), system{system_} {}
|
||||
WaitTreeModel::~WaitTreeModel() = default;
|
||||
|
||||
QModelIndex WaitTreeModel::index(int row, int column, const QModelIndex& parent) const {
|
||||
if (!hasIndex(row, column, parent))
|
||||
return {};
|
||||
|
||||
if (parent.isValid()) {
|
||||
WaitTreeItem* parent_item = static_cast<WaitTreeItem*>(parent.internalPointer());
|
||||
parent_item->Expand();
|
||||
return createIndex(row, column, parent_item->Children()[row].get());
|
||||
}
|
||||
|
||||
return createIndex(row, column, thread_items[row].get());
|
||||
}
|
||||
|
||||
QModelIndex WaitTreeModel::parent(const QModelIndex& index) const {
|
||||
if (!index.isValid())
|
||||
return {};
|
||||
|
||||
WaitTreeItem* parent_item = static_cast<WaitTreeItem*>(index.internalPointer())->Parent();
|
||||
if (!parent_item) {
|
||||
return QModelIndex();
|
||||
}
|
||||
return createIndex(static_cast<int>(parent_item->Row()), 0, parent_item);
|
||||
}
|
||||
|
||||
int WaitTreeModel::rowCount(const QModelIndex& parent) const {
|
||||
if (!parent.isValid())
|
||||
return static_cast<int>(thread_items.size());
|
||||
|
||||
WaitTreeItem* parent_item = static_cast<WaitTreeItem*>(parent.internalPointer());
|
||||
parent_item->Expand();
|
||||
return static_cast<int>(parent_item->Children().size());
|
||||
}
|
||||
|
||||
int WaitTreeModel::columnCount(const QModelIndex&) const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
QVariant WaitTreeModel::data(const QModelIndex& index, int role) const {
|
||||
if (!index.isValid())
|
||||
return {};
|
||||
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return static_cast<WaitTreeItem*>(index.internalPointer())->GetText();
|
||||
case Qt::ForegroundRole:
|
||||
return static_cast<WaitTreeItem*>(index.internalPointer())->GetColor();
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void WaitTreeModel::ClearItems() {
|
||||
thread_items.clear();
|
||||
}
|
||||
|
||||
void WaitTreeModel::InitItems() {
|
||||
thread_items = WaitTreeItem::MakeThreadItemList(system);
|
||||
}
|
||||
|
||||
WaitTreeWidget::WaitTreeWidget(Core::System& system_, QWidget* parent)
|
||||
: QDockWidget(tr("&Wait Tree"), parent), system{system_} {
|
||||
setObjectName(QStringLiteral("WaitTreeWidget"));
|
||||
view = new QTreeView(this);
|
||||
view->setHeaderHidden(true);
|
||||
setWidget(view);
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
WaitTreeWidget::~WaitTreeWidget() = default;
|
||||
|
||||
void WaitTreeWidget::OnDebugModeEntered() {
|
||||
if (!system.IsPoweredOn())
|
||||
return;
|
||||
model->InitItems();
|
||||
view->setModel(model);
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
void WaitTreeWidget::OnDebugModeLeft() {
|
||||
setEnabled(false);
|
||||
view->setModel(nullptr);
|
||||
model->ClearItems();
|
||||
}
|
||||
|
||||
void WaitTreeWidget::OnEmulationStarting(EmuThread* emu_thread) {
|
||||
model = new WaitTreeModel(system, this);
|
||||
view->setModel(model);
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
void WaitTreeWidget::OnEmulationStopping() {
|
||||
view->setModel(nullptr);
|
||||
delete model;
|
||||
setEnabled(false);
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QTreeView>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/k_auto_object.h"
|
||||
#include "core/hle/kernel/svc_common.h"
|
||||
|
||||
class EmuThread;
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class KHandleTable;
|
||||
class KReadableEvent;
|
||||
class KSynchronizationObject;
|
||||
class KThread;
|
||||
} // namespace Kernel
|
||||
|
||||
class WaitTreeThread;
|
||||
|
||||
class WaitTreeItem : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
WaitTreeItem();
|
||||
~WaitTreeItem() override;
|
||||
|
||||
virtual bool IsExpandable() const;
|
||||
virtual std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const;
|
||||
virtual QString GetText() const = 0;
|
||||
virtual QColor GetColor() const;
|
||||
|
||||
void Expand();
|
||||
WaitTreeItem* Parent() const;
|
||||
const std::vector<std::unique_ptr<WaitTreeItem>>& Children() const;
|
||||
std::size_t Row() const;
|
||||
static std::vector<std::unique_ptr<WaitTreeThread>> MakeThreadItemList(Core::System& system);
|
||||
|
||||
private:
|
||||
std::size_t row;
|
||||
bool expanded = false;
|
||||
WaitTreeItem* parent = nullptr;
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> children;
|
||||
};
|
||||
|
||||
class WaitTreeText : public WaitTreeItem {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WaitTreeText(QString text);
|
||||
~WaitTreeText() override;
|
||||
|
||||
QString GetText() const override;
|
||||
|
||||
private:
|
||||
QString text;
|
||||
};
|
||||
|
||||
class WaitTreeExpandableItem : public WaitTreeItem {
|
||||
Q_OBJECT
|
||||
public:
|
||||
WaitTreeExpandableItem();
|
||||
~WaitTreeExpandableItem() override;
|
||||
|
||||
bool IsExpandable() const override;
|
||||
};
|
||||
|
||||
class WaitTreeCallstack : public WaitTreeExpandableItem {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WaitTreeCallstack(const Kernel::KThread& thread_, Core::System& system_);
|
||||
~WaitTreeCallstack() override;
|
||||
|
||||
QString GetText() const override;
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
|
||||
|
||||
private:
|
||||
const Kernel::KThread& thread;
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
class WaitTreeSynchronizationObject : public WaitTreeExpandableItem {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WaitTreeSynchronizationObject(const Kernel::KSynchronizationObject& object_,
|
||||
Core::System& system_);
|
||||
~WaitTreeSynchronizationObject() override;
|
||||
|
||||
static std::unique_ptr<WaitTreeSynchronizationObject> make(
|
||||
const Kernel::KSynchronizationObject& object, Core::System& system);
|
||||
QString GetText() const override;
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
|
||||
|
||||
protected:
|
||||
const Kernel::KSynchronizationObject& object;
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
class WaitTreeThread : public WaitTreeSynchronizationObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WaitTreeThread(const Kernel::KThread& thread, Core::System& system_);
|
||||
~WaitTreeThread() override;
|
||||
|
||||
QString GetText() const override;
|
||||
QColor GetColor() const override;
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
class WaitTreeEvent : public WaitTreeSynchronizationObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WaitTreeEvent(const Kernel::KReadableEvent& object_, Core::System& system_);
|
||||
~WaitTreeEvent() override;
|
||||
};
|
||||
|
||||
class WaitTreeThreadList : public WaitTreeExpandableItem {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WaitTreeThreadList(std::vector<Kernel::KThread*>&& list, Core::System& system_);
|
||||
~WaitTreeThreadList() override;
|
||||
|
||||
QString GetText() const override;
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
|
||||
|
||||
private:
|
||||
std::vector<Kernel::KThread*> thread_list;
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
class WaitTreeModel : public QAbstractItemModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit WaitTreeModel(Core::System& system_, QObject* parent = nullptr);
|
||||
~WaitTreeModel() override;
|
||||
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
QModelIndex index(int row, int column, const QModelIndex& parent) const override;
|
||||
QModelIndex parent(const QModelIndex& index) const override;
|
||||
int rowCount(const QModelIndex& parent) const override;
|
||||
int columnCount(const QModelIndex& parent) const override;
|
||||
|
||||
void ClearItems();
|
||||
void InitItems();
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<WaitTreeThread>> thread_items;
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
class WaitTreeWidget : public QDockWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit WaitTreeWidget(Core::System& system_, QWidget* parent = nullptr);
|
||||
~WaitTreeWidget() override;
|
||||
|
||||
public slots:
|
||||
void OnDebugModeEntered();
|
||||
void OnDebugModeLeft();
|
||||
|
||||
void OnEmulationStarting(EmuThread* emu_thread);
|
||||
void OnEmulationStopping();
|
||||
|
||||
private:
|
||||
QTreeView* view;
|
||||
WaitTreeModel* model;
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
@@ -18,7 +18,7 @@ DepsDialog::DepsDialog(QWidget* parent)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
constexpr size_t rows = Common::dep_hashes.size();
|
||||
constexpr int rows = (int) Common::dep_hashes.size();
|
||||
ui->tableDeps->setRowCount(rows);
|
||||
|
||||
QStringList labels;
|
||||
@@ -29,7 +29,7 @@ DepsDialog::DepsDialog(QWidget* parent)
|
||||
ui->tableDeps->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeMode::Fixed);
|
||||
ui->tableDeps->horizontalHeader()->setMinimumSectionSize(200);
|
||||
|
||||
for (size_t i = 0; i < rows; ++i) {
|
||||
for (int i = 0; i < rows; ++i) {
|
||||
const std::string name = Common::dep_names.at(i);
|
||||
const std::string sha = Common::dep_hashes.at(i);
|
||||
const std::string url = Common::dep_urls.at(i);
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "yuzu/compatibility_list.h"
|
||||
#include "yuzu/game_list_p.h"
|
||||
#include "yuzu/game_list_worker.h"
|
||||
#include "yuzu/main.h"
|
||||
#include "yuzu/main_window.h"
|
||||
#include "yuzu/util/controller_navigation.h"
|
||||
#include <fmt/ranges.h>
|
||||
#include <regex>
|
||||
@@ -314,7 +314,7 @@ void GameList::OnFilterCloseClicked() {
|
||||
|
||||
GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvider* provider_,
|
||||
PlayTime::PlayTimeManager& play_time_manager_, Core::System& system_,
|
||||
GMainWindow* parent)
|
||||
MainWindow* parent)
|
||||
: QWidget{parent}, vfs{std::move(vfs_)}, provider{provider_},
|
||||
play_time_manager{play_time_manager_}, system{system_} {
|
||||
watcher = new QFileSystemWatcher(this);
|
||||
@@ -347,7 +347,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
|
||||
tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time);
|
||||
item_model->setSortRole(GameListItemPath::SortRole);
|
||||
|
||||
connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons);
|
||||
connect(main_window, &MainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons);
|
||||
connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
|
||||
connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
|
||||
connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded);
|
||||
@@ -943,8 +943,8 @@ void GameList::RemoveFavorite(u64 program_id) {
|
||||
}
|
||||
}
|
||||
|
||||
GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent} {
|
||||
connect(parent, &GMainWindow::UpdateThemedIcons, this,
|
||||
GameListPlaceholder::GameListPlaceholder(MainWindow* parent) : QWidget{parent} {
|
||||
connect(parent, &MainWindow::UpdateThemedIcons, this,
|
||||
&GameListPlaceholder::onUpdateThemedIcons);
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
|
||||
@@ -33,7 +33,7 @@ class ControllerNavigation;
|
||||
class GameListWorker;
|
||||
class GameListSearchField;
|
||||
class GameListDir;
|
||||
class GMainWindow;
|
||||
class MainWindow;
|
||||
enum class AmLaunchType;
|
||||
enum class StartGameType;
|
||||
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs_,
|
||||
FileSys::ManualContentProvider* provider_,
|
||||
PlayTime::PlayTimeManager& play_time_manager_, Core::System& system_,
|
||||
GMainWindow* parent = nullptr);
|
||||
MainWindow* parent = nullptr);
|
||||
~GameList() override;
|
||||
|
||||
QString GetLastFilterResultItem() const;
|
||||
@@ -153,7 +153,7 @@ private:
|
||||
std::shared_ptr<FileSys::VfsFilesystem> vfs;
|
||||
FileSys::ManualContentProvider* provider;
|
||||
GameListSearchField* search_field;
|
||||
GMainWindow* main_window = nullptr;
|
||||
MainWindow* main_window = nullptr;
|
||||
QVBoxLayout* layout = nullptr;
|
||||
QTreeView* tree_view = nullptr;
|
||||
QStandardItemModel* item_model = nullptr;
|
||||
@@ -171,7 +171,7 @@ private:
|
||||
class GameListPlaceholder : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit GameListPlaceholder(GMainWindow* parent = nullptr);
|
||||
explicit GameListPlaceholder(MainWindow* parent = nullptr);
|
||||
~GameListPlaceholder();
|
||||
|
||||
signals:
|
||||
|
||||
4984
src/yuzu/main.cpp
4899
src/yuzu/main_window.cpp
Normal file
@@ -29,7 +29,7 @@
|
||||
#include <QDBusObjectPath>
|
||||
#include <QVariant>
|
||||
#include <QtDBus/QDBusInterface>
|
||||
#include <QtDBus/QtDBus>
|
||||
#include <QSocketNotifier>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_UPDATE_CHECKER
|
||||
@@ -38,7 +38,6 @@
|
||||
#endif
|
||||
|
||||
class QtConfig;
|
||||
class ClickableLabel;
|
||||
class EmuThread;
|
||||
class GameList;
|
||||
class GImageInfo;
|
||||
@@ -154,7 +153,7 @@ private:
|
||||
constexpr static int MaxMultiplier = 8;
|
||||
};
|
||||
|
||||
class GMainWindow : public QMainWindow {
|
||||
class MainWindow : public QMainWindow {
|
||||
Q_OBJECT
|
||||
|
||||
/// Max number of recently loaded items to keep track of
|
||||
@@ -163,8 +162,8 @@ class GMainWindow : public QMainWindow {
|
||||
public:
|
||||
void filterBarSetChecked(bool state);
|
||||
void UpdateUITheme();
|
||||
explicit GMainWindow(bool has_broken_vulkan);
|
||||
~GMainWindow() override;
|
||||
explicit MainWindow(bool has_broken_vulkan);
|
||||
~MainWindow() override;
|
||||
|
||||
bool DropAction(QDropEvent* event);
|
||||
void AcceptDropEvent(QDropEvent* event);
|
||||
@@ -390,7 +389,6 @@ private slots:
|
||||
void OnToggleFilterBar();
|
||||
void OnToggleStatusBar();
|
||||
void OnGameListRefresh();
|
||||
void OnDisplayTitleBars(bool);
|
||||
void InitializeHotkeys();
|
||||
void ToggleFullscreen();
|
||||
bool UsingExclusiveFullscreen();
|
||||
@@ -468,11 +466,9 @@ private:
|
||||
*/
|
||||
bool question(QWidget* parent, const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons =
|
||||
QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
|
||||
QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
|
||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
|
||||
std::string GetProfileID();
|
||||
|
||||
std::unique_ptr<Ui::MainWindow> ui;
|
||||
|
||||
std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc;
|
||||
@@ -535,7 +531,6 @@ private:
|
||||
QString startup_icon_theme;
|
||||
|
||||
// Debugger panes
|
||||
WaitTreeWidget* waitTreeWidget;
|
||||
ControllerDialog* controller_dialog;
|
||||
|
||||
QAction* actions_recent_files[max_recent_files_item];
|
||||
@@ -11,11 +11,11 @@
|
||||
|
||||
#include "common/fs/path_util.h"
|
||||
|
||||
MigrationWorker::MigrationWorker(const Emulator selected_legacy_emu_,
|
||||
MigrationWorker::MigrationWorker(const Emulator selected_emu_,
|
||||
const bool clear_shader_cache_,
|
||||
const MigrationStrategy strategy_)
|
||||
: QObject()
|
||||
, selected_legacy_emu(selected_legacy_emu_)
|
||||
, selected_emu(selected_emu_)
|
||||
, clear_shader_cache(clear_shader_cache_)
|
||||
, strategy(strategy_)
|
||||
{}
|
||||
@@ -25,15 +25,20 @@ void MigrationWorker::process()
|
||||
namespace fs = std::filesystem;
|
||||
constexpr auto copy_options = fs::copy_options::update_existing | fs::copy_options::recursive;
|
||||
|
||||
const fs::path legacy_user_dir = selected_legacy_emu.get_user_dir();
|
||||
const fs::path legacy_config_dir = selected_legacy_emu.get_config_dir();
|
||||
const fs::path legacy_cache_dir = selected_legacy_emu.get_cache_dir();
|
||||
const fs::path legacy_user_dir = selected_emu.get_user_dir();
|
||||
const fs::path legacy_config_dir = selected_emu.get_config_dir();
|
||||
const fs::path legacy_cache_dir = selected_emu.get_cache_dir();
|
||||
|
||||
// TODO(crueter): Make these constexpr since they're defaulted
|
||||
const fs::path eden_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::EdenDir);
|
||||
const fs::path config_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir);
|
||||
const fs::path cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir);
|
||||
const fs::path shader_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
|
||||
fs::path eden_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::EdenDir);
|
||||
fs::path config_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir);
|
||||
fs::path cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir);
|
||||
fs::path shader_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
|
||||
|
||||
eden_dir.make_preferred();
|
||||
config_dir.make_preferred();
|
||||
cache_dir.make_preferred();
|
||||
shader_dir.make_preferred();
|
||||
|
||||
try {
|
||||
fs::remove_all(eden_dir);
|
||||
@@ -55,8 +60,8 @@ void MigrationWorker::process()
|
||||
std::exit(-1);
|
||||
}
|
||||
|
||||
// Windows doesn't need any more links, because cache and config
|
||||
// are already children of the root directory
|
||||
// Windows doesn't need any more links, because cache and config
|
||||
// are already children of the root directory
|
||||
#ifndef WIN32
|
||||
if (fs::is_directory(legacy_config_dir)) {
|
||||
Common::FS::CreateSymlink(legacy_config_dir, config_dir);
|
||||
@@ -69,7 +74,7 @@ void MigrationWorker::process()
|
||||
|
||||
success_text.append(tr("\n\nNote that your configuration and data will be shared with %1.\n"
|
||||
"If this is not desirable, delete the following files:\n%2\n%3\n%4")
|
||||
.arg(selected_legacy_emu.name(),
|
||||
.arg(selected_emu.name(),
|
||||
QString::fromStdString(eden_dir.string()),
|
||||
QString::fromStdString(config_dir.string()),
|
||||
QString::fromStdString(cache_dir.string())));
|
||||
@@ -79,8 +84,8 @@ void MigrationWorker::process()
|
||||
// Rename directories if deletion is requested (achieves the same result)
|
||||
fs::rename(legacy_user_dir, eden_dir);
|
||||
|
||||
// Windows doesn't need any more renames, because cache and config
|
||||
// are already children of the root directory
|
||||
// Windows doesn't need any more renames, because cache and config
|
||||
// are already children of the root directory
|
||||
#ifndef WIN32
|
||||
if (fs::is_directory(legacy_config_dir)) {
|
||||
fs::rename(legacy_config_dir, config_dir);
|
||||
@@ -96,8 +101,8 @@ void MigrationWorker::process()
|
||||
// Default behavior: copy
|
||||
fs::copy(legacy_user_dir, eden_dir, copy_options);
|
||||
|
||||
// Windows doesn't need any more copies, because cache and config
|
||||
// are already children of the root directory
|
||||
// Windows doesn't need any more copies, because cache and config
|
||||
// are already children of the root directory
|
||||
#ifndef WIN32
|
||||
if (fs::is_directory(legacy_config_dir)) {
|
||||
fs::copy(legacy_config_dir, config_dir, copy_options);
|
||||
|
||||
@@ -7,14 +7,12 @@
|
||||
#include <QObject>
|
||||
#include "common/fs/path_util.h"
|
||||
|
||||
using namespace Common::FS;
|
||||
|
||||
typedef struct Emulator {
|
||||
const char *m_name;
|
||||
|
||||
EmuPath e_user_dir;
|
||||
EmuPath e_config_dir;
|
||||
EmuPath e_cache_dir;
|
||||
Common::FS::EmuPath e_user_dir;
|
||||
Common::FS::EmuPath e_config_dir;
|
||||
Common::FS::EmuPath e_cache_dir;
|
||||
|
||||
const std::string get_user_dir() const {
|
||||
return Common::FS::GetLegacyPath(e_user_dir).string();
|
||||
@@ -35,11 +33,13 @@ typedef struct Emulator {
|
||||
}
|
||||
} Emulator;
|
||||
|
||||
#define STRUCT_EMU(name, enumName) Emulator{name, Common::FS::enumName##Dir, Common::FS::enumName##ConfigDir, Common::FS::enumName##CacheDir}
|
||||
|
||||
static constexpr std::array<Emulator, 4> legacy_emus = {
|
||||
Emulator{QT_TR_NOOP("Citron"), CitronDir, CitronConfigDir, CitronCacheDir},
|
||||
Emulator{QT_TR_NOOP("Sudachi"), SudachiDir, SudachiConfigDir, SudachiCacheDir},
|
||||
Emulator{QT_TR_NOOP("Suyu"), SuyuDir, SuyuConfigDir, SuyuCacheDir},
|
||||
Emulator{QT_TR_NOOP("Yuzu"), YuzuDir, YuzuConfigDir, YuzuCacheDir},
|
||||
STRUCT_EMU(QT_TR_NOOP("Citron"), Citron),
|
||||
STRUCT_EMU(QT_TR_NOOP("Sudachi"), Sudachi),
|
||||
STRUCT_EMU(QT_TR_NOOP("Suyu"), Suyu),
|
||||
STRUCT_EMU(QT_TR_NOOP("Yuzu"), Yuzu),
|
||||
};
|
||||
|
||||
class MigrationWorker : public QObject
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
Link,
|
||||
};
|
||||
|
||||
MigrationWorker(const Emulator selected_legacy_emu,
|
||||
MigrationWorker(const Emulator selected_emu,
|
||||
const bool clear_shader_cache,
|
||||
const MigrationStrategy strategy);
|
||||
|
||||
@@ -64,7 +64,7 @@ signals:
|
||||
void error(const QString &error_message);
|
||||
|
||||
private:
|
||||
Emulator selected_legacy_emu;
|
||||
Emulator selected_emu;
|
||||
bool clear_shader_cache;
|
||||
MigrationStrategy strategy;
|
||||
QString success_text = tr("Data was migrated successfully.");
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "core/internal_network/network_interface.h"
|
||||
#include "network/network.h"
|
||||
#include "ui_direct_connect.h"
|
||||
#include "yuzu/main.h"
|
||||
#include "yuzu/main_window.h"
|
||||
#include "yuzu/multiplayer/client_room.h"
|
||||
#include "yuzu/multiplayer/direct_connect.h"
|
||||
#include "yuzu/multiplayer/message.h"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "network/announce_multiplayer_session.h"
|
||||
#include "ui_host_room.h"
|
||||
#include "yuzu/game_list_p.h"
|
||||
#include "yuzu/main.h"
|
||||
#include "yuzu/main_window.h"
|
||||
#include "yuzu/multiplayer/host_room.h"
|
||||
#include "yuzu/multiplayer/message.h"
|
||||
#include "yuzu/multiplayer/state.h"
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "network/network.h"
|
||||
#include "ui_lobby.h"
|
||||
#include "yuzu/game_list_p.h"
|
||||
#include "yuzu/main.h"
|
||||
#include "yuzu/main_window.h"
|
||||
#include "yuzu/multiplayer/client_room.h"
|
||||
#include "yuzu/multiplayer/lobby.h"
|
||||
#include "yuzu/multiplayer/lobby_p.h"
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "qt_common/abstract/frontend.h"
|
||||
#include "ryujinx_dialog.h"
|
||||
#include "qt_common/util/fs.h"
|
||||
#include "ui_ryujinx_dialog.h"
|
||||
#include <filesystem>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
RyujinxDialog::RyujinxDialog(std::filesystem::path eden_path,
|
||||
std::filesystem::path ryu_path,
|
||||
QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::RyujinxDialog)
|
||||
, m_eden(eden_path.make_preferred())
|
||||
, m_ryu(ryu_path.make_preferred())
|
||||
, ui(new Ui::RyujinxDialog)
|
||||
, m_eden(eden_path.make_preferred())
|
||||
, m_ryu(ryu_path.make_preferred())
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
connect(ui->eden, &QPushButton::clicked, this, &RyujinxDialog::fromEden);
|
||||
connect(ui->ryujinx, &QPushButton::clicked, this, &RyujinxDialog::fromRyujinx);
|
||||
connect(ui->cancel, &QPushButton::clicked, this, &RyujinxDialog::reject);
|
||||
}
|
||||
|
||||
RyujinxDialog::~RyujinxDialog()
|
||||
@@ -30,7 +30,21 @@ RyujinxDialog::~RyujinxDialog()
|
||||
void RyujinxDialog::fromEden()
|
||||
{
|
||||
accept();
|
||||
QtCommon::FS::LinkRyujinx(m_eden, m_ryu);
|
||||
|
||||
// Workaround: Ryujinx deletes and re-creates its directory structure???
|
||||
// So we just copy Eden's data to Ryujinx and then link the other way
|
||||
namespace fs = std::filesystem;
|
||||
try {
|
||||
fs::remove_all(m_ryu);
|
||||
fs::create_directories(m_ryu);
|
||||
fs::copy(m_eden, m_ryu, fs::copy_options::recursive);
|
||||
} catch (std::exception &e) {
|
||||
QtCommon::Frontend::Critical(tr("Failed to link save data"),
|
||||
tr("OS returned error: %1").arg(QString::fromStdString(e.what())));
|
||||
}
|
||||
|
||||
// ?ploo
|
||||
QtCommon::FS::LinkRyujinx(m_ryu, m_eden);
|
||||
}
|
||||
|
||||
void RyujinxDialog::fromRyujinx()
|
||||
|
||||
@@ -23,8 +23,6 @@
|
||||
#include <QThread>
|
||||
#include <filesystem>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
UserDataMigrator::UserDataMigrator(QMainWindow *main_window)
|
||||
{
|
||||
// NOTE: Logging is not initialized yet, do not produce logs here.
|
||||
@@ -32,7 +30,7 @@ UserDataMigrator::UserDataMigrator(QMainWindow *main_window)
|
||||
// Check migration if config directory does not exist
|
||||
// TODO: ProfileManager messes with us a bit here, and force-creates the /nand/system/save/8000000000000010/su/avators/profiles.dat
|
||||
// file. Find a way to reorder operations and have it create after this guy runs.
|
||||
if (!fs::is_directory(Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir))) {
|
||||
if (!std::filesystem::is_directory(Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir))) {
|
||||
ShowMigrationPrompt(main_window);
|
||||
}
|
||||
}
|
||||
@@ -40,23 +38,7 @@ UserDataMigrator::UserDataMigrator(QMainWindow *main_window)
|
||||
void UserDataMigrator::ShowMigrationPrompt(QMainWindow *main_window)
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// define strings here for easy access
|
||||
|
||||
QString prompt_prefix_text = QtCommon::StringLookup::Lookup(
|
||||
QtCommon::StringLookup::MigrationPromptPrefix);
|
||||
QString migration_prompt_message = QtCommon::StringLookup::Lookup(
|
||||
QtCommon::StringLookup::MigrationPrompt);
|
||||
QString clear_shader_tooltip = QtCommon::StringLookup::Lookup(
|
||||
QtCommon::StringLookup::MigrationTooltipClearShader);
|
||||
QString keep_old_data_tooltip = QtCommon::StringLookup::Lookup(
|
||||
QtCommon::StringLookup::MigrationTooltipKeepOld);
|
||||
QString clear_old_data_tooltip = QtCommon::StringLookup::Lookup(
|
||||
QtCommon::StringLookup::MigrationTooltipClearOld);
|
||||
QString link_old_dir_tooltip = QtCommon::StringLookup::Lookup(
|
||||
QtCommon::StringLookup::MigrationTooltipLinkOld);
|
||||
|
||||
// actual migration code
|
||||
using namespace QtCommon::StringLookup;
|
||||
|
||||
MigrationDialog migration_prompt;
|
||||
migration_prompt.setWindowTitle(QObject::tr("Migration"));
|
||||
@@ -69,11 +51,11 @@ void UserDataMigrator::ShowMigrationPrompt(QMainWindow *main_window)
|
||||
#define BUTTON(clazz, name, text, tooltip, checkState) \
|
||||
clazz *name = new clazz(&migration_prompt); \
|
||||
name->setText(text); \
|
||||
name->setToolTip(tooltip); \
|
||||
name->setToolTip(Lookup(tooltip)); \
|
||||
name->setChecked(checkState); \
|
||||
migration_prompt.addBox(name);
|
||||
|
||||
BUTTON(QCheckBox, clear_shaders, QObject::tr("Clear Shader Cache"), clear_shader_tooltip, true)
|
||||
BUTTON(QCheckBox, clear_shaders, QObject::tr("Clear Shader Cache"), MigrationTooltipClearShader, true)
|
||||
|
||||
u32 id = 0;
|
||||
|
||||
@@ -81,9 +63,9 @@ void UserDataMigrator::ShowMigrationPrompt(QMainWindow *main_window)
|
||||
BUTTON(QRadioButton, name, text, tooltip, checkState) \
|
||||
group->addButton(name, ++id);
|
||||
|
||||
RADIO(keep_old, QObject::tr("Keep Old Data"), keep_old_data_tooltip, true)
|
||||
RADIO(clear_old, QObject::tr("Clear Old Data"), clear_old_data_tooltip, false)
|
||||
RADIO(link_old, QObject::tr("Link Old Directory"), link_old_dir_tooltip, false)
|
||||
RADIO(keep_old, QObject::tr("Keep Old Data"), MigrationTooltipKeepOld, true)
|
||||
RADIO(clear_old, QObject::tr("Clear Old Data"), MigrationTooltipClearOld, false)
|
||||
RADIO(link_old, QObject::tr("Link Old Directory"), MigrationTooltipLinkOld, false)
|
||||
|
||||
#undef RADIO
|
||||
#undef BUTTON
|
||||
@@ -101,7 +83,7 @@ void UserDataMigrator::ShowMigrationPrompt(QMainWindow *main_window)
|
||||
// makes my life easier
|
||||
qRegisterMetaType<Emulator>();
|
||||
|
||||
QString prompt_text = prompt_prefix_text;
|
||||
QString prompt_text = Lookup(MigrationPromptPrefix);
|
||||
|
||||
// natural language processing is a nightmare
|
||||
for (const Emulator &emu : found) {
|
||||
@@ -114,7 +96,7 @@ void UserDataMigrator::ShowMigrationPrompt(QMainWindow *main_window)
|
||||
}
|
||||
|
||||
prompt_text.append(QObject::tr("\n\n"));
|
||||
prompt_text = prompt_text % QStringLiteral("\n\n") % migration_prompt_message;
|
||||
prompt_text = prompt_text % QStringLiteral("\n\n") % Lookup(MigrationPrompt);
|
||||
|
||||
migration_prompt.setText(prompt_text);
|
||||
migration_prompt.addButton(QObject::tr("No"), true);
|
||||
@@ -127,24 +109,12 @@ void UserDataMigrator::ShowMigrationPrompt(QMainWindow *main_window)
|
||||
return ShowMigrationCancelledMessage(main_window);
|
||||
}
|
||||
|
||||
MigrationWorker::MigrationStrategy strategy;
|
||||
MigrationWorker::MigrationStrategy strategy = static_cast<MigrationWorker::MigrationStrategy>(
|
||||
group->checkedId());
|
||||
|
||||
switch (group->checkedId()) {
|
||||
default:
|
||||
[[fallthrough]];
|
||||
case 1:
|
||||
strategy = MigrationWorker::MigrationStrategy::Copy;
|
||||
break;
|
||||
case 2:
|
||||
strategy = MigrationWorker::MigrationStrategy::Move;
|
||||
break;
|
||||
case 3:
|
||||
strategy = MigrationWorker::MigrationStrategy::Link;
|
||||
break;
|
||||
}
|
||||
selected_emu = button->property("emulator").value<Emulator>();
|
||||
|
||||
MigrateUserData(main_window,
|
||||
button->property("emulator").value<Emulator>(),
|
||||
clear_shaders->isChecked(),
|
||||
strategy);
|
||||
}
|
||||
@@ -161,12 +131,9 @@ void UserDataMigrator::ShowMigrationCancelledMessage(QMainWindow *main_window)
|
||||
}
|
||||
|
||||
void UserDataMigrator::MigrateUserData(QMainWindow *main_window,
|
||||
const Emulator selected_legacy_emu,
|
||||
const bool clear_shader_cache,
|
||||
const MigrationWorker::MigrationStrategy strategy)
|
||||
{
|
||||
selected_emu = selected_legacy_emu;
|
||||
|
||||
// Create a dialog to let the user know it's migrating
|
||||
QProgressDialog *progress = new QProgressDialog(main_window);
|
||||
progress->setWindowTitle(QObject::tr("Migrating"));
|
||||
@@ -176,7 +143,7 @@ void UserDataMigrator::MigrateUserData(QMainWindow *main_window,
|
||||
progress->setWindowModality(Qt::WindowModality::ApplicationModal);
|
||||
|
||||
QThread *thread = new QThread(main_window);
|
||||
MigrationWorker *worker = new MigrationWorker(selected_legacy_emu, clear_shader_cache, strategy);
|
||||
MigrationWorker *worker = new MigrationWorker(selected_emu, clear_shader_cache, strategy);
|
||||
worker->moveToThread(thread);
|
||||
|
||||
thread->connect(thread, &QThread::started, worker, &MigrationWorker::process);
|
||||
|
||||
@@ -22,7 +22,6 @@ private:
|
||||
void ShowMigrationPrompt(QMainWindow* main_window);
|
||||
void ShowMigrationCancelledMessage(QMainWindow* main_window);
|
||||
void MigrateUserData(QMainWindow* main_window,
|
||||
const Emulator selected_legacy_emu,
|
||||
const bool clear_shader_cache,
|
||||
const MigrationWorker::MigrationStrategy strategy);
|
||||
};
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
#include <cmath>
|
||||
#include <QPainter>
|
||||
|
||||
#include "applets/qt_profile_select.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/frontend/applets/profile_select.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "qt_common/qt_common.h"
|
||||
#include "yuzu/util/util.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -153,3 +157,49 @@ bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image)
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
const std::optional<Common::UUID> GetProfileID() {
|
||||
// if there's only a single profile, the user probably wants to use that... right?
|
||||
const auto& profiles = QtCommon::system->GetProfileManager().FindExistingProfileUUIDs();
|
||||
if (profiles.size() == 1) {
|
||||
return profiles[0];
|
||||
}
|
||||
|
||||
const auto select_profile = [] {
|
||||
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, QtCommon::rootObject, 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 std::nullopt;
|
||||
}
|
||||
|
||||
const auto uuid =
|
||||
QtCommon::system->GetProfileManager().GetUser(static_cast<std::size_t>(index));
|
||||
ASSERT(uuid);
|
||||
|
||||
return uuid;
|
||||
}
|
||||
std::string GetProfileIDString() {
|
||||
const auto uuid = GetProfileID();
|
||||
if (!uuid)
|
||||
return "";
|
||||
|
||||
auto user_id = uuid->AsU128();
|
||||
|
||||
return fmt::format("{:016X}{:016X}", user_id[1], user_id[0]);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -6,6 +9,7 @@
|
||||
#include <filesystem>
|
||||
#include <QFont>
|
||||
#include <QString>
|
||||
#include "common/uuid.h"
|
||||
|
||||
/// Returns a QFont object appropriate to use as a monospace font for debugging widgets, etc.
|
||||
[[nodiscard]] QFont GetMonospaceFont();
|
||||
@@ -27,3 +31,15 @@
|
||||
* @return bool If the operation succeeded
|
||||
*/
|
||||
[[nodiscard]] bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image);
|
||||
|
||||
/**
|
||||
* Prompt the user for a profile ID. If there is only one valid profile, returns that profile.
|
||||
* @return The selected profile, or an std::nullopt if none were selected
|
||||
*/
|
||||
const std::optional<Common::UUID> GetProfileID();
|
||||
|
||||
/**
|
||||
* Prompt the user for a profile ID. If there is only one valid profile, returns that profile.
|
||||
* @return A string representation of the selected profile, or an empty string if none were seleeced
|
||||
*/
|
||||
std::string GetProfileIDString();
|
||||
|
||||
@@ -24,6 +24,7 @@ Tools for Eden and other subprojects.
|
||||
- `find-unused-strings.sh`: Find any unused strings in the Android app (XML -> Kotlin).
|
||||
|
||||
## Android
|
||||
|
||||
It's recommended to run these scritps after almost any Android change, as they are relatively fast and important both for APK bloat and CI.
|
||||
|
||||
- `unused-strings.sh`: Finds unused strings in `strings.xml` files.
|
||||
@@ -32,3 +33,7 @@ It's recommended to run these scritps after almost any Android change, as they a
|
||||
## Translations
|
||||
|
||||
- [Translation Scripts](./translations)
|
||||
|
||||
## Mirror
|
||||
|
||||
- [Mirroring scripts](./mirror)
|
||||
|
||||
@@ -13,7 +13,7 @@ MAXDEPTH=3
|
||||
# For your project you'll want to change this to define what dirs you have cpmfiles in
|
||||
# Remember to account for the MAXDEPTH variable!
|
||||
# Adding ./ before each will help to remove duplicates
|
||||
[ -z "$CPMFILES" ] && CPMFILES=$(find . ./src -maxdepth "$MAXDEPTH" -name cpmfile.json | sort | uniq)
|
||||
[ -z "$CPMFILES" ] && CPMFILES=$(find . ./src -maxdepth "$MAXDEPTH" -name cpmfile.json -not -path build | sort | uniq)
|
||||
|
||||
# shellcheck disable=SC2016
|
||||
[ -z "$PACKAGES" ] && PACKAGES=$(echo "$CPMFILES" | xargs jq -s 'reduce .[] as $item ({}; . * $item)')
|
||||
|
||||
6
tools/mirror/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Mirroring scripts
|
||||
|
||||
Scripts for setting up a Git mirror or performing offline work (i.e no GitHub) when using systems like CPM.
|
||||
|
||||
- `helper.sh`: Mini helper script - made for `cgit` jails but may work for Forgejo as well.
|
||||
- `common.sh`: Common functions and stuff.
|
||||
30
tools/mirror/common.sh
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/bin/sh -e
|
||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
git_clone_mirror_repo() {
|
||||
NAME=$(echo "$1" | cut -d '/' -f 5)
|
||||
ORG=$(echo "$1" | cut -d '/' -f 4)
|
||||
git clone --mirror $1 $ORG/$NAME
|
||||
}
|
||||
git_retrieve_file() {
|
||||
TMPDIR=$1
|
||||
BRANCH=$(cd $TMPDIR && git rev-parse --abbrev-ref HEAD)
|
||||
cd $1 && git show $BRANCH:$2 2>/dev/null | git lfs smudge 2>/dev/null
|
||||
}
|
||||
git_retrieve_gitmodules() {
|
||||
TMP=$1
|
||||
git_retrieve_file $TMP '.gitmodules' | awk '{$1=$1};1' \
|
||||
| grep "url = " | sed "s,url = ,," \
|
||||
| while IFS= read -r line; do
|
||||
case "$line" in
|
||||
../*)
|
||||
# TODO: maybe one day handle case where its NOT ALL GITHUB - thanks boost
|
||||
ORGAN=$(echo "$TMP" | awk -F'/' '{print $2}')
|
||||
echo "$line" | sed "s|../|https://github.com/$ORGAN/|"
|
||||
;;
|
||||
http*)
|
||||
echo "$line"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
84
tools/mirror/helper.sh
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/bin/sh -e
|
||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# You must run this at the root of the eden git repo
|
||||
CPM_DIR=$PWD
|
||||
NPROC=$(nproc || echo 8)
|
||||
. ./tools/mirror/common.sh
|
||||
. ./tools/cpm/common.sh
|
||||
die() {
|
||||
echo "$@" >&2
|
||||
exit 1
|
||||
}
|
||||
help() {
|
||||
cat << EOF
|
||||
--path <path> Specify the given path (must be the root of your SCM folder)
|
||||
--initial Initial cloning (no submodules) - fetched from cpmfiles
|
||||
--clone-submodules Clones submodules, can also be used multiple times to clone
|
||||
newly referenced submodules
|
||||
--remote-update Update all remotes (of the repos) - aka. sync with remote
|
||||
--mirror Perform a mirror clone; URL must be specified before,
|
||||
if name or organisation are not specified, it is derived from URL
|
||||
--url Set URL of clone
|
||||
--org Set organisation folder of clone
|
||||
--name Set name of clone
|
||||
EOF
|
||||
}
|
||||
[ -z "$SCM_ROOT_DIR" ] && SCM_ROOT_DIR="/usr/local/jails/containers/cgit-www/srv/git/repos"
|
||||
|
||||
op_initial() {
|
||||
sudo chmod 777 $SCM_ROOT_DIR
|
||||
# stuff to parse the cpmfile json and then spit out full repo path
|
||||
REPOS=$(echo "$PACKAGES" \
|
||||
| jq -r 'reduce .[] as $i (""; . + (if $i.git_host == null then "https://github.com" else "https://" + $i.git_host end) + "/" + $i.repo + " ")' \
|
||||
| tr ' ' '\n' | xargs -I {} echo {})
|
||||
# clone the stuff
|
||||
cd $SCM_ROOT_DIR && echo "$REPOS" \
|
||||
| xargs -P $NPROC -I {} sh -c ". $CPM_DIR/tools/mirror/common.sh && git_clone_mirror_repo {}"
|
||||
sudo chmod 755 $SCM_ROOT_DIR
|
||||
}
|
||||
op_clone_submodules() {
|
||||
sudo chmod 777 $SCM_ROOT_DIR
|
||||
cd $SCM_ROOT_DIR && find . -maxdepth 2 -type d -name '*.git' -print0 \
|
||||
| xargs -0 -I {} sh -c ". $CPM_DIR/tools/mirror/common.sh && git_retrieve_gitmodules {}" \
|
||||
| while IFS= read -r url; do
|
||||
git_clone_mirror_repo $url || echo "skipped $url"
|
||||
done
|
||||
sudo chmod 755 $SCM_ROOT_DIR
|
||||
}
|
||||
op_remote_update() {
|
||||
sudo chmod 777 $SCM_ROOT_DIR
|
||||
cd $SCM_ROOT_DIR && find . -maxdepth 3 -type d -name '*.git' -print0 \
|
||||
| xargs -0 -P $NPROC -I {} sh -c 'cd {} && git remote update && echo {}'
|
||||
sudo chmod 755 $SCM_ROOT_DIR
|
||||
}
|
||||
op_mirror() {
|
||||
sudo chmod 777 $SCM_ROOT_DIR
|
||||
[ -z "$URL" ] && die "Specify repo --url"
|
||||
[ -z "$NAME" ] && NAME=$(echo "$URL" | cut -d '/' -f 5)
|
||||
[ -z "$ORG" ] && ORG=$(echo "$URL" | cut -d '/' -f 4)
|
||||
cd $SCM_ROOT_DIR && git clone --mirror $URL $ORG/$NAME || echo "skipped $URL"
|
||||
sudo chmod 755 $SCM_ROOT_DIR
|
||||
}
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
--path) shift; SCM_ROOT_DIR=$1; [ -z "$SCM_ROOT_DIR" ] && die "Empty target root dir";;
|
||||
--initial) OP_INITIAL=1;;
|
||||
--clone-submodules) OP_CLONE_SUBMODULES=1;;
|
||||
--remote-update) OP_REMOTE_UPDATE=1;;
|
||||
--mirror) OP_MIRROR=1;;
|
||||
--url) shift; URL=$1; [ -z "$URL" ] && die "Expected url";;
|
||||
--name) shift; NAME=$1; [ -z "$NAME" ] && die "Expected name";;
|
||||
--org) shift; ORG=$1; [ -z "$ORG" ] && die "Expected organisation";;
|
||||
--help) help "$@";;
|
||||
--*) die "Invalid option $1" ;;
|
||||
"$0" | "") break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[ "$OP_INITIAL" = 1 ] && op_initial
|
||||
[ "$OP_CLONE_SUBMODULES" = 1 ] && op_clone_submodules
|
||||
[ "$OP_REMOTE_UPDATE" = 1 ] && op_remote_update
|
||||
[ "$OP_MIRROR" = 1 ] && op_mirror
|
||||