Compare commits

...

7 Commits

Author SHA1 Message Date
lizzie 9fac15716d Fix license headers 2026-09-20 14:43:02 +00:00
lizzie 565f9a3005 2026-09-20 13:20:40
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-20 13:20:40 +00:00
lizzie 01f149ddfd 2026-09-20 13:02:52
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-20 13:02:52 +00:00
lizzie 4bcc2a2319 2026-09-20 12:52:02
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-20 12:52:02 +00:00
lizzie 8b03a1ba01 Fix license headers 2026-09-20 07:46:07 +00:00
lizzie 652ab5c25c 2026-09-20 07:03:36
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-20 07:46:07 +00:00
xbzk a277b62fe4 [fs] revert #4319 savedata_factory changes (#4462)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

This one reverts part of the PR 4319 which was aimed to allow child titles to inherit bundle settings.
The fix seems good so far, but by applying it to save folders caused a trap:
Now child's save folders move to bundle folder, hence saves will vanish, and the android/qt save tools are still pointing to old folders.
Since the save part was only a compliance fix, instead of fiddle in master i'd rather revert this part now, then only touch this again if any bug show up.
No worries. I'll be right here waiting.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4462
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-20 04:40:14 +02:00
15 changed files with 125 additions and 112 deletions
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 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
@@ -35,7 +38,7 @@ void CircularBufferSinkInfo::Update(BehaviorInfo::ErrorInfo& error_info, OutStat
auto current_params{reinterpret_cast<CircularBufferInParameter*>(parameter.data())};
auto current_state{reinterpret_cast<CircularBufferState*>(state.data())};
if (in_use == buffer_params->in_use && !buffer_unmapped) {
if (in_use == bool(buffer_params->in_use) && !buffer_unmapped) {
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
out_status.writeOffset = current_state->last_pos2;
+19 -20
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -31,10 +31,10 @@ public:
};
struct DeviceInParameter {
/* 0x000 */ char name[0x100];
/* 0x000 */ u8 name[0x100];
/* 0x100 */ u32 input_count;
/* 0x104 */ std::array<s8, MaxChannels> inputs;
/* 0x10A */ char unk10A[0x1];
/* 0x10A */ u8 unk10A[0x1];
/* 0x10B */ bool downmix_enabled;
/* 0x10C */ std::array<f32, 4> downmix_coeff;
};
@@ -43,7 +43,7 @@ public:
struct DeviceState {
/* 0x00 */ UpsamplerInfo* upsampler_info;
/* 0x08 */ std::array<Common::FixedPoint<16, 16>, 4> downmix_coeff;
/* 0x18 */ char unk18[0x18];
/* 0x18 */ u8 unk18[0x18];
};
static_assert(sizeof(DeviceState) == 0x30, "DeviceState has the wrong size!");
@@ -55,8 +55,8 @@ public:
/* 0x14 */ u32 previous_pos;
/* 0x18 */ SampleFormat format;
/* 0x1C */ std::array<s8, MaxChannels> inputs;
/* 0x22 */ bool in_use;
/* 0x23 */ char unk23[0x5];
/* 0x22 */ u8 in_use;
/* 0x23 */ u8 unk23[0x5];
};
static_assert(sizeof(CircularBufferInParameter) == 0x28,
"CircularBufferInParameter has the wrong size!");
@@ -65,16 +65,16 @@ public:
/* 0x00 */ u32 last_pos2;
/* 0x04 */ s32 current_pos;
/* 0x08 */ u32 last_pos;
/* 0x0C */ char unk0C[0x4];
/* 0x0C */ u8 unk0C[0x4];
/* 0x10 */ AddressInfo address_info;
};
static_assert(sizeof(CircularBufferState) == 0x30, "CircularBufferState has the wrong size!");
struct InParameter {
/* 0x000 */ Type type;
/* 0x001 */ bool in_use;
/* 0x001 */ u8 in_use;
/* 0x004 */ u32 node_id;
/* 0x008 */ char unk08[0x18];
/* 0x008 */ u8 unk08[0x18];
union {
/* 0x020 */ DeviceInParameter device;
/* 0x020 */ CircularBufferInParameter circular_buffer;
@@ -84,7 +84,7 @@ public:
struct OutStatus {
/* 0x00 */ u32 writeOffset;
/* 0x04 */ char unk04[0x1C];
/* 0x04 */ u8 unk04[0x1C];
}; // size == 0x20
static_assert(sizeof(OutStatus) == 0x20, "SinkInfoBase::OutStatus has the wrong size!");
@@ -162,19 +162,18 @@ public:
u8* GetParameter();
protected:
/// Type of this sink
Type type{Type::Invalid};
/// Is this sink in use?
bool in_use{};
/// Is this sink's buffer unmapped? Circular only
bool buffer_unmapped{};
/// Node id for this sink
u32 node_id{};
/// State buffer for this sink
std::array<u8, (std::max)(sizeof(DeviceState), sizeof(CircularBufferState))> state{};
/// Parameter buffer for this sink
std::array<u8, (std::max)(sizeof(DeviceInParameter), sizeof(CircularBufferInParameter))>
parameter{};
std::array<u8, (std::max)(sizeof(DeviceInParameter), sizeof(CircularBufferInParameter))> parameter{};
/// Type of this sink
Type type{Type::Invalid};
/// Node id for this sink
u32 node_id{};
/// Is this sink in use?
bool in_use : 1 = false;
/// Is this sink's buffer unmapped? Circular only
bool buffer_unmapped : 1 = false;
};
} // namespace AudioCore::Renderer
+1 -1
View File
@@ -428,8 +428,8 @@ struct System::Impl {
core_timing.SyncPause(false);
Network::CancelPendingSocketOperations();
kernel.SuspendEmulation(true);
kernel.CloseServices();
kernel.ShutdownCores();
kernel.CloseServices();
services.reset();
service_manager.reset();
fs_controller.Reset();
+8 -14
View File
@@ -9,7 +9,6 @@
#include "common/logging.h"
#include "common/uuid.h"
#include "core/core.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs/vfs.h"
@@ -62,24 +61,17 @@ SaveDataFactory::SaveDataFactory(Core::System& system_, ProgramId program_id_,
SaveDataFactory::~SaveDataFactory() = default;
std::string SaveDataFactory::GetSaveDataPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, u64 save_id) const {
if (type == SaveDataType::Account || type == SaveDataType::Device) {
const auto requested_id = title_id != 0 ? title_id : program_id;
const auto parent_id = system.GetContentProvider().GetParentApplicationId(requested_id);
title_id = parent_id.value_or(requested_id);
}
return GetFullPath(program_id, dir, space, type, title_id, user_id, save_id);
}
VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
const auto save_directory = GetSaveDataPath(space, meta.type, meta.program_id, meta.user_id, meta.system_save_data_id);
const auto save_directory = GetFullPath(program_id, dir, space, meta.type, meta.program_id,
meta.user_id, meta.system_save_data_id);
return dir->CreateDirectoryRelative(save_directory);
}
VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
const auto save_directory = GetSaveDataPath(space, meta.type, meta.program_id, meta.user_id, meta.system_save_data_id);
const auto save_directory = GetFullPath(program_id, dir, space, meta.type, meta.program_id,
meta.user_id, meta.system_save_data_id);
auto out = dir->GetDirectoryRelative(save_directory);
@@ -162,7 +154,8 @@ std::string SaveDataFactory::GetUserGameSaveDataRoot(u128 user_id, bool future)
SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
u128 user_id) const {
const auto path = GetSaveDataPath(SaveDataSpaceId::User, type, title_id, user_id, 0);
const auto path =
GetFullPath(program_id, dir, SaveDataSpaceId::User, type, title_id, user_id, 0);
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName());
@@ -180,7 +173,8 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
SaveDataSize new_value) const {
const auto path = GetSaveDataPath(SaveDataSpaceId::User, type, title_id, user_id, 0);
const auto path =
GetFullPath(program_id, dir, SaveDataSpaceId::User, type, title_id, user_id, 0);
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName());
-1
View File
@@ -50,7 +50,6 @@ public:
void SetAutoCreate(bool state);
private:
std::string GetSaveDataPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, u64 save_id) const;
Core::System& system;
ProgramId program_id;
VirtualDir dir;
@@ -72,11 +72,9 @@ void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) noe
void GlobalSchedulerContext::WakeupWaitingDummyThreads(KernelCore& kernel) noexcept {
ASSERT(this->IsLocked());
if (m_woken_dummy_threads.size() > 0) {
for (auto* thread : m_woken_dummy_threads)
thread->DummyThreadEndWait(kernel);
m_woken_dummy_threads.clear();
}
for (auto* thread : m_woken_dummy_threads)
thread->DummyThreadEndWait(kernel);
m_woken_dummy_threads.clear();
}
} // namespace Kernel
+3 -7
View File
@@ -1236,14 +1236,10 @@ namespace Kernel {
KScopedSchedulerLock sl{kernel};
// Determine if this is the first termination request.
const bool first_request = [&]() -> bool {
// Perform an atomic compare-and-swap from false to true.
bool expected = false;
return m_termination_requested.compare_exchange_strong(expected, true);
}();
// Perform an atomic compare-and-swap from false to true.
bool expected = false;
// If this is the first request, start termination procedure.
if (first_request) {
if (m_termination_requested.compare_exchange_strong(expected, true)) {
// If the thread is in initialized state, just change state to terminated.
if (this->GetState() == ThreadState::Initialized) {
m_thread_state = ThreadState::Terminated;
+9 -11
View File
@@ -967,12 +967,16 @@ Kernel::KHardwareTimer& KernelCore::HardwareTimer() {
return *impl->hardware_timer;
}
KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
return *impl->global_object_list_container;
KAutoObjectWithListContainer* KernelCore::ObjectListContainer() {
if (!impl->global_object_list_container)
return nullptr;
return std::addressof(*impl->global_object_list_container);
}
const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
return *impl->global_object_list_container;
const KAutoObjectWithListContainer* KernelCore::ObjectListContainer() const {
if (!impl->global_object_list_container)
return nullptr;
return std::addressof(*impl->global_object_list_container);
}
void KernelCore::PrepareReschedule(std::size_t id) {
@@ -1001,16 +1005,10 @@ void KernelCore::UnregisterInUseObject(KAutoObject* object) {
void KernelCore::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) {
auto* manager = server_manager.get();
{
if (!impl->is_shutting_down) {
std::scoped_lock lk{impl->server_lock};
if (impl->is_shutting_down) {
return;
}
impl->server_managers.emplace_back(std::move(server_manager));
}
manager->LoopProcess();
}
+2 -3
View File
@@ -175,9 +175,8 @@ public:
/// Stops execution of 'id' core, in order to reschedule a new thread.
void PrepareReschedule(std::size_t id);
KAutoObjectWithListContainer& ObjectListContainer();
const KAutoObjectWithListContainer& ObjectListContainer() const;
KAutoObjectWithListContainer* ObjectListContainer();
const KAutoObjectWithListContainer* ObjectListContainer() const;
/// Registers all kernel objects with the global emulation state, this is purely for tracking
/// leaks after emulation has been shutdown.
+4 -3
View File
@@ -151,7 +151,8 @@ public:
const bool is_initialized = this->IsInitialized();
uintptr_t arg = 0;
if (is_initialized) {
kernel.ObjectListContainer().Unregister(this);
if (auto const olc = kernel.ObjectListContainer(); olc)
olc->Unregister(this);
arg = this->GetPostDestroyArgument();
this->Finalize(kernel);
}
@@ -175,7 +176,7 @@ public:
public:
static void InitializeSlabHeap(KernelCore& kernel, void* memory, size_t memory_size) {
kernel.SlabHeap<Derived>().Initialize(memory, memory_size);
kernel.ObjectListContainer().Initialize();
kernel.ObjectListContainer()->Initialize();
}
static Derived* Create(KernelCore& kernel) {
@@ -186,7 +187,7 @@ public:
}
static void Register(KernelCore& kernel, Derived* obj) {
return kernel.ObjectListContainer().Register(obj);
return kernel.ObjectListContainer()->Register(obj);
}
static size_t GetObjectSize(KernelCore& kernel) {
+18 -1
View File
@@ -1,6 +1,15 @@
// SPDX-FileCopyrightText: Copyright 2026 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
// gcc14 bug
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
#include "common/assert.h"
#include "common/scope_exit.h"
#include "core/memory/dmnt_cheat_types.h"
@@ -223,7 +232,10 @@ DmntCheatVm::Callbacks::~Callbacks() = default;
bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
// If we've ever seen a decode failure, return false.
bool valid = decode_success;
CheatVmOpcode opcode = {};
CheatVmOpcode opcode = {
.begin_conditional_block = false,
.opcode = {}
};
SCOPE_EXIT {
decode_success &= valid;
if (valid) {
@@ -1266,3 +1278,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
}
} // namespace Core::Memory
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
+11 -9
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -254,14 +254,16 @@ struct UnrecognizedInstruction {
struct CheatVmOpcode {
bool begin_conditional_block{};
std::variant<StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode,
LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode,
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode,
ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction>
opcode{};
std::variant<
std::monostate,
StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode,
LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode,
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode,
ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction
> opcode{};
};
class DmntCheatVm {
@@ -170,13 +170,19 @@ template<>
// check for marked bit, use as unmapped if marked
if (ctx.conf.page_table_marked_bit) {
code.bt(page, *ctx.conf.page_table_marked_bit);
code.jc(abort, code.T_NEAR);
auto const marked_bit = *ctx.conf.page_table_marked_bit;
if (s64(s32(1 << marked_bit)) == s64(1 << marked_bit)) {
code.test(page, s32(1 << marked_bit));
code.jnz(abort, code.T_NEAR);
} else {
code.bt(page, marked_bit);
code.jc(abort, code.T_NEAR);
}
}
// mask away attributes
if (ctx.conf.page_table_pointer_mask == 0) {
code.test(page, page);
} else if (std::in_range<s32>(ctx.conf.page_table_pointer_mask)) {
} else if (s64(s32(ctx.conf.page_table_pointer_mask)) == s64(ctx.conf.page_table_pointer_mask)) {
code.and_(page, ctx.conf.page_table_pointer_mask);
} else {
code.mov(tmp, ctx.conf.page_table_pointer_mask);
@@ -79,8 +79,10 @@ void NpadAbstractButtonHandler::UpdateAllButtonLifo() {
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
for (std::size_t i = 0; i < AruidIndexMax; i++) {
auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
UpdateButtonLifo(npad_entry, data->aruid);
if (auto const shfmt = data->shared_memory_format; shfmt) {
auto& npad_entry = shfmt->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
UpdateButtonLifo(npad_entry, data->aruid);
}
}
}
@@ -88,19 +90,19 @@ void NpadAbstractButtonHandler::UpdateCoreBatteryState() {
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
for (std::size_t i = 0; i < AruidIndexMax; i++) {
auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
UpdateButtonLifo(npad_entry, data->aruid);
if (auto const shfmt = data->shared_memory_format; shfmt) {
auto& npad_entry = shfmt->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
UpdateButtonLifo(npad_entry, data->aruid);
}
}
}
void NpadAbstractButtonHandler::UpdateButtonState(u64 aruid) {
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid);
if (data == nullptr) {
return;
if (auto data = applet_resource_holder->applet_resource->GetAruidData(aruid); data) {
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
UpdateButtonLifo(npad_entry, aruid);
}
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
UpdateButtonLifo(npad_entry, aruid);
}
Result NpadAbstractButtonHandler::SetHomeProtection(bool is_enabled, u64 aruid) {
@@ -496,31 +496,30 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
}
void PatchPhiNodes(IR::Program& program, EmitContext& ctx) {
// Flatten all leading PHIs from each block into a vector
std::vector<IR::Inst*> phi_instructions;
for (IR::Block* block : program.blocks) {
for (auto it = block->begin(); it != block->end(); ++it) {
if (it->GetOpcode() != IR::Opcode::Phi)
break;
phi_instructions.push_back(&*it);
}
}
if (phi_instructions.empty()) {
return; // nothing to patch
}
// Start "before" first PHI; advance on phi_arg == 0
size_t phi_index = static_cast<size_t>(-1);
ctx.PatchDeferredPhi([&](size_t phi_arg, Id parent) -> std::pair<Id, Id> {
if (phi_arg == 0) {
++phi_index;
}
IR::Inst* phi = phi_instructions[phi_index];
return { ctx.Def(phi->Arg(phi_arg)), parent };
});
// Flatten all leading PHIs from each block into a vector
std::vector<IR::Inst*> phi_instructions;
for (IR::Block* block : program.blocks) {
for (auto it = block->begin(); it != block->end(); ++it) {
if (it->GetOpcode() != IR::Opcode::Phi)
break;
phi_instructions.push_back(&*it);
}
}
if (phi_instructions.empty()) {
return; // nothing to patch
}
// Start "before" first PHI; advance on phi_arg == 0
size_t phi_index = size_t(-1);
ctx.PatchDeferredPhi([&ctx, &phi_index, phi_insts = std::move(phi_instructions)](size_t phi_arg, Id parent) -> std::pair<Id, Id> {
if (phi_arg == 0) {
++phi_index;
}
IR::Inst* phi = phi_insts[phi_index];
return { ctx.Def(phi->Arg(phi_arg)), parent };
});
}
} // Anonymous namespace
std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program, Bindings& bindings) {