Compare commits

..

10 Commits

Author SHA1 Message Date
lizzie
4da9c6303b better script opts
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-11-10 01:31:01 +01:00
lizzie
5478ddde87 better
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-11-10 01:31:01 +01:00
lizzie
8f1d895932 [cpm]: Ability to override and set custom hosts (like a self hosted instance or whatever)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-11-10 01:31:01 +01:00
PavelBARABANOV
42863027e2 Revert "[core/memory] Remove defered heap allocation on Linux." (#2974)
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2974
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: PavelBARABANOV <pavelbarabanov94@gmail.com>
Co-committed-by: PavelBARABANOV <pavelbarabanov94@gmail.com>
2025-11-09 22:42:09 +01:00
crueter
08f3639c80 [desktop, fs] main_window separation; fix Ryujinx save data link issues (#2929)
Some genius decided to put the entire MainWindow class into main.h and
main.cpp, which is not only horrific practice but also completely
destroys clangd beyond repair. Please, just don't do this.

(this will probably merge conflict to hell and back)

Also, fixes a bunch of issues with Ryujinx save data link:
- Paths with spaces would cause mklink to fail
- Add support for portable directories
- Symlink detection was incorrect sometimes(????)
- Some other stuff I'm forgetting

Furthermore, when selecting "From Eden" and attempting to save in Ryujinx, Ryujinx would destroy the link for... some reason? So to get around this we just copy the Eden data to Ryujinx then treat it like a "From Ryujinx" op

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2929
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2025-11-09 18:07:38 +01:00
Bix
e13c7ef3f8 [dist] Switch back to default Eden logo (#2990)
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2990
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: Bix <bix@bixed.xyz>
Co-committed-by: Bix <bix@bixed.xyz>
2025-11-08 23:16:50 +01:00
unknown
89dd133a2f [frontend] Remove QDockWidget Wait Tree (#2949)
No one uses it and knows how to use it. So, remove it and replace it later with something more useful :)

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2949
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Co-authored-by: unknown <sahyno1996@gmail.com>
Co-committed-by: unknown <sahyno1996@gmail.com>
2025-11-08 22:16:30 +01:00
John
86e9c32800 [desktop] Save option location in the Data Manager tool fix (#2986)
The first attempt missed a few files and did not move it properly.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2986
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: John <john@eden-emu.dev>
Co-committed-by: John <john@eden-emu.dev>
2025-11-08 19:41:35 +01:00
lizzie
f1cf30bc2a [dynarmic] fix build error for dynarmic_tests_generator on aarch64 (#2978)
Signed-off-by: lizzie lizzie@eden-emu.dev
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2978
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-11-08 18:47:55 +01:00
John
5af5214451 [desktop] Move save option location in the Data Manager tool. (#2985)
Move the save option further away from the shader option to help alleviate users accidents when deleting data.

(I slipped and I accidentally deleted all my saves instead of shaders when testing so many builds)

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2985
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Co-authored-by: John <john@eden-emu.dev>
Co-committed-by: John <john@eden-emu.dev>
2025-11-08 18:46:30 +01:00
83 changed files with 5722 additions and 6273 deletions

View File

@@ -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)

View File

@@ -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()

View File

@@ -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
View File

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

BIN
dist/eden.icns vendored

Binary file not shown.

BIN
dist/eden.ico vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 403 KiB

After

Width:  |  Height:  |  Size: 315 KiB

83
dist/eden_named.svg vendored

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -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;

View File

@@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 256 KiB

BIN
dist/yuzu.icns vendored

Binary file not shown.

View File

@@ -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`.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 65 KiB

View File

@@ -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()

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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++) {

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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

View File

@@ -495,6 +495,8 @@ public:
bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size);
bool InvalidateSeparateHeap(void* fault_address);
private:
Core::System& system;

View File

@@ -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

View File

@@ -152,7 +152,7 @@ struct Jit::Impl final {
}
std::string Disassemble() const {
UNREACHABLE();
return {};
}
private:

View File

@@ -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
}

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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};

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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));

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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]);
}

View File

@@ -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>

View File

@@ -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);
}

View File

@@ -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;
};

View File

@@ -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);

View File

@@ -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;

View File

@@ -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:

File diff suppressed because it is too large Load Diff

4899
src/yuzu/main_window.cpp Normal file

File diff suppressed because it is too large Load Diff

View 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];

View File

@@ -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);

View File

@@ -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.");

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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()

View File

@@ -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);

View File

@@ -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);
};

View File

@@ -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]);
}

View File

@@ -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();

View File

@@ -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)

View File

@@ -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
View 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
View 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
View 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