diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index df0429489..3ffca9810 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -28,7 +28,7 @@ android { namespace = "org.citron.citron_emu" compileSdk = 35 - ndkVersion = "26.3.11579264" + ndkVersion = "28.0.13004108" // "26.3.11579264" buildFeatures { viewBinding = true @@ -36,8 +36,8 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_23 + targetCompatibility = JavaVersion.VERSION_23 } kotlinOptions { @@ -107,9 +107,11 @@ android { resValue("string", "app_name_suffixed", "Citron") isDefault = true isMinifyEnabled = true + isShrinkResources = true + isJniDebuggable = false isDebuggable = false proguardFiles( - getDefaultProguardFile("proguard-android.txt"), + getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } @@ -122,7 +124,7 @@ android { isMinifyEnabled = true isDebuggable = true proguardFiles( - getDefaultProguardFile("proguard-android.txt"), + getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) versionNameSuffix = "-relWithDebInfo" @@ -181,7 +183,7 @@ android { "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON" ) - abiFilters("arm64-v8a", "x86_64") + abiFilters("arm64-v8a") //, "x86_64") } } } @@ -250,8 +252,6 @@ dependencies { // Third Party Libraries implementation("io.coil-kt:coil:2.2.2") implementation("info.debatty:java-string-similarity:2.0.0") - -]] } fun runGitCommand(command: List): String { diff --git a/src/citron/configuration/shared_translation.cpp b/src/citron/configuration/shared_translation.cpp index 393f49daf..55f3ec71c 100644 --- a/src/citron/configuration/shared_translation.cpp +++ b/src/citron/configuration/shared_translation.cpp @@ -352,6 +352,7 @@ std::unique_ptr ComboboxEnumeration(QWidget* parent) { PAIR(CpuAccuracy, Auto, tr("Auto")), PAIR(CpuAccuracy, Accurate, tr("Accurate")), PAIR(CpuAccuracy, Unsafe, tr("Unsafe")), + PAIR(CpuAccuracy, Safe, tr("Safe (recommended for CTGP)")), PAIR(CpuAccuracy, Paranoid, tr("Paranoid (disables most optimizations)")), }}); translations->insert({Settings::EnumMetadata::Index(), @@ -513,10 +514,11 @@ std::unique_ptr ComboboxEnumeration(QWidget* parent) { translations->insert({Settings::EnumMetadata::Index(), { PAIR(MemoryLayout, Memory_4Gb, tr("4GB DRAM (Default)")), - PAIR(MemoryLayout, Memory_6Gb, tr("6GB DRAM (Unsafe)")), - PAIR(MemoryLayout, Memory_8Gb, tr("8GB DRAM (Unsafe)")), - PAIR(MemoryLayout, Memory_10Gb, tr("10GB DRAM (Unsafe)")), - PAIR(MemoryLayout, Memory_12Gb, tr("12GB DRAM (Unsafe)")), + PAIR(MemoryLayout, Memory_6Gb, tr("6GB DRAM")), + PAIR(MemoryLayout, Memory_8Gb, tr("8GB DRAM")), + PAIR(MemoryLayout, Memory_10Gb, tr("10GB DRAM")), + PAIR(MemoryLayout, Memory_12Gb, tr("12GB DRAM")), + PAIR(MemoryLayout, Software, tr("Software (Maximum compatibility)")), }}); translations->insert({Settings::EnumMetadata::Index(), { diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index ca19f22e6..466e803d2 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -132,9 +132,9 @@ ENUM(GpuAccuracy, Normal, High, Extreme); ENUM(CpuBackend, Dynarmic, Nce); -ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid); +ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid, Safe); -ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb, Memory_10Gb, Memory_12Gb); +ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb, Memory_10Gb, Memory_12Gb, Software); ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never); diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 21d45235e..5c2224e4f 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -195,30 +195,77 @@ std::vector PatchManager::CollectPatches(const std::vectorGetName()).find("ctgp") != std::string::npos; + std::vector out; out.reserve(patch_dirs.size()); for (const auto& subdir : patch_dirs) { - if (std::find(disabled.cbegin(), disabled.cend(), subdir->GetName()) != disabled.cend()) + // For CTGP patches, ensure we load them even if disabled + if (!is_ctgp && std::find(disabled.cbegin(), disabled.cend(), subdir->GetName()) != disabled.cend()) continue; - auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs"); - if (exefs_dir != nullptr) { - for (const auto& file : exefs_dir->GetFiles()) { - if (file->GetExtension() == "ips") { - auto name = file->GetName(); + if (is_ctgp) { + LOG_INFO(Loader, "Processing CTGP patch directory: {}", subdir->GetName()); - const auto this_build_id = - fmt::format("{:0<64}", name.substr(0, name.find('.'))); - if (nso_build_id == this_build_id) - out.push_back(file); - } else if (file->GetExtension() == "pchtxt") { - IPSwitchCompiler compiler{file}; - if (!compiler.IsValid()) - continue; + auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs"); + if (exefs_dir != nullptr) { + bool main_npdm_found = false; + bool subsdk1_found = false; + for (const auto& file : exefs_dir->GetFiles()) { + if (file->GetName() == "main.npdm") { + main_npdm_found = true; + LOG_INFO(Loader, "Found main.npdm in CTGP patch directory."); + } + if (file->GetName() == "subsdk1") { + subsdk1_found = true; + LOG_INFO(Loader, "Found subsdk1 in CTGP patch directory."); + } + if (file->GetExtension() == "ips") { + auto name = file->GetName(); - const auto this_build_id = Common::HexToString(compiler.GetBuildID()); - if (nso_build_id == this_build_id) - out.push_back(file); + const auto this_build_id = + fmt::format("{:0<64}", name.substr(0, name.find('.'))); + if (nso_build_id == this_build_id) + out.push_back(file); + } else if (file->GetExtension() == "pchtxt") { + IPSwitchCompiler compiler{file}; + if (!compiler.IsValid()) + continue; + + const auto this_build_id = Common::HexToString(compiler.GetBuildID()); + if (nso_build_id == this_build_id) + out.push_back(file); + } + } + if (!main_npdm_found) { + LOG_ERROR(Loader, "main.npdm not found in CTGP patch directory."); + } + if (!subsdk1_found) { + LOG_ERROR(Loader, "subsdk1 not found in CTGP patch directory."); + } + } + } else { + auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs"); + if (exefs_dir != nullptr) { + for (const auto& file : exefs_dir->GetFiles()) { + if (file->GetExtension() == "ips") { + auto name = file->GetName(); + + const auto this_build_id = + fmt::format("{:0<64}", name.substr(0, name.find('.'))); + if (nso_build_id == this_build_id) + out.push_back(file); + } else if (file->GetExtension() == "pchtxt") { + IPSwitchCompiler compiler{file}; + if (!compiler.IsValid()) + continue; + + const auto this_build_id = Common::HexToString(compiler.GetBuildID()); + if (nso_build_id == this_build_id) + out.push_back(file); + } } } } @@ -263,10 +310,27 @@ std::vector PatchManager::PatchNSO(const std::vector& nso, const std::st return nso; } + // Special handling for CTGP + if (name == "main" || name == "subsdk0") { + LOG_INFO(Loader, "Applying CTGP-specific patch handling for {}", name); + } + auto patch_dirs = load_dir->GetSubdirectories(); std::sort(patch_dirs.begin(), patch_dirs.end(), [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); - const auto patches = CollectPatches(patch_dirs, build_id); + + // Prioritize CTGP patches if present + const auto ctgp_dir = FindSubdirectoryCaseless(load_dir, "CTGP"); + std::vector patches; + if (ctgp_dir != nullptr && (name == "main" || name == "subsdk0")) { + patches = CollectPatches({ctgp_dir}, build_id); + if (!patches.empty()) { + LOG_INFO(Loader, "Found CTGP-specific patches for {}", name); + } + } + if (patches.empty()) { + patches = CollectPatches(patch_dirs, build_id); + } auto out = nso; for (const auto& patch_file : patches) { diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp index d374e2d6c..0151d5b1c 100644 --- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp +++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp @@ -52,6 +52,8 @@ u32 GetMemorySizeForInit() { return Smc::MemorySize_10GB; case Settings::MemoryLayout::Memory_12Gb: return Smc::MemorySize_12GB; + case Settings::MemoryLayout::Software: + return Smc::MemorySize_12GB; // Use maximum size for software mode } return Smc::MemorySize_4GB; } @@ -68,6 +70,8 @@ Smc::MemoryArrangement GetMemoryArrangeForInit() { return Smc::MemoryArrangement_10GB; case Settings::MemoryLayout::Memory_12Gb: return Smc::MemoryArrangement_12GB; + case Settings::MemoryLayout::Software: + return Smc::MemoryArrangement_12GB; // Use maximum arrangement for software mode } return Smc::MemoryArrangement_4GB; } diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h index 7ca5a5b41..af04ff5ce 100644 --- a/src/core/hle/kernel/k_memory_layout.h +++ b/src/core/hle/kernel/k_memory_layout.h @@ -26,6 +26,7 @@ constexpr std::size_t GetMaximumOverheadSize(std::size_t size) { constexpr std::size_t MainMemorySize = 4_GiB; constexpr std::size_t MainMemorySizeMax = 12_GiB; +constexpr std::size_t SoftwareMemorySize = 4_GiB; // Fixed size for software memory management mode constexpr std::size_t ReservedEarlyDramSize = 384_KiB; constexpr std::size_t DramPhysicalAddress = 0x80000000; diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp index d6bd27296..846012abe 100644 --- a/src/core/hle/kernel/k_memory_manager.cpp +++ b/src/core/hle/kernel/k_memory_manager.cpp @@ -36,13 +36,14 @@ constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) { } // namespace KMemoryManager::KMemoryManager(Core::System& system) - : m_system{system}, m_memory_layout{system.Kernel().MemoryLayout()}, - m_pool_locks{ - KLightLock{system.Kernel()}, - KLightLock{system.Kernel()}, - KLightLock{system.Kernel()}, - KLightLock{system.Kernel()}, - } {} + : m_system{system}, + m_memory_layout{system.Kernel().MemoryLayout()}, + m_pool_locks{KLightLock{system.Kernel()}, // Application + KLightLock{system.Kernel()}, // Applet + KLightLock{system.Kernel()}, // System + KLightLock{system.Kernel()}, // SystemNonSecure + KLightLock{system.Kernel()}} { // Software +} void KMemoryManager::Initialize(KVirtualAddress management_region, size_t management_region_size) { diff --git a/src/core/hle/kernel/k_memory_manager.h b/src/core/hle/kernel/k_memory_manager.h index c5a487af9..62d6e9763 100644 --- a/src/core/hle/kernel/k_memory_manager.h +++ b/src/core/hle/kernel/k_memory_manager.h @@ -28,6 +28,7 @@ public: Applet = 1, System = 2, SystemNonSecure = 3, + Software = 4, // Software memory management mode Count, @@ -37,6 +38,7 @@ public: // Aliases. Unsafe = Application, Secure = System, + Safe = Software, // Alias for software mode }; enum class Direction : u32 { diff --git a/src/core/memory.cpp b/src/core/memory.cpp index c598edbc2..0c87b551a 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -182,6 +182,11 @@ struct Memory::Impl { } void Write8(const Common::ProcessAddress addr, const u8 data) { + // Check for null pointer write + if (addr == 0 || addr < 0x1000) { + LOG_ERROR(HW_Memory, "Null pointer Write8 @ 0x{:016X} = 0x{:02X}", addr, data); + return; + } Write(addr, data); } @@ -992,6 +997,11 @@ u64 Memory::Read64(const Common::ProcessAddress addr) { } void Memory::Write8(Common::ProcessAddress addr, u8 data) { + // Check for null pointer write + if (addr == 0 || addr < 0x1000) { + LOG_ERROR(HW_Memory, "Null pointer Write8 @ 0x{:016X} = 0x{:02X}", addr, data); + return; + } impl->Write8(addr, data); }