mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-29 18:08:08 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aa0878b636 | |||
| c0a85d0e53 | |||
| 54cd5fb8eb | |||
| 119291dc77 | |||
| f7dbff2157 | |||
| b859d7fdf4 | |||
| f635827fa6 | |||
| 243453c172 | |||
| 39158b67a7 | |||
| 50cb8fd1c9 | |||
| faaf1bac64 | |||
| 0295dc5fff | |||
| 60a474b8df | |||
| d9336ce6ce | |||
| 4eb8dd1458 | |||
| 4a3cc9a3c2 | |||
| 86ba79b1ba | |||
| b610b03d29 | |||
| 421e0b834c | |||
| 55acf5d260 | |||
| 3e07b466eb |
+1
@@ -624,6 +624,7 @@ abstract class SettingsItem(
|
||||
IntSetting.FSR_SHARPENING_SLIDER,
|
||||
titleId = R.string.fsr_sharpness,
|
||||
descriptionId = R.string.fsr_sharpness_description,
|
||||
max = 200,
|
||||
units = "%"
|
||||
)
|
||||
)
|
||||
|
||||
+4
-1
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -24,6 +27,6 @@ object SettingsFile {
|
||||
|
||||
fun loadCustomConfig(game: Game) {
|
||||
val fileName = FileUtil.getFilename(Uri.parse(game.path))
|
||||
NativeConfig.initializePerGameConfig(game.programId, fileName)
|
||||
NativeConfig.initializePerGameConfig(game.applicationId, fileName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ class AddonsFragment : Fragment() {
|
||||
fragmentManager = parentFragmentManager,
|
||||
addonViewModel = addonViewModel,
|
||||
documents = documents,
|
||||
programId = args.game.programId
|
||||
programId = args.game.applicationId
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -347,7 +347,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
try {
|
||||
if (GpuDriverHelper.isAdrenoGpu()) {
|
||||
val programIdHex = game!!.programIdHex
|
||||
val programIdHex = game!!.applicationIdHex
|
||||
if (NativeFreedrenoConfig.loadPerGameConfigWithGlobalFallback(programIdHex)) {
|
||||
Log.info("[EmulationFragment] Loaded per-game Freedreno config for $programIdHex")
|
||||
} else {
|
||||
@@ -1182,7 +1182,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
container,
|
||||
IntSetting.FSR_SHARPENING_SLIDER,
|
||||
minValue = 0,
|
||||
maxValue = 100,
|
||||
maxValue = 200,
|
||||
units = "%"
|
||||
)
|
||||
}
|
||||
|
||||
+2
-2
@@ -59,7 +59,7 @@ class FreedrenoSettingsFragment : Fragment() {
|
||||
NativeFreedrenoConfig.initializeFreedrenoConfig()
|
||||
|
||||
if (isPerGameConfig) {
|
||||
NativeFreedrenoConfig.loadPerGameConfig(game!!.programIdHex)
|
||||
NativeFreedrenoConfig.loadPerGameConfig(game!!.applicationIdHex)
|
||||
} else {
|
||||
NativeFreedrenoConfig.reloadFreedrenoConfig()
|
||||
}
|
||||
@@ -157,7 +157,7 @@ class FreedrenoSettingsFragment : Fragment() {
|
||||
|
||||
binding.buttonSave.setOnClickListener {
|
||||
if (isPerGameConfig) {
|
||||
NativeFreedrenoConfig.savePerGameConfig(game!!.programIdHex)
|
||||
NativeFreedrenoConfig.savePerGameConfig(game!!.applicationIdHex)
|
||||
showSnackbar(getString(R.string.freedreno_per_game_saved))
|
||||
} else {
|
||||
NativeFreedrenoConfig.saveFreedrenoConfig()
|
||||
|
||||
@@ -455,7 +455,7 @@ class GamePropertiesFragment : Fragment() {
|
||||
|
||||
val shaderCacheDir = File(
|
||||
DirectoryInitialization.userDirectory +
|
||||
"/cache/shader/" + args.game.settingsName.lowercase()
|
||||
"/cache/shader/" + args.game.shaderCacheName.lowercase()
|
||||
)
|
||||
if (shaderCacheDir.exists()) {
|
||||
add(
|
||||
@@ -600,7 +600,7 @@ class GamePropertiesFragment : Fragment() {
|
||||
val files = cacheSaveDir.listFiles()
|
||||
var savesFolderFile: File? = null
|
||||
if (files != null) {
|
||||
val savesFolderName = args.game.programIdHex
|
||||
val savesFolderName = args.game.applicationIdHex
|
||||
for (file in files) {
|
||||
if (file.isDirectory && file.name == savesFolderName) {
|
||||
savesFolderFile = file
|
||||
|
||||
@@ -232,7 +232,7 @@ class InstallableFragment : Fragment() {
|
||||
fragmentManager = parentFragmentManager,
|
||||
addonViewModel = addonViewModel,
|
||||
documents = documents,
|
||||
programId = addonViewModel.game?.programId
|
||||
programId = addonViewModel.game?.applicationId
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -142,10 +142,11 @@ class AddonViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
fun onDeleteAddon(patch: Patch) {
|
||||
val currentGame = game ?: return
|
||||
when (PatchType.from(patch.type)) {
|
||||
PatchType.Update -> NativeLibrary.removeUpdate(patch.programId)
|
||||
PatchType.DLC -> NativeLibrary.removeDLC(patch.programId)
|
||||
PatchType.Mod -> NativeLibrary.removeMod(patch.programId, patch.name)
|
||||
PatchType.Update -> NativeLibrary.removeUpdate(currentGame.programId)
|
||||
PatchType.DLC -> NativeLibrary.removeDLC(currentGame.applicationId)
|
||||
PatchType.Mod -> NativeLibrary.removeMod(currentGame.programId, patch.name)
|
||||
}
|
||||
refreshAddons(force = true)
|
||||
}
|
||||
@@ -165,7 +166,7 @@ class AddonViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
NativeConfig.setDisabledAddons(
|
||||
currentGame.programId,
|
||||
currentGame.applicationId,
|
||||
currentList.mapNotNull {
|
||||
if (it.enabled) {
|
||||
null
|
||||
@@ -199,6 +200,6 @@ class AddonViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
private fun gameKey(game: Game): String {
|
||||
return "${game.programId}|${game.path}"
|
||||
return "${game.applicationId}|${game.path}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ class DriverViewModel : ViewModel() {
|
||||
?: return@withContext
|
||||
val shaderDir = File(
|
||||
externalFilesDir.absolutePath +
|
||||
"/shader/" + game.settingsName.lowercase()
|
||||
"/shader/" + game.shaderCacheName.lowercase()
|
||||
)
|
||||
if (shaderDir.exists()) {
|
||||
shaderDir.deleteRecursively()
|
||||
|
||||
@@ -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: 2023 yuzu Emulator Project
|
||||
@@ -35,19 +35,26 @@ class Game(
|
||||
val keyAddedToLibraryTime get() = "${path}_AddedToLibraryTime"
|
||||
val keyLastPlayedTime get() = "${path}_LastPlayed"
|
||||
|
||||
private val programIdLong: Long
|
||||
get() = programId.toLongOrNull() ?: 0L
|
||||
|
||||
private val applicationIdLong: Long
|
||||
get() = programIdLong and -8192L
|
||||
|
||||
val applicationId: String
|
||||
get() = applicationIdLong.toString()
|
||||
|
||||
val settingsName: String
|
||||
get() {
|
||||
val programIdLong = programId.toLong()
|
||||
return if (programIdLong == 0L) {
|
||||
return if (applicationIdLong == 0L) {
|
||||
FileUtil.getFilename(Uri.parse(path))
|
||||
} else {
|
||||
"0" + programIdLong.toString(16).uppercase()
|
||||
"0" + applicationIdLong.toString(16).uppercase()
|
||||
}
|
||||
}
|
||||
|
||||
val programIdHex: String
|
||||
get() {
|
||||
val programIdLong = programId.toLong()
|
||||
return if (programIdLong == 0L) {
|
||||
"0"
|
||||
} else {
|
||||
@@ -55,16 +62,32 @@ class Game(
|
||||
}
|
||||
}
|
||||
|
||||
val shaderCacheName: String
|
||||
get() = if (programIdLong == 0L) {
|
||||
FileUtil.getFilename(Uri.parse(path))
|
||||
} else {
|
||||
"0" + programIdLong.toString(16).uppercase()
|
||||
}
|
||||
|
||||
val applicationIdHex: String
|
||||
get() {
|
||||
return if (applicationIdLong == 0L) {
|
||||
"0"
|
||||
} else {
|
||||
"0" + applicationIdLong.toString(16).uppercase()
|
||||
}
|
||||
}
|
||||
|
||||
val saveZipName: String
|
||||
get() = "$title ${YuzuApplication.appContext.getString(R.string.save_data).lowercase()} - ${
|
||||
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
|
||||
}.zip"
|
||||
|
||||
val saveDir: String
|
||||
get() = NativeConfig.getSaveDir() + NativeLibrary.getSavePath(programId)
|
||||
get() = NativeConfig.getSaveDir() + NativeLibrary.getSavePath(applicationId)
|
||||
|
||||
val addonDir: String
|
||||
get() = DirectoryInitialization.userDirectory + "/load/" + programIdHex + "/"
|
||||
get() = DirectoryInitialization.userDirectory + "/load/" + applicationIdHex + "/"
|
||||
|
||||
val launchIntent: Intent
|
||||
get() = Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply {
|
||||
|
||||
@@ -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: 2023 yuzu Emulator Project
|
||||
@@ -65,7 +65,7 @@ object CustomSettingsHandler {
|
||||
// Initialize per-game config
|
||||
try {
|
||||
val fileName = FileUtil.getFilename(Uri.parse(game.path))
|
||||
NativeConfig.initializePerGameConfig(game.programId, fileName)
|
||||
NativeConfig.initializePerGameConfig(game.applicationId, fileName)
|
||||
Log.info("[CustomSettingsHandler] Successfully applied custom settings")
|
||||
return game
|
||||
} catch (e: Exception) {
|
||||
@@ -333,20 +333,20 @@ object CustomSettingsHandler {
|
||||
*/
|
||||
fun findGameByTitleId(titleId: String, context: Context): Game? {
|
||||
Log.info("[CustomSettingsHandler] Searching for game with title ID: $titleId")
|
||||
// Convert hex title ID to decimal for comparison with programId
|
||||
val programIdDecimal = try {
|
||||
titleId.toLong(16).toString()
|
||||
// Convert the program ID to the application ID used by per-game settings.
|
||||
val applicationIdLong = try {
|
||||
titleId.toLong(16) and -8192L
|
||||
} catch (e: NumberFormatException) {
|
||||
Log.error("[CustomSettingsHandler] Invalid title ID format: $titleId")
|
||||
return null
|
||||
}
|
||||
val applicationIdDecimal = applicationIdLong.toString()
|
||||
|
||||
// Expected hex format with "0" prefix
|
||||
val expectedHex = "0${titleId.uppercase()}"
|
||||
val expectedHex = "0${applicationIdLong.toString(16).uppercase()}"
|
||||
// First check cached games for fast lookup
|
||||
GameHelper.cachedGameList.find { game ->
|
||||
game.programId == programIdDecimal ||
|
||||
game.programIdHex.equals(expectedHex, ignoreCase = true)
|
||||
game.applicationId == applicationIdDecimal || game.applicationIdHex.equals(expectedHex, ignoreCase = true)
|
||||
}?.let { foundGame ->
|
||||
Log.info("[CustomSettingsHandler] Found game in cache: ${foundGame.title}")
|
||||
return foundGame
|
||||
@@ -355,8 +355,7 @@ object CustomSettingsHandler {
|
||||
Log.info("[CustomSettingsHandler] Game not in cache, scanning full library...")
|
||||
val allGames = GameHelper.getGames()
|
||||
val foundGame = allGames.find { game ->
|
||||
game.programId == programIdDecimal ||
|
||||
game.programIdHex.equals(expectedHex, ignoreCase = true)
|
||||
game.applicationId == applicationIdDecimal || game.applicationIdHex.equals(expectedHex, ignoreCase = true)
|
||||
}
|
||||
if (foundGame != null) {
|
||||
Log.info("[CustomSettingsHandler] Found game: ${foundGame.title} at ${foundGame.path}")
|
||||
|
||||
@@ -170,12 +170,12 @@ object GameHelper {
|
||||
val game = getGame(it.uri, true, false)
|
||||
if (game != null) {
|
||||
games.add(game)
|
||||
if (game.programId != "0") {
|
||||
gamesByProgramId[game.programId] = game
|
||||
if (game.applicationId != "0") {
|
||||
gamesByProgramId[game.applicationId] = game
|
||||
}
|
||||
} else if (mountedContainer) {
|
||||
GameMetadata.getProgramId(filePath).toLongOrNull()?.let { programId ->
|
||||
gamesByProgramId[(programId and 0x800L.inv()).toString()]
|
||||
gamesByProgramId[(programId and -8192L).toString()]
|
||||
}?.let { existingGame ->
|
||||
NativeLibrary.getPatchesForFile(existingGame.path, existingGame.programId)
|
||||
existingGame.version = GameMetadata.getVersion(
|
||||
|
||||
@@ -76,6 +76,7 @@ extern "C" {
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
#include "core/frontend/applets/web_browser.h"
|
||||
#include "common/android/applets/web_browser.h"
|
||||
#include "core/file_sys/common_funcs.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
#include "core/hle/service/am/frontend/applets.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
@@ -311,11 +312,23 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
|
||||
ConfigureFilesystemProvider(filepath);
|
||||
|
||||
// Load the ROM.
|
||||
const u64 previous_program_id =
|
||||
program_index != 0 && m_next_program_id.load() >
|
||||
static_cast<u64>(Service::AM::AppletProgramId::MaxProgramId)
|
||||
? m_next_program_id.load()
|
||||
: 0;
|
||||
|
||||
Service::AM::FrontendAppletParameters params{
|
||||
.program_id = previous_program_id,
|
||||
.applet_id = static_cast<Service::AM::AppletId>(m_applet_id),
|
||||
.launch_type = frontend_initiated ? Service::AM::LaunchType::FrontendInitiated
|
||||
: Service::AM::LaunchType::ApplicationInitiated,
|
||||
.program_index = static_cast<s32>(program_index),
|
||||
.previous_program_index =
|
||||
previous_program_id != 0
|
||||
? static_cast<s32>(previous_program_id -
|
||||
FileSys::GetBaseTitleID(previous_program_id))
|
||||
: -1,
|
||||
};
|
||||
|
||||
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath, params);
|
||||
@@ -328,8 +341,12 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
|
||||
m_system.GetCpuManager().OnGpuReady();
|
||||
m_system.RegisterExitCallback([&] { HaltEmulation(); });
|
||||
|
||||
m_system.RegisterApplicationChangedCallback(
|
||||
[&](u64 changed_program_id) { RequestDiskShaderCacheReload(changed_program_id); });
|
||||
|
||||
// Register an ExecuteProgram callback such that Core can execute a sub-program
|
||||
m_system.RegisterExecuteProgramCallback([&](std::size_t program_index_) {
|
||||
m_next_program_id = m_system.GetApplicationProcessProgramID();
|
||||
m_next_program_index = program_index_;
|
||||
EmulationSession::GetInstance().HaltEmulation();
|
||||
});
|
||||
@@ -407,20 +424,58 @@ void EmulationSession::RunEmulation() {
|
||||
}
|
||||
|
||||
while (true) {
|
||||
std::optional<u64> reload_title;
|
||||
{
|
||||
[[maybe_unused]] std::unique_lock lock(m_mutex);
|
||||
if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
|
||||
[&]() { return !m_is_running; })) {
|
||||
// Emulation halted.
|
||||
break;
|
||||
if (m_cv.wait_for(lock, std::chrono::milliseconds(800), [&]() {
|
||||
return !m_is_running || m_pending_shader_cache_title.has_value();
|
||||
})) {
|
||||
if (!m_is_running) {
|
||||
break;
|
||||
}
|
||||
reload_title = std::exchange(m_pending_shader_cache_title, std::nullopt);
|
||||
}
|
||||
}
|
||||
|
||||
if (reload_title.has_value())
|
||||
ReloadDiskShaderCache(*reload_title);
|
||||
}
|
||||
|
||||
// Reset current applet ID.
|
||||
m_applet_id = static_cast<int>(Service::AM::AppletId::Application);
|
||||
}
|
||||
|
||||
void EmulationSession::RequestDiskShaderCacheReload(u64 program_id) {
|
||||
{
|
||||
std::scoped_lock lock(m_mutex);
|
||||
m_pending_shader_cache_title = program_id;
|
||||
}
|
||||
m_cv.notify_one();
|
||||
}
|
||||
|
||||
void EmulationSession::ReloadDiskShaderCache(u64 program_id) {
|
||||
if (!Settings::values.use_disk_shader_cache.GetValue())
|
||||
return;
|
||||
|
||||
LOG_INFO(Frontend, "Reloading disk shader cache for {:016X}", program_id);
|
||||
|
||||
const bool was_paused = m_is_paused;
|
||||
|
||||
m_system.Pause();
|
||||
m_system.GPU().WaitForIdle();
|
||||
m_system.GPU().ObtainContext();
|
||||
|
||||
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||
m_system.Renderer().ReadRasterizer()->LoadDiskResources(program_id, std::stop_token{},
|
||||
LoadDiskCacheProgress);
|
||||
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
|
||||
|
||||
m_system.GPU().ReleaseContext();
|
||||
|
||||
if (!was_paused)
|
||||
m_system.Run();
|
||||
}
|
||||
|
||||
Common::Android::SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() {
|
||||
return m_software_keyboard;
|
||||
}
|
||||
@@ -733,7 +788,7 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
|
||||
jstring jprogramId,
|
||||
jstring jupdatePath) {
|
||||
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||
const u64 program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
|
||||
std::string updatePath = Common::Android::GetJString(env, jupdatePath);
|
||||
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
|
||||
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
|
||||
@@ -741,7 +796,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en
|
||||
for (const auto& item : nsp->GetNCAs()) {
|
||||
for (const auto& nca_details : item.second) {
|
||||
if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
|
||||
auto update_id = nca_details.second->GetTitleId() & ~0xFFFULL;
|
||||
const auto update_id = FileSys::GetBaseTitleID(nca_details.second->GetTitleId());
|
||||
if (update_id == program_id) {
|
||||
return true;
|
||||
}
|
||||
@@ -1436,7 +1491,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_firmwareVersion(JNIEnv* env, jclas
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_gameRequiresFirmware(JNIEnv* env, jclass clazz, jstring jprogramId) {
|
||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||
const auto program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
|
||||
|
||||
return FirmwareManager::GameRequiresFirmware(program_id);
|
||||
}
|
||||
@@ -1520,20 +1575,21 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeUpdate(JNIEnv* env, jobject jobj,
|
||||
jstring jprogramId) {
|
||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||
const auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||
ContentManager::RemoveUpdate(EmulationSession::GetInstance().System().GetFileSystemController(),
|
||||
program_id);
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj,
|
||||
jstring jprogramId) {
|
||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||
const auto program_id = FileSys::GetBaseTitleID(
|
||||
EmulationSession::GetProgramId(env, jprogramId));
|
||||
ContentManager::RemoveAllDLC(EmulationSession::GetInstance().System(), program_id);
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId,
|
||||
jstring jname) {
|
||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||
const auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||
ContentManager::RemoveMod(EmulationSession::GetInstance().System().GetFileSystemController(),
|
||||
program_id, Common::Android::GetJString(env, jname));
|
||||
}
|
||||
@@ -1580,7 +1636,7 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyGameContents(JNIEnv* env, jobje
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
|
||||
jstring jprogramId) {
|
||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||
const auto program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
|
||||
if (program_id == 0) {
|
||||
return Common::Android::ToJString(env, "");
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <android/native_window_jni.h>
|
||||
#include "common/android/applets/software_keyboard.h"
|
||||
#include "core/core.h"
|
||||
@@ -44,6 +46,7 @@ public:
|
||||
void HaltEmulation();
|
||||
void RunEmulation();
|
||||
void ShutdownEmulation();
|
||||
void RequestDiskShaderCacheReload(u64 program_id);
|
||||
|
||||
const Core::PerfStatsResults& PerfStats();
|
||||
int ShadersBuilding();
|
||||
@@ -65,6 +68,7 @@ private:
|
||||
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
|
||||
static void OnEmulationStopped(Core::SystemResultStatus result);
|
||||
static void ChangeProgram(std::size_t program_index);
|
||||
void ReloadDiskShaderCache(u64 program_id);
|
||||
|
||||
private:
|
||||
// Window management
|
||||
@@ -83,6 +87,7 @@ private:
|
||||
Common::Android::SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
|
||||
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
|
||||
int m_applet_id{1};
|
||||
std::optional<u64> m_pending_shader_cache_title;
|
||||
|
||||
// GPU driver parameters
|
||||
std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
|
||||
@@ -93,4 +98,5 @@ private:
|
||||
|
||||
// Program index for next boot
|
||||
std::atomic<s32> m_next_program_index = -1;
|
||||
std::atomic<u64> m_next_program_id = 0;
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/file_sys/common_funcs.h"
|
||||
#include "frontend_common/config.h"
|
||||
#include "frontend_common/settings_generator.h"
|
||||
#include "native.h"
|
||||
@@ -56,7 +57,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveGlobalConfig(JNIEnv* env, jo
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializePerGameConfig(JNIEnv* env, jobject obj,
|
||||
jstring jprogramId,
|
||||
jstring jfileName) {
|
||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||
const auto program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
|
||||
auto file_name = Common::Android::GetJString(env, jfileName);
|
||||
const auto config_file_name = program_id == 0 ? file_name : fmt::format("{:016X}", program_id);
|
||||
per_game_config =
|
||||
@@ -322,7 +323,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_addGameDir(JNIEnv* env, jobject
|
||||
|
||||
jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDisabledAddons(JNIEnv* env, jobject obj,
|
||||
jstring jprogramId) {
|
||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||
const auto program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
|
||||
auto& disabledAddons = Settings::values.disabled_addons[program_id];
|
||||
jobjectArray jdisabledAddonsArray =
|
||||
env->NewObjectArray(disabledAddons.size(), Common::Android::GetStringClass(),
|
||||
@@ -337,7 +338,7 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDisabledAddons(JNIEnv
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, jobject obj,
|
||||
jstring jprogramId,
|
||||
jobjectArray jdisabledAddons) {
|
||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||
const auto program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
|
||||
Settings::values.disabled_addons[program_id].clear();
|
||||
std::vector<std::string> disabled_addons;
|
||||
const int size = env->GetArrayLength(jdisabledAddons);
|
||||
|
||||
@@ -1107,7 +1107,6 @@
|
||||
<string name="theme_mode_light">فاتح</string>
|
||||
<string name="theme_mode_dark">داكن</string>
|
||||
|
||||
<string name="multiplier_none">لا شيء</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">خلفيات سوداء</string>
|
||||
|
||||
@@ -991,7 +991,6 @@ Wirklich fortfahren?</string>
|
||||
<string name="theme_mode_light">Hell</string>
|
||||
<string name="theme_mode_dark">Dunkel</string>
|
||||
|
||||
<string name="multiplier_none">Keine</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Schwarze Hintergründe</string>
|
||||
|
||||
@@ -1092,7 +1092,6 @@
|
||||
<string name="theme_mode_light">Claro</string>
|
||||
<string name="theme_mode_dark">Oscuro</string>
|
||||
|
||||
<string name="multiplier_none">Nada</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Fondos oscuros</string>
|
||||
|
||||
@@ -808,9 +808,6 @@
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">پسزمینه مشکی</string>
|
||||
|
||||
@@ -1004,7 +1004,6 @@
|
||||
<string name="theme_mode_light">Lumineux</string>
|
||||
<string name="theme_mode_dark">Sombre</string>
|
||||
|
||||
<string name="multiplier_none">Aucun</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Arrière-plan noir</string>
|
||||
|
||||
@@ -935,7 +935,6 @@
|
||||
<string name="theme_mode_light">Jasny</string>
|
||||
<string name="theme_mode_dark">Ciemny</string>
|
||||
|
||||
<string name="multiplier_none">Brak</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Czarne tła</string>
|
||||
|
||||
@@ -891,7 +891,6 @@
|
||||
<string name="theme_mode_light">Claro</string>
|
||||
<string name="theme_mode_dark">Escuro</string>
|
||||
|
||||
<string name="multiplier_none">Nenhum</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Planos de fundo pretos</string>
|
||||
|
||||
@@ -1071,7 +1071,6 @@
|
||||
<string name="theme_mode_light">Светлая</string>
|
||||
<string name="theme_mode_dark">Темная</string>
|
||||
|
||||
<string name="multiplier_none">Отключено</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Чёрный фон</string>
|
||||
|
||||
@@ -1053,7 +1053,6 @@
|
||||
<string name="theme_mode_light">Світла</string>
|
||||
<string name="theme_mode_dark">Темна</string>
|
||||
|
||||
<string name="multiplier_none">Жодного</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Чорний фон</string>
|
||||
|
||||
@@ -1081,7 +1081,6 @@
|
||||
<string name="theme_mode_light">浅色</string>
|
||||
<string name="theme_mode_dark">深色</string>
|
||||
|
||||
<string name="multiplier_none">无</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">使用黑色背景</string>
|
||||
|
||||
@@ -1006,7 +1006,6 @@
|
||||
<string name="theme_mode_light">淺色</string>
|
||||
<string name="theme_mode_dark">深色</string>
|
||||
|
||||
<string name="multiplier_none">無</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">黑色背景</string>
|
||||
|
||||
@@ -111,43 +111,38 @@
|
||||
<item>1</item>
|
||||
</integer-array>
|
||||
|
||||
<!-- VRAM USAGE MODE CHOICES -->
|
||||
<string-array name="vramUsageMethodNames">
|
||||
<item>@string/vram_usage_conservative</item>
|
||||
<item>@string/vram_usage_aggressive</item>
|
||||
</string-array>
|
||||
|
||||
<!-- VRAM USAGE MODE VALUES -->
|
||||
<integer-array name="vramUsageMethodValues">
|
||||
<item>0</item> <!-- Conservative -->
|
||||
<item>1</item> <!-- Aggressive -->
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
</integer-array>
|
||||
|
||||
<!-- ASTC Decoding Method Choices -->
|
||||
<string-array name="astcDecodingMethodNames">
|
||||
<item>@string/accelerate_astc_cpu</item>
|
||||
<item>@string/accelerate_astc_gpu</item>
|
||||
<item>@string/accelerate_astc_async</item>
|
||||
</string-array>
|
||||
|
||||
<!-- ASTC Decoding Method Values -->
|
||||
<integer-array name="astcDecodingMethodValues">
|
||||
<item>0</item> <!-- CPU -->
|
||||
<item>1</item> <!-- GPU -->
|
||||
<item>2</item> <!-- CPU Asynchronously -->
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</integer-array>
|
||||
<!-- NVDEC Emulation Choices -->
|
||||
|
||||
<string-array name="rendererNvdecNames">
|
||||
<item>@string/nvdec_emulation_none</item> <!-- Off -->
|
||||
<item>@string/nvdec_emulation_cpu</item> <!-- Cpu -->
|
||||
<item>@string/nvdec_emulation_gpu</item> <!-- Gpu -->
|
||||
<item>@string/nvdec_emulation_none</item>
|
||||
<item>@string/nvdec_emulation_cpu</item>
|
||||
<item>@string/nvdec_emulation_gpu</item>
|
||||
</string-array>
|
||||
|
||||
<!-- NVDEC Emulation Values -->
|
||||
<integer-array name="rendererNvdecValues">
|
||||
<item>3</item> <!-- Off value -->
|
||||
<item>1</item> <!-- CPU value -->
|
||||
<item>2</item> <!-- GPU value -->
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="rendererResolutionNames">
|
||||
@@ -513,9 +508,6 @@
|
||||
<item>@string/multiplier_x4</item>
|
||||
<item>@string/multiplier_x8</item>
|
||||
<item>@string/multiplier_x16</item>
|
||||
<item>@string/multiplier_x32</item>
|
||||
<item>@string/multiplier_x64</item>
|
||||
<item>@string/multiplier_none</item>
|
||||
</string-array>
|
||||
<integer-array name="anisoValues">
|
||||
<item>0</item>
|
||||
@@ -524,9 +516,6 @@
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
<item>5</item>
|
||||
<item>6</item>
|
||||
<item>7</item>
|
||||
<item>8</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="verticalAlignmentEntries">
|
||||
|
||||
@@ -1246,9 +1246,6 @@
|
||||
<string name="multiplier_x4" translatable="false">x4</string>
|
||||
<string name="multiplier_x8" translatable="false">x8</string>
|
||||
<string name="multiplier_x16" translatable="false">x16</string>
|
||||
<string name="multiplier_x32" translatable="false">x32</string>
|
||||
<string name="multiplier_x64" translatable="false">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Black backgrounds</string>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 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
|
||||
|
||||
@@ -7,13 +10,12 @@
|
||||
namespace AudioCore::ADSP::OpusDecoder {
|
||||
|
||||
namespace {
|
||||
bool IsValidChannelCount(u32 channel_count) {
|
||||
return channel_count == 1 || channel_count == 2;
|
||||
}
|
||||
constexpr u32 OpusStreamCountMax = 255;
|
||||
|
||||
bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
|
||||
return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 &&
|
||||
stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count);
|
||||
return total_stream_count > 0 && total_stream_count <= OpusStreamCountMax &&
|
||||
static_cast<s32>(stereo_stream_count) >= 0 &&
|
||||
stereo_stream_count <= total_stream_count;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 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
|
||||
|
||||
@@ -105,7 +108,7 @@ Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 chan
|
||||
shared_memory.host_send_data[4] = total_stream_count;
|
||||
shared_memory.host_send_data[5] = stereo_stream_count;
|
||||
|
||||
ASSERT(channel_count <= MaxChannels);
|
||||
ASSERT(channel_count <= shared_memory.channel_mapping.size());
|
||||
std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8));
|
||||
|
||||
opus_decoder.Send(ADSP::Direction::DSP,
|
||||
|
||||
@@ -19,6 +19,23 @@
|
||||
namespace AudioCore::Sink {
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] bool InitializeAudio() {
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
// See https://github.com/PCSX2/pcsx2/pull/12312
|
||||
// "SDL and cubeb backends previously resulted in different names for the output which
|
||||
// caused them be identified as different applications by the OS."
|
||||
//
|
||||
// Keep in sync with cubeb_sink.cpp name.
|
||||
SDL_SetHint("SDL_AUDIO_DEVICE_APP_NAME", "yuzu Latency Getter");
|
||||
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SDL_AudioDeviceID FindAudioDeviceByName(const std::string& device_name, bool capture) {
|
||||
int device_count = 0;
|
||||
SDL_AudioDeviceID* devices = capture ? SDL_GetAudioRecordingDevices(&device_count)
|
||||
@@ -204,20 +221,14 @@ private:
|
||||
};
|
||||
|
||||
SDLSink::SDLSink(std::string_view target_device_name) {
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
||||
return;
|
||||
if (InitializeAudio()) {
|
||||
if (target_device_name != auto_device_name && !target_device_name.empty()) {
|
||||
output_device = target_device_name;
|
||||
} else {
|
||||
output_device.clear();
|
||||
}
|
||||
device_channels = 2;
|
||||
}
|
||||
|
||||
if (target_device_name != auto_device_name && !target_device_name.empty()) {
|
||||
output_device = target_device_name;
|
||||
} else {
|
||||
output_device.clear();
|
||||
}
|
||||
|
||||
device_channels = 2;
|
||||
}
|
||||
|
||||
SDLSink::~SDLSink() = default;
|
||||
@@ -265,15 +276,10 @@ void SDLSink::SetSystemVolume(f32 volume) {
|
||||
}
|
||||
|
||||
std::vector<std::string> ListSDLSinkDevices(bool capture) {
|
||||
if (!InitializeAudio())
|
||||
return {}; //no devices
|
||||
|
||||
std::vector<std::string> device_list;
|
||||
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
int device_count = 0;
|
||||
SDL_AudioDeviceID* devices =
|
||||
capture ? SDL_GetAudioRecordingDevices(&device_count)
|
||||
@@ -304,13 +310,8 @@ bool IsSDLSuitable() {
|
||||
return false;
|
||||
#else
|
||||
// Check SDL can init
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||
LOG_ERROR(Audio_Sink, "SDL failed to init, it is not suitable. Error: {}",
|
||||
SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!InitializeAudio()!
|
||||
return false;
|
||||
|
||||
// We can set any latency frequency we want with SDL, so no need to check that.
|
||||
|
||||
|
||||
+56
-45
@@ -39,6 +39,19 @@
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
/// @brief A log entry. Log entries are store in a structured format to permit more varied output
|
||||
/// formatting on different frontends, as well as facilitating filtering and aggregation.
|
||||
struct Entry {
|
||||
char const* message = nullptr;
|
||||
size_t message_len = 0;
|
||||
std::chrono::microseconds timestamp;
|
||||
Class log_class{};
|
||||
Level log_level{};
|
||||
const char* filename = nullptr;
|
||||
const char* function = nullptr;
|
||||
uint32_t line_num = 0;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
/// @brief Returns the name of the passed log class as a C-string. Subclasses are separated by periods
|
||||
@@ -70,8 +83,6 @@ const char* GetLevelName(Level log_level) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Some IDEs prefer <file>:<line> instead, so let's just do that :)
|
||||
std::string FormatLogMessage(const Entry& entry) noexcept {
|
||||
if (!entry.filename) return "";
|
||||
@@ -79,10 +90,9 @@ std::string FormatLogMessage(const Entry& entry) noexcept {
|
||||
auto const time_fractional = uint32_t(entry.timestamp.count() % 1000000);
|
||||
auto const class_name = GetLogClassName(entry.log_class);
|
||||
auto const level_name = GetLevelName(entry.log_level);
|
||||
return fmt::format("[{:4d}.{:06d}] {} <{}> {}:{}:{}: {}", time_seconds, time_fractional, class_name, level_name, entry.filename, entry.line_num, entry.function, entry.message);
|
||||
return fmt::format("[{:4d}.{:06d}] {} <{}> {}:{}:{}: {}\n", time_seconds, time_fractional, class_name, level_name, entry.filename, entry.line_num, entry.function, entry.message);
|
||||
}
|
||||
|
||||
namespace {
|
||||
template <typename It>
|
||||
Level GetLevelByName(const It begin, const It end) {
|
||||
for (u32 i = 0; i < u32(Level::Count); ++i) {
|
||||
@@ -127,25 +137,6 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
instance.SetClassLevel(log_class, level);
|
||||
return true;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void Filter::ParseFilterString(std::string_view filter_view) {
|
||||
auto clause_begin = filter_view.cbegin();
|
||||
while (clause_begin != filter_view.cend()) {
|
||||
auto clause_end = std::find(clause_begin, filter_view.cend(), ' ');
|
||||
// If clause isn't empty
|
||||
if (clause_end != clause_begin) {
|
||||
ParseFilterRule(*this, clause_begin, clause_end);
|
||||
}
|
||||
if (clause_end != filter_view.cend()) {
|
||||
// Skip over the whitespace
|
||||
++clause_end;
|
||||
}
|
||||
clause_begin = clause_end;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/// @brief Trims up to and including the last of ../, ..\, src/, src\ in a string
|
||||
/// do not be fooled this isn't generating new strings on .rodata :)
|
||||
@@ -208,7 +199,7 @@ struct ColorConsoleBackend final : public Backend {
|
||||
}());
|
||||
SetConsoleTextAttribute(console_handle, color);
|
||||
auto const df = GetDirectFormatArgs(entry);
|
||||
std::fprintf(stdout, CCB_PRINTF_FMT "\n", df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message.c_str());
|
||||
std::fprintf(stdout, CCB_PRINTF_FMT "\n", df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message);
|
||||
}
|
||||
}
|
||||
void Flush() noexcept override {}
|
||||
@@ -220,22 +211,24 @@ struct ColorConsoleBackend final : public Backend {
|
||||
~ColorConsoleBackend() noexcept override {}
|
||||
void Write(const Entry& entry) noexcept override {
|
||||
if (enabled) {
|
||||
#define ESC "\x1b"
|
||||
auto const color_str = [&entry]() -> const char* {
|
||||
switch (entry.log_level) {
|
||||
#define CCB_MAKE_COLOR_FMT(X) ESC X CCB_PRINTF_FMT ESC "[0m\n"
|
||||
case Level::Debug: return CCB_MAKE_COLOR_FMT("[0;36m"); // Cyan
|
||||
case Level::Info: return CCB_MAKE_COLOR_FMT("[0;37m"); // Bright gray
|
||||
case Level::Warning: return CCB_MAKE_COLOR_FMT("[1;33m"); // Bright yellow
|
||||
case Level::Error: return CCB_MAKE_COLOR_FMT("[1;31m"); // Bright red
|
||||
case Level::Critical: return CCB_MAKE_COLOR_FMT("[1;35m"); // Bright magenta
|
||||
default: return CCB_MAKE_COLOR_FMT("[1;30m"); // Grey
|
||||
#undef CCB_MAKE_COLOR_FMT
|
||||
case Level::Debug: return "[0;36m"; // Cyan
|
||||
case Level::Info: return "[0;37m"; // Bright gray
|
||||
case Level::Warning: return "[1;33m"; // Bright yellow
|
||||
case Level::Error: return "[1;31m"; // Bright red
|
||||
case Level::Critical: return "[1;35m"; // Bright magenta
|
||||
default: return "[1;30m"; // Grey
|
||||
}
|
||||
}();
|
||||
auto const df = GetDirectFormatArgs(entry);
|
||||
std::fprintf(stdout, color_str, df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message.c_str());
|
||||
#undef ESC
|
||||
// more restrictive, because take for example this simple prelude:
|
||||
// [ 50.872256] Config <Info> common/settings.cpp:142:LogSettings:
|
||||
char buffer[256];
|
||||
auto result = fmt::format_to_n(buffer, sizeof(buffer) - 1, "\x1b{}[{:4d}.{:06d}] {} <{}> {}:{}:{}: ", color_str, df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message);
|
||||
std::fwrite(buffer, 1, (std::min)(sizeof(buffer) - 1, result.size), stdout);
|
||||
std::fwrite(entry.message, 1, entry.message_len, stdout);
|
||||
std::fwrite("\x1b[0m\n", 1, sizeof("\x1b[0m\n"), stdout);
|
||||
}
|
||||
}
|
||||
void Flush() noexcept override {}
|
||||
@@ -246,7 +239,7 @@ struct ColorConsoleBackend final : public Backend {
|
||||
#ifndef __OPENORBIS__
|
||||
/// @brief Backend that writes to a file passed into the constructor
|
||||
struct FileBackend final : public Backend {
|
||||
explicit FileBackend(const std::filesystem::path& filename) noexcept {
|
||||
explicit FileBackend(const std::filesystem::path filename) noexcept {
|
||||
auto old_filename = filename;
|
||||
old_filename += ".old.txt";
|
||||
// Existence checks are done within the functions themselves.
|
||||
@@ -261,7 +254,7 @@ struct FileBackend final : public Backend {
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
auto message = FormatLogMessage(entry).append(1, '\n');
|
||||
auto message = FormatLogMessage(entry);
|
||||
#ifndef __ANDROID__
|
||||
if (Settings::values.censor_username.GetValue()) {
|
||||
// This must be a static otherwise it would get checked on EVERY
|
||||
@@ -269,8 +262,7 @@ struct FileBackend final : public Backend {
|
||||
static std::string username = []() -> std::string {
|
||||
// in order of precedence
|
||||
// LOGNAME usually works on UNIX, USERNAME on Windows
|
||||
// Some UNIX systems suck and don't use LOGNAME so we also
|
||||
// need USER :(
|
||||
// Some UNIX systems suck and don't use LOGNAME so we also need USER :(
|
||||
for (auto const var : { "LOGNAME", "USERNAME", "USER", })
|
||||
if (auto const s = ::getenv(var); s != nullptr)
|
||||
return std::string{s};
|
||||
@@ -280,7 +272,7 @@ struct FileBackend final : public Backend {
|
||||
boost::replace_all(message, username, "user");
|
||||
}
|
||||
#endif
|
||||
bytes_written += file->WriteString(message);
|
||||
bytes_written += file->WriteSpan(std::span<const char>{message.begin(), message.end()});
|
||||
|
||||
// Option to log each line rather than 4k buffers
|
||||
if (Settings::values.log_flush_line.GetValue())
|
||||
@@ -308,14 +300,13 @@ private:
|
||||
bool enabled = true;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
/// @brief Backend that writes to Visual Studio's output window
|
||||
struct DebuggerBackend final : public Backend {
|
||||
explicit DebuggerBackend() noexcept = default;
|
||||
~DebuggerBackend() noexcept override = default;
|
||||
void Write(const Entry& entry) noexcept override {
|
||||
::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
|
||||
::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry)).c_str());
|
||||
}
|
||||
void Flush() noexcept override {}
|
||||
};
|
||||
@@ -338,7 +329,7 @@ struct LogcatBackend : public Backend {
|
||||
}
|
||||
}();
|
||||
auto const df = GetDirectFormatArgs(entry);
|
||||
__android_log_print(android_log_priority, "YuzuNative", CCB_PRINTF_FMT, df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message.c_str());
|
||||
__android_log_print(android_log_priority, "YuzuNative", CCB_PRINTF_FMT, df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message);
|
||||
}
|
||||
void Flush() noexcept override {}
|
||||
};
|
||||
@@ -377,7 +368,23 @@ struct Impl {
|
||||
#endif
|
||||
std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
|
||||
};
|
||||
} // namespace
|
||||
} // Anonymous namespace
|
||||
|
||||
void Filter::ParseFilterString(std::string_view filter_view) {
|
||||
auto clause_begin = filter_view.cbegin();
|
||||
while (clause_begin < filter_view.cend()) {
|
||||
auto clause_end = std::find(clause_begin, filter_view.cend(), ' ');
|
||||
// If clause isn't empty
|
||||
if (clause_end != clause_begin) {
|
||||
ParseFilterRule(*this, clause_begin, clause_end);
|
||||
}
|
||||
if (clause_end != filter_view.cend()) {
|
||||
// Skip over the whitespace
|
||||
++clause_end;
|
||||
}
|
||||
clause_begin = clause_end;
|
||||
}
|
||||
}
|
||||
|
||||
// Constructor shall NOT depend upon Settings() or whatever
|
||||
// it's ran at global static ctor() time... so BE CAREFUL MFER!
|
||||
@@ -419,9 +426,13 @@ void SetColorConsoleBackendEnabled(bool enabled) {
|
||||
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, fmt::string_view format, const fmt::format_args& args) {
|
||||
if (logging_instance && logging_instance->filter.CheckMessage(log_class, log_level)) {
|
||||
auto const flush = ::Settings::values.log_flush_line.GetValue();
|
||||
char buffer[BUFSIZ];
|
||||
auto result = fmt::vformat_to_n(buffer, sizeof(buffer) - 1, format, args);
|
||||
buffer[(std::min)(result.size, sizeof(buffer) - 1)] = '\0';
|
||||
logging_instance->ForEachBackend([=](Backend& backend) {
|
||||
backend.Write(Entry{
|
||||
.message = fmt::vformat(format, args),
|
||||
.message = buffer,
|
||||
.message_len = (std::min)(result.size, sizeof(buffer) - 1),
|
||||
.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - logging_instance->time_origin),
|
||||
.log_class = log_class,
|
||||
.log_level = log_level,
|
||||
|
||||
@@ -140,25 +140,4 @@ void Stop();
|
||||
void SetGlobalFilter(const Filter& filter);
|
||||
void SetColorConsoleBackendEnabled(bool enabled);
|
||||
|
||||
/// @brief A log entry. Log entries are store in a structured format to permit more varied output
|
||||
/// formatting on different frontends, as well as facilitating filtering and aggregation.
|
||||
struct Entry {
|
||||
std::string message;
|
||||
std::chrono::microseconds timestamp;
|
||||
Class log_class{};
|
||||
Level log_level{};
|
||||
const char* filename = nullptr;
|
||||
const char* function = nullptr;
|
||||
unsigned int line_num = 0;
|
||||
};
|
||||
|
||||
/// Formats a log entry into the provided text buffer.
|
||||
std::string FormatLogMessage(const Entry& entry) noexcept;
|
||||
|
||||
/// Prints the same message as `PrintMessage`, but colored according to the severity level.
|
||||
void PrintColoredMessage(const Entry& entry) noexcept;
|
||||
|
||||
/// Formats and prints a log entry to the android logcat.
|
||||
void PrintMessageToLogcat(const Entry& entry) noexcept;
|
||||
|
||||
} // namespace Common::Log
|
||||
|
||||
@@ -132,11 +132,9 @@ void LogSettings() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string settings_str{};
|
||||
LOG_INFO(Config, "Eden Configuration:");
|
||||
for (auto const& e : settings_list)
|
||||
settings_str += e;
|
||||
LOG_INFO(Config, "Eden Configuration:\n{}", settings_str);
|
||||
LOG_INFO(Config, "{}", e);
|
||||
#define LOG_PATH(NAME) \
|
||||
LOG_INFO(Config, #NAME ": {}", Common::FS::PathToUTF8String(Common::FS::GetEdenPath(Common::FS::EdenPath::NAME)))
|
||||
LOG_PATH(CacheDir);
|
||||
|
||||
@@ -128,7 +128,7 @@ ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt
|
||||
GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica,
|
||||
Japan, Kwajalein, Libya, Met, Mst, Mst7Mdt, Navajo, Nz, NzChat, Poland, Portugal, Prc, Pst8Pdt,
|
||||
Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu);
|
||||
ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16, X32, X64, None);
|
||||
ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16);
|
||||
ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous);
|
||||
ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
|
||||
ENUM(FramePacingMode, Target_Auto, Target_30, Target_60, Target_90, Target_120);
|
||||
|
||||
@@ -7,6 +7,4 @@
|
||||
#define STB_IMAGE_IMPLEMENTATION 1
|
||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION 1
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION 1
|
||||
#define STBI_ONLY_JPEG 1
|
||||
|
||||
#include "common/stb.h"
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#define STBI_ONLY_JPEG 1
|
||||
#define STBI_WRITE_NO_STDIO 1
|
||||
#include <stb_image.h>
|
||||
#include <stb_image_resize.h>
|
||||
#include <stb_image_write.h>
|
||||
|
||||
@@ -286,6 +286,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||
break;
|
||||
// Paranoia mode for debugging optimizations
|
||||
case Settings::CpuAccuracy::Paranoid:
|
||||
|
||||
@@ -338,6 +338,7 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
||||
config.unsafe_optimizations = true;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
config.fastmem_address_space_bits = 64;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||
break;
|
||||
// Paranoia mode for debugging optimizations
|
||||
case Settings::CpuAccuracy::Paranoid:
|
||||
|
||||
+79
-9
@@ -4,6 +4,7 @@
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "game_settings.h"
|
||||
@@ -16,6 +17,7 @@
|
||||
#include "common/string_util.h"
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/common_funcs.h"
|
||||
|
||||
#include "launch_timestamp_cache.h"
|
||||
#include "core/core_timing.h"
|
||||
@@ -248,12 +250,30 @@ struct System::Impl {
|
||||
}
|
||||
}
|
||||
|
||||
void SetNVDECActive(bool is_nvdec_active) {
|
||||
nvdec_active = is_nvdec_active;
|
||||
void NotifyNVDECChannelOpen(u64 process_id) {
|
||||
std::scoped_lock lock{nvdec_active_mutex};
|
||||
++nvdec_active_channels[process_id];
|
||||
}
|
||||
|
||||
void NotifyNVDECChannelClose(u64 process_id) {
|
||||
std::scoped_lock lock{nvdec_active_mutex};
|
||||
const auto it = nvdec_active_channels.find(process_id);
|
||||
if (it == nvdec_active_channels.end()) {
|
||||
return;
|
||||
}
|
||||
if (--it->second == 0) {
|
||||
nvdec_active_channels.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
bool GetNVDECActive() {
|
||||
return nvdec_active;
|
||||
std::scoped_lock lock{nvdec_active_mutex};
|
||||
return !nvdec_active_channels.empty();
|
||||
}
|
||||
|
||||
bool IsNVDECActiveForProcess(u64 process_id) {
|
||||
std::scoped_lock lock{nvdec_active_mutex};
|
||||
return nvdec_active_channels.contains(process_id);
|
||||
}
|
||||
|
||||
void InitializeDebugger(System& system, u16 port) {
|
||||
@@ -295,6 +315,10 @@ struct System::Impl {
|
||||
SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath, Service::AM::FrontendAppletParameters& params) {
|
||||
InitializeKernel(system);
|
||||
|
||||
if (params.applet_type == Service::AM::AppletType::Application) {
|
||||
current_application_filepath = filepath;
|
||||
}
|
||||
|
||||
const auto file = GetGameFileFromPath(virtual_filesystem, filepath);
|
||||
|
||||
// Create the application process
|
||||
@@ -330,7 +354,7 @@ struct System::Impl {
|
||||
LaunchTimestampCache::SaveLaunchTimestamp(params.program_id);
|
||||
|
||||
// Make the process created be the application
|
||||
kernel.MakeApplicationProcess(process->GetHandle());
|
||||
kernel.SetApplicationProcess(process->GetHandle());
|
||||
|
||||
// Set up the rest of the system.
|
||||
SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)};
|
||||
@@ -373,7 +397,7 @@ struct System::Impl {
|
||||
LOG_ERROR(Core, "Failed to find program id for ROM");
|
||||
}
|
||||
|
||||
GameSettings::LoadOverrides(program_id, gpu_core->Renderer());
|
||||
GameSettings::LoadOverrides(FileSys::GetBaseTitleID(program_id), gpu_core->Renderer());
|
||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||
Network::GameInfo game_info;
|
||||
game_info.name = name;
|
||||
@@ -467,6 +491,7 @@ struct System::Impl {
|
||||
Core::SpeedLimiter speed_limiter;
|
||||
ExecuteProgramCallback execute_program_callback;
|
||||
ExitCallback exit_callback;
|
||||
ApplicationChangedCallback application_changed_callback;
|
||||
|
||||
std::optional<Service::Services> services;
|
||||
std::optional<Core::Debugger> debugger;
|
||||
@@ -488,6 +513,8 @@ struct System::Impl {
|
||||
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
|
||||
std::array<u8, 0x20> build_id{};
|
||||
|
||||
std::string current_application_filepath;
|
||||
|
||||
/// Service manager
|
||||
std::shared_ptr<Service::SM::ServiceManager> service_manager;
|
||||
/// ContentProviderUnion instance
|
||||
@@ -498,6 +525,8 @@ struct System::Impl {
|
||||
|
||||
mutable std::mutex suspend_guard;
|
||||
std::mutex general_channel_mutex;
|
||||
std::mutex nvdec_active_mutex;
|
||||
std::unordered_map<u64, u32> nvdec_active_channels;
|
||||
std::atomic_bool is_paused{};
|
||||
std::atomic_bool is_shutting_down{};
|
||||
std::atomic_bool is_powered_on{};
|
||||
@@ -505,7 +534,6 @@ struct System::Impl {
|
||||
bool extended_memory_layout : 1 = false;
|
||||
bool exit_locked : 1 = false;
|
||||
bool exit_requested : 1 = false;
|
||||
bool nvdec_active : 1 = false;
|
||||
|
||||
void EnsureGeneralChannelInitialized(System& system) {
|
||||
if (!general_channel_event) {
|
||||
@@ -569,14 +597,22 @@ void System::UnstallApplication() {
|
||||
impl->UnstallApplication();
|
||||
}
|
||||
|
||||
void System::SetNVDECActive(bool is_nvdec_active) {
|
||||
impl->SetNVDECActive(is_nvdec_active);
|
||||
void System::NotifyNVDECChannelOpen(u64 process_id) {
|
||||
impl->NotifyNVDECChannelOpen(process_id);
|
||||
}
|
||||
|
||||
void System::NotifyNVDECChannelClose(u64 process_id) {
|
||||
impl->NotifyNVDECChannelClose(process_id);
|
||||
}
|
||||
|
||||
bool System::GetNVDECActive() {
|
||||
return impl->GetNVDECActive();
|
||||
}
|
||||
|
||||
bool System::IsNVDECActiveForProcess(u64 process_id) {
|
||||
return impl->IsNVDECActiveForProcess(process_id);
|
||||
}
|
||||
|
||||
void System::InitializeDebugger() {
|
||||
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
||||
}
|
||||
@@ -727,7 +763,25 @@ const Core::SpeedLimiter& System::SpeedLimiter() const {
|
||||
}
|
||||
|
||||
u64 System::GetApplicationProcessProgramID() const {
|
||||
return impl->kernel.ApplicationProcess()->GetProgramId();
|
||||
const auto* const process = impl->kernel.ApplicationProcess();
|
||||
return process != nullptr ? process->GetProgramId() : 0;
|
||||
}
|
||||
|
||||
u64 System::GetProgramIdForProcessId(u64 process_id) const {
|
||||
auto process = impl->kernel.GetProcessByProcessId(process_id);
|
||||
return process.IsNull() ? 0 : process->GetProgramId();
|
||||
}
|
||||
|
||||
u64 System::ResolveCallerProgramId(u64 process_id) const {
|
||||
if (const auto program_id = this->GetProgramIdForProcessId(process_id); program_id != 0) {
|
||||
return program_id;
|
||||
}
|
||||
|
||||
const auto fallback = this->GetApplicationProcessProgramID();
|
||||
LOG_WARNING(Core,
|
||||
"Could not resolve caller process_id={}, falling back to application {:016X}",
|
||||
process_id, fallback);
|
||||
return fallback;
|
||||
}
|
||||
|
||||
Loader::ResultStatus System::GetGameName(std::string& out) const {
|
||||
@@ -910,6 +964,10 @@ void System::ExecuteProgram(std::size_t program_index) {
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& System::GetCurrentApplicationFilePath() const {
|
||||
return impl->current_application_filepath;
|
||||
}
|
||||
|
||||
/// @brief Gets a reference to the user channel stack.
|
||||
/// It is used to transfer data between programs.
|
||||
std::vector<std::vector<u8>>& System::GetUserChannel() {
|
||||
@@ -961,6 +1019,18 @@ void System::Exit() {
|
||||
}
|
||||
}
|
||||
|
||||
void System::RegisterApplicationChangedCallback(ApplicationChangedCallback&& callback) {
|
||||
impl->application_changed_callback = std::move(callback);
|
||||
}
|
||||
|
||||
void System::NotifyApplicationChanged(u64 program_id) {
|
||||
//LOG_DEBUG(Core, "Running application changed to {:016X}", program_id);
|
||||
|
||||
if (impl->application_changed_callback) {
|
||||
impl->application_changed_callback(program_id);
|
||||
}
|
||||
}
|
||||
|
||||
void System::ApplySettings() {
|
||||
impl->RefreshTime(*this);
|
||||
|
||||
|
||||
+12
-1
@@ -191,8 +191,10 @@ public:
|
||||
std::unique_lock<std::mutex> StallApplication();
|
||||
void UnstallApplication();
|
||||
|
||||
void SetNVDECActive(bool is_nvdec_active);
|
||||
void NotifyNVDECChannelOpen(u64 process_id);
|
||||
void NotifyNVDECChannelClose(u64 process_id);
|
||||
[[nodiscard]] bool GetNVDECActive();
|
||||
[[nodiscard]] bool IsNVDECActiveForProcess(u64 process_id);
|
||||
|
||||
/**
|
||||
* Initialize the debugger.
|
||||
@@ -322,6 +324,10 @@ public:
|
||||
|
||||
[[nodiscard]] u64 GetApplicationProcessProgramID() const;
|
||||
|
||||
[[nodiscard]] u64 GetProgramIdForProcessId(u64 process_id) const;
|
||||
|
||||
[[nodiscard]] u64 ResolveCallerProgramId(u64 process_id) const;
|
||||
|
||||
/// Gets the name of the current game
|
||||
[[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const;
|
||||
|
||||
@@ -422,6 +428,7 @@ public:
|
||||
void PushGeneralChannelData(std::vector<u8>&& data);
|
||||
bool TryPopGeneralChannel(std::vector<u8>& out_data);
|
||||
[[nodiscard]] Service::Event& GetGeneralChannelEvent();
|
||||
[[nodiscard]] const std::string& GetCurrentApplicationFilePath() const;
|
||||
|
||||
/// Type used for the frontend to designate a callback for System to exit the application.
|
||||
using ExitCallback = std::function<void()>;
|
||||
@@ -435,6 +442,10 @@ public:
|
||||
/// Instructs the frontend to exit the application.
|
||||
void Exit();
|
||||
|
||||
using ApplicationChangedCallback = std::function<void(u64 program_id)>;
|
||||
void RegisterApplicationChangedCallback(ApplicationChangedCallback&& callback);
|
||||
void NotifyApplicationChanged(u64 program_id);
|
||||
|
||||
/// Applies any changes to settings to this core instance.
|
||||
void ApplySettings();
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ static u8 MasterKeyIdForKeyGeneration(u8 key_generation) {
|
||||
return std::max<u8>(key_generation, 1) - 1;
|
||||
}
|
||||
|
||||
NCA::NCA(VirtualFile file_, const NCA* base_nca)
|
||||
NCA::NCA(VirtualFile file_, const NCA* base_nca, bool allow_missing_base)
|
||||
: file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} {
|
||||
if (file == nullptr) {
|
||||
status = Loader::ResultStatus::ErrorNullFile;
|
||||
@@ -110,7 +110,7 @@ NCA::NCA(VirtualFile file_, const NCA* base_nca)
|
||||
}
|
||||
}
|
||||
|
||||
if (is_update && base_nca == nullptr) {
|
||||
if (is_update && base_nca == nullptr && !allow_missing_base) {
|
||||
status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
|
||||
} else {
|
||||
status = Loader::ResultStatus::Success;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 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
|
||||
|
||||
@@ -63,7 +66,7 @@ inline bool IsDirectoryLogoPartition(const VirtualDir& pfs) {
|
||||
// After construction, use GetStatus to determine if the file is valid and ready to be used.
|
||||
class NCA : public ReadOnlyVfsDirectory {
|
||||
public:
|
||||
explicit NCA(VirtualFile file, const NCA* base_nca = nullptr);
|
||||
explicit NCA(VirtualFile file, const NCA* base_nca = nullptr, bool allow_missing_base = false);
|
||||
~NCA() override;
|
||||
|
||||
Loader::ResultStatus GetStatus() const;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/file_sys/fssystem/fssystem_indirect_storage.h"
|
||||
|
||||
@@ -99,6 +100,18 @@ Result IndirectStorage::GetEntryList(Entry* out_entries, s32* out_entry_count, s
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void IndirectStorage::ReportMissingOriginal(s64 offset, s64 size) {
|
||||
if (m_reported_missing_original) {
|
||||
return;
|
||||
}
|
||||
m_reported_missing_original = true;
|
||||
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Patch storage requests {:#x} bytes at {:#x} from a base storage that is not "
|
||||
"present; this data will read as garbage",
|
||||
size, offset);
|
||||
}
|
||||
|
||||
size_t IndirectStorage::Read(u8* buffer, size_t size, size_t offset) const {
|
||||
// Validate pre-conditions.
|
||||
ASSERT(this->IsInitialized());
|
||||
|
||||
@@ -85,6 +85,9 @@ public:
|
||||
void SetStorage(s32 idx, VirtualFile storage) {
|
||||
ASSERT(0 <= idx && idx < StorageCount);
|
||||
m_data_storage[idx] = storage;
|
||||
if (idx == 0) {
|
||||
m_original_missing = storage == nullptr || storage->GetSize() == 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -130,6 +133,9 @@ protected:
|
||||
|
||||
template <bool ContinuousCheck, bool RangeCheck, typename F>
|
||||
Result OperatePerEntry(s64 offset, s64 size, F func);
|
||||
// Launching another game makes the original storage inaccessable.
|
||||
// This is a helper for multi-nca games.
|
||||
void ReportMissingOriginal(s64 offset, s64 size);
|
||||
|
||||
private:
|
||||
struct ContinuousReadingEntry {
|
||||
@@ -154,6 +160,8 @@ private:
|
||||
private:
|
||||
mutable BucketTree m_table;
|
||||
std::array<VirtualFile, StorageCount> m_data_storage;
|
||||
bool m_original_missing{false};
|
||||
bool m_reported_missing_original{false};
|
||||
};
|
||||
|
||||
template <bool ContinuousCheck, bool RangeCheck, typename F>
|
||||
@@ -272,6 +280,10 @@ Result IndirectStorage::OperatePerEntry(s64 offset, s64 size, F func) {
|
||||
if (needs_operate) {
|
||||
const auto cur_entry_phys_offset = cur_entry.GetPhysicalOffset();
|
||||
|
||||
if (cur_entry.storage_index == 0 && m_original_missing) {
|
||||
this->ReportMissingOriginal(cur_offset, cur_size);
|
||||
}
|
||||
|
||||
if constexpr (RangeCheck) {
|
||||
// Get the current data storage's size.
|
||||
s64 cur_data_storage_size = m_data_storage[cur_entry.storage_index]->GetSize();
|
||||
|
||||
@@ -48,11 +48,11 @@ struct ContentRecord {
|
||||
std::array<u8, 0x10> nca_id;
|
||||
std::array<u8, 0x6> size;
|
||||
ContentRecordType type;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u8 id_offset;
|
||||
};
|
||||
static_assert(sizeof(ContentRecord) == 0x38, "ContentRecord has incorrect size.");
|
||||
|
||||
constexpr ContentRecord EMPTY_META_CONTENT_RECORD{{}, {}, {}, ContentRecordType::Meta, {}};
|
||||
constexpr ContentRecord EMPTY_META_CONTENT_RECORD{{}, {}, {}, ContentRecordType::Meta, 0};
|
||||
|
||||
struct MetaRecord {
|
||||
u64_le title_id;
|
||||
|
||||
@@ -161,7 +161,7 @@ std::string GetUpdateVersionStringFromSlot(const ContentProvider* provider, u64
|
||||
PatchManager::PatchManager(u64 title_id_,
|
||||
const Service::FileSystem::FileSystemController& fs_controller_,
|
||||
const ContentProvider& content_provider_)
|
||||
: title_id{title_id_}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
|
||||
: title_id{title_id_}, application_id{GetBaseTitleID(title_id_)}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
|
||||
|
||||
PatchManager::~PatchManager() = default;
|
||||
|
||||
@@ -169,13 +169,41 @@ u64 PatchManager::GetTitleID() const {
|
||||
return title_id;
|
||||
}
|
||||
|
||||
u64 PatchManager::GetUpdateTitleIDForContent() const {
|
||||
const auto program_update_id = GetUpdateTitleID(title_id);
|
||||
if (program_update_id == GetUpdateTitleID(application_id) || content_provider.HasEntry(program_update_id, ContentRecordType::Program)) {
|
||||
return program_update_id;
|
||||
}
|
||||
return GetUpdateTitleID(application_id);
|
||||
}
|
||||
|
||||
std::vector<VirtualDir> PatchManager::GetModificationLoadRoots() const {
|
||||
std::vector<VirtualDir> roots;
|
||||
roots.push_back(fs_controller.GetModificationLoadRoot(title_id));
|
||||
if (application_id != title_id) {
|
||||
roots.push_back(fs_controller.GetModificationLoadRoot(application_id));
|
||||
}
|
||||
std::erase(roots, nullptr);
|
||||
return roots;
|
||||
}
|
||||
|
||||
std::vector<VirtualDir> PatchManager::GetSDMCModificationLoadRoots() const {
|
||||
std::vector<VirtualDir> roots;
|
||||
roots.push_back(fs_controller.GetSDMCModificationLoadRoot(title_id));
|
||||
if (application_id != title_id) {
|
||||
roots.push_back(fs_controller.GetSDMCModificationLoadRoot(application_id));
|
||||
}
|
||||
std::erase(roots, nullptr);
|
||||
return roots;
|
||||
}
|
||||
|
||||
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
|
||||
|
||||
if (exefs == nullptr)
|
||||
return exefs;
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
const auto& disabled = Settings::values.disabled_addons[application_id];
|
||||
|
||||
bool update_disabled = true;
|
||||
std::optional<u32> enabled_version;
|
||||
@@ -183,7 +211,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
bool checked_manual = false;
|
||||
|
||||
const auto* content_union = static_cast<const ContentProviderUnion*>(&content_provider);
|
||||
const auto update_tid = GetUpdateTitleID(title_id);
|
||||
const auto update_tid = GetUpdateTitleIDForContent();
|
||||
|
||||
if (content_union) {
|
||||
// First, check ExternalContentProvider
|
||||
@@ -303,17 +331,21 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
}
|
||||
|
||||
// LayeredExeFS
|
||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
||||
const auto load_dirs = GetModificationLoadRoots();
|
||||
const auto sdmc_load_dirs = GetSDMCModificationLoadRoots();
|
||||
|
||||
std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
|
||||
if (load_dir != nullptr) {
|
||||
std::vector<VirtualDir> patch_dirs;
|
||||
for (const auto& sdmc_load_dir : sdmc_load_dirs) {
|
||||
patch_dirs.push_back(sdmc_load_dir);
|
||||
}
|
||||
for (const auto& load_dir : load_dirs) {
|
||||
const auto load_patch_dirs = load_dir->GetSubdirectories();
|
||||
patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
|
||||
}
|
||||
|
||||
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
||||
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
||||
std::stable_sort(patch_dirs.begin(), patch_dirs.end(), [](const VirtualDir& l, const VirtualDir& r) {
|
||||
return l->GetName() < r->GetName();
|
||||
});
|
||||
|
||||
std::vector<VirtualDir> layers;
|
||||
layers.reserve(patch_dirs.size() + 1);
|
||||
@@ -347,7 +379,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
|
||||
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
||||
const std::string& build_id) const {
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
const auto& disabled = Settings::values.disabled_addons[application_id];
|
||||
const auto nso_build_id = fmt::format("{:0<64}", build_id);
|
||||
|
||||
std::vector<VirtualFile> out;
|
||||
@@ -412,15 +444,20 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
|
||||
|
||||
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
|
||||
|
||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||
if (load_dir == nullptr) {
|
||||
const auto load_dirs = GetModificationLoadRoots();
|
||||
if (load_dirs.empty()) {
|
||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||
return nso;
|
||||
}
|
||||
|
||||
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(); });
|
||||
std::vector<VirtualDir> patch_dirs;
|
||||
for (const auto& load_dir : load_dirs) {
|
||||
const auto load_patch_dirs = load_dir->GetSubdirectories();
|
||||
patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
|
||||
}
|
||||
std::stable_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);
|
||||
|
||||
auto out = nso;
|
||||
@@ -455,29 +492,39 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
|
||||
|
||||
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}, name={}", build_id, name);
|
||||
|
||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||
if (load_dir == nullptr) {
|
||||
const auto load_dirs = GetModificationLoadRoots();
|
||||
if (load_dirs.empty()) {
|
||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
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(); });
|
||||
std::vector<VirtualDir> patch_dirs;
|
||||
for (const auto& load_dir : load_dirs) {
|
||||
const auto load_patch_dirs = load_dir->GetSubdirectories();
|
||||
patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
|
||||
}
|
||||
std::stable_sort(patch_dirs.begin(), patch_dirs.end(), [](const VirtualDir& l, const VirtualDir& r) {
|
||||
return l->GetName() < r->GetName();
|
||||
});
|
||||
|
||||
return !CollectPatches(patch_dirs, build_id).empty();
|
||||
}
|
||||
|
||||
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
|
||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||
if (load_dir == nullptr) {
|
||||
const auto load_dirs = GetModificationLoadRoots();
|
||||
if (load_dirs.empty()) {
|
||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
auto patch_dirs = load_dir->GetSubdirectories();
|
||||
std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
|
||||
const auto& disabled = Settings::values.disabled_addons[application_id];
|
||||
std::vector<VirtualDir> patch_dirs;
|
||||
for (const auto& load_dir : load_dirs) {
|
||||
const auto load_patch_dirs = load_dir->GetSubdirectories();
|
||||
patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
|
||||
}
|
||||
std::stable_sort(patch_dirs.begin(), patch_dirs.end(),
|
||||
[](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
|
||||
|
||||
// <mod dir> / <folder> / cheats / <build id>.txt
|
||||
std::vector<Core::Memory::CheatEntry> out;
|
||||
@@ -493,39 +540,52 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildI
|
||||
}
|
||||
// Uncareless user-friendly loading of patches (must start with 'cheat_')
|
||||
// <mod dir> / <cheat file>.txt
|
||||
for (auto const& f : load_dir->GetFiles()) {
|
||||
auto const name = f->GetName();
|
||||
if (name.starts_with("cheat_") && std::find(disabled.cbegin(), disabled.cend(), name) == disabled.cend()) {
|
||||
std::vector<u8> data(f->GetSize());
|
||||
if (f->Read(data.data(), data.size()) == data.size()) {
|
||||
const Core::Memory::TextCheatParser parser;
|
||||
auto const res = parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
|
||||
std::copy(res.begin(), res.end(), std::back_inserter(out));
|
||||
} else {
|
||||
LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}", title_id);
|
||||
for (const auto& load_dir : load_dirs) {
|
||||
for (auto const& f : load_dir->GetFiles()) {
|
||||
auto const name = f->GetName();
|
||||
if (name.starts_with("cheat_") && std::find(disabled.cbegin(), disabled.cend(), name) == disabled.cend()) {
|
||||
std::vector<u8> data(f->GetSize());
|
||||
if (f->Read(data.data(), data.size()) == data.size()) {
|
||||
const Core::Memory::TextCheatParser parser;
|
||||
auto const res = parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
|
||||
std::copy(res.begin(), res.end(), std::back_inserter(out));
|
||||
} else {
|
||||
LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}", title_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type,
|
||||
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, u64 application_id, ContentRecordType type,
|
||||
const Service::FileSystem::FileSystemController& fs_controller) {
|
||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
||||
std::vector<VirtualDir> load_dirs{fs_controller.GetModificationLoadRoot(title_id)};
|
||||
std::vector<VirtualDir> sdmc_load_dirs{fs_controller.GetSDMCModificationLoadRoot(title_id)};
|
||||
if (application_id != title_id) {
|
||||
load_dirs.push_back(fs_controller.GetModificationLoadRoot(application_id));
|
||||
sdmc_load_dirs.push_back(fs_controller.GetSDMCModificationLoadRoot(application_id));
|
||||
}
|
||||
std::erase(load_dirs, nullptr);
|
||||
std::erase(sdmc_load_dirs, nullptr);
|
||||
if ((type != ContentRecordType::Program && type != ContentRecordType::Data &&
|
||||
type != ContentRecordType::HtmlDocument) ||
|
||||
(load_dir == nullptr && sdmc_load_dir == nullptr)) {
|
||||
(load_dirs.empty() && sdmc_load_dirs.empty())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories();
|
||||
if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
|
||||
patch_dirs.push_back(sdmc_load_dir);
|
||||
const auto& disabled = Settings::values.disabled_addons[application_id];
|
||||
std::vector<VirtualDir> patch_dirs;
|
||||
for (const auto& load_dir : load_dirs) {
|
||||
const auto load_patch_dirs = load_dir->GetSubdirectories();
|
||||
patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
|
||||
}
|
||||
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
||||
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
||||
if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
|
||||
patch_dirs.insert(patch_dirs.end(), sdmc_load_dirs.begin(), sdmc_load_dirs.end());
|
||||
}
|
||||
std::stable_sort(patch_dirs.begin(), patch_dirs.end(), [](const VirtualDir& l, const VirtualDir& r) {
|
||||
return l->GetName() < r->GetName();
|
||||
});
|
||||
|
||||
std::vector<VirtualDir> layers;
|
||||
std::vector<VirtualDir> layers_ext;
|
||||
@@ -597,8 +657,8 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
|
||||
auto romfs = base_romfs;
|
||||
|
||||
// Game Updates
|
||||
const auto update_tid = GetUpdateTitleID(title_id);
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
const auto update_tid = GetUpdateTitleIDForContent();
|
||||
const auto& disabled = Settings::values.disabled_addons[application_id];
|
||||
|
||||
bool update_disabled = true;
|
||||
std::optional<u32> enabled_version;
|
||||
@@ -705,7 +765,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
|
||||
|
||||
// LayeredFS
|
||||
if (apply_layeredfs) {
|
||||
ApplyLayeredFS(romfs, title_id, type, fs_controller);
|
||||
ApplyLayeredFS(romfs, title_id, application_id, type, fs_controller);
|
||||
}
|
||||
|
||||
return romfs;
|
||||
@@ -717,10 +777,10 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
||||
}
|
||||
|
||||
std::vector<Patch> out;
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
const auto& disabled = Settings::values.disabled_addons[application_id];
|
||||
|
||||
// Game Updates
|
||||
const auto update_tid = GetUpdateTitleID(title_id);
|
||||
const auto update_tid = GetUpdateTitleIDForContent();
|
||||
|
||||
std::vector<Patch> external_update_patches;
|
||||
|
||||
@@ -869,7 +929,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
||||
.version = "",
|
||||
.type = PatchType::Update,
|
||||
.program_id = title_id,
|
||||
.title_id = title_id,
|
||||
.title_id = update_tid,
|
||||
.source = PatchSource::Unknown,
|
||||
.numeric_version = 0};
|
||||
|
||||
@@ -895,8 +955,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
||||
}
|
||||
|
||||
// General Mods (LayeredFS and IPS)
|
||||
const auto mod_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||
if (mod_dir != nullptr) {
|
||||
for (const auto& mod_dir : GetModificationLoadRoots()) {
|
||||
for (auto const& f : mod_dir->GetFiles())
|
||||
if (auto const name = f->GetName(); name.starts_with("cheat_")) {
|
||||
auto const mod_disabled = std::find(disabled.begin(), disabled.end(), name) != disabled.end();
|
||||
@@ -963,8 +1022,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
||||
}
|
||||
|
||||
// SDMC mod directory (RomFS LayeredFS)
|
||||
const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
||||
if (sdmc_mod_dir != nullptr) {
|
||||
for (const auto& sdmc_mod_dir : GetSDMCModificationLoadRoots()) {
|
||||
std::string types;
|
||||
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs")))
|
||||
AppendCommaIfNotEmpty(types, "LayeredExeFS");
|
||||
@@ -999,10 +1057,10 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
||||
dlc_match.reserve(dlc_entries_with_origin.size());
|
||||
for (const auto& [slot, entry] : dlc_entries_with_origin) {
|
||||
const auto base_tid = GetBaseTitleID(entry.title_id);
|
||||
const bool matches_base = base_tid == title_id;
|
||||
const bool matches_base = base_tid == application_id;
|
||||
if (!matches_base) {
|
||||
LOG_DEBUG(Loader, "DLC {:016X} base {:016X} doesn't match title {:016X}",
|
||||
entry.title_id, base_tid, title_id);
|
||||
entry.title_id, base_tid, application_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1077,16 +1135,22 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
||||
}
|
||||
|
||||
std::optional<u32> PatchManager::GetGameVersion() const {
|
||||
const auto update_tid = GetUpdateTitleID(title_id);
|
||||
const auto update_tid = GetUpdateTitleIDForContent();
|
||||
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
|
||||
return content_provider.GetEntryVersion(update_tid);
|
||||
}
|
||||
|
||||
return content_provider.GetEntryVersion(title_id);
|
||||
if (const auto version = content_provider.GetEntryVersion(title_id); version.has_value()) {
|
||||
return version;
|
||||
}
|
||||
return content_provider.GetEntryVersion(application_id);
|
||||
}
|
||||
|
||||
PatchManager::Metadata PatchManager::GetControlMetadata() const {
|
||||
const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
|
||||
auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
|
||||
if (base_control_nca == nullptr && application_id != title_id) {
|
||||
base_control_nca = content_provider.GetEntry(application_id, ContentRecordType::Control);
|
||||
}
|
||||
if (base_control_nca == nullptr) {
|
||||
return {};
|
||||
}
|
||||
@@ -1162,8 +1226,14 @@ PatchManager::Metadata PatchManager::ParseControlNCA(const NCA& nca) const {
|
||||
auto metadata = pm.GetControlMetadata();
|
||||
if (metadata.first != nullptr)
|
||||
return metadata;
|
||||
const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(application_id), system.GetFileSystemController(), system.GetContentProvider()};
|
||||
return pm_update.GetControlMetadata();
|
||||
const auto update_id = FileSys::GetUpdateTitleID(application_id);
|
||||
const auto application_update_id = FileSys::GetUpdateTitleID(GetBaseTitleID(application_id));
|
||||
const FileSys::PatchManager pm_update{update_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||
metadata = pm_update.GetControlMetadata();
|
||||
if (metadata.first != nullptr || update_id == application_update_id)
|
||||
return metadata;
|
||||
const FileSys::PatchManager pm_application_update{application_update_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||
return pm_application_update.GetControlMetadata();
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/vfs/vfs_types.h"
|
||||
@@ -109,10 +110,14 @@ public:
|
||||
[[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept;
|
||||
|
||||
private:
|
||||
[[nodiscard]] u64 GetUpdateTitleIDForContent() const;
|
||||
[[nodiscard]] std::vector<VirtualDir> GetModificationLoadRoots() const;
|
||||
[[nodiscard]] std::vector<VirtualDir> GetSDMCModificationLoadRoots() const;
|
||||
[[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
||||
const std::string& build_id) const;
|
||||
|
||||
u64 title_id;
|
||||
u64 application_id;
|
||||
const Service::FileSystem::FileSystemController& fs_controller;
|
||||
const ContentProvider& content_provider;
|
||||
};
|
||||
|
||||
@@ -567,20 +567,45 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
|
||||
}
|
||||
|
||||
static std::optional<NcaID> CheckMapForContentRecord(const ankerl::unordered_dense::map<u64, CNMT>& map, u64 title_id, ContentRecordType type) {
|
||||
const auto cmnt_iter = map.find(title_id);
|
||||
auto cmnt_iter = map.find(title_id);
|
||||
u8 id_offset = 0;
|
||||
|
||||
if (cmnt_iter == map.cend()) {
|
||||
return std::nullopt;
|
||||
const auto program_index = title_id & AOC_TITLE_ID_MASK;
|
||||
if (program_index == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
cmnt_iter = map.find(title_id & ~AOC_TITLE_ID_MASK);
|
||||
if (cmnt_iter == map.cend()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
id_offset = static_cast<u8>(program_index);
|
||||
}
|
||||
|
||||
const auto& cnmt = cmnt_iter->second;
|
||||
const auto& content_records = cnmt.GetContentRecords();
|
||||
const auto iter = std::find_if(content_records.cbegin(), content_records.cend(),
|
||||
[type](const ContentRecord& rec) { return rec.type == type; });
|
||||
if (iter == content_records.cend()) {
|
||||
[type, id_offset](const ContentRecord& rec) {
|
||||
return rec.type == type && rec.id_offset == id_offset;
|
||||
});
|
||||
if (iter != content_records.cend()) {
|
||||
return std::make_optional(iter->nca_id);
|
||||
}
|
||||
|
||||
if (id_offset != 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return std::make_optional(iter->nca_id);
|
||||
const auto fallback_iter =
|
||||
std::find_if(content_records.cbegin(), content_records.cend(),
|
||||
[type](const ContentRecord& rec) { return rec.type == type; });
|
||||
if (fallback_iter == content_records.cend()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return std::make_optional(fallback_iter->nca_id);
|
||||
}
|
||||
|
||||
std::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id,
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/common_funcs.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/partition_filesystem.h"
|
||||
@@ -65,10 +66,12 @@ u64 NSP::GetProgramTitleID() const {
|
||||
}
|
||||
|
||||
auto program_id = expected_program_id;
|
||||
if (program_id == 0) {
|
||||
if (!program_status.empty()) {
|
||||
program_id = program_status.begin()->first;
|
||||
}
|
||||
if (program_id == 0 && !program_status.empty()) {
|
||||
program_id = std::min_element(program_status.cbegin(), program_status.cend(),
|
||||
[](const auto& lhs, const auto& rhs) {
|
||||
return lhs.first < rhs.first;
|
||||
})
|
||||
->first;
|
||||
}
|
||||
|
||||
program_id = program_id + program_index;
|
||||
@@ -273,8 +276,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
|
||||
// If the last 3 hexadecimal digits of the NCA's TitleID is between 0x1 and
|
||||
// 0x7FF, this is a multi-program update NCA. Otherwise, this is a regular
|
||||
// update NCA.
|
||||
if ((next_nca->GetTitleId() & 0x7FF) != 0 &&
|
||||
(next_nca->GetTitleId() & 0x800) == 0) {
|
||||
if ((next_nca->GetTitleId() & AOC_TITLE_ID_MASK) != 0) {
|
||||
ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] =
|
||||
std::move(next_nca);
|
||||
} else {
|
||||
|
||||
@@ -349,9 +349,18 @@ struct KernelCore::Impl {
|
||||
object_name_global_data.emplace(kernel);
|
||||
}
|
||||
|
||||
void MakeApplicationProcess(KernelCore& kernel, KProcess* process) {
|
||||
void SetApplicationProcess(KernelCore& kernel, KProcess* process) {
|
||||
if (application_process == process)
|
||||
return;
|
||||
|
||||
KProcess* const previous = application_process;
|
||||
application_process = process;
|
||||
application_process->Open(kernel);
|
||||
|
||||
if (application_process != nullptr)
|
||||
application_process->Open(kernel);
|
||||
|
||||
if (previous != nullptr)
|
||||
previous->Close(kernel);
|
||||
}
|
||||
|
||||
/// Sets the host thread ID for the caller.
|
||||
@@ -879,8 +888,8 @@ void KernelCore::RemoveProcess(KProcess* process) {
|
||||
}
|
||||
}
|
||||
|
||||
void KernelCore::MakeApplicationProcess(KProcess* process) {
|
||||
impl->MakeApplicationProcess(*this, process);
|
||||
void KernelCore::SetApplicationProcess(KProcess* process) {
|
||||
impl->SetApplicationProcess(*this, process);
|
||||
}
|
||||
|
||||
KProcess* KernelCore::ApplicationProcess() {
|
||||
@@ -891,6 +900,14 @@ const KProcess* KernelCore::ApplicationProcess() const {
|
||||
return impl->application_process;
|
||||
}
|
||||
|
||||
KScopedAutoObject<KProcess> KernelCore::GetProcessByProcessId(u64 process_id) {
|
||||
std::scoped_lock lk{impl->process_list_lock};
|
||||
for (auto* const process : impl->process_list)
|
||||
if (process != nullptr && process->GetProcessId() == process_id)
|
||||
return {*this, process};
|
||||
return {*this, nullptr};
|
||||
}
|
||||
|
||||
std::list<KScopedAutoObject<KProcess>> KernelCore::GetProcessList() {
|
||||
std::list<KScopedAutoObject<KProcess>> processes;
|
||||
std::scoped_lock lk{impl->process_list_lock};
|
||||
|
||||
@@ -124,8 +124,8 @@ public:
|
||||
void AppendNewProcess(KProcess* process);
|
||||
void RemoveProcess(KProcess* process);
|
||||
|
||||
/// Makes the given process the new application process.
|
||||
void MakeApplicationProcess(KProcess* process);
|
||||
/// Makes the given process the current application process.
|
||||
void SetApplicationProcess(KProcess* process);
|
||||
|
||||
/// Retrieves a pointer to the application process.
|
||||
KProcess* ApplicationProcess();
|
||||
@@ -133,6 +133,9 @@ public:
|
||||
/// Retrieves a const pointer to the application process.
|
||||
const KProcess* ApplicationProcess() const;
|
||||
|
||||
/// Retrieves the process with the given process ID, or a null object.
|
||||
KScopedAutoObject<KProcess> GetProcessByProcessId(u64 process_id);
|
||||
|
||||
/// Retrieves the list of processes.
|
||||
std::list<KScopedAutoObject<KProcess>> GetProcessList();
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -10,6 +13,7 @@ namespace Service::AM {
|
||||
constexpr Result ResultNoDataInChannel{ErrorModule::AM, 2};
|
||||
constexpr Result ResultNoMessages{ErrorModule::AM, 3};
|
||||
constexpr Result ResultLibraryAppletTerminated{ErrorModule::AM, 22};
|
||||
constexpr Result ResultApplicationRecordNotFound{ErrorModule::AM, 37};
|
||||
constexpr Result ResultInvalidOffset{ErrorModule::AM, 503};
|
||||
constexpr Result ResultInvalidStorageType{ErrorModule::AM, 511};
|
||||
constexpr Result ResultFatalSectionCountImbalance{ErrorModule::AM, 512};
|
||||
|
||||
@@ -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 2024 yuzu Emulator Project
|
||||
@@ -201,6 +201,14 @@ enum class ProgramSpecifyKind : u32 {
|
||||
RestartProgram = 2,
|
||||
};
|
||||
|
||||
// Maufeat: Use enums for zindex instead of using random zindex numbers
|
||||
enum AppletZIndex : s32 {
|
||||
Background = 0,
|
||||
Foreground = 1,
|
||||
ForegroundVisible = 2,
|
||||
Overlay = 3,
|
||||
};
|
||||
|
||||
struct CommonArguments {
|
||||
CommonArgumentVersion arguments_version;
|
||||
CommonArgumentSize size;
|
||||
|
||||
@@ -56,22 +56,22 @@ void Applet::UpdateSuspensionStateLocked(bool force_message) {
|
||||
}
|
||||
}
|
||||
|
||||
void Applet::SetInteractibleLocked(bool interactible) {
|
||||
if (is_interactible == interactible) {
|
||||
void Applet::SetInteractibleLocked(bool pad_interactible, bool touch_interactible) {
|
||||
if (is_pad_interactible == pad_interactible && is_touch_interactible == touch_interactible) {
|
||||
return;
|
||||
}
|
||||
|
||||
is_interactible = interactible;
|
||||
is_pad_interactible = pad_interactible;
|
||||
is_touch_interactible = touch_interactible;
|
||||
|
||||
const bool exit_requested = lifecycle_manager.GetExitRequested();
|
||||
const bool input_enabled = interactible && !exit_requested;
|
||||
const bool pad_enabled = pad_interactible && !exit_requested;
|
||||
const bool touch_enabled = touch_interactible && !exit_requested;
|
||||
|
||||
if (applet_id == AppletId::OverlayDisplay || applet_id == AppletId::Application) {
|
||||
LOG_DEBUG(Service_AM, "called, applet={} interactible={} exit_requested={} input_enabled={} overlay_in_foreground={}",
|
||||
static_cast<u32>(applet_id), interactible, exit_requested, input_enabled, overlay_in_foreground);
|
||||
}
|
||||
LOG_DEBUG(Service_AM, "applet={} pad={} touch={} exit_requested={}",
|
||||
static_cast<u32>(applet_id), pad_enabled, touch_enabled, exit_requested);
|
||||
|
||||
hid_registration.EnableAppletToGetInput(input_enabled);
|
||||
hid_registration.EnableAppletToGetInput(pad_enabled, touch_enabled);
|
||||
}
|
||||
|
||||
void Applet::OnProcessTerminatedLocked() {
|
||||
|
||||
@@ -125,9 +125,11 @@ struct Applet {
|
||||
bool album_image_taken_notification_enabled{};
|
||||
bool record_volume_muted{};
|
||||
bool is_activity_runnable{};
|
||||
bool is_interactible{true};
|
||||
bool is_pad_interactible{true};
|
||||
bool is_touch_interactible{true};
|
||||
bool window_visible{true};
|
||||
bool overlay_in_foreground{false};
|
||||
bool overlay_watching_short_home_button{false};
|
||||
bool overlay_handling_touch_input{false};
|
||||
|
||||
// Events
|
||||
Event overlay_event;
|
||||
@@ -148,7 +150,7 @@ struct Applet {
|
||||
|
||||
// Process state management
|
||||
void UpdateSuspensionStateLocked(bool force_message);
|
||||
void SetInteractibleLocked(bool interactible);
|
||||
void SetInteractibleLocked(bool pad_interactible, bool touch_interactible);
|
||||
void OnProcessTerminatedLocked();
|
||||
};
|
||||
|
||||
|
||||
@@ -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 2024 yuzu Emulator Project
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/display_layer_manager.h"
|
||||
#include "core/hle/service/nvnflinger/hwc_layer.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/vi/application_display_service.h"
|
||||
#include "core/hle/service/vi/container.h"
|
||||
@@ -33,6 +34,7 @@ void DisplayLayerManager::Initialize(Core::System& system, Kernel::KProcess* pro
|
||||
m_system_shared_buffer_id = 0;
|
||||
m_system_shared_layer_id = 0;
|
||||
m_applet_id = applet_id;
|
||||
m_library_applet_mode = mode;
|
||||
m_buffer_sharing_enabled = false;
|
||||
m_blending_enabled = mode == LibraryAppletMode::PartialForeground ||
|
||||
mode == LibraryAppletMode::PartialForegroundIndirectDisplay;
|
||||
@@ -72,14 +74,16 @@ Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
|
||||
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
|
||||
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
|
||||
(void)m_display_service->GetContainer()->SetLayerStackMask(*out_layer_id,
|
||||
this->GetLayerStackMask());
|
||||
|
||||
if (m_applet_id != AppletId::Application) {
|
||||
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
|
||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||
(void)m_manager_display_service->SetLayerZIndex(-1, *out_layer_id);
|
||||
(void)m_manager_display_service->SetLayerZIndex(Overlay, *out_layer_id);
|
||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true);
|
||||
} else {
|
||||
(void)m_manager_display_service->SetLayerZIndex(1, *out_layer_id);
|
||||
(void)m_manager_display_service->SetLayerZIndex(Foreground, *out_layer_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,10 +126,12 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
|
||||
|
||||
// Ensure the overlay layer is visible
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
||||
(void)m_display_service->GetContainer()->SetLayerStackMask(m_system_shared_layer_id,
|
||||
this->GetLayerStackMask());
|
||||
m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id);
|
||||
s32 initial_z = 1;
|
||||
s32 initial_z = Foreground;
|
||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||
initial_z = -1;
|
||||
initial_z = Overlay;
|
||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true);
|
||||
}
|
||||
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
|
||||
@@ -142,6 +148,36 @@ Result DisplayLayerManager::GetSystemSharedLayerHandle(u64* out_system_shared_bu
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
u32 DisplayLayerManager::GetLayerStackMask() const {
|
||||
using Nvnflinger::LayerStackBit;
|
||||
using Nvnflinger::LayerStackId;
|
||||
|
||||
constexpr u32 Displayed = LayerStackBit(LayerStackId::Default);
|
||||
constexpr u32 Screenshot = LayerStackBit(LayerStackId::Screenshot);
|
||||
constexpr u32 Recording = LayerStackBit(LayerStackId::Recording);
|
||||
constexpr u32 LastFrame = LayerStackBit(LayerStackId::LastFrame);
|
||||
constexpr u32 Debug = LayerStackBit(LayerStackId::ApplicationForDebug);
|
||||
|
||||
switch (m_applet_id) {
|
||||
case AppletId::Application:
|
||||
return Displayed | Screenshot | Recording | LastFrame | Debug;
|
||||
case AppletId::OverlayDisplay:
|
||||
return Displayed;
|
||||
case AppletId::QLaunch:
|
||||
return Displayed;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (m_library_applet_mode) {
|
||||
case LibraryAppletMode::AllForeground:
|
||||
case LibraryAppletMode::AllForegroundInitiallyHidden:
|
||||
return Displayed | Screenshot | LastFrame;
|
||||
default:
|
||||
return Displayed | Screenshot;
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayLayerManager::SetWindowVisibility(bool visible) {
|
||||
if (m_visible == visible) {
|
||||
return;
|
||||
@@ -185,10 +221,17 @@ void DisplayLayerManager::SetOverlayZIndex(s32 z_index) {
|
||||
}
|
||||
|
||||
Result DisplayLayerManager::WriteAppletCaptureBuffer(bool* out_was_written,
|
||||
s32* out_fbshare_layer_index) {
|
||||
s32* out_fbshare_layer_index,
|
||||
VI::CaptureKind kind) {
|
||||
R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
|
||||
R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->WriteAppletCaptureBuffer(
|
||||
out_was_written, out_fbshare_layer_index));
|
||||
out_was_written, out_fbshare_layer_index, kind));
|
||||
}
|
||||
|
||||
Result DisplayLayerManager::ClearAppletCaptureBuffer(s32 fbshare_layer_index, u32 color) {
|
||||
R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
|
||||
R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->ClearAppletCaptureBuffer(
|
||||
fbshare_layer_index, color));
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -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 2024 yuzu Emulator Project
|
||||
@@ -23,6 +23,7 @@ class KProcess;
|
||||
namespace Service::VI {
|
||||
class IApplicationDisplayService;
|
||||
class IManagerDisplayService;
|
||||
enum class CaptureKind : u32;
|
||||
} // namespace Service::VI
|
||||
|
||||
namespace Service::AM {
|
||||
@@ -48,9 +49,13 @@ public:
|
||||
|
||||
void SetOverlayZIndex(s32 z_index);
|
||||
|
||||
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index);
|
||||
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index,
|
||||
VI::CaptureKind kind);
|
||||
Result ClearAppletCaptureBuffer(s32 fbshare_layer_index, u32 color);
|
||||
|
||||
private:
|
||||
u32 GetLayerStackMask() const;
|
||||
|
||||
Kernel::KProcess* m_process{};
|
||||
std::shared_ptr<VI::IApplicationDisplayService> m_display_service{};
|
||||
std::shared_ptr<VI::IManagerDisplayService> m_manager_display_service{};
|
||||
@@ -59,6 +64,7 @@ private:
|
||||
u64 m_system_shared_buffer_id{};
|
||||
u64 m_system_shared_layer_id{};
|
||||
AppletId m_applet_id{};
|
||||
LibraryAppletMode m_library_applet_mode{};
|
||||
bool m_buffer_sharing_enabled{};
|
||||
bool m_blending_enabled{};
|
||||
bool m_visible{true};
|
||||
|
||||
@@ -36,12 +36,17 @@ HidRegistration::~HidRegistration() {
|
||||
}
|
||||
}
|
||||
|
||||
void HidRegistration::EnableAppletToGetInput(bool enable) {
|
||||
if (m_process.IsInitialized()) {
|
||||
m_hid_server->GetResourceManager()->SetAruidValidForVibration(m_process.GetProcessId(),
|
||||
enable);
|
||||
m_hid_server->GetResourceManager()->EnableInput(m_process.GetProcessId(), enable);
|
||||
}
|
||||
void HidRegistration::EnableAppletToGetInput(bool enable_pad, bool enable_touch) {
|
||||
if (!m_process.IsInitialized())
|
||||
return;
|
||||
|
||||
const auto resource_manager = m_hid_server->GetResourceManager();
|
||||
const u64 aruid = m_process.GetProcessId();
|
||||
|
||||
resource_manager->EnablePadInput(aruid, enable_pad);
|
||||
resource_manager->EnableTouchScreen(aruid, enable_touch);
|
||||
|
||||
resource_manager->SetAruidValidForVibration(aruid, enable_pad);
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
~HidRegistration();
|
||||
|
||||
void RegisterCurrentProcess();
|
||||
void EnableAppletToGetInput(bool enable);
|
||||
void EnableAppletToGetInput(bool enable_pad, bool enable_touch);
|
||||
|
||||
private:
|
||||
Process& m_process;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <optional>
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/common_funcs.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
@@ -104,8 +105,16 @@ std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control,
|
||||
|
||||
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
|
||||
// current_process_game_card use correct StorageId
|
||||
launch.base_game_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program));
|
||||
launch.update_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
|
||||
auto base_slot = storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program);
|
||||
if (!base_slot) {
|
||||
base_slot = storage.GetSlotForEntry(FileSys::GetBaseTitleID(launch.title_id), FileSys::ContentRecordType::Program);
|
||||
}
|
||||
launch.base_game_storage_id = GetStorageIdForFrontendSlot(base_slot);
|
||||
auto update_slot = storage.GetSlotForEntry(FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program);
|
||||
if (!update_slot) {
|
||||
update_slot = storage.GetSlotForEntry(FileSys::GetUpdateTitleID(FileSys::GetBaseTitleID(launch.title_id)), FileSys::ContentRecordType::Program);
|
||||
}
|
||||
launch.update_storage_id = GetStorageIdForFrontendSlot(update_slot);
|
||||
|
||||
system.GetARPManager().Register(launch.title_id, launch, out_control);
|
||||
return process;
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/common_funcs.h"
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/service/applet_common_functions.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
@@ -78,7 +80,7 @@ Result IAppletCommonFunctions::SetCpuBoostRequestPriority(s32 priority) {
|
||||
|
||||
Result IAppletCommonFunctions::GetCurrentApplicationId(Out<u64> out_application_id) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
*out_application_id = system.GetApplicationProcessProgramID() & ~0xFFFULL;
|
||||
*out_application_id = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "common/hex_util.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/uuid.h"
|
||||
#include "core/file_sys/common_funcs.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
@@ -28,6 +30,27 @@
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
namespace {
|
||||
|
||||
FileSys::PatchManager::Metadata GetApplicationMetadata(Core::System& system, u64 program_id) {
|
||||
auto metadata = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, program_id);
|
||||
if (metadata.first != nullptr) {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
const auto application_id = FileSys::GetBaseTitleID(program_id);
|
||||
if (application_id == program_id) {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_AM,
|
||||
"no metadata for program_id={:016X}, falling back to application_id={:016X}",
|
||||
program_id, application_id);
|
||||
return FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, application_id);
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet)
|
||||
: ServiceFramework{system_, "IApplicationFunctions"}, m_applet{std::move(applet)} {
|
||||
// clang-format off
|
||||
@@ -135,7 +158,7 @@ Result IApplicationFunctions::EnsureSaveData(Out<u64> out_size, Common::UUID use
|
||||
LOG_INFO(Service_AM, "called, uid={}", user_id.FormattedString());
|
||||
|
||||
FileSys::SaveDataAttribute attribute{};
|
||||
attribute.program_id = m_applet->program_id;
|
||||
attribute.program_id = FileSys::GetBaseTitleID(m_applet->program_id);
|
||||
attribute.user_id = user_id.AsU128();
|
||||
attribute.type = FileSys::SaveDataType::Account;
|
||||
|
||||
@@ -156,7 +179,7 @@ Result IApplicationFunctions::GetDesiredLanguage(Out<u64> out_language_code) {
|
||||
// Default to 0 (all languages supported)
|
||||
u32 supported_languages = 0;
|
||||
|
||||
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
|
||||
const auto res = GetApplicationMetadata(system, m_applet->program_id);
|
||||
if (res.first != nullptr) {
|
||||
supported_languages = res.first->GetSupportedLanguages();
|
||||
}
|
||||
@@ -194,7 +217,7 @@ Result IApplicationFunctions::SetTerminateResult(Result terminate_result) {
|
||||
Result IApplicationFunctions::GetDisplayVersion(Out<DisplayVersion> out_display_version) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
|
||||
const auto res = GetApplicationMetadata(system, m_applet->program_id);
|
||||
if (res.first != nullptr) {
|
||||
const auto& version = res.first->GetVersionString();
|
||||
std::memcpy(out_display_version->string.data(), version.data(),
|
||||
@@ -215,7 +238,7 @@ Result IApplicationFunctions::ExtendSaveData(Out<u64> out_required_size, FileSys
|
||||
static_cast<u8>(type), user_id.FormattedString(), normal_size, journal_size);
|
||||
|
||||
system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize(
|
||||
type, m_applet->program_id, user_id.AsU128(), {normal_size, journal_size});
|
||||
type, FileSys::GetBaseTitleID(m_applet->program_id), user_id.AsU128(), {normal_size, journal_size});
|
||||
|
||||
// The following value is used to indicate the amount of space remaining on failure
|
||||
// due to running out of space. Since we always succeed, this should be 0.
|
||||
@@ -229,7 +252,7 @@ Result IApplicationFunctions::GetSaveDataSize(Out<u64> out_normal_size, Out<u64>
|
||||
LOG_DEBUG(Service_AM, "called with type={} user_id={}", type, user_id.FormattedString());
|
||||
|
||||
const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize(
|
||||
type, m_applet->program_id, user_id.AsU128());
|
||||
type, FileSys::GetBaseTitleID(m_applet->program_id), user_id.AsU128());
|
||||
|
||||
*out_normal_size = size.normal;
|
||||
*out_journal_size = size.journal;
|
||||
@@ -324,6 +347,7 @@ Result IApplicationFunctions::NotifyRunning(Out<bool> out_became_running) {
|
||||
|
||||
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
|
||||
LOG_WARNING(Service_AM, "(stubbed)");
|
||||
R_UNLESS(out_pseudo_device_id, ResultUnknown);
|
||||
|
||||
// This should be hashed with the device specific hash
|
||||
// for now this will do
|
||||
@@ -430,15 +454,21 @@ Result IApplicationFunctions::ExecuteProgram(ProgramSpecifyKind kind, u64 value)
|
||||
|
||||
// https://switchbrew.org/wiki/Applet_Manager_services#CreateApplicationAndRequestToStart
|
||||
Result IApplicationFunctions::CreateApplicationAndRequestToStart(u64 application_id) {
|
||||
LOG_INFO(Service_AM, "called, application_id={:016X}", application_id);
|
||||
LOG_INFO(Service_AM, "called, application_id={:016X} current_program_id={:016X}", application_id, m_applet->program_id);
|
||||
|
||||
// If application_id is 0, relaunch the current application
|
||||
const u64 target_application_id =
|
||||
(application_id == 0) ? m_applet->program_id : application_id;
|
||||
if (application_id == 0 ||
|
||||
FileSys::GetBaseTitleID(application_id) == FileSys::GetBaseTitleID(m_applet->program_id)) {
|
||||
const auto program_index = application_id == 0
|
||||
? 0
|
||||
: application_id - FileSys::GetBaseTitleID(application_id);
|
||||
|
||||
system.GetUserChannel() = m_applet->user_channel_launch_parameter;
|
||||
system.ExecuteProgram(target_application_id);
|
||||
R_SUCCEED();
|
||||
system.GetUserChannel() = m_applet->user_channel_launch_parameter;
|
||||
system.ExecuteProgram(program_index);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
LOG_ERROR(Service_AM, "Launching a different application ({:016X}) is not implemented!", application_id);
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
Result IApplicationFunctions::ClearUserChannel() {
|
||||
|
||||
@@ -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 2024 yuzu Emulator Project
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/service/display_controller.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/vi/shared_buffer_manager.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
@@ -71,16 +72,16 @@ Result IDisplayController::TakeScreenShotOfOwnLayer(bool unknown0, s32 fbshare_l
|
||||
}
|
||||
|
||||
Result IDisplayController::ClearCaptureBuffer(bool unknown0, s32 fbshare_layer_index, u32 color) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called, unknown0={} fbshare_layer_index={} color={:#x}",
|
||||
unknown0, fbshare_layer_index, color);
|
||||
R_SUCCEED();
|
||||
LOG_DEBUG(Service_AM, "called, unknown0={} fbshare_layer_index={} color={:#x}", unknown0,
|
||||
fbshare_layer_index, color);
|
||||
R_RETURN(applet->display_layer_manager.ClearAppletCaptureBuffer(fbshare_layer_index, color));
|
||||
}
|
||||
|
||||
Result IDisplayController::AcquireLastForegroundCaptureSharedBuffer(
|
||||
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
|
||||
out_fbshare_layer_index));
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(
|
||||
out_was_written, out_fbshare_layer_index, VI::CaptureKind::LastForeground));
|
||||
}
|
||||
|
||||
Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() {
|
||||
@@ -90,9 +91,9 @@ Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() {
|
||||
|
||||
Result IDisplayController::AcquireCallerAppletCaptureSharedBuffer(
|
||||
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
|
||||
out_fbshare_layer_index));
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(
|
||||
out_was_written, out_fbshare_layer_index, VI::CaptureKind::CallerApplet));
|
||||
}
|
||||
|
||||
Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() {
|
||||
@@ -102,9 +103,9 @@ Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() {
|
||||
|
||||
Result IDisplayController::AcquireLastApplicationCaptureSharedBuffer(
|
||||
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
|
||||
out_fbshare_layer_index));
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(
|
||||
out_was_written, out_fbshare_layer_index, VI::CaptureKind::LastApplication));
|
||||
}
|
||||
|
||||
Result IDisplayController::ReleaseLastApplicationCaptureSharedBuffer() {
|
||||
|
||||
@@ -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
|
||||
|
||||
#include "core/hle/service/am/applet.h"
|
||||
@@ -22,7 +22,7 @@ namespace Service::AM {
|
||||
{10, nullptr, "StartShutdownSequenceForOverlay"},
|
||||
{11, nullptr, "StartRebootSequenceForOverlay"},
|
||||
{20, D<&IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
|
||||
{21, nullptr, "SetHandlingTouchScreenInputEnabled"},
|
||||
{21, D<&IOverlayFunctions::SetHandlingTouchScreenInputEnabled>, "SetHandlingTouchScreenInputEnabled"},
|
||||
{30, nullptr, "SetHealthWarningShowingState"},
|
||||
{31, D<&IOverlayFunctions::IsHealthWarningRequired>, "IsHealthWarningRequired"},
|
||||
{40, nullptr, "GetApplicationNintendoLogo"},
|
||||
@@ -43,10 +43,12 @@ namespace Service::AM {
|
||||
Result IOverlayFunctions::BeginToWatchShortHomeButtonMessage() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
m_applet->overlay_in_foreground = true;
|
||||
m_applet->home_button_short_pressed_blocked = false;
|
||||
{
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->overlay_watching_short_home_button = true;
|
||||
}
|
||||
|
||||
if (auto *window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||
window_system->RequestUpdate();
|
||||
}
|
||||
|
||||
@@ -56,16 +58,26 @@ namespace Service::AM {
|
||||
Result IOverlayFunctions::EndToWatchShortHomeButtonMessage() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
m_applet->overlay_in_foreground = false;
|
||||
m_applet->home_button_short_pressed_blocked = false;
|
||||
{
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->overlay_watching_short_home_button = false;
|
||||
}
|
||||
|
||||
if (auto *window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||
window_system->RequestUpdate();
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::SetHandlingTouchScreenInputEnabled(bool enabled) {
|
||||
LOG_DEBUG(Service_AM, "called, enabled={}", enabled);
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->overlay_handling_touch_input = enabled;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::GetApplicationIdForLogo(Out<u64> out_application_id) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
#pragma once
|
||||
@@ -20,6 +20,7 @@ namespace Service::AM {
|
||||
Result SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled);
|
||||
Result IsHealthWarningRequired(Out<bool> is_required);
|
||||
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
|
||||
Result SetHandlingTouchScreenInputEnabled(bool enabled);
|
||||
Result Unknown70();
|
||||
|
||||
private:
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/service/am/am_results.h"
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
@@ -32,46 +36,82 @@ void WindowSystem::RequestUpdate() {
|
||||
}
|
||||
|
||||
void WindowSystem::Update() {
|
||||
std::scoped_lock lk{m_lock};
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, home_menu={} application={} overlay={}",
|
||||
m_home_menu != nullptr, m_application != nullptr, m_overlay_display != nullptr);
|
||||
LOG_DEBUG(Service_AM, "called, home_menu={} application={} overlay={}",
|
||||
m_home_menu != nullptr, m_application != nullptr, m_overlay_display != nullptr);
|
||||
|
||||
// Loop through all applets and remove terminated applets.
|
||||
this->PruneTerminatedAppletsLocked();
|
||||
// Loop through all applets and remove terminated applets.
|
||||
this->PruneTerminatedAppletsLocked();
|
||||
|
||||
// If the home menu is being locked into the foreground, handle that.
|
||||
if (this->LockHomeMenuIntoForegroundLocked()) {
|
||||
return;
|
||||
// If the home menu is being locked into the foreground, handle that.
|
||||
if (!this->LockHomeMenuIntoForegroundLocked()) {
|
||||
const bool overlay_takes_input = this->DoesOverlayTakeInputLocked();
|
||||
|
||||
this->UpdateAppletStateLocked(m_overlay_display, true, overlay_takes_input);
|
||||
this->UpdateAppletStateLocked(m_home_menu, m_foreground_requested_applet == m_home_menu, overlay_takes_input);
|
||||
this->UpdateAppletStateLocked(m_application, m_foreground_requested_applet == m_application, overlay_takes_input);
|
||||
}
|
||||
}
|
||||
|
||||
bool overlay_blocks_input = false;
|
||||
if (m_overlay_display) {
|
||||
std::scoped_lock lk_overlay{m_overlay_display->lock};
|
||||
overlay_blocks_input = m_overlay_display->overlay_in_foreground;
|
||||
}
|
||||
|
||||
// Recursively update each applet root.
|
||||
this->UpdateAppletStateLocked(m_home_menu, m_foreground_requested_applet == m_home_menu, overlay_blocks_input);
|
||||
this->UpdateAppletStateLocked(m_application, m_foreground_requested_applet == m_application, overlay_blocks_input);
|
||||
this->UpdateAppletStateLocked(m_overlay_display, true, false); // overlay is always updated, never blocked
|
||||
this->NotifyApplicationChangedIfNeeded();
|
||||
}
|
||||
|
||||
void WindowSystem::TrackApplet(std::shared_ptr<Applet> applet, bool is_application) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
if (applet->applet_id == AppletId::QLaunch) {
|
||||
ASSERT(m_home_menu == nullptr);
|
||||
m_home_menu = applet.get();
|
||||
} else if (applet->applet_id == AppletId::OverlayDisplay) {
|
||||
m_overlay_display = applet.get();
|
||||
} else if (is_application) {
|
||||
ASSERT(m_application == nullptr);
|
||||
m_application = applet.get();
|
||||
if (applet->applet_id == AppletId::QLaunch) {
|
||||
ASSERT(m_home_menu == nullptr);
|
||||
m_home_menu = applet.get();
|
||||
} else if (applet->applet_id == AppletId::OverlayDisplay) {
|
||||
m_overlay_display = applet.get();
|
||||
} else if (is_application) {
|
||||
ASSERT(m_application == nullptr);
|
||||
m_application = applet.get();
|
||||
}
|
||||
|
||||
this->UpdateCurrentApplicationLocked();
|
||||
|
||||
m_event_observer->TrackAppletProcess(*applet);
|
||||
m_applets.emplace(applet->aruid.pid, std::move(applet));
|
||||
}
|
||||
|
||||
m_event_observer->TrackAppletProcess(*applet);
|
||||
m_applets.emplace(applet->aruid.pid, std::move(applet));
|
||||
this->NotifyApplicationChangedIfNeeded();
|
||||
}
|
||||
|
||||
void WindowSystem::UpdateCurrentApplicationLocked() {
|
||||
const Applet* const candidate = m_application != nullptr ? m_application : m_home_menu;
|
||||
if (candidate == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* const process = candidate->process->GetHandle();
|
||||
if (process == nullptr || process == m_system.Kernel().ApplicationProcess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO(Service_AM, "Current application is now {:016X}", candidate->program_id);
|
||||
|
||||
m_system.Kernel().SetApplicationProcess(process);
|
||||
Settings::SetCurrentProgramID(candidate->program_id);
|
||||
|
||||
m_pending_application_notification = candidate->program_id;
|
||||
}
|
||||
|
||||
void WindowSystem::NotifyApplicationChangedIfNeeded() {
|
||||
std::optional<u64> program_id;
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
program_id = std::exchange(m_pending_application_notification, std::nullopt);
|
||||
}
|
||||
|
||||
if (!program_id.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_system.NotifyApplicationChanged(*program_id);
|
||||
}
|
||||
|
||||
std::shared_ptr<Applet> WindowSystem::GetByAppletResourceUserId(u64 aruid) {
|
||||
@@ -169,18 +209,40 @@ void WindowSystem::OnExitRequested() {
|
||||
}
|
||||
|
||||
void WindowSystem::SendButtonAppletMessageLocked(AppletMessage message) {
|
||||
if (m_home_menu) {
|
||||
std::scoped_lock lk_home{m_home_menu->lock};
|
||||
m_home_menu->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
|
||||
}
|
||||
if (m_overlay_display) {
|
||||
std::scoped_lock lk_overlay{m_overlay_display->lock};
|
||||
m_overlay_display->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
|
||||
}
|
||||
if (m_application) {
|
||||
std::scoped_lock lk_application{m_application->lock};
|
||||
m_application->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
|
||||
}
|
||||
const auto is_blocked = [message](const Applet& applet) {
|
||||
if (message == AppletMessage::DetectShortPressingHomeButton &&
|
||||
applet.applet_id == AppletId::OverlayDisplay &&
|
||||
!applet.overlay_watching_short_home_button) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (message) {
|
||||
case AppletMessage::DetectShortPressingHomeButton:
|
||||
return applet.home_button_short_pressed_blocked;
|
||||
case AppletMessage::DetectLongPressingHomeButton:
|
||||
return applet.home_button_long_pressed_blocked;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const auto send_to = [&](Applet* applet) {
|
||||
if (!applet) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lk{applet->lock};
|
||||
if (is_blocked(*applet)) {
|
||||
LOG_DEBUG(Service_AM, "Applet {} is blocking message {}",
|
||||
static_cast<u32>(applet->applet_id), static_cast<u32>(message));
|
||||
return;
|
||||
}
|
||||
applet->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
|
||||
};
|
||||
|
||||
send_to(m_home_menu);
|
||||
send_to(m_overlay_display);
|
||||
send_to(m_application);
|
||||
|
||||
if (m_event_observer) {
|
||||
m_event_observer->RequestUpdate();
|
||||
}
|
||||
@@ -192,19 +254,9 @@ void WindowSystem::OnSystemButtonPress(SystemButtonType type) {
|
||||
case SystemButtonType::HomeButtonShortPressing:
|
||||
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingHomeButton);
|
||||
break;
|
||||
case SystemButtonType::HomeButtonLongPressing: {
|
||||
// Toggle overlay foreground visibility on long home press
|
||||
if (m_overlay_display) {
|
||||
std::scoped_lock lk_overlay{m_overlay_display->lock};
|
||||
m_overlay_display->overlay_in_foreground = !m_overlay_display->overlay_in_foreground;
|
||||
LOG_INFO(Service_AM, "Overlay long-press toggle: overlay_in_foreground={} window_visible={}", m_overlay_display->overlay_in_foreground, m_overlay_display->window_visible);
|
||||
}
|
||||
case SystemButtonType::HomeButtonLongPressing:
|
||||
SendButtonAppletMessageLocked(AppletMessage::DetectLongPressingHomeButton);
|
||||
// Force a state update after toggling overlay
|
||||
if (m_event_observer) {
|
||||
m_event_observer->RequestUpdate();
|
||||
}
|
||||
break; }
|
||||
break;
|
||||
case SystemButtonType::CaptureButtonShortPressing:
|
||||
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingCaptureButton);
|
||||
break;
|
||||
@@ -317,6 +369,8 @@ void WindowSystem::PruneTerminatedAppletsLocked() {
|
||||
m_overlay_display = nullptr;
|
||||
}
|
||||
|
||||
this->UpdateCurrentApplicationLocked();
|
||||
|
||||
// Finalize applet.
|
||||
applet->OnProcessTerminatedLocked();
|
||||
|
||||
@@ -389,7 +443,20 @@ void WindowSystem::TerminateChildAppletsLocked(Applet* applet) {
|
||||
applet->lock.lock();
|
||||
}
|
||||
|
||||
void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking) {
|
||||
bool WindowSystem::IsOverlayOpenLocked(const Applet& overlay) const {
|
||||
return overlay.window_visible && overlay.overlay_watching_short_home_button;
|
||||
}
|
||||
|
||||
bool WindowSystem::DoesOverlayTakeInputLocked() const {
|
||||
if (m_overlay_display == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::scoped_lock lk{m_overlay_display->lock};
|
||||
return this->IsOverlayOpenLocked(*m_overlay_display);
|
||||
}
|
||||
|
||||
void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_takes_input) {
|
||||
// With no applet, we don't have anything to do.
|
||||
if (!applet) {
|
||||
return;
|
||||
@@ -420,24 +487,18 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
|
||||
return false;
|
||||
}();
|
||||
|
||||
const bool is_overlay = applet->applet_id == AppletId::OverlayDisplay;
|
||||
|
||||
// Update visibility state.
|
||||
// Overlay applets should always be visible when window_visible is true, regardless of foreground state
|
||||
const bool should_be_visible = (applet->applet_id == AppletId::OverlayDisplay)
|
||||
? applet->window_visible
|
||||
: (is_foreground && applet->window_visible);
|
||||
const bool should_be_visible =
|
||||
is_overlay ? applet->window_visible : (is_foreground && applet->window_visible);
|
||||
applet->display_layer_manager.SetWindowVisibility(should_be_visible);
|
||||
|
||||
const bool needs_hid_input =
|
||||
is_overlay ? this->IsOverlayOpenLocked(*applet)
|
||||
: (is_foreground && applet->window_visible && !overlay_takes_input);
|
||||
|
||||
const bool should_be_interactible = (applet->applet_id == AppletId::OverlayDisplay)
|
||||
? applet->overlay_in_foreground
|
||||
: (is_foreground && applet->window_visible && !overlay_blocking);
|
||||
|
||||
if (applet->applet_id == AppletId::OverlayDisplay || applet->applet_id == AppletId::Application) {
|
||||
LOG_DEBUG(Service_AM, "UpdateAppletStateLocked: applet={} overlay_in_foreground={} is_foreground={} window_visible={} overlay_blocking={} should_be_interactible={}",
|
||||
static_cast<u32>(applet->applet_id), applet->overlay_in_foreground, is_foreground, applet->window_visible, overlay_blocking, should_be_interactible);
|
||||
}
|
||||
|
||||
applet->SetInteractibleLocked(should_be_interactible);
|
||||
applet->SetInteractibleLocked(needs_hid_input, needs_hid_input);
|
||||
|
||||
// Update focus state and suspension.
|
||||
const bool is_obscured = has_obscuring_child_applets || !applet->window_visible;
|
||||
@@ -453,23 +514,18 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
|
||||
applet->UpdateSuspensionStateLocked(true);
|
||||
}
|
||||
|
||||
// Z-index logic like in reference C# implementation (tuned for overlay extremes)
|
||||
s32 z_index = 0;
|
||||
const bool now_foreground = inherited_foreground;
|
||||
if (applet->applet_id == AppletId::OverlayDisplay) {
|
||||
z_index = applet->overlay_in_foreground ? 100000 : -1;
|
||||
} else if (now_foreground && !is_obscured) {
|
||||
z_index = 2;
|
||||
} else if (now_foreground) {
|
||||
z_index = 1;
|
||||
} else {
|
||||
z_index = 0;
|
||||
// Layer ordering. Composition sorts back-to-front. Now with enums for calrity.
|
||||
s32 z_index = Background;
|
||||
if (is_overlay) {
|
||||
z_index = Overlay;
|
||||
} else if (inherited_foreground) {
|
||||
z_index = is_obscured ? Foreground : ForegroundVisible;
|
||||
}
|
||||
applet->display_layer_manager.SetOverlayZIndex(z_index);
|
||||
|
||||
// Recurse into child applets.
|
||||
for (const auto& child_applet : applet->child_applets) {
|
||||
this->UpdateAppletStateLocked(child_applet.get(), is_foreground, overlay_blocking);
|
||||
this->UpdateAppletStateLocked(child_applet.get(), is_foreground, overlay_takes_input);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/am/am_types.h"
|
||||
@@ -60,11 +61,16 @@ public:
|
||||
void OnPowerButtonPressed(ButtonPressDuration type) {}
|
||||
|
||||
private:
|
||||
void UpdateCurrentApplicationLocked();
|
||||
void NotifyApplicationChangedIfNeeded();
|
||||
void PruneTerminatedAppletsLocked();
|
||||
bool RestartAppletProcessLocked(Applet* applet);
|
||||
bool LockHomeMenuIntoForegroundLocked();
|
||||
void TerminateChildAppletsLocked(Applet* applet);
|
||||
void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking = false);
|
||||
bool IsOverlayOpenLocked(const Applet& overlay) const;
|
||||
bool DoesOverlayTakeInputLocked() const;
|
||||
|
||||
void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_takes_input);
|
||||
void SendButtonAppletMessageLocked(AppletMessage message);
|
||||
|
||||
private:
|
||||
@@ -88,6 +94,7 @@ private:
|
||||
|
||||
// Applet map by aruid.
|
||||
std::map<u64, std::shared_ptr<Applet>> m_applets{};
|
||||
std::optional<u64> m_pending_application_notification{};
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/hle/service/am/am_results.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Service::AOC {
|
||||
@@ -31,6 +32,10 @@ static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
|
||||
return FileSys::GetBaseTitleID(title_id) == base;
|
||||
}
|
||||
|
||||
static u64 GetCallerBaseTitleID(Core::System& system, const ClientProcessId& process_id) {
|
||||
return FileSys::GetBaseTitleID(system.ResolveCallerProgramId(*process_id));
|
||||
}
|
||||
|
||||
static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
|
||||
std::vector<u64> add_on_content;
|
||||
const auto& rcu = system.GetContentProvider();
|
||||
@@ -91,7 +96,7 @@ IAddOnContentManager::~IAddOnContentManager() {
|
||||
Result IAddOnContentManager::CountAddOnContent(Out<u32> out_count, ClientProcessId process_id) {
|
||||
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
|
||||
|
||||
const auto current = system.GetApplicationProcessProgramID();
|
||||
const auto current = GetCallerBaseTitleID(system, process_id);
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
|
||||
@@ -112,7 +117,7 @@ Result IAddOnContentManager::ListAddOnContent(Out<u32> out_count,
|
||||
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
|
||||
process_id.pid);
|
||||
|
||||
const auto current = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
|
||||
const auto current = GetCallerBaseTitleID(system, process_id);
|
||||
|
||||
std::vector<u32> out;
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
@@ -126,8 +131,7 @@ Result IAddOnContentManager::ListAddOnContent(Out<u32> out_count,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(DarkLordZach): Find the correct error code.
|
||||
R_UNLESS(out.size() >= offset, ResultUnknown);
|
||||
R_UNLESS(out.size() >= offset, AM::ResultApplicationRecordNotFound);
|
||||
|
||||
*out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
|
||||
std::rotate(out.begin(), out.begin() + offset, out.end());
|
||||
@@ -141,7 +145,7 @@ Result IAddOnContentManager::GetAddOnContentBaseId(Out<u64> out_title_id,
|
||||
ClientProcessId process_id) {
|
||||
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
|
||||
|
||||
const auto title_id = system.GetApplicationProcessProgramID();
|
||||
const auto title_id = system.ResolveCallerProgramId(*process_id);
|
||||
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ static u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) {
|
||||
return out;
|
||||
}
|
||||
|
||||
IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_)
|
||||
: ServiceFramework{system_, "IBcatService"}, backend{backend_},
|
||||
IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_, u64 program_id_)
|
||||
: ServiceFramework{system_, "IBcatService"}, backend{backend_}, program_id{program_id_},
|
||||
progress{{
|
||||
ProgressServiceBackend{system_, "Normal"},
|
||||
ProgressServiceBackend{system_, "Directory"},
|
||||
@@ -70,8 +70,7 @@ Result IBcatService::RequestSyncDeliveryCache(
|
||||
LOG_DEBUG(Service_BCAT, "called");
|
||||
|
||||
auto& progress_backend{GetProgressBackend(SyncType::Normal)};
|
||||
backend.Synchronize(system.Kernel(), {system.GetApplicationProcessProgramID(),
|
||||
GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
||||
backend.Synchronize(system.Kernel(), {program_id, GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
||||
GetProgressBackend(SyncType::Normal));
|
||||
|
||||
*out_interface = std::make_shared<IDeliveryCacheProgressService>(
|
||||
@@ -86,9 +85,8 @@ Result IBcatService::RequestSyncDeliveryCacheWithDirectoryName(
|
||||
LOG_DEBUG(Service_BCAT, "called, name={}", name);
|
||||
|
||||
auto& progress_backend{GetProgressBackend(SyncType::Directory)};
|
||||
backend.SynchronizeDirectory(system.Kernel(), {system.GetApplicationProcessProgramID(),
|
||||
GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
||||
name, progress_backend);
|
||||
backend.SynchronizeDirectory(system.Kernel(), {program_id, GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
||||
name, progress_backend);
|
||||
|
||||
*out_interface = std::make_shared<IDeliveryCacheProgressService>(
|
||||
system, progress_backend.GetEvent(), progress_backend.GetImpl());
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
@@ -19,7 +22,7 @@ class IDeliveryCacheProgressService;
|
||||
|
||||
class IBcatService final : public ServiceFramework<IBcatService> {
|
||||
public:
|
||||
explicit IBcatService(Core::System& system_, BcatBackend& backend_);
|
||||
explicit IBcatService(Core::System& system_, BcatBackend& backend_, u64 program_id_);
|
||||
~IBcatService() override;
|
||||
|
||||
private:
|
||||
@@ -39,6 +42,7 @@ private:
|
||||
const ProgressServiceBackend& GetProgressBackend(SyncType type) const;
|
||||
|
||||
BcatBackend& backend;
|
||||
u64 program_id;
|
||||
std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 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
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/bcat/bcat_service.h"
|
||||
#include "core/hle/service/bcat/delivery_cache_storage_service.h"
|
||||
#include "core/hle/service/bcat/service_creator.h"
|
||||
@@ -37,7 +41,8 @@ IServiceCreator::~IServiceCreator() = default;
|
||||
Result IServiceCreator::CreateBcatService(ClientProcessId process_id,
|
||||
OutInterface<IBcatService> out_interface) {
|
||||
LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
|
||||
*out_interface = std::make_shared<IBcatService>(system, *backend);
|
||||
*out_interface =
|
||||
std::make_shared<IBcatService>(system, *backend, system.ResolveCallerProgramId(*process_id));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -45,7 +50,7 @@ Result IServiceCreator::CreateDeliveryCacheStorageService(
|
||||
ClientProcessId process_id, OutInterface<IDeliveryCacheStorageService> out_interface) {
|
||||
LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
|
||||
|
||||
const auto title_id = system.GetApplicationProcessProgramID();
|
||||
const auto title_id = system.ResolveCallerProgramId(*process_id);
|
||||
*out_interface =
|
||||
std::make_shared<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
|
||||
R_SUCCEED();
|
||||
|
||||
@@ -243,10 +243,15 @@ Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry,
|
||||
return SaveScreenShot(out_entry, attribute, report_option, {}, image_data, aruid);
|
||||
}
|
||||
|
||||
Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, AlbumReportOption report_option, const ApplicationData& app_data, std::span<const u8> image_data, u64 aruid) {
|
||||
Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry,
|
||||
const ScreenShotAttribute& attribute,
|
||||
AlbumReportOption report_option,
|
||||
const ApplicationData& app_data, std::span<const u8> image_data,
|
||||
u64 aruid) {
|
||||
R_UNLESS(!image_data.empty(), ResultUnknown); //TODO: ???
|
||||
|
||||
const u64 title_id = system.GetApplicationProcessProgramID();
|
||||
const u64 title_id = system.ResolveCallerProgramId(aruid);
|
||||
|
||||
auto static_service =
|
||||
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
|
||||
|
||||
|
||||
@@ -95,7 +95,8 @@ void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption r
|
||||
manager->FlipVerticallyOnWrite(invert_y);
|
||||
manager->SaveScreenShot(entry, attribute, report_option, image_data, {});
|
||||
},
|
||||
layout);
|
||||
layout,
|
||||
Nvnflinger::LayerStackId::Screenshot);
|
||||
}
|
||||
|
||||
} // namespace Service::Capture
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/bis_factory.h"
|
||||
#include "core/file_sys/card_image.h"
|
||||
#include "core/file_sys/common_funcs.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
@@ -339,7 +340,7 @@ Result FileSystemController::RegisterProcess(
|
||||
registrations.emplace(process_id, Registration{
|
||||
.program_id = program_id,
|
||||
.romfs_factory = std::move(romfs_factory),
|
||||
.save_data_factory = CreateSaveDataFactory(program_id),
|
||||
.save_data_factory = CreateSaveDataFactory(FileSys::GetBaseTitleID(program_id)),
|
||||
});
|
||||
|
||||
LOG_DEBUG(Service_FS, "Registered for process {}", process_id);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "common/settings.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/common_funcs.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/file_sys/fs_directory.h"
|
||||
@@ -313,7 +314,7 @@ Result FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId(OutInterface<IFileSyste
|
||||
FileSys::ResultInvalidArgument);
|
||||
|
||||
if (attribute.program_id == 0) {
|
||||
attribute.program_id = program_id;
|
||||
attribute.program_id = FileSys::GetBaseTitleID(program_id);
|
||||
}
|
||||
|
||||
FileSys::VirtualDir dir{};
|
||||
|
||||
@@ -1164,7 +1164,7 @@ Result IHidServer::InitializeSevenSixAxisSensor(ClientAppletResourceUserId aruid
|
||||
GetResourceManager()->GetConsoleSixAxis()->Activate();
|
||||
GetResourceManager()->GetSevenSixAxis()->Activate();
|
||||
|
||||
GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress());
|
||||
GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress(), t_mem_1->GetOwner());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -312,7 +312,7 @@ Result Hidbus::EnableJoyPollingReceiveMode(u32 t_mem_size, JoyPollingMode pollin
|
||||
|
||||
auto& device = devices[device_index.value()].device;
|
||||
device->SetPollingMode(polling_mode);
|
||||
device->SetTransferMemoryAddress(t_mem->GetSourceAddress());
|
||||
device->SetTransferMemoryAddress(t_mem->GetSourceAddress(), t_mem->GetOwner());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 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
|
||||
|
||||
@@ -147,7 +150,7 @@ Result IRS::RunImageTransferProcessor(
|
||||
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
|
||||
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
|
||||
image_transfer_processor.SetConfig(processor_config);
|
||||
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
|
||||
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress(), t_mem->GetOwner());
|
||||
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
|
||||
Common::Input::PollingMode::IR);
|
||||
|
||||
@@ -295,7 +298,8 @@ Result IRS::RunImageTransferExProcessor(
|
||||
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
|
||||
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
|
||||
image_transfer_processor.SetConfig(processor_config);
|
||||
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
|
||||
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress(),
|
||||
t_mem->GetOwner());
|
||||
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
|
||||
Common::Input::PollingMode::IR);
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
, process{kernel, process_}
|
||||
, user_rx{std::move(user_rx_)}
|
||||
, user_ro{std::move(user_ro_)}
|
||||
, context{system_.ApplicationMemory()}
|
||||
, context{process_->GetMemory()}
|
||||
{
|
||||
|
||||
// clang-format off
|
||||
|
||||
@@ -9,15 +9,12 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#define STBI_ONLY_JPEG 1
|
||||
#include <stb_image.h>
|
||||
#include <stb_image_resize.h>
|
||||
#include <stb_image_write.h>
|
||||
|
||||
#include "common/stb.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/vfs/vfs.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_transfer_memory.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ns/language.h"
|
||||
@@ -336,8 +333,8 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestCo
|
||||
|
||||
constexpr s32 data_offset = 0;
|
||||
|
||||
if (t_mem != nullptr && app_count > 0) {
|
||||
auto& memory = system.ApplicationMemory();
|
||||
if (t_mem != nullptr && t_mem->GetOwner() != nullptr && app_count > 0) {
|
||||
auto& memory = t_mem->GetOwner()->GetMemory();
|
||||
const auto t_mem_address = t_mem->GetSourceAddress();
|
||||
|
||||
for (size_t i = 0; i < app_count; ++i) {
|
||||
|
||||
@@ -77,6 +77,7 @@ void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers
|
||||
.transform_flags = layer.transform,
|
||||
.crop_rect = layer.crop_rect,
|
||||
.blending = ConvertBlending(layer.blending),
|
||||
.layer_stack_mask = layer.layer_stack_mask,
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) {
|
||||
|
||||
@@ -69,7 +69,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
|
||||
case 0x3:
|
||||
return WrapFixed(this, &nvhost_gpu::ChannelSetTimeout, input, output);
|
||||
case 0x8:
|
||||
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, false);
|
||||
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, fd, false);
|
||||
case 0x9:
|
||||
return WrapFixed(this, &nvhost_gpu::AllocateObjectContext, input, output);
|
||||
case 0xb:
|
||||
@@ -83,7 +83,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
|
||||
case 0x1a:
|
||||
return WrapFixed(this, &nvhost_gpu::AllocGPFIFOEx2, input, output, fd);
|
||||
case 0x1b:
|
||||
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, true);
|
||||
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, fd, true);
|
||||
case 0x1d:
|
||||
return WrapFixed(this, &nvhost_gpu::ChannelSetTimeslice, input, output);
|
||||
default:
|
||||
@@ -387,8 +387,19 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandL
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
Core::Memory::Memory& nvhost_gpu::GetSessionMemory(DeviceFD fd) {
|
||||
if (const auto it = sessions.find(fd); it != sessions.end())
|
||||
if (auto* const session = core.GetSession(it->second);
|
||||
session != nullptr && session->process != nullptr)
|
||||
return session->process->GetMemory();
|
||||
|
||||
LOG_ERROR(Service_NVDRV, "No session for fd={}, falling back to application memory", fd);
|
||||
return system.ApplicationMemory();
|
||||
}
|
||||
|
||||
NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
|
||||
std::span<Tegra::CommandListHeader> commands, bool kickoff) {
|
||||
std::span<Tegra::CommandListHeader> commands, DeviceFD fd,
|
||||
bool kickoff) {
|
||||
if (params.num_entries > commands.size()) {
|
||||
UNIMPLEMENTED();
|
||||
return NvResult::InvalidSize;
|
||||
@@ -396,7 +407,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
|
||||
|
||||
Tegra::CommandList entries(params.num_entries);
|
||||
if (kickoff) {
|
||||
system.ApplicationMemory().ReadBlock(params.address, entries.command_lists.data(),
|
||||
this->GetSessionMemory(fd).ReadBlock(params.address, entries.command_lists.data(),
|
||||
params.num_entries * sizeof(Tegra::CommandListHeader));
|
||||
} else {
|
||||
std::memcpy(entries.command_lists.data(), commands.data(),
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
#include "video_core/dma_pusher.h"
|
||||
|
||||
namespace Core::Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace Tegra {
|
||||
namespace Control {
|
||||
struct ChannelState;
|
||||
@@ -196,8 +200,11 @@ private:
|
||||
|
||||
NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandList&& entries);
|
||||
|
||||
Core::Memory::Memory& GetSessionMemory(DeviceFD fd);
|
||||
|
||||
NvResult SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
|
||||
std::span<Tegra::CommandListHeader> commands, bool kickoff = false);
|
||||
std::span<Tegra::CommandListHeader> commands, DeviceFD fd,
|
||||
bool kickoff = false);
|
||||
NvResult SubmitGPFIFOBase2(IoctlSubmitGpfifo& params,
|
||||
std::span<const Tegra::CommandListHeader> commands);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/logging.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
|
||||
@@ -71,17 +72,23 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
|
||||
|
||||
void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
|
||||
LOG_INFO(Service_NVDRV, "NVDEC video stream started");
|
||||
system.SetNVDECActive(true);
|
||||
sessions[fd] = session_id;
|
||||
if (const auto* session = core.GetSession(session_id);
|
||||
session != nullptr && session->process != nullptr) {
|
||||
system.NotifyNVDECChannelOpen(session->process->GetId());
|
||||
}
|
||||
host1x.StartDevice(fd, Tegra::Host1x::ChannelType::NvDec, channel_syncpoint);
|
||||
}
|
||||
|
||||
void nvhost_nvdec::OnClose(DeviceFD fd) {
|
||||
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
|
||||
host1x.StopDevice(fd, Tegra::Host1x::ChannelType::NvDec);
|
||||
system.SetNVDECActive(false);
|
||||
auto it = sessions.find(fd);
|
||||
if (it != sessions.end()) {
|
||||
if (const auto* session = core.GetSession(it->second);
|
||||
session != nullptr && session->process != nullptr) {
|
||||
system.NotifyNVDECChannelClose(session->process->GetId());
|
||||
}
|
||||
sessions.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,12 @@ void LoopProcess(Core::System& system) {
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
auto module = std::make_shared<Module>(system);
|
||||
server_manager->RegisterNamedService("nvdrv", std::make_shared<NVDRV>(system, module, "nvdrv"));
|
||||
server_manager->RegisterNamedService("nvdrv:a", std::make_shared<NVDRV>(system, module, "nvdrv:a"));
|
||||
|
||||
const auto NvdrvInterfaceFactoryForApplets = [&, module] {
|
||||
return std::make_shared<NVDRV>(system, module, "nvdrv:a");
|
||||
};
|
||||
|
||||
server_manager->RegisterNamedService("nvdrv:a", NvdrvInterfaceFactoryForApplets);
|
||||
server_manager->RegisterNamedService("nvdrv:s", std::make_shared<NVDRV>(system, module, "nvdrv:s"));
|
||||
server_manager->RegisterNamedService("nvdrv:t", std::make_shared<NVDRV>(system, module, "nvdrv:t"));
|
||||
server_manager->RegisterNamedService("nvmemp", std::make_shared<NVMEMP>(system));
|
||||
|
||||
@@ -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 2024 yuzu Emulator Project
|
||||
@@ -15,7 +15,8 @@ struct Layer {
|
||||
explicit Layer(std::shared_ptr<android::BufferItemConsumer> buffer_item_consumer_,
|
||||
s32 consumer_id_)
|
||||
: buffer_item_consumer(std::move(buffer_item_consumer_)), consumer_id(consumer_id_),
|
||||
blending(LayerBlending::None), visible(true), z_index(0), is_overlay(false) {}
|
||||
blending(LayerBlending::None), visible(true), z_index(0), is_overlay(false),
|
||||
layer_stack_mask(DefaultLayerStackMask) {}
|
||||
~Layer() {
|
||||
buffer_item_consumer->Abandon();
|
||||
}
|
||||
@@ -26,6 +27,7 @@ struct Layer {
|
||||
bool visible;
|
||||
s32 z_index;
|
||||
bool is_overlay;
|
||||
u32 layer_stack_mask;
|
||||
};
|
||||
|
||||
struct LayerStack {
|
||||
|
||||
@@ -115,6 +115,7 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
|
||||
.transform = static_cast<android::BufferTransformFlags>(item.transform),
|
||||
.crop_rect = item.crop,
|
||||
.acquire_fence = item.fence,
|
||||
.layer_stack_mask = layer->layer_stack_mask,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
@@ -23,6 +26,25 @@ enum class LayerBlending : u32 {
|
||||
Coverage = 0x405,
|
||||
};
|
||||
|
||||
enum class LayerStackId : u32 {
|
||||
Default = 0,
|
||||
Lcd = 1,
|
||||
Screenshot = 2,
|
||||
Recording = 3,
|
||||
LastFrame = 4,
|
||||
Arbitrary = 5,
|
||||
ApplicationForDebug = 6,
|
||||
Null = 10,
|
||||
};
|
||||
|
||||
constexpr u32 LayerStackBit(LayerStackId id) {
|
||||
return 1U << static_cast<u32>(id);
|
||||
}
|
||||
|
||||
constexpr u32 DefaultLayerStackMask =
|
||||
LayerStackBit(LayerStackId::Default) | LayerStackBit(LayerStackId::Screenshot) |
|
||||
LayerStackBit(LayerStackId::Recording) | LayerStackBit(LayerStackId::LastFrame);
|
||||
|
||||
struct HwcLayer {
|
||||
u32 buffer_handle;
|
||||
u32 offset;
|
||||
@@ -35,6 +57,7 @@ struct HwcLayer {
|
||||
android::BufferTransformFlags transform;
|
||||
Common::Rectangle<int> crop_rect;
|
||||
android::Fence acquire_fence;
|
||||
u32 layer_stack_mask;
|
||||
};
|
||||
|
||||
} // namespace Service::Nvnflinger
|
||||
|
||||
@@ -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 2024 yuzu Emulator Project
|
||||
@@ -104,6 +104,14 @@ void SurfaceFlinger::SetLayerBlending(s32 consumer_binder_id, LayerBlending blen
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceFlinger::SetLayerStackMask(s32 consumer_binder_id, u32 layer_stack_mask) {
|
||||
if (const auto layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
|
||||
layer->layer_stack_mask = layer_stack_mask;
|
||||
LOG_DEBUG(Service_VI, "Layer {} stack mask set to {:#x}", consumer_binder_id,
|
||||
layer_stack_mask);
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceFlinger::SetLayerIsOverlay(s32 consumer_binder_id, bool is_overlay) {
|
||||
if (const auto layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
|
||||
layer->is_overlay = is_overlay;
|
||||
|
||||
@@ -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 2024 yuzu Emulator Project
|
||||
@@ -48,6 +48,7 @@ public:
|
||||
void SetLayerVisibility(s32 consumer_binder_id, bool visible);
|
||||
void SetLayerBlending(s32 consumer_binder_id, LayerBlending blending);
|
||||
void SetLayerIsOverlay(s32 consumer_binder_id, bool is_overlay);
|
||||
void SetLayerStackMask(s32 consumer_binder_id, u32 layer_stack_mask);
|
||||
|
||||
std::shared_ptr<Layer> FindLayer(s32 consumer_binder_id);
|
||||
|
||||
|
||||
@@ -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 2024 yuzu Emulator Project
|
||||
@@ -13,8 +13,10 @@
|
||||
|
||||
namespace Service::PCTL {
|
||||
|
||||
IParentalControlService::IParentalControlService(Core::System& system_, Capability capability_)
|
||||
IParentalControlService::IParentalControlService(Core::System& system_, Capability capability_,
|
||||
u64 program_id_)
|
||||
: ServiceFramework{system_, "IParentalControlService"}, capability{capability_},
|
||||
program_id{program_id_},
|
||||
service_context{system_, "IParentalControlService"}, synchronization_event{service_context},
|
||||
unlinked_event{service_context}, request_suspension_event{service_context} {
|
||||
// clang-format off
|
||||
@@ -202,7 +204,6 @@ Result IParentalControlService::Initialize() {
|
||||
|
||||
// TODO(ogniK): Recovery flag initialization for pctl:r
|
||||
|
||||
const auto program_id = system.GetApplicationProcessProgramID();
|
||||
if (program_id != 0) {
|
||||
const FileSys::PatchManager pm{program_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
||||
@@ -16,7 +16,8 @@ namespace Service::PCTL {
|
||||
|
||||
class IParentalControlService final : public ServiceFramework<IParentalControlService> {
|
||||
public:
|
||||
explicit IParentalControlService(Core::System& system_, Capability capability_);
|
||||
explicit IParentalControlService(Core::System& system_, Capability capability_,
|
||||
u64 program_id_);
|
||||
~IParentalControlService() override;
|
||||
|
||||
private:
|
||||
@@ -84,6 +85,7 @@ private:
|
||||
RestrictionSettings restriction_settings{};
|
||||
std::array<char, 8> pin_code{};
|
||||
Capability capability{};
|
||||
u64 program_id{};
|
||||
// TODO: this is raw
|
||||
PlayTimerSettings raw_play_timer_settings{};
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/pctl/parental_control_service.h"
|
||||
#include "core/hle/service/pctl/parental_control_service_factory.h"
|
||||
@@ -23,17 +27,17 @@ IParentalControlServiceFactory::~IParentalControlServiceFactory() = default;
|
||||
|
||||
Result IParentalControlServiceFactory::CreateService(
|
||||
Out<SharedPointer<IParentalControlService>> out_service, ClientProcessId process_id) {
|
||||
LOG_DEBUG(Service_PCTL, "called");
|
||||
// TODO(ogniK): Get application id from process
|
||||
*out_service = std::make_shared<IParentalControlService>(system, capability);
|
||||
LOG_DEBUG(Service_PCTL, "called, process_id={}", process_id.pid);
|
||||
*out_service = std::make_shared<IParentalControlService>(
|
||||
system, capability, system.ResolveCallerProgramId(*process_id));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IParentalControlServiceFactory::CreateServiceWithoutInitialize(
|
||||
Out<SharedPointer<IParentalControlService>> out_service, ClientProcessId process_id) {
|
||||
LOG_DEBUG(Service_PCTL, "called");
|
||||
// TODO(ogniK): Get application id from process
|
||||
*out_service = std::make_shared<IParentalControlService>(system, capability);
|
||||
LOG_DEBUG(Service_PCTL, "called, process_id={}", process_id.pid);
|
||||
*out_service = std::make_shared<IParentalControlService>(
|
||||
system, capability, system.ResolveCallerProgramId(*process_id));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -132,8 +132,7 @@ private:
|
||||
|
||||
LOG_WARNING(Service_PM, "(Partial Implementation) called, pid={:016X}", pid);
|
||||
|
||||
auto list = kernel.GetProcessList();
|
||||
auto process = SearchProcessList(system.Kernel(), list, [pid](auto& p) { return p->GetProcessId() == pid; });
|
||||
auto process = kernel.GetProcessByProcessId(pid);
|
||||
|
||||
if (process.IsNull()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -186,9 +185,7 @@ private:
|
||||
|
||||
LOG_DEBUG(Service_PM, "called, process_id={:016X}", process_id);
|
||||
|
||||
auto list = kernel.GetProcessList();
|
||||
auto process = SearchProcessList(system.Kernel(),
|
||||
list, [process_id](auto& p) { return p->GetProcessId() == process_id; });
|
||||
auto process = kernel.GetProcessByProcessId(process_id);
|
||||
|
||||
if (process.IsNull()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
||||
@@ -76,7 +76,7 @@ private:
|
||||
Type, process_id, data1.size(), data2.size());
|
||||
|
||||
const auto& reporter{system.GetReporter()};
|
||||
reporter.SavePlayReport(Type, system.GetApplicationProcessProgramID(), {data1, data2},
|
||||
reporter.SavePlayReport(Type, system.ResolveCallerProgramId(process_id), {data1, data2},
|
||||
process_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -98,7 +98,7 @@ private:
|
||||
Type, user_id[1], user_id[0], process_id, data1.size(), data2.size());
|
||||
|
||||
const auto& reporter{system.GetReporter()};
|
||||
reporter.SavePlayReport(Type, system.GetApplicationProcessProgramID(), {data1, data2},
|
||||
reporter.SavePlayReport(Type, system.ResolveCallerProgramId(process_id), {data1, data2},
|
||||
process_id, user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
||||
@@ -588,7 +588,12 @@ void LoopProcess(Core::System& system) {
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
|
||||
auto ro = std::make_shared<RoContext>();
|
||||
server_manager->RegisterNamedService("ldr:ro", std::make_shared<RoInterface>(system, "ldr:ro", ro, NrrKind::User));
|
||||
|
||||
const auto RoInterfaceFactoryForUser = [&, ro] {
|
||||
return std::make_shared<RoInterface>(system, "ldr:ro", ro, NrrKind::User);
|
||||
};
|
||||
|
||||
server_manager->RegisterNamedService("ldr:ro", std::move(RoInterfaceFactoryForUser));
|
||||
server_manager->RegisterNamedService("ro:1", std::make_shared<RoInterface>(system, "ro:1", ro, NrrKind::JitPlugin));
|
||||
server_manager->RegisterNamedService("ro:dmnt", std::make_shared<IDebugMonitorInterface>(system));
|
||||
ServerManager::RunServer(std::move(server_manager));
|
||||
|
||||
@@ -4,12 +4,16 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
#include <fmt/ranges.h>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
@@ -33,6 +37,7 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se
|
||||
: SessionRequestHandler(system_.Kernel(), service_name_)
|
||||
, system{system_}
|
||||
, service_name{service_name_}
|
||||
, is_i_storage{std::string_view{service_name_} == "IStorage"}
|
||||
, handler_invoker{handler_invoker_}
|
||||
, max_sessions{max_sessions_}
|
||||
{}
|
||||
@@ -77,13 +82,22 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx,
|
||||
}
|
||||
|
||||
void ServiceFrameworkBase::InvokeRequest(HLERequestContext& ctx) {
|
||||
auto it = handlers.find(ctx.GetCommand());
|
||||
const auto command = ctx.GetCommand();
|
||||
auto it = handlers.find(command);
|
||||
const bool is_cmd_read = command == 0;
|
||||
FunctionInfoBase const* info = it == handlers.end() ? nullptr : &it->second;
|
||||
if (info == nullptr || info->handler_callback == nullptr)
|
||||
return ReportUnimplementedFunction(ctx, info);
|
||||
|
||||
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
|
||||
handler_invoker(this, info->handler_callback, ctx);
|
||||
|
||||
if (is_i_storage && is_cmd_read) {
|
||||
const auto* const process = ctx.GetThread().GetOwnerProcess();
|
||||
if (process != nullptr && system.IsNVDECActiveForProcess(process->GetId())) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds{600});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceFrameworkBase::InvokeRequestTipc(HLERequestContext& ctx) {
|
||||
|
||||
@@ -107,6 +107,8 @@ protected:
|
||||
Core::System& system;
|
||||
/// Identifier string used to connect to the service.
|
||||
const char* service_name;
|
||||
/// Whether this is the IStorage service.
|
||||
const bool is_i_storage;
|
||||
/// Function used to safely up-cast pointers to the derived class before invoking a handler.
|
||||
InvokerFn* handler_invoker;
|
||||
/// Maximum number of concurrent sessions that this service can handle.
|
||||
|
||||
@@ -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 2024 yuzu Emulator Project
|
||||
@@ -166,6 +166,16 @@ Result Container::GetLayerZIndex(u64 layer_id, s32* out_z_index) {
|
||||
R_RETURN(VI::ResultNotFound);
|
||||
}
|
||||
|
||||
Result Container::SetLayerStackMask(u64 layer_id, u32 layer_stack_mask) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
auto* const layer = m_layers.GetLayerById(layer_id);
|
||||
R_UNLESS(layer != nullptr, VI::ResultNotFound);
|
||||
|
||||
m_surface_flinger->SetLayerStackMask(layer->GetConsumerBinderId(), layer_stack_mask);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Container::SetLayerIsOverlay(u64 layer_id, bool is_overlay) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user