diff --git a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt
index c6c97e762..75c97b101 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt
@@ -272,6 +272,12 @@ object NativeLibrary {
canContinue = false
}
+ CoreError.ErrorN3DSApplication -> {
+ title = emulationActivity.getString(R.string.invalid_system_mode)
+ message = emulationActivity.getString(R.string.invalid_system_mode_message)
+ canContinue = false
+ }
+
CoreError.ErrorUnknown -> {
title = emulationActivity.getString(R.string.fatal_error)
message = emulationActivity.getString(R.string.fatal_error_message)
@@ -729,6 +735,7 @@ object NativeLibrary {
ErrorSystemFiles,
ErrorSavestate,
ErrorArticDisconnected,
+ ErrorN3DSApplication,
ErrorUnknown
}
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index a2880d6dc..e52c09a65 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -95,6 +95,7 @@ static jobject ToJavaCoreError(Core::System::ResultStatus result) {
{Core::System::ResultStatus::ErrorSystemFiles, "ErrorSystemFiles"},
{Core::System::ResultStatus::ErrorSavestate, "ErrorSavestate"},
{Core::System::ResultStatus::ErrorArticDisconnected, "ErrorArticDisconnected"},
+ {Core::System::ResultStatus::ErrorN3DSApplication, "ErrorN3DSApplication"},
{Core::System::ResultStatus::ErrorUnknown, "ErrorUnknown"},
};
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 8d40452ee..d3e5d958d 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -559,6 +559,8 @@
Fatal Error
A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.
Unsupported encrypted application
+ Invalid system mode
+ New 3DS exclusive applications cannot be loaded without enabling the New 3DS mode.
Preparing Shaders
diff --git a/src/citra_qt/citra_qt.cpp b/src/citra_qt/citra_qt.cpp
index c15811f99..6398d1f22 100644
--- a/src/citra_qt/citra_qt.cpp
+++ b/src/citra_qt/citra_qt.cpp
@@ -1313,6 +1313,11 @@ bool GMainWindow::LoadROM(const QString& filename) {
system.GetStatusDetails())
.c_str()));
break;
+ case Core::System::ResultStatus::ErrorN3DSApplication:
+ QMessageBox::critical(this, tr("Invalid system mode"),
+ tr("New 3DS exclusive applications cannot be loaded without "
+ "enabling the New 3DS mode."));
+ break;
default:
QMessageBox::critical(
this, tr("Error while loading App!"),
diff --git a/src/core/core.cpp b/src/core/core.cpp
index ee338232d..e960571a7 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -27,6 +27,7 @@
#include "core/frontend/image_interface.h"
#include "core/gdbstub/gdbstub.h"
#include "core/global.h"
+#include "core/hle/kernel/ipc_debugger/recorder.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
@@ -317,49 +318,110 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
}
}
- auto memory_mode = app_loader->LoadKernelMemoryMode();
- if (memory_mode.second != Loader::ResultStatus::Success) {
- LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!",
- static_cast(memory_mode.second));
+ Kernel::MemoryMode app_mem_mode;
+ Kernel::MemoryMode system_mem_mode;
+ bool used_default_mem_mode = false;
+ Kernel::New3dsHwCapabilities app_n3ds_hw_capabilities;
- switch (memory_mode.second) {
- case Loader::ResultStatus::ErrorEncrypted:
- return ResultStatus::ErrorLoader_ErrorEncrypted;
- case Loader::ResultStatus::ErrorInvalidFormat:
- return ResultStatus::ErrorLoader_ErrorInvalidFormat;
- case Loader::ResultStatus::ErrorGbaTitle:
- return ResultStatus::ErrorLoader_ErrorGbaTitle;
- case Loader::ResultStatus::ErrorArtic:
- return ResultStatus::ErrorArticDisconnected;
- default:
- return ResultStatus::ErrorSystemMode;
+ if (m_mem_mode) {
+ // Use memory mode set by the FIRM launch parameters
+ system_mem_mode = static_cast(m_mem_mode.value());
+ m_mem_mode = {};
+ } else {
+ // Use default memory mode based on the n3ds setting
+ system_mem_mode = Settings::values.is_new_3ds.GetValue() ? Kernel::MemoryMode::NewProd
+ : Kernel::MemoryMode::Prod;
+ used_default_mem_mode = true;
+ }
+
+ {
+ auto memory_mode = app_loader->LoadKernelMemoryMode();
+ if (memory_mode.second != Loader::ResultStatus::Success) {
+ LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!",
+ static_cast(memory_mode.second));
+
+ switch (memory_mode.second) {
+ case Loader::ResultStatus::ErrorEncrypted:
+ return ResultStatus::ErrorLoader_ErrorEncrypted;
+ case Loader::ResultStatus::ErrorInvalidFormat:
+ return ResultStatus::ErrorLoader_ErrorInvalidFormat;
+ case Loader::ResultStatus::ErrorGbaTitle:
+ return ResultStatus::ErrorLoader_ErrorGbaTitle;
+ case Loader::ResultStatus::ErrorArtic:
+ return ResultStatus::ErrorArticDisconnected;
+ default:
+ return ResultStatus::ErrorSystemMode;
+ }
+ }
+
+ ASSERT(memory_mode.first);
+ app_mem_mode = memory_mode.first.value();
+ }
+
+ auto n3ds_hw_caps = app_loader->LoadNew3dsHwCapabilities();
+ ASSERT(n3ds_hw_caps.first);
+ app_n3ds_hw_capabilities = n3ds_hw_caps.first.value();
+
+ if (!Settings::values.is_new_3ds.GetValue() &&
+ app_n3ds_hw_capabilities.memory_mode != Kernel::New3dsMemoryMode::Legacy) {
+ return ResultStatus::ErrorN3DSApplication;
+ }
+
+ // If the default mem mode has been used, we do not come from a FIRM launch. On real HW
+ // however, the home menu is in charge or setting the proper memory mode when launching
+ // applications by doing a FIRM launch. Since we launch the application without going
+ // through the home menu, we need to emulate the FIRM launch having happened and set the
+ // proper memory mode.
+ if (used_default_mem_mode) {
+
+ // If we are on the Old 3DS prod mode, the application is not a New 3DS application and
+ // the application memory mode does not match, we need to adjust it. We do not need
+ // adjustment if we are on the New 3DS prod mode, as that one overrides all the Old 3DS
+ // memory modes.
+ if (system_mem_mode == Kernel::MemoryMode::Prod &&
+ app_n3ds_hw_capabilities.memory_mode == Kernel::New3dsMemoryMode::Legacy &&
+ app_mem_mode != system_mem_mode) {
+
+ system_mem_mode = app_mem_mode;
+ }
+
+ // If we are on the New 3DS prod mode, and the application needs the New 3DS extended
+ // memory mode (only CTRAging is known to do this), adjust the memory mode.
+ else if (system_mem_mode == Kernel::MemoryMode::NewProd &&
+ app_n3ds_hw_capabilities.memory_mode == Kernel::New3dsMemoryMode::NewDev1) {
+
+ system_mem_mode = Kernel::MemoryMode::NewDev1;
}
}
- ASSERT(memory_mode.first);
- auto n3ds_hw_caps = app_loader->LoadNew3dsHwCapabilities();
- ASSERT(n3ds_hw_caps.first);
u32 num_cores = 2;
if (Settings::values.is_new_3ds) {
num_cores = 4;
}
- ResultStatus init_result{
- Init(emu_window, secondary_window, *memory_mode.first, *n3ds_hw_caps.first, num_cores)};
+ ResultStatus init_result{Init(emu_window, secondary_window, system_mem_mode, num_cores)};
if (init_result != ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
static_cast(init_result));
System::Shutdown();
return init_result;
}
+
+ kernel->UpdateCPUAndMemoryState(program_id, app_mem_mode, app_n3ds_hw_capabilities);
+
gpu->ReportLoadingProgramID(program_id);
// Restore any parameters that should be carried through a reset.
- if (restore_deliver_arg.has_value()) {
- if (auto apt = Service::APT::GetModule(*this)) {
+ if (auto apt = Service::APT::GetModule(*this)) {
+ if (restore_deliver_arg.has_value()) {
apt->GetAppletManager()->SetDeliverArg(restore_deliver_arg);
+ restore_deliver_arg.reset();
+ }
+ if (restore_sys_menu_arg.has_value()) {
+ apt->GetAppletManager()->SetSysMenuArg(restore_sys_menu_arg.value());
+ restore_sys_menu_arg.reset();
}
- restore_deliver_arg.reset();
}
+
if (restore_plugin_context.has_value()) {
if (auto plg_ldr = Service::PLGLDR::GetService(*this)) {
plg_ldr->SetPluginLoaderContext(restore_plugin_context.value());
@@ -367,6 +429,10 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
restore_plugin_context.reset();
}
+ if (restore_ipc_recorder) {
+ kernel->RestoreIPCRecorder(std::move(restore_ipc_recorder));
+ }
+
std::shared_ptr process;
const Loader::ResultStatus load_result{app_loader->Load(process)};
if (Loader::ResultStatus::Success != load_result) {
@@ -449,8 +515,7 @@ void System::Reschedule() {
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window,
Frontend::EmuWindow* secondary_window,
- Kernel::MemoryMode memory_mode,
- const Kernel::New3dsHwCapabilities& n3ds_hw_caps, u32 num_cores) {
+ Kernel::MemoryMode memory_mode, u32 num_cores) {
LOG_DEBUG(HW_Memory, "initialized OK");
memory = std::make_unique(*this);
@@ -459,7 +524,7 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window,
movie.GetOverrideBaseTicks());
kernel = std::make_unique(
- *memory, *timing, [this] { PrepareReschedule(); }, memory_mode, num_cores, n3ds_hw_caps,
+ *memory, *timing, [this] { PrepareReschedule(); }, memory_mode, num_cores,
movie.GetOverrideInitTime());
exclusive_monitor = MakeExclusiveMonitor(*memory, num_cores);
@@ -678,11 +743,14 @@ void System::Reset() {
// This is needed as we don't currently support proper app jumping.
if (auto apt = Service::APT::GetModule(*this)) {
restore_deliver_arg = apt->GetAppletManager()->ReceiveDeliverArg();
+ restore_sys_menu_arg = apt->GetAppletManager()->GetSysMenuArg();
}
if (auto plg_ldr = Service::PLGLDR::GetService(*this)) {
restore_plugin_context = plg_ldr->GetPluginLoaderContext();
}
+ restore_ipc_recorder = std::move(kernel->BackupIPCRecorder());
+
Shutdown();
if (!m_chainloadpath.empty()) {
@@ -775,17 +843,19 @@ void System::serialize(Archive& ar, const unsigned int file_version) {
}
ar & lle_modules;
+ Kernel::MemoryMode mem_mode{};
+ if (!Archive::is_loading::value) {
+ mem_mode = kernel->GetMemoryMode();
+ }
+ ar & mem_mode;
if (Archive::is_loading::value) {
// When loading, we want to make sure any lingering state gets cleared out before we begin.
// Shutdown, but persist a few things between loads...
Shutdown(true);
- // Re-initialize everything like it was before
- auto memory_mode = this->app_loader->LoadKernelMemoryMode();
- auto n3ds_hw_caps = this->app_loader->LoadNew3dsHwCapabilities();
- [[maybe_unused]] const System::ResultStatus result = Init(
- *m_emu_window, m_secondary_window, *memory_mode.first, *n3ds_hw_caps.first, num_cores);
+ [[maybe_unused]] const System::ResultStatus result =
+ Init(*m_emu_window, m_secondary_window, mem_mode, num_cores);
}
// Flush on save, don't flush on load
diff --git a/src/core/core.h b/src/core/core.h
index 4ed8ff326..122401eed 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -101,6 +101,7 @@ public:
ErrorSystemFiles, ///< Error in finding system files
ErrorSavestate, ///< Error saving or loading
ErrorArticDisconnected, ///< Error when artic base disconnects
+ ErrorN3DSApplication, ///< Error launching New 3DS application in Old 3DS mode
ShutdownRequested, ///< Emulated program requested a system shutdown
ErrorUnknown ///< Any other error
};
@@ -137,8 +138,9 @@ public:
bool SendSignal(Signal signal, u32 param = 0);
/// Request reset of the system
- void RequestReset(const std::string& chainload = "") {
+ void RequestReset(const std::string& chainload = "", std::optional mem_mode = {}) {
m_chainloadpath = chainload;
+ m_mem_mode = mem_mode;
SendSignal(Signal::Reset);
}
@@ -386,9 +388,7 @@ private:
*/
[[nodiscard]] ResultStatus Init(Frontend::EmuWindow& emu_window,
Frontend::EmuWindow* secondary_window,
- Kernel::MemoryMode memory_mode,
- const Kernel::New3dsHwCapabilities& n3ds_hw_caps,
- u32 num_cores);
+ Kernel::MemoryMode memory_mode, u32 num_cores);
/// Reschedule the core emulation
void Reschedule();
@@ -463,6 +463,7 @@ private:
Frontend::EmuWindow* m_secondary_window;
std::string m_filepath;
std::string m_chainloadpath;
+ std::optional m_mem_mode;
u64 title_id;
bool self_delete_pending;
@@ -474,7 +475,9 @@ private:
bool mic_permission_granted = false;
boost::optional restore_deliver_arg;
+ boost::optional restore_sys_menu_arg;
boost::optional restore_plugin_context;
+ std::unique_ptr restore_ipc_recorder;
std::vector lle_modules;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index c722b8e30..d49bcb2d1 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -26,14 +26,13 @@ namespace Kernel {
/// Initialize the kernel
KernelSystem::KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing,
std::function prepare_reschedule_callback,
- MemoryMode memory_mode, u32 num_cores,
- const New3dsHwCapabilities& n3ds_hw_caps, u64 override_init_time)
+ MemoryMode memory_mode, u32 num_cores, u64 override_init_time)
: memory(memory), timing(timing),
- prepare_reschedule_callback(std::move(prepare_reschedule_callback)), memory_mode(memory_mode),
- n3ds_hw_caps(n3ds_hw_caps) {
+ prepare_reschedule_callback(std::move(prepare_reschedule_callback)),
+ memory_mode(memory_mode) {
std::generate(memory_regions.begin(), memory_regions.end(),
[] { return std::make_shared(); });
- MemoryInit(memory_mode, n3ds_hw_caps.memory_mode, override_init_time);
+ MemoryInit(memory_mode, override_init_time);
resource_limits = std::make_unique(*this);
for (u32 core_id = 0; core_id < num_cores; ++core_id) {
@@ -151,6 +150,14 @@ const IPCDebugger::Recorder& KernelSystem::GetIPCRecorder() const {
return *ipc_recorder;
}
+std::unique_ptr KernelSystem::BackupIPCRecorder() {
+ return std::move(ipc_recorder);
+}
+
+void KernelSystem::RestoreIPCRecorder(std::unique_ptr recorder) {
+ ipc_recorder = std::move(recorder);
+}
+
void KernelSystem::AddNamedPort(std::string name, std::shared_ptr port) {
named_ports.emplace(std::move(name), std::move(port));
}
@@ -163,6 +170,35 @@ void KernelSystem::ResetThreadIDs() {
next_thread_id = 0;
}
+void KernelSystem::UpdateCPUAndMemoryState(u64 title_id, MemoryMode memory_mode,
+ New3dsHwCapabilities n3ds_hw_cap) {
+ SetRunning804MHz(n3ds_hw_cap.enable_804MHz_cpu);
+
+ u32 tid_high = static_cast(title_id >> 32);
+
+ constexpr u32 TID_HIGH_APPLET = 0x00040030;
+ constexpr u32 TID_HIGH_SYSMODULE = 0x00040130;
+
+ // PM only updates the reported memory for normal applications.
+ // TODO(PabloMK7): Using the title ID is not correct, but close enough.
+ if (tid_high != TID_HIGH_APPLET && tid_high != TID_HIGH_SYSMODULE) {
+ UpdateReportedMemory(memory_mode, n3ds_hw_cap.memory_mode);
+ }
+}
+
+void KernelSystem::RestoreMemoryState(u64 title_id) {
+ u32 tid_high = static_cast(title_id >> 32);
+
+ constexpr u32 TID_HIGH_APPLET = 0x00040030;
+ constexpr u32 TID_HIGH_SYSMODULE = 0x00040130;
+
+ // PM only updates the reported memory for normal applications.
+ // TODO(PabloMK7): Using the title ID is not correct, but close enough.
+ if (tid_high != TID_HIGH_APPLET && tid_high != TID_HIGH_SYSMODULE) {
+ RestoreReportedMemory();
+ }
+}
+
template
void KernelSystem::serialize(Archive& ar, const unsigned int) {
ar & memory_regions;
@@ -184,7 +220,7 @@ void KernelSystem::serialize(Archive& ar, const unsigned int) {
ar & stored_processes;
ar & next_thread_id;
ar & memory_mode;
- ar & n3ds_hw_caps;
+ ar & running_804MHz;
ar & main_thread_extended_sleep;
// Deliberately don't include debugger info to allow debugging through loads
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index d3b0bc53e..53d88cddf 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -111,6 +111,9 @@ enum class MemoryMode : u8 {
Dev2 = 3, ///< 80MB app memory
Dev3 = 4, ///< 72MB app memory
Dev4 = 5, ///< 32MB app memory
+
+ NewProd = 6, ///< 124MB app memory
+ NewDev1 = 7, ///< 178MB app memory
};
/// New 3DS memory modes.
@@ -137,8 +140,7 @@ class KernelSystem {
public:
explicit KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing,
std::function prepare_reschedule_callback, MemoryMode memory_mode,
- u32 num_cores, const New3dsHwCapabilities& n3ds_hw_caps,
- u64 override_init_time = 0);
+ u32 num_cores, u64 override_init_time = 0);
~KernelSystem();
using PortPair = std::pair, std::shared_ptr>;
@@ -305,6 +307,8 @@ public:
IPCDebugger::Recorder& GetIPCRecorder();
const IPCDebugger::Recorder& GetIPCRecorder() const;
+ std::unique_ptr BackupIPCRecorder();
+ void RestoreIPCRecorder(std::unique_ptr recorder);
std::shared_ptr GetMemoryRegion(MemoryRegion region);
@@ -327,8 +331,12 @@ public:
return memory_mode;
}
- const New3dsHwCapabilities& GetNew3dsHwCapabilities() const {
- return n3ds_hw_caps;
+ void SetRunning804MHz(bool enable) {
+ running_804MHz = enable;
+ }
+
+ bool GetRunning804MHz() const {
+ return running_804MHz;
}
std::recursive_mutex& GetHLELock() {
@@ -365,8 +373,16 @@ public:
return pending_async_operations != 0;
}
+ void UpdateCPUAndMemoryState(u64 title_id, MemoryMode memory_mode,
+ New3dsHwCapabilities n3ds_hw_cap);
+
+ void RestoreMemoryState(u64 title_id);
+
private:
- void MemoryInit(MemoryMode memory_mode, New3dsMemoryMode n3ds_mode, u64 override_init_time);
+ void MemoryInit(MemoryMode memory_mode, u64 override_init_time);
+
+ void UpdateReportedMemory(MemoryMode memory_mode, New3dsMemoryMode n3ds_mode);
+ void RestoreReportedMemory();
std::function prepare_reschedule_callback;
@@ -404,7 +420,7 @@ private:
u32 next_thread_id;
MemoryMode memory_mode;
- New3dsHwCapabilities n3ds_hw_caps;
+ bool running_804MHz = false;
/*
* Synchronizes access to the internal HLE kernel structures, it is acquired when a guest
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp
index fffa8dd6f..362e88ac5 100644
--- a/src/core/hle/kernel/memory.cpp
+++ b/src/core/hle/kernel/memory.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -41,23 +41,9 @@ static const u32 memory_region_sizes[8][3] = {
{0x0B200000, 0x02E00000, 0x02000000}, // 7
};
-void KernelSystem::MemoryInit(MemoryMode memory_mode, New3dsMemoryMode n3ds_mode,
- u64 override_init_time) {
+void KernelSystem::MemoryInit(MemoryMode memory_mode, u64 override_init_time) {
const bool is_new_3ds = Settings::values.is_new_3ds.GetValue();
- u32 mem_type_index = static_cast(memory_mode);
- u32 reported_mem_type = static_cast(memory_mode);
- if (is_new_3ds) {
- if (n3ds_mode == New3dsMemoryMode::NewProd || n3ds_mode == New3dsMemoryMode::NewDev2) {
- mem_type_index = 6;
- reported_mem_type = 6;
- } else if (n3ds_mode == New3dsMemoryMode::NewDev1) {
- mem_type_index = 7;
- reported_mem_type = 7;
- } else {
- // On the N3ds, all O3ds configurations (<=5) are forced to 6 instead.
- mem_type_index = 6;
- }
- }
+ const u32 mem_type_index = static_cast(memory_mode);
// The kernel allocation regions (APPLICATION, SYSTEM and BASE) are laid out in sequence, with
// the sizes specified in the memory_region_sizes table.
@@ -73,14 +59,41 @@ void KernelSystem::MemoryInit(MemoryMode memory_mode, New3dsMemoryMode n3ds_mode
config_mem_handler = std::make_shared();
auto& config_mem = config_mem_handler->GetConfigMem();
- config_mem.app_mem_type = reported_mem_type;
- config_mem.app_mem_alloc = memory_region_sizes[reported_mem_type][0];
+ config_mem.app_mem_type = static_cast(memory_mode);
+ config_mem.app_mem_alloc = memory_regions[0]->size;
config_mem.sys_mem_alloc = memory_regions[1]->size;
config_mem.base_mem_alloc = memory_regions[2]->size;
shared_page_handler = std::make_shared(timing, override_init_time);
}
+void KernelSystem::UpdateReportedMemory(MemoryMode memory_mode, New3dsMemoryMode n3ds_mode) {
+ // If we are in New 3DS prod memory mode, and the application n3ds memory mode is set to legacy
+ // (all Old 3DS applications), then update reported available memory to the proper one according
+ // to the memory mode.
+ // This is normally done by PM when launching applications using svcSetResourceLimitLimitValues,
+ // but we do not implement that.
+ if (GetMemoryMode() == Kernel::MemoryMode::NewProd &&
+ n3ds_mode == Kernel::New3dsMemoryMode::Legacy) {
+ const u32 mem_type_index = static_cast(memory_mode);
+ auto& config_mem = config_mem_handler->GetConfigMem();
+ config_mem.app_mem_alloc = memory_region_sizes[mem_type_index][0];
+ }
+}
+
+void KernelSystem::RestoreReportedMemory() {
+ // If we are on New 3DS prod memory mode and we have terminated a process, restore the available
+ // memory to the proper size.
+ // This is normally done by PM when the application ends using svcSetResourceLimitLimitValues,
+ // but we do not implement that.
+ auto mem_mode = GetMemoryMode();
+ if (mem_mode == Kernel::MemoryMode::NewProd) {
+ const u32 mem_type_index = static_cast(mem_mode);
+ auto& config_mem = config_mem_handler->GetConfigMem();
+ config_mem.app_mem_alloc = memory_region_sizes[mem_type_index][0];
+ }
+}
+
std::shared_ptr KernelSystem::GetMemoryRegion(MemoryRegion region) {
switch (region) {
case MemoryRegion::APPLICATION:
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index e4200e5df..5e0d3810f 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -124,6 +124,8 @@ void KernelSystem::TerminateProcess(std::shared_ptr process) {
GetThreadManager(core).TerminateProcessThreads(process);
}
+ RestoreMemoryState(process->codeset->program_id);
+
process->Exit();
std::erase(process_list, process);
}
diff --git a/src/core/hle/service/apt/applet_manager.cpp b/src/core/hle/service/apt/applet_manager.cpp
index 2d21f9ae8..47c0792c6 100644
--- a/src/core/hle/service/apt/applet_manager.cpp
+++ b/src/core/hle/service/apt/applet_manager.cpp
@@ -122,6 +122,37 @@ static u64 GetTitleIdForApplet(AppletId id, u32 region_value) {
return itr->title_ids[region_value];
}
+static constexpr std::size_t NumTitleIDConverts = 3;
+static constexpr std::array, NumTitleIDConverts> TitleIDConvertTable = {{
+ // MSET
+ {{0x0004001000020000, 0x0004001000021000, 0x0004001000022000, 0x0004001000020000,
+ 0x0004001000026000, 0x0004001000027000, 0x0004001000028000}},
+ // eShop
+ {{0x0004001000020900, 0x0004001000021900, 0x0004001000022900, 0x0004001000020900,
+ 0x0004001000020900, 0x0004001000027900, 0x0004001000028900}},
+ // NNID Settings
+ {{0x000400100002BF00, 0x000400100002C000, 0x000400100002C100, 0x000400100002BF00,
+ 0x000400100002BF00, 0x000400100002BF00, 0x000400100002BF00}},
+}};
+
+static u64 ConvertTitleID(Core::System& system, u64 base_title_id) {
+
+ auto cfg = Service::CFG::GetModule(system);
+ if (!cfg) {
+ return base_title_id;
+ }
+
+ u32 region_value = cfg->GetRegionValue(false);
+
+ for (auto& entry : TitleIDConvertTable) {
+ if (base_title_id == entry[0]) {
+ return entry[region_value];
+ }
+ }
+
+ return base_title_id;
+}
+
static bool IsSystemAppletId(AppletId applet_id) {
return (static_cast(applet_id) & static_cast(AppletId::AnySystemApplet)) != 0;
}
@@ -1013,6 +1044,22 @@ Result AppletManager::LeaveHomeMenu(std::shared_ptr object,
return ResultSuccess;
}
+Result AppletManager::LoadSysMenuArg(std::vector& buffer) {
+ if (sys_menu_arg.has_value()) {
+ std::memcpy(buffer.data(), sys_menu_arg.value().data(),
+ std::min(buffer.size(), sys_menu_arg.value().size()));
+ }
+ // Always succeed, even if there is no data to copy.
+ return ResultSuccess;
+}
+
+Result AppletManager::StoreSysMenuArg(const std::vector& buffer) {
+ sys_menu_arg = std::array();
+ std::memcpy(sys_menu_arg.value().data(), buffer.data(),
+ std::min(buffer.size(), sys_menu_arg.value().size()));
+ return ResultSuccess;
+}
+
Result AppletManager::OrderToCloseApplication() {
if (active_slot == AppletSlot::Error) {
return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
@@ -1213,8 +1260,8 @@ ApplicationRunningMode AppletManager::GetApplicationRunningMode() {
// APT checks whether the system is a New 3DS and the 804MHz CPU speed is enabled to determine
// the result.
- auto new_3ds_mode = GetTargetPlatform() == TargetPlatform::New3ds &&
- system.Kernel().GetNew3dsHwCapabilities().enable_804MHz_cpu;
+ auto new_3ds_mode =
+ GetTargetPlatform() == TargetPlatform::New3ds && system.Kernel().GetRunning804MHz();
if (slot_data->registered) {
return new_3ds_mode ? ApplicationRunningMode::New3dsRegistered
: ApplicationRunningMode::Old3dsRegistered;
@@ -1242,7 +1289,7 @@ Result AppletManager::PrepareToDoApplicationJump(u64 title_id, FS::MediaType med
app_jump_parameters.next_title_id = app_jump_parameters.current_title_id;
app_jump_parameters.next_media_type = app_jump_parameters.current_media_type;
} else {
- app_jump_parameters.next_title_id = title_id;
+ app_jump_parameters.next_title_id = ConvertTitleID(system, title_id);
app_jump_parameters.next_media_type = media_type;
}
app_jump_parameters.flags = flags;
@@ -1301,7 +1348,7 @@ Result AppletManager::DoApplicationJump(const DeliverArg& arg) {
*/
NS::RebootToTitle(system, app_jump_parameters.next_media_type,
- app_jump_parameters.next_title_id);
+ app_jump_parameters.next_title_id, std::nullopt);
return ResultSuccess;
}
}
@@ -1320,6 +1367,53 @@ Result AppletManager::PrepareToStartApplication(u64 title_id, FS::MediaType medi
ErrorLevel::Status};
}
+ title_id = ConvertTitleID(system, title_id);
+
+ std::string path = AM::GetTitleContentPath(media_type, title_id);
+ auto loader = Loader::GetLoader(path);
+
+ if (!loader) {
+ LOG_ERROR(Service_APT, "Could not find .app for title 0x{:016x}", title_id);
+ // TODO: Find proper error code
+ return ResultUnknown;
+ }
+
+ auto plg_ldr = Service::PLGLDR::GetService(system);
+ if (plg_ldr) {
+ const auto& plg_context = plg_ldr->GetPluginLoaderContext();
+ if (plg_context.is_enabled && plg_context.use_user_load_parameters &&
+ plg_context.user_load_parameters.low_title_Id == static_cast(title_id) &&
+ plg_context.user_load_parameters.plugin_memory_strategy ==
+ PLGLDR::PLG_LDR::PluginMemoryStrategy::PLG_STRATEGY_MODE3) {
+ loader->SetKernelMemoryModeOverride(Kernel::MemoryMode::Dev2);
+ }
+ }
+
+ auto mem_mode = loader->LoadKernelMemoryMode();
+ if (mem_mode.second != Loader::ResultStatus::Success || !mem_mode.first.has_value()) {
+ // This cannot happen on real HW at this point of execution
+ LOG_ERROR(Service_APT, "Could not determine memory mode");
+ return ResultUnknown;
+ }
+
+ auto curr_mem_mode = system.Kernel().GetMemoryMode();
+
+ if (mem_mode.first.value() != curr_mem_mode) {
+ if (system.Kernel().GetMemoryMode() == Kernel::MemoryMode::NewProd) {
+ // On New 3DS prod memory mode, only incorrect state is if the app
+ // reports having the "unused" memory mode 1. TODO: Figure out
+ // how this works and if it is even used.
+ if (mem_mode.first.value() == static_cast(1)) {
+ return {ErrCodes::IncorrectMemoryMode, ErrorModule::Applet,
+ ErrorSummary::InvalidState, ErrorLevel::Status};
+ }
+ } else {
+ // On other memory modes, the state is incorrect.
+ return {ErrCodes::IncorrectMemoryMode, ErrorModule::Applet, ErrorSummary::InvalidState,
+ ErrorLevel::Status};
+ }
+ }
+
ASSERT_MSG(!app_start_parameters,
"Trying to prepare an application when another is already prepared");
@@ -1403,6 +1497,44 @@ Result AppletManager::CancelApplication() {
return ResultSuccess;
}
+Result AppletManager::PrepareToStartNewestHomeMenu() {
+ if (active_slot == AppletSlot::Error ||
+ GetAppletSlot(active_slot)->attributes.applet_pos != AppletPos::System) {
+ return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState,
+ ErrorLevel::Status};
+ }
+
+ bool is_standard;
+ if (Settings::values.is_new_3ds) {
+ // Memory layout is standard if it is not NewDev1 (178MB)
+ is_standard = system.Kernel().GetMemoryMode() != Kernel::MemoryMode::NewDev1;
+ } else {
+ // Memory layout is standard if it is Prod (64MB)
+ is_standard = system.Kernel().GetMemoryMode() == Kernel::MemoryMode::Prod;
+ }
+
+ if (is_standard) {
+ return Result{ErrorDescription::AlreadyExists, ErrorModule::Applet,
+ ErrorSummary::InvalidState, ErrorLevel::Status};
+ }
+
+ home_menu_tid_to_start = GetAppletSlot(active_slot)->title_id;
+ return ResultSuccess;
+}
+
+Result AppletManager::StartNewestHomeMenu() {
+ if (!home_menu_tid_to_start) {
+ return Result{ErrorDescription::AlreadyExists, ErrorModule::Applet,
+ ErrorSummary::InvalidState, ErrorLevel::Status};
+ }
+
+ u64 titleID = home_menu_tid_to_start;
+ home_menu_tid_to_start = 0;
+
+ NS::RebootToTitle(system, Service::FS::MediaType::NAND, titleID, std::nullopt);
+ return ResultSuccess;
+}
+
void AppletManager::SendApplicationParameterAfterRegistration(const MessageParameter& parameter) {
auto slot = GetAppletSlotFromId(parameter.destination_id);
diff --git a/src/core/hle/service/apt/applet_manager.h b/src/core/hle/service/apt/applet_manager.h
index 5aaf7fdac..d4d99895b 100644
--- a/src/core/hle/service/apt/applet_manager.h
+++ b/src/core/hle/service/apt/applet_manager.h
@@ -1,4 +1,4 @@
-// Copyright 2018 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -118,6 +118,8 @@ enum class ApplicationRunningMode : u8 {
New3dsUnregistered = 4,
};
+constexpr std::size_t SysMenuArgSize = 0x40;
+
/// Holds information about the parameters used in Send/Glance/ReceiveParameter
struct MessageParameter {
AppletId sender_id = AppletId::None;
@@ -180,6 +182,8 @@ private:
friend class boost::serialization::access;
};
+using SysMenuArg = std::array;
+
struct ApplicationJumpParameters {
u64 next_title_id;
FS::MediaType next_media_type;
@@ -322,6 +326,16 @@ public:
Result PrepareToLeaveHomeMenu();
Result LeaveHomeMenu(std::shared_ptr object, const std::vector& buffer);
+ Result LoadSysMenuArg(std::vector& buffer);
+ Result StoreSysMenuArg(const std::vector& buffer);
+
+ boost::optional GetSysMenuArg() {
+ return sys_menu_arg;
+ }
+ void SetSysMenuArg(const SysMenuArg& arg) {
+ sys_menu_arg = arg;
+ }
+
Result OrderToCloseApplication();
Result PrepareToCloseApplication(bool return_to_sys);
Result CloseApplication(std::shared_ptr object, const std::vector& buffer);
@@ -376,6 +390,9 @@ public:
Result WakeupApplication(std::shared_ptr object, const std::vector& buffer);
Result CancelApplication();
+ Result PrepareToStartNewestHomeMenu();
+ Result StartNewestHomeMenu();
+
struct AppletManInfo {
AppletPos active_applet_pos;
AppletId requested_applet_id;
@@ -417,6 +434,8 @@ private:
ApplicationJumpParameters app_jump_parameters{};
boost::optional app_start_parameters{};
boost::optional deliver_arg{};
+ boost::optional sys_menu_arg{};
+ u64 home_menu_tid_to_start{};
boost::optional capture_info;
boost::optional capture_buffer_info;
@@ -532,6 +551,8 @@ private:
ar & delayed_parameter;
ar & app_start_parameters;
ar & deliver_arg;
+ ar & sys_menu_arg;
+ ar & home_menu_tid_to_start;
ar & capture_info;
ar & capture_buffer_info;
ar & active_slot;
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index 002bd3728..8e34bb0b7 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -92,16 +92,15 @@ void Module::NSInterface::RebootSystem(Kernel::HLERequestContext& ctx) {
const auto title_id = rp.Pop();
const auto media_type = static_cast(rp.Pop());
rp.Skip(1, false); // Skip padding
- // TODO: Utilize requested memory type.
const auto mem_type = rp.Pop();
LOG_WARNING(Service_APT,
"called launch_title={}, title_id={:016X}, media_type={:02X}, mem_type={:02X}",
launch_title, title_id, media_type, mem_type);
- // TODO: Handle mem type.
if (launch_title) {
- NS::RebootToTitle(apt->system, media_type, title_id);
+ NS::RebootToTitle(apt->system, media_type, title_id,
+ static_cast(mem_type));
} else {
apt->system.RequestReset();
}
@@ -763,16 +762,12 @@ void Module::APTInterface::PrepareToStartSystemApplet(Kernel::HLERequestContext&
void Module::APTInterface::PrepareToStartNewestHomeMenu(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
+
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
LOG_DEBUG(Service_APT, "called");
- // TODO(Subv): This command can only be called by a System Applet (return 0xC8A0CC04 otherwise).
-
- // This command must return an error when called, otherwise the Home Menu will try to reboot the
- // system.
- rb.Push(Result(ErrorDescription::AlreadyExists, ErrorModule::Applet, ErrorSummary::InvalidState,
- ErrorLevel::Status));
+ rb.Push(apt->applet_manager->PrepareToStartNewestHomeMenu());
}
void Module::APTInterface::PreloadLibraryApplet(Kernel::HLERequestContext& ctx) {
@@ -821,6 +816,19 @@ void Module::APTInterface::StartSystemApplet(Kernel::HLERequestContext& ctx) {
rb.Push(apt->applet_manager->StartSystemApplet(applet_id, object, buffer));
}
+void Module::APTInterface::StartNewestHomeMenu(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp(ctx);
+
+ const auto buffer_size = rp.Pop();
+ [[maybe_unused]] const auto object = rp.PopGenericObject();
+ [[maybe_unused]] const auto buffer = rp.PopStaticBuffer();
+
+ LOG_DEBUG(Service_APT, "called, size={:08X}", buffer_size);
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(apt->applet_manager->StartNewestHomeMenu());
+}
+
void Module::APTInterface::OrderToCloseApplication(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
@@ -1011,10 +1019,10 @@ void Module::APTInterface::LoadSysMenuArg(Kernel::HLERequestContext& ctx) {
// This service function does not clear the buffer.
std::vector buffer(size);
- std::copy_n(apt->sys_menu_arg_buffer.cbegin(), size, buffer.begin());
+ Result res = apt->applet_manager->LoadSysMenuArg(buffer);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
- rb.Push(ResultSuccess);
+ rb.Push(res);
rb.PushStaticBuffer(std::move(buffer), 0);
}
@@ -1026,10 +1034,10 @@ void Module::APTInterface::StoreSysMenuArg(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_APT, "called");
ASSERT_MSG(buffer.size() >= size, "Buffer too small to hold requested data");
- std::copy_n(buffer.cbegin(), size, apt->sys_menu_arg_buffer.begin());
+ Result res = apt->applet_manager->StoreSysMenuArg(buffer);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
- rb.Push(ResultSuccess);
+ rb.Push(res);
}
void Module::APTInterface::SendCaptureBufferInfo(Kernel::HLERequestContext& ctx) {
@@ -1365,8 +1373,8 @@ void Module::APTInterface::Reboot(Kernel::HLERequestContext& ctx) {
"called title_id={:016X}, media_type={:02X}, mem_type={:02X}, firm_tid_low={:08X}",
title_id, media_type, mem_type, firm_tid_low);
- // TODO: Handle mem type and FIRM TID low.
- NS::RebootToTitle(apt->system, media_type, title_id);
+ // TODO: Handle FIRM TID low.
+ NS::RebootToTitle(apt->system, media_type, title_id, static_cast(mem_type));
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
@@ -1420,8 +1428,7 @@ void Module::APTInterface::IsStandardMemoryLayout(Kernel::HLERequestContext& ctx
bool is_standard;
if (Settings::values.is_new_3ds) {
// Memory layout is standard if it is not NewDev1 (178MB)
- is_standard = apt->system.Kernel().GetNew3dsHwCapabilities().memory_mode !=
- Kernel::New3dsMemoryMode::NewDev1;
+ is_standard = apt->system.Kernel().GetMemoryMode() != Kernel::MemoryMode::NewDev1;
} else {
// Memory layout is standard if it is Prod (64MB)
is_standard = apt->system.Kernel().GetMemoryMode() == Kernel::MemoryMode::Prod;
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
index 8e20a788b..9676a94f0 100644
--- a/src/core/hle/service/apt/apt.h
+++ b/src/core/hle/service/apt/apt.h
@@ -1,4 +1,4 @@
-// Copyright 2015 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -31,8 +31,6 @@ class AppletManager;
/// Each APT service can only have up to 2 sessions connected at the same time.
static const u32 MaxAPTSessions = 2;
-constexpr std::size_t SysMenuArgSize = 0x40;
-
enum class StartupArgumentType : u32 {
OtherApp = 0,
Restart = 1,
@@ -547,6 +545,21 @@ public:
*/
void StartSystemApplet(Kernel::HLERequestContext& ctx);
+ /**
+ * APT::StartNewestHomeMenu service function
+ * Inputs:
+ * 0 : Command header [0x00200044]
+ * 1 : Partameters size
+ * 2 : 0x0
+ * 3 : Handle parameter
+ * 4 : (Parameters Size << 14) | 2
+ * 5 : void*, Parameters
+ * Outputs:
+ * 0 : Return header
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+ void StartNewestHomeMenu(Kernel::HLERequestContext& ctx);
+
/**
* APT::OrderToCloseApplication service function
* Inputs:
@@ -1073,8 +1086,6 @@ private:
u32 cpu_percent = 0; ///< CPU time available to the running application
- std::array sys_menu_arg_buffer;
-
ScreencapPostPermission screen_capture_post_permission =
ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value
diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp
index dc4c72c90..9de07a57d 100644
--- a/src/core/hle/service/apt/apt_a.cpp
+++ b/src/core/hle/service/apt/apt_a.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -42,7 +42,7 @@ APT_A::APT_A(std::shared_ptr apt)
{0x001D, &APT_A::CancelApplication, "CancelApplication"},
{0x001E, &APT_A::StartLibraryApplet, "StartLibraryApplet"},
{0x001F, &APT_A::StartSystemApplet, "StartSystemApplet"},
- {0x0020, nullptr, "StartNewestHomeMenu"},
+ {0x0020, &APT_A::StartNewestHomeMenu, "StartNewestHomeMenu"},
{0x0021, &APT_A::OrderToCloseApplication, "OrderToCloseApplication"},
{0x0022, &APT_A::PrepareToCloseApplication, "PrepareToCloseApplication"},
{0x0023, nullptr, "PrepareToJumpToApplication"},
diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp
index 9bd6d81ed..a332cebf6 100644
--- a/src/core/hle/service/apt/apt_s.cpp
+++ b/src/core/hle/service/apt/apt_s.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -42,7 +42,7 @@ APT_S::APT_S(std::shared_ptr apt)
{0x001D, &APT_S::CancelApplication, "CancelApplication"},
{0x001E, &APT_S::StartLibraryApplet, "StartLibraryApplet"},
{0x001F, &APT_S::StartSystemApplet, "StartSystemApplet"},
- {0x0020, nullptr, "StartNewestHomeMenu"},
+ {0x0020, &APT_S::StartNewestHomeMenu, "StartNewestHomeMenu"},
{0x0021, &APT_S::OrderToCloseApplication, "OrderToCloseApplication"},
{0x0022, &APT_S::PrepareToCloseApplication, "PrepareToCloseApplication"},
{0x0023, nullptr, "PrepareToJumpToApplication"},
diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp
index fd302ab4e..8e1fd18c6 100644
--- a/src/core/hle/service/apt/apt_u.cpp
+++ b/src/core/hle/service/apt/apt_u.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -42,7 +42,7 @@ APT_U::APT_U(std::shared_ptr apt)
{0x001D, &APT_U::CancelApplication, "CancelApplication"},
{0x001E, &APT_U::StartLibraryApplet, "StartLibraryApplet"},
{0x001F, &APT_U::StartSystemApplet, "StartSystemApplet"},
- {0x0020, nullptr, "StartNewestHomeMenu"},
+ {0x0020, &APT_U::StartNewestHomeMenu, "StartNewestHomeMenu"},
{0x0021, &APT_U::OrderToCloseApplication, "OrderToCloseApplication"},
{0x0022, &APT_U::PrepareToCloseApplication, "PrepareToCloseApplication"},
{0x0023, nullptr, "PrepareToJumpToApplication"},
diff --git a/src/core/hle/service/apt/errors.h b/src/core/hle/service/apt/errors.h
index 25bffb9af..4de973906 100644
--- a/src/core/hle/service/apt/errors.h
+++ b/src/core/hle/service/apt/errors.h
@@ -1,4 +1,4 @@
-// Copyright 2018 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -8,6 +8,7 @@ namespace Service::APT::ErrCodes {
enum {
ParameterPresent = 2,
InvalidAppletSlot = 4,
+ IncorrectMemoryMode = 5,
AppNotRunning = 11,
};
} // namespace Service::APT::ErrCodes
diff --git a/src/core/hle/service/apt/ns.cpp b/src/core/hle/service/apt/ns.cpp
index c71c23419..785b83198 100644
--- a/src/core/hle/service/apt/ns.cpp
+++ b/src/core/hle/service/apt/ns.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -30,6 +30,23 @@ std::shared_ptr LaunchTitle(Core::System& system, FS::MediaType
}
}
+ {
+ // This is normally done by PM, but we don't emulate it
+ // so we do it here instead.
+ auto mem_mode_res = loader->LoadKernelMemoryMode();
+ Kernel::MemoryMode mem_mode{};
+ auto n3ds_cap_res = loader->LoadNew3dsHwCapabilities();
+ Kernel::New3dsHwCapabilities n3ds_hw_cap{};
+
+ if (mem_mode_res.second == Loader::ResultStatus::Success && mem_mode_res.first) {
+ mem_mode = mem_mode_res.first.value();
+ }
+ if (n3ds_cap_res.second == Loader::ResultStatus::Success && n3ds_cap_res.first) {
+ n3ds_hw_cap = n3ds_cap_res.first.value();
+ }
+ system.Kernel().UpdateCPUAndMemoryState(title_id, mem_mode, n3ds_hw_cap);
+ }
+
std::shared_ptr process;
Loader::ResultStatus result = loader->Load(process);
@@ -41,7 +58,8 @@ std::shared_ptr LaunchTitle(Core::System& system, FS::MediaType
return process;
}
-void RebootToTitle(Core::System& system, FS::MediaType media_type, u64 title_id) {
+void RebootToTitle(Core::System& system, FS::MediaType media_type, u64 title_id,
+ std::optional mem_mode) {
auto new_path = AM::GetTitleContentPath(media_type, title_id);
if (new_path.empty() || !FileUtil::Exists(new_path)) {
// TODO: This can happen if the requested title is not installed. Need a way to find
@@ -51,7 +69,12 @@ void RebootToTitle(Core::System& system, FS::MediaType media_type, u64 title_id)
new_path);
new_path.clear();
}
- system.RequestReset(new_path);
+
+ std::optional mem_mode_u8;
+ if (mem_mode) {
+ mem_mode_u8 = static_cast(mem_mode.value());
+ }
+ system.RequestReset(new_path, mem_mode_u8);
}
} // namespace Service::NS
diff --git a/src/core/hle/service/apt/ns.h b/src/core/hle/service/apt/ns.h
index 8fa80e11b..2a7dda254 100644
--- a/src/core/hle/service/apt/ns.h
+++ b/src/core/hle/service/apt/ns.h
@@ -1,4 +1,4 @@
-// Copyright 2017 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -20,6 +20,7 @@ std::shared_ptr LaunchTitle(Core::System& system, FS::MediaType
u64 title_id);
/// Reboots the system to the specified title.
-void RebootToTitle(Core::System& system, FS::MediaType media_type, u64 title_id);
+void RebootToTitle(Core::System& system, FS::MediaType media_type, u64 title_id,
+ std::optional mem_mode);
} // namespace Service::NS
diff --git a/src/tests/audio_core/hle/hle.cpp b/src/tests/audio_core/hle/hle.cpp
index 09e7ec5ab..9c4396973 100644
--- a/src/tests/audio_core/hle/hle.cpp
+++ b/src/tests/audio_core/hle/hle.cpp
@@ -1,4 +1,4 @@
-// Copyright 2023 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -22,8 +22,7 @@ TEST_CASE("DSP LLE vs HLE", "[audio_core][hle]") {
Memory::MemorySystem lle_memory{system};
Core::Timing lle_core_timing(1, 100);
Kernel::KernelSystem lle_kernel(
- lle_memory, lle_core_timing, [] {}, Kernel::MemoryMode::Prod, 1,
- Kernel::New3dsHwCapabilities{false, false, Kernel::New3dsMemoryMode::Legacy});
+ lle_memory, lle_core_timing, [] {}, Kernel::MemoryMode::NewProd, 1);
AudioCore::DspHle hle(system, hle_memory, hle_core_timing);
AudioCore::DspLle lle(system, lle_memory, lle_core_timing, true);
diff --git a/src/tests/core/hle/kernel/hle_ipc.cpp b/src/tests/core/hle/kernel/hle_ipc.cpp
index f908e5f2c..372ea563f 100644
--- a/src/tests/core/hle/kernel/hle_ipc.cpp
+++ b/src/tests/core/hle/kernel/hle_ipc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -23,9 +23,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel
Core::Timing timing(1, 100);
Core::System system;
Memory::MemorySystem memory{system};
- Kernel::KernelSystem kernel(
- memory, timing, [] {}, Kernel::MemoryMode::Prod, 1,
- Kernel::New3dsHwCapabilities{false, false, Kernel::New3dsMemoryMode::Legacy});
+ Kernel::KernelSystem kernel(memory, timing, [] {}, Kernel::MemoryMode::NewProd, 1);
auto [server, client] = kernel.CreateSessionPair();
HLERequestContext context(kernel, std::move(server), nullptr);
@@ -256,9 +254,7 @@ TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") {
Core::Timing timing(1, 100);
Core::System system;
Memory::MemorySystem memory{system};
- Kernel::KernelSystem kernel(
- memory, timing, [] {}, Kernel::MemoryMode::Prod, 1,
- Kernel::New3dsHwCapabilities{false, false, Kernel::New3dsMemoryMode::Legacy});
+ Kernel::KernelSystem kernel(memory, timing, [] {}, Kernel::MemoryMode::NewProd, 1);
auto [server, client] = kernel.CreateSessionPair();
HLERequestContext context(kernel, std::move(server), nullptr);
diff --git a/src/tests/core/memory/memory.cpp b/src/tests/core/memory/memory.cpp
index 597ac5132..6254c3b0d 100644
--- a/src/tests/core/memory/memory.cpp
+++ b/src/tests/core/memory/memory.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -12,9 +12,7 @@ TEST_CASE("memory.IsValidVirtualAddress", "[core][memory]") {
Core::Timing timing(1, 100);
Core::System system;
Memory::MemorySystem memory{system};
- Kernel::KernelSystem kernel(
- memory, timing, [] {}, Kernel::MemoryMode::Prod, 1,
- Kernel::New3dsHwCapabilities{false, false, Kernel::New3dsMemoryMode::Legacy});
+ Kernel::KernelSystem kernel(memory, timing, [] {}, Kernel::MemoryMode::NewProd, 1);
SECTION("these regions should not be mapped on an empty process") {
auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0));
CHECK(memory.IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false);
diff --git a/src/tests/core/memory/vm_manager.cpp b/src/tests/core/memory/vm_manager.cpp
index f58ad2e5b..c15760d8a 100644
--- a/src/tests/core/memory/vm_manager.cpp
+++ b/src/tests/core/memory/vm_manager.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -16,9 +16,7 @@ TEST_CASE("Memory Basics", "[kernel][memory]") {
Core::Timing timing(1, 100);
Core::System system;
Memory::MemorySystem memory{system};
- Kernel::KernelSystem kernel(
- memory, timing, [] {}, Kernel::MemoryMode::Prod, 1,
- Kernel::New3dsHwCapabilities{false, false, Kernel::New3dsMemoryMode::Legacy});
+ Kernel::KernelSystem kernel(memory, timing, [] {}, Kernel::MemoryMode::NewProd, 1);
Kernel::Process process(kernel);
SECTION("mapping memory") {
// Because of the PageTable, Kernel::VMManager is too big to be created on the stack.