Compare commits

..

2 Commits

Author SHA1 Message Date
xbzk aa0878b636 [core, *] support bundled application program IDs 2026-08-29 12:39:26 -03:00
xbzk c0a85d0e53 [service, nvhost] added machinery to allow microsleep between nvdec read requests to avoid guest panic in some games (#4316)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
This one deserves a long story, but imma try to resume:

During investigating Absolum 1.2 black screen of death upon loading intro video, i've discovered guest was aborting for failing to allocate room for the video.

By logging everything prior to crash and decoding guest side instructions managed to confirm its media allocator was reading data faster than it was updating free available bucket list.

Since the IStorage::Read was happening 247 times before the crash, i've decided to add a very small sleep there, and boom, not only Absolum but some other titles got the same issue fixed.

But i was unsatisfied with the sleep and kept tracking guest instructions upstream in order to find a sync point for the read worker and the memory allocation update. But unfortunately the media allocator helpers live in guest, accessing memory directly via MMU, so any sync signaling would need to come from some dynarmic hack.

It's been 6 days now, so i've decided to polish the sleep: Moved it upstream to where i could have access for proper predicate, and added machinery to service and nvhost to support that. Now the sleep is restricted only for nvdec istorage reads. Any other reads will flow normally.

TL;DR: currently our code is so blazing async that guest is capable to request reads before its very self refresh it have freed room to do so. The sleep accepted as broadly stable was 600 us (MICROseconds), and it affects ONLY nvdec chunk reading.
Reports confirm that now videos are smoother now.

Code was polished at my knowledge limits.
Mostly machinery to track when a request comes from a process with nvdec active, and is istorage read.
I can provide more details if it comes to be needed.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4316
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Samuel <lizzie@eden-emu.dev>
2026-08-29 14:22:09 +02:00
175 changed files with 1818 additions and 1682 deletions
-20
View File
@@ -69,26 +69,6 @@ if (YUZU_STATIC_ROOM)
set(fmt_FORCE_BUNDLED ON) set(fmt_FORCE_BUNDLED ON)
endif() endif()
# my unity/jumbo build
option(ENABLE_UNITY_BUILD "Enable Unity/Jumbo build" OFF)
# 0 compiles all files in
# not ideal, but if you're going gung-ho with a unity build, expect failure
# MSVC physically can't compile that many files into one TU, so we limit it to 100.
if (MSVC)
set(_unity_default 100)
else()
set(_unity_default 0)
endif()
set(UNITY_BATCH_SIZE ${_unity_default} CACHE STRING "Unity build batch size")
if(MSVC AND ENABLE_UNITY_BUILD)
message(STATUS "Unity build")
# Unity builds need big objects for MSVC...
add_compile_options(/bigobj)
endif()
# qt stuff # qt stuff
option(ENABLE_QT "Enable the Qt frontend" ON) option(ENABLE_QT "Enable the Qt frontend" ON)
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF) option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
-1
View File
@@ -39,7 +39,6 @@ These options control dependencies.
- This option is subject for removal. - This option is subject for removal.
- `YUZU_TESTS` (ON) Compile tests - requires Catch2 - `YUZU_TESTS` (ON) Compile tests - requires Catch2
- `ENABLE_LTO` (OFF) Enable link-time optimization - `ENABLE_LTO` (OFF) Enable link-time optimization
- `ENABLE_UNITY_BUILD` (OFF) Enables "Unity/Jumbo" builds
- Not recommended on Windows - Not recommended on Windows
- UNIX may be better off appending `-flto=thin` to compiler args - UNIX may be better off appending `-flto=thin` to compiler args
- `USE_FASTER_LINKER` (OFF) Check if a faster linker is available - `USE_FASTER_LINKER` (OFF) Check if a faster linker is available
-5
View File
@@ -7,11 +7,6 @@
# Enable modules to include each other's files # Enable modules to include each other's files
include_directories(.) include_directories(.)
if (ENABLE_UNITY_BUILD)
set(CMAKE_UNITY_BUILD ON)
set(CMAKE_UNITY_BUILD_BATCH_SIZE ${UNITY_BATCH_SIZE})
endif()
# Dynarmic # Dynarmic
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64) AND NOT YUZU_STATIC_ROOM) if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64) AND NOT YUZU_STATIC_ROOM)
add_subdirectory(dynarmic) add_subdirectory(dynarmic)
@@ -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-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -24,6 +27,6 @@ object SettingsFile {
fun loadCustomConfig(game: Game) { fun loadCustomConfig(game: Game) {
val fileName = FileUtil.getFilename(Uri.parse(game.path)) 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, fragmentManager = parentFragmentManager,
addonViewModel = addonViewModel, addonViewModel = addonViewModel,
documents = documents, documents = documents,
programId = args.game.programId programId = args.game.applicationId
) )
} }
@@ -347,7 +347,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
try { try {
if (GpuDriverHelper.isAdrenoGpu()) { if (GpuDriverHelper.isAdrenoGpu()) {
val programIdHex = game!!.programIdHex val programIdHex = game!!.applicationIdHex
if (NativeFreedrenoConfig.loadPerGameConfigWithGlobalFallback(programIdHex)) { if (NativeFreedrenoConfig.loadPerGameConfigWithGlobalFallback(programIdHex)) {
Log.info("[EmulationFragment] Loaded per-game Freedreno config for $programIdHex") Log.info("[EmulationFragment] Loaded per-game Freedreno config for $programIdHex")
} else { } else {
@@ -59,7 +59,7 @@ class FreedrenoSettingsFragment : Fragment() {
NativeFreedrenoConfig.initializeFreedrenoConfig() NativeFreedrenoConfig.initializeFreedrenoConfig()
if (isPerGameConfig) { if (isPerGameConfig) {
NativeFreedrenoConfig.loadPerGameConfig(game!!.programIdHex) NativeFreedrenoConfig.loadPerGameConfig(game!!.applicationIdHex)
} else { } else {
NativeFreedrenoConfig.reloadFreedrenoConfig() NativeFreedrenoConfig.reloadFreedrenoConfig()
} }
@@ -157,7 +157,7 @@ class FreedrenoSettingsFragment : Fragment() {
binding.buttonSave.setOnClickListener { binding.buttonSave.setOnClickListener {
if (isPerGameConfig) { if (isPerGameConfig) {
NativeFreedrenoConfig.savePerGameConfig(game!!.programIdHex) NativeFreedrenoConfig.savePerGameConfig(game!!.applicationIdHex)
showSnackbar(getString(R.string.freedreno_per_game_saved)) showSnackbar(getString(R.string.freedreno_per_game_saved))
} else { } else {
NativeFreedrenoConfig.saveFreedrenoConfig() NativeFreedrenoConfig.saveFreedrenoConfig()
@@ -455,7 +455,7 @@ class GamePropertiesFragment : Fragment() {
val shaderCacheDir = File( val shaderCacheDir = File(
DirectoryInitialization.userDirectory + DirectoryInitialization.userDirectory +
"/cache/shader/" + args.game.settingsName.lowercase() "/cache/shader/" + args.game.shaderCacheName.lowercase()
) )
if (shaderCacheDir.exists()) { if (shaderCacheDir.exists()) {
add( add(
@@ -600,7 +600,7 @@ class GamePropertiesFragment : Fragment() {
val files = cacheSaveDir.listFiles() val files = cacheSaveDir.listFiles()
var savesFolderFile: File? = null var savesFolderFile: File? = null
if (files != null) { if (files != null) {
val savesFolderName = args.game.programIdHex val savesFolderName = args.game.applicationIdHex
for (file in files) { for (file in files) {
if (file.isDirectory && file.name == savesFolderName) { if (file.isDirectory && file.name == savesFolderName) {
savesFolderFile = file savesFolderFile = file
@@ -232,7 +232,7 @@ class InstallableFragment : Fragment() {
fragmentManager = parentFragmentManager, fragmentManager = parentFragmentManager,
addonViewModel = addonViewModel, addonViewModel = addonViewModel,
documents = documents, documents = documents,
programId = addonViewModel.game?.programId programId = addonViewModel.game?.applicationId
) )
} }
@@ -142,10 +142,11 @@ class AddonViewModel : ViewModel() {
} }
fun onDeleteAddon(patch: Patch) { fun onDeleteAddon(patch: Patch) {
val currentGame = game ?: return
when (PatchType.from(patch.type)) { when (PatchType.from(patch.type)) {
PatchType.Update -> NativeLibrary.removeUpdate(patch.programId) PatchType.Update -> NativeLibrary.removeUpdate(currentGame.programId)
PatchType.DLC -> NativeLibrary.removeDLC(patch.programId) PatchType.DLC -> NativeLibrary.removeDLC(currentGame.applicationId)
PatchType.Mod -> NativeLibrary.removeMod(patch.programId, patch.name) PatchType.Mod -> NativeLibrary.removeMod(currentGame.programId, patch.name)
} }
refreshAddons(force = true) refreshAddons(force = true)
} }
@@ -165,7 +166,7 @@ class AddonViewModel : ViewModel() {
} }
NativeConfig.setDisabledAddons( NativeConfig.setDisabledAddons(
currentGame.programId, currentGame.applicationId,
currentList.mapNotNull { currentList.mapNotNull {
if (it.enabled) { if (it.enabled) {
null null
@@ -199,6 +200,6 @@ class AddonViewModel : ViewModel() {
} }
private fun gameKey(game: Game): String { 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 ?: return@withContext
val shaderDir = File( val shaderDir = File(
externalFilesDir.absolutePath + externalFilesDir.absolutePath +
"/shader/" + game.settingsName.lowercase() "/shader/" + game.shaderCacheName.lowercase()
) )
if (shaderDir.exists()) { if (shaderDir.exists()) {
shaderDir.deleteRecursively() 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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-FileCopyrightText: 2023 yuzu Emulator Project
@@ -35,19 +35,26 @@ class Game(
val keyAddedToLibraryTime get() = "${path}_AddedToLibraryTime" val keyAddedToLibraryTime get() = "${path}_AddedToLibraryTime"
val keyLastPlayedTime get() = "${path}_LastPlayed" 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 val settingsName: String
get() { get() {
val programIdLong = programId.toLong() return if (applicationIdLong == 0L) {
return if (programIdLong == 0L) {
FileUtil.getFilename(Uri.parse(path)) FileUtil.getFilename(Uri.parse(path))
} else { } else {
"0" + programIdLong.toString(16).uppercase() "0" + applicationIdLong.toString(16).uppercase()
} }
} }
val programIdHex: String val programIdHex: String
get() { get() {
val programIdLong = programId.toLong()
return if (programIdLong == 0L) { return if (programIdLong == 0L) {
"0" "0"
} else { } 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 val saveZipName: String
get() = "$title ${YuzuApplication.appContext.getString(R.string.save_data).lowercase()} - ${ get() = "$title ${YuzuApplication.appContext.getString(R.string.save_data).lowercase()} - ${
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
}.zip" }.zip"
val saveDir: String val saveDir: String
get() = NativeConfig.getSaveDir() + NativeLibrary.getSavePath(programId) get() = NativeConfig.getSaveDir() + NativeLibrary.getSavePath(applicationId)
val addonDir: String val addonDir: String
get() = DirectoryInitialization.userDirectory + "/load/" + programIdHex + "/" get() = DirectoryInitialization.userDirectory + "/load/" + applicationIdHex + "/"
val launchIntent: Intent val launchIntent: Intent
get() = Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply { 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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-FileCopyrightText: 2023 yuzu Emulator Project
@@ -65,7 +65,7 @@ object CustomSettingsHandler {
// Initialize per-game config // Initialize per-game config
try { try {
val fileName = FileUtil.getFilename(Uri.parse(game.path)) 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") Log.info("[CustomSettingsHandler] Successfully applied custom settings")
return game return game
} catch (e: Exception) { } catch (e: Exception) {
@@ -333,20 +333,20 @@ object CustomSettingsHandler {
*/ */
fun findGameByTitleId(titleId: String, context: Context): Game? { fun findGameByTitleId(titleId: String, context: Context): Game? {
Log.info("[CustomSettingsHandler] Searching for game with title ID: $titleId") Log.info("[CustomSettingsHandler] Searching for game with title ID: $titleId")
// Convert hex title ID to decimal for comparison with programId // Convert the program ID to the application ID used by per-game settings.
val programIdDecimal = try { val applicationIdLong = try {
titleId.toLong(16).toString() titleId.toLong(16) and -8192L
} catch (e: NumberFormatException) { } catch (e: NumberFormatException) {
Log.error("[CustomSettingsHandler] Invalid title ID format: $titleId") Log.error("[CustomSettingsHandler] Invalid title ID format: $titleId")
return null return null
} }
val applicationIdDecimal = applicationIdLong.toString()
// Expected hex format with "0" prefix // 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 // First check cached games for fast lookup
GameHelper.cachedGameList.find { game -> GameHelper.cachedGameList.find { game ->
game.programId == programIdDecimal || game.applicationId == applicationIdDecimal || game.applicationIdHex.equals(expectedHex, ignoreCase = true)
game.programIdHex.equals(expectedHex, ignoreCase = true)
}?.let { foundGame -> }?.let { foundGame ->
Log.info("[CustomSettingsHandler] Found game in cache: ${foundGame.title}") Log.info("[CustomSettingsHandler] Found game in cache: ${foundGame.title}")
return foundGame return foundGame
@@ -355,8 +355,7 @@ object CustomSettingsHandler {
Log.info("[CustomSettingsHandler] Game not in cache, scanning full library...") Log.info("[CustomSettingsHandler] Game not in cache, scanning full library...")
val allGames = GameHelper.getGames() val allGames = GameHelper.getGames()
val foundGame = allGames.find { game -> val foundGame = allGames.find { game ->
game.programId == programIdDecimal || game.applicationId == applicationIdDecimal || game.applicationIdHex.equals(expectedHex, ignoreCase = true)
game.programIdHex.equals(expectedHex, ignoreCase = true)
} }
if (foundGame != null) { if (foundGame != null) {
Log.info("[CustomSettingsHandler] Found game: ${foundGame.title} at ${foundGame.path}") Log.info("[CustomSettingsHandler] Found game: ${foundGame.title} at ${foundGame.path}")
@@ -170,12 +170,12 @@ object GameHelper {
val game = getGame(it.uri, true, false) val game = getGame(it.uri, true, false)
if (game != null) { if (game != null) {
games.add(game) games.add(game)
if (game.programId != "0") { if (game.applicationId != "0") {
gamesByProgramId[game.programId] = game gamesByProgramId[game.applicationId] = game
} }
} else if (mountedContainer) { } else if (mountedContainer) {
GameMetadata.getProgramId(filePath).toLongOrNull()?.let { programId -> GameMetadata.getProgramId(filePath).toLongOrNull()?.let { programId ->
gamesByProgramId[(programId and 0x800L.inv()).toString()] gamesByProgramId[(programId and -8192L).toString()]
}?.let { existingGame -> }?.let { existingGame ->
NativeLibrary.getPatchesForFile(existingGame.path, existingGame.programId) NativeLibrary.getPatchesForFile(existingGame.path, existingGame.programId)
existingGame.version = GameMetadata.getVersion( existingGame.version = GameMetadata.getVersion(
+8 -7
View File
@@ -788,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, jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
jstring jprogramId, jstring jprogramId,
jstring jupdatePath) { 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::string updatePath = Common::Android::GetJString(env, jupdatePath);
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>( std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
@@ -796,7 +796,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en
for (const auto& item : nsp->GetNCAs()) { for (const auto& item : nsp->GetNCAs()) {
for (const auto& nca_details : item.second) { for (const auto& nca_details : item.second) {
if (nca_details.second->GetName().ends_with(".cnmt.nca")) { 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) { if (update_id == program_id) {
return true; return true;
} }
@@ -1491,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) { 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); return FirmwareManager::GameRequiresFirmware(program_id);
} }
@@ -1575,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, void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeUpdate(JNIEnv* env, jobject jobj,
jstring jprogramId) { jstring jprogramId) {
auto program_id = EmulationSession::GetProgramId(env, jprogramId); const auto program_id = EmulationSession::GetProgramId(env, jprogramId);
ContentManager::RemoveUpdate(EmulationSession::GetInstance().System().GetFileSystemController(), ContentManager::RemoveUpdate(EmulationSession::GetInstance().System().GetFileSystemController(),
program_id); program_id);
} }
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj, void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj,
jstring jprogramId) { 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); ContentManager::RemoveAllDLC(EmulationSession::GetInstance().System(), program_id);
} }
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId, void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId,
jstring jname) { jstring jname) {
auto program_id = EmulationSession::GetProgramId(env, jprogramId); const auto program_id = EmulationSession::GetProgramId(env, jprogramId);
ContentManager::RemoveMod(EmulationSession::GetInstance().System().GetFileSystemController(), ContentManager::RemoveMod(EmulationSession::GetInstance().System().GetFileSystemController(),
program_id, Common::Android::GetJString(env, jname)); program_id, Common::Android::GetJString(env, jname));
} }
@@ -1635,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 Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
jstring jprogramId) { jstring jprogramId) {
auto program_id = EmulationSession::GetProgramId(env, jprogramId); const auto program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
if (program_id == 0) { if (program_id == 0) {
return Common::Android::ToJString(env, ""); return Common::Android::ToJString(env, "");
} }
@@ -12,6 +12,7 @@
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
#include "common/logging.h" #include "common/logging.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/file_sys/common_funcs.h"
#include "frontend_common/config.h" #include "frontend_common/config.h"
#include "frontend_common/settings_generator.h" #include "frontend_common/settings_generator.h"
#include "native.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, void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializePerGameConfig(JNIEnv* env, jobject obj,
jstring jprogramId, jstring jprogramId,
jstring jfileName) { 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); auto file_name = Common::Android::GetJString(env, jfileName);
const auto config_file_name = program_id == 0 ? file_name : fmt::format("{:016X}", program_id); const auto config_file_name = program_id == 0 ? file_name : fmt::format("{:016X}", program_id);
per_game_config = 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, jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDisabledAddons(JNIEnv* env, jobject obj,
jstring jprogramId) { 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]; auto& disabledAddons = Settings::values.disabled_addons[program_id];
jobjectArray jdisabledAddonsArray = jobjectArray jdisabledAddonsArray =
env->NewObjectArray(disabledAddons.size(), Common::Android::GetStringClass(), 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, void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, jobject obj,
jstring jprogramId, jstring jprogramId,
jobjectArray jdisabledAddons) { 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(); Settings::values.disabled_addons[program_id].clear();
std::vector<std::string> disabled_addons; std::vector<std::string> disabled_addons;
const int size = env->GetArrayLength(jdisabledAddons); const int size = env->GetArrayLength(jdisabledAddons);
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -8,11 +5,17 @@
#include "common/assert.h" #include "common/assert.h"
namespace AudioCore::ADSP::OpusDecoder { namespace AudioCore::ADSP::OpusDecoder {
namespace {
bool IsValidChannelCount(u32 channel_count) {
return channel_count == 1 || channel_count == 2;
}
} // namespace
u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) { u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) {
if (channel_count == 1 || channel_count == 2) if (!IsValidChannelCount(channel_count)) {
return 0; return 0;
return u32(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count); }
return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count);
} }
OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) { OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) {
@@ -22,6 +22,10 @@ namespace AudioCore::ADSP::OpusDecoder {
namespace { namespace {
constexpr size_t OpusStreamCountMax = 255; constexpr size_t OpusStreamCountMax = 255;
bool IsValidChannelCount(u32 channel_count) {
return channel_count == 1 || channel_count == 2;
}
bool IsValidMultiStreamChannelCount(u32 channel_count) { bool IsValidMultiStreamChannelCount(u32 channel_count) {
return channel_count <= OpusStreamCountMax; return channel_count <= OpusStreamCountMax;
} }
-3
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
+4 -4
View File
@@ -14,16 +14,14 @@
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_event.h"
namespace AudioCore::AudioIn {
// See texture_cache/util.h // See texture_cache/util.h
template<typename T, size_t N> template<typename T, size_t N>
#if BOOST_VERSION >= 108100 || __GNUC__ > 12 #if BOOST_VERSION >= 108100 || __GNUC__ > 12
[[nodiscard]] static inline boost::container::static_vector<T, N> FixStaticVectorADL(const boost::container::static_vector<T, N>& v) { [[nodiscard]] boost::container::static_vector<T, N> FixStaticVectorADL(const boost::container::static_vector<T, N>& v) {
return v; return v;
} }
#else #else
[[nodiscard]] static inline std::vector<T> FixStaticVectorADL(const boost::container::static_vector<T, N>& v) { [[nodiscard]] std::vector<T> FixStaticVectorADL(const boost::container::static_vector<T, N>& v) {
std::vector<T> u; std::vector<T> u;
for (auto const& e : v) for (auto const& e : v)
u.push_back(e); u.push_back(e);
@@ -31,6 +29,8 @@ template<typename T, size_t N>
} }
#endif #endif
namespace AudioCore::AudioIn {
System::System(Core::System& system_, Kernel::KEvent* event_, const size_t session_id_) System::System(Core::System& system_, Kernel::KEvent* event_, const size_t session_id_)
: system{system_}, buffer_event{event_}, : system{system_}, buffer_event{event_},
session_id{session_id_}, session{std::make_unique<DeviceSession>(system_)} {} session_id{session_id_}, session{std::make_unique<DeviceSession>(system_)} {}
+4 -3
View File
@@ -14,15 +14,14 @@
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_event.h"
namespace AudioCore::AudioOut {
// See texture_cache/util.h // See texture_cache/util.h
template<typename T, size_t N> template<typename T, size_t N>
#if BOOST_VERSION >= 108100 || __GNUC__ > 12 #if BOOST_VERSION >= 108100 || __GNUC__ > 12
[[nodiscard]] static inline boost::container::static_vector<T, N> FixStaticVectorADL(const boost::container::static_vector<T, N>& v) { [[nodiscard]] boost::container::static_vector<T, N> FixStaticVectorADL(const boost::container::static_vector<T, N>& v) {
return v; return v;
} }
#else #else
[[nodiscard]] static inline std::vector<T> FixStaticVectorADL(const boost::container::static_vector<T, N>& v) { [[nodiscard]] std::vector<T> FixStaticVectorADL(const boost::container::static_vector<T, N>& v) {
std::vector<T> u; std::vector<T> u;
for (auto const& e : v) for (auto const& e : v)
u.push_back(e); u.push_back(e);
@@ -30,6 +29,8 @@ template<typename T, size_t N>
} }
#endif #endif
namespace AudioCore::AudioOut {
System::System(Core::System& system_, Kernel::KEvent* event_, size_t session_id_) System::System(Core::System& system_, Kernel::KEvent* event_, size_t session_id_)
: system{system_}, buffer_event{event_}, : system{system_}, buffer_event{event_},
session_id{session_id_}, session{std::make_unique<DeviceSession>(system_)} {} session_id{session_id_}, session{std::make_unique<DeviceSession>(system_)} {}
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -16,7 +16,7 @@ namespace AudioCore::Renderer {
* @param memory - Core memory for writing. * @param memory - Core memory for writing.
* @param aux_info - Memory address pointing to the AuxInfo to reset. * @param aux_info - Memory address pointing to the AuxInfo to reset.
*/ */
static void CaptureResetAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr aux_info) { static void ResetAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr aux_info) {
if (aux_info == 0) { if (aux_info == 0) {
LOG_ERROR(Service_Audio, "Aux info is 0!"); LOG_ERROR(Service_Audio, "Aux info is 0!");
return; return;
@@ -134,7 +134,7 @@ void CaptureCommand::Process(const AudioRenderer::CommandListProcessor& processo
WriteAuxBufferDsp(*processor.memory, send_buffer_info, send_buffer, count_max, input_buffer, WriteAuxBufferDsp(*processor.memory, send_buffer_info, send_buffer, count_max, input_buffer,
processor.sample_count, write_offset, update_count); processor.sample_count, write_offset, update_count);
} else { } else {
CaptureResetAuxBufferDsp(*processor.memory, send_buffer_info); ResetAuxBufferDsp(*processor.memory, send_buffer_info);
} }
} }
+8 -9
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2011 Google, Inc. // SPDX-FileCopyrightText: 2011 Google, Inc.
// SPDX-FileContributor: Geoff Pike // SPDX-FileContributor: Geoff Pike
// SPDX-FileContributor: Jyrki Alakuijala // SPDX-FileContributor: Jyrki Alakuijala
@@ -30,6 +27,8 @@
#define WORDS_BIGENDIAN 1 #define WORDS_BIGENDIAN 1
#endif #endif
using namespace std;
namespace Common { namespace Common {
static u64 unaligned_load64(const char* p) { static u64 unaligned_load64(const char* p) {
@@ -136,18 +135,18 @@ static u64 HashLen17to32(const char* s, size_t len) {
// Return a 16-byte hash for 48 bytes. Quick and dirty. // Return a 16-byte hash for 48 bytes. Quick and dirty.
// Callers do best to use "random-looking" values for a and b. // Callers do best to use "random-looking" values for a and b.
static std::pair<u64, u64> WeakHashLen32WithSeeds(u64 w, u64 x, u64 y, u64 z, u64 a, u64 b) { static pair<u64, u64> WeakHashLen32WithSeeds(u64 w, u64 x, u64 y, u64 z, u64 a, u64 b) {
a += w; a += w;
b = Rotate(b + a + z, 21); b = Rotate(b + a + z, 21);
u64 c = a; u64 c = a;
a += x; a += x;
a += y; a += y;
b += Rotate(a, 44); b += Rotate(a, 44);
return std::make_pair(a + z, b + c); return make_pair(a + z, b + c);
} }
// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. // Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty.
static std::pair<u64, u64> WeakHashLen32WithSeeds(const char* s, u64 a, u64 b) { static pair<u64, u64> WeakHashLen32WithSeeds(const char* s, u64 a, u64 b) {
return WeakHashLen32WithSeeds(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16), Fetch64(s + 24), a, return WeakHashLen32WithSeeds(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16), Fetch64(s + 24), a,
b); b);
} }
@@ -190,8 +189,8 @@ u64 CityHash64(const char* s, size_t len) {
u64 x = Fetch64(s + len - 40); u64 x = Fetch64(s + len - 40);
u64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56); u64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56);
u64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24)); u64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24));
std::pair<u64, u64> v = WeakHashLen32WithSeeds(s + len - 64, len, z); pair<u64, u64> v = WeakHashLen32WithSeeds(s + len - 64, len, z);
std::pair<u64, u64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x); pair<u64, u64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x);
x = x * k1 + Fetch64(s); x = x * k1 + Fetch64(s);
// Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
@@ -259,7 +258,7 @@ u128 CityHash128WithSeed(const char* s, size_t len, u128 seed) {
// We expect len >= 128 to be the common case. Keep 56 bytes of state: // We expect len >= 128 to be the common case. Keep 56 bytes of state:
// v, w, x, y, and z. // v, w, x, y, and z.
std::pair<u64, u64> v, w; pair<u64, u64> v, w;
u64 x = seed[0]; u64 x = seed[0];
u64 y = seed[1]; u64 y = seed[1];
u64 z = len * k1; u64 z = len * k1;
-2
View File
@@ -16,5 +16,3 @@
#ifdef __GNUC__ #ifdef __GNUC__
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
#undef INVALID_SOCKET
+8 -7
View File
@@ -7,18 +7,19 @@
#pragma once #pragma once
#include <dynarmic/interface/halt_reason.h> #include <dynarmic/interface/halt_reason.h>
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
namespace Core { namespace Core {
inline constexpr Dynarmic::HaltReason StepThread = Dynarmic::HaltReason::Step; constexpr Dynarmic::HaltReason StepThread = Dynarmic::HaltReason::Step;
inline constexpr Dynarmic::HaltReason DataAbort = Dynarmic::HaltReason::MemoryAbort; constexpr Dynarmic::HaltReason DataAbort = Dynarmic::HaltReason::MemoryAbort;
inline constexpr Dynarmic::HaltReason BreakLoop = Dynarmic::HaltReason::UserDefined2; constexpr Dynarmic::HaltReason BreakLoop = Dynarmic::HaltReason::UserDefined2;
inline constexpr Dynarmic::HaltReason SupervisorCall = Dynarmic::HaltReason::UserDefined3; constexpr Dynarmic::HaltReason SupervisorCall = Dynarmic::HaltReason::UserDefined3;
inline constexpr Dynarmic::HaltReason InstructionBreakpoint = Dynarmic::HaltReason::UserDefined4; constexpr Dynarmic::HaltReason InstructionBreakpoint = Dynarmic::HaltReason::UserDefined4;
inline constexpr Dynarmic::HaltReason PrefetchAbort = Dynarmic::HaltReason::UserDefined6; constexpr Dynarmic::HaltReason PrefetchAbort = Dynarmic::HaltReason::UserDefined6;
[[nodiscard]] inline constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) { constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
static_assert(u64(HaltReason::StepThread) == u64(StepThread)); static_assert(u64(HaltReason::StepThread) == u64(StepThread));
static_assert(u64(HaltReason::DataAbort) == u64(DataAbort)); static_assert(u64(HaltReason::DataAbort) == u64(DataAbort));
static_assert(u64(HaltReason::BreakLoop) == u64(BreakLoop)); static_assert(u64(HaltReason::BreakLoop) == u64(BreakLoop));
+36 -7
View File
@@ -4,6 +4,7 @@
#include <array> #include <array>
#include <atomic> #include <atomic>
#include <memory> #include <memory>
#include <unordered_map>
#include <utility> #include <utility>
#include "game_settings.h" #include "game_settings.h"
@@ -16,6 +17,7 @@
#include "common/string_util.h" #include "common/string_util.h"
#include "core/arm/exclusive_monitor.h" #include "core/arm/exclusive_monitor.h"
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/common_funcs.h"
#include "launch_timestamp_cache.h" #include "launch_timestamp_cache.h"
#include "core/core_timing.h" #include "core/core_timing.h"
@@ -248,12 +250,30 @@ struct System::Impl {
} }
} }
void SetNVDECActive(bool is_nvdec_active) { void NotifyNVDECChannelOpen(u64 process_id) {
nvdec_active = is_nvdec_active; 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() { 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) { void InitializeDebugger(System& system, u16 port) {
@@ -377,7 +397,7 @@ struct System::Impl {
LOG_ERROR(Core, "Failed to find program id for ROM"); 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()) { if (auto room_member = Network::GetRoomMember().lock()) {
Network::GameInfo game_info; Network::GameInfo game_info;
game_info.name = name; game_info.name = name;
@@ -505,6 +525,8 @@ struct System::Impl {
mutable std::mutex suspend_guard; mutable std::mutex suspend_guard;
std::mutex general_channel_mutex; 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_paused{};
std::atomic_bool is_shutting_down{}; std::atomic_bool is_shutting_down{};
std::atomic_bool is_powered_on{}; std::atomic_bool is_powered_on{};
@@ -512,7 +534,6 @@ struct System::Impl {
bool extended_memory_layout : 1 = false; bool extended_memory_layout : 1 = false;
bool exit_locked : 1 = false; bool exit_locked : 1 = false;
bool exit_requested : 1 = false; bool exit_requested : 1 = false;
bool nvdec_active : 1 = false;
void EnsureGeneralChannelInitialized(System& system) { void EnsureGeneralChannelInitialized(System& system) {
if (!general_channel_event) { if (!general_channel_event) {
@@ -576,14 +597,22 @@ void System::UnstallApplication() {
impl->UnstallApplication(); impl->UnstallApplication();
} }
void System::SetNVDECActive(bool is_nvdec_active) { void System::NotifyNVDECChannelOpen(u64 process_id) {
impl->SetNVDECActive(is_nvdec_active); impl->NotifyNVDECChannelOpen(process_id);
}
void System::NotifyNVDECChannelClose(u64 process_id) {
impl->NotifyNVDECChannelClose(process_id);
} }
bool System::GetNVDECActive() { bool System::GetNVDECActive() {
return impl->GetNVDECActive(); return impl->GetNVDECActive();
} }
bool System::IsNVDECActiveForProcess(u64 process_id) {
return impl->IsNVDECActiveForProcess(process_id);
}
void System::InitializeDebugger() { void System::InitializeDebugger() {
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue()); impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
} }
+3 -1
View File
@@ -191,8 +191,10 @@ public:
std::unique_lock<std::mutex> StallApplication(); std::unique_lock<std::mutex> StallApplication();
void UnstallApplication(); void UnstallApplication();
void SetNVDECActive(bool is_nvdec_active); void NotifyNVDECChannelOpen(u64 process_id);
void NotifyNVDECChannelClose(u64 process_id);
[[nodiscard]] bool GetNVDECActive(); [[nodiscard]] bool GetNVDECActive();
[[nodiscard]] bool IsNVDECActiveForProcess(u64 process_id);
/** /**
* Initialize the debugger. * Initialize the debugger.
-1
View File
@@ -38,7 +38,6 @@ constexpr u32 CpuClockTargetMhz(Settings::CpuClock clock) {
} }
} }
#undef CreateEvent
std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) {
return std::make_shared<EventType>(std::move(callback), std::move(name)); return std::make_shared<EventType>(std::move(callback), std::move(name));
} }
+5 -5
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -185,13 +185,13 @@ static_assert(sizeof(SaveDataFilter) == 0x48, "SaveDataFilter has invalid size."
static_assert(std::is_trivially_copyable_v<SaveDataFilter>, static_assert(std::is_trivially_copyable_v<SaveDataFilter>,
"Data type must be trivially copyable."); "Data type must be trivially copyable.");
struct SaveDataHashSalt { struct HashSalt {
static constexpr size_t Size = 32; static constexpr size_t Size = 32;
std::array<u8, Size> value; std::array<u8, Size> value;
}; };
static_assert(std::is_trivially_copyable_v<SaveDataHashSalt>, "Data type must be trivially copyable."); static_assert(std::is_trivially_copyable_v<HashSalt>, "Data type must be trivially copyable.");
static_assert(sizeof(SaveDataHashSalt) == SaveDataHashSalt::Size); static_assert(sizeof(HashSalt) == HashSalt::Size);
struct SaveDataCreationInfo2 { struct SaveDataCreationInfo2 {
@@ -210,7 +210,7 @@ struct SaveDataCreationInfo2 {
u8 reserved1; u8 reserved1;
bool is_hash_salt_enabled; bool is_hash_salt_enabled;
u8 reserved2; u8 reserved2;
SaveDataHashSalt hash_salt; HashSalt hash_salt;
SaveDataMetaType meta_type; SaveDataMetaType meta_type;
u8 reserved3; u8 reserved3;
s32 meta_size; s32 meta_size;
+1 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -14,7 +11,7 @@
#include "core/file_sys/vfs/vfs.h" #include "core/file_sys/vfs/vfs.h"
#include "core/file_sys/vfs/vfs_vector.h" #include "core/file_sys/vfs/vfs_vector.h"
namespace FileSys::RomFSBuilder { namespace FileSys {
constexpr u64 FS_MAX_PATH = 0x301; constexpr u64 FS_MAX_PATH = 0x301;
+1 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -12,7 +9,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "core/file_sys/vfs/vfs.h" #include "core/file_sys/vfs/vfs.h"
namespace FileSys::RomFSBuilder { namespace FileSys {
struct RomFSBuildDirectoryContext; struct RomFSBuildDirectoryContext;
struct RomFSBuildFileContext; struct RomFSBuildFileContext;
+132 -62
View File
@@ -161,7 +161,7 @@ std::string GetUpdateVersionStringFromSlot(const ContentProvider* provider, u64
PatchManager::PatchManager(u64 title_id_, PatchManager::PatchManager(u64 title_id_,
const Service::FileSystem::FileSystemController& fs_controller_, const Service::FileSystem::FileSystemController& fs_controller_,
const ContentProvider& content_provider_) 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; PatchManager::~PatchManager() = default;
@@ -169,13 +169,41 @@ u64 PatchManager::GetTitleID() const {
return title_id; 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 { VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id); LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
if (exefs == nullptr) if (exefs == nullptr)
return exefs; return exefs;
const auto& disabled = Settings::values.disabled_addons[title_id]; const auto& disabled = Settings::values.disabled_addons[application_id];
bool update_disabled = true; bool update_disabled = true;
std::optional<u32> enabled_version; std::optional<u32> enabled_version;
@@ -183,7 +211,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
bool checked_manual = false; bool checked_manual = false;
const auto* content_union = static_cast<const ContentProviderUnion*>(&content_provider); 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) { if (content_union) {
// First, check ExternalContentProvider // First, check ExternalContentProvider
@@ -303,17 +331,21 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
} }
// LayeredExeFS // LayeredExeFS
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); const auto load_dirs = GetModificationLoadRoots();
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); const auto sdmc_load_dirs = GetSDMCModificationLoadRoots();
std::vector<VirtualDir> patch_dirs = {sdmc_load_dir}; std::vector<VirtualDir> patch_dirs;
if (load_dir != nullptr) { 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(); const auto load_patch_dirs = load_dir->GetSubdirectories();
patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end()); patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
} }
std::sort(patch_dirs.begin(), patch_dirs.end(), std::stable_sort(patch_dirs.begin(), patch_dirs.end(), [](const VirtualDir& l, const VirtualDir& r) {
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); return l->GetName() < r->GetName();
});
std::vector<VirtualDir> layers; std::vector<VirtualDir> layers;
layers.reserve(patch_dirs.size() + 1); 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, std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs,
const std::string& build_id) const { 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); const auto nso_build_id = fmt::format("{:0<64}", build_id);
std::vector<VirtualFile> out; 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); LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); const auto load_dirs = GetModificationLoadRoots();
if (load_dir == nullptr) { if (load_dirs.empty()) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return nso; return nso;
} }
auto patch_dirs = load_dir->GetSubdirectories(); std::vector<VirtualDir> patch_dirs;
std::sort(patch_dirs.begin(), patch_dirs.end(), for (const auto& load_dir : load_dirs) {
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); 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); const auto patches = CollectPatches(patch_dirs, build_id);
auto out = nso; 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); LOG_INFO(Loader, "Querying NSO patch existence for build_id={}, name={}", build_id, name);
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); const auto load_dirs = GetModificationLoadRoots();
if (load_dir == nullptr) { if (load_dirs.empty()) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return false; return false;
} }
auto patch_dirs = load_dir->GetSubdirectories(); std::vector<VirtualDir> patch_dirs;
std::sort(patch_dirs.begin(), patch_dirs.end(), for (const auto& load_dir : load_dirs) {
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); 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(); return !CollectPatches(patch_dirs, build_id).empty();
} }
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const { std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); const auto load_dirs = GetModificationLoadRoots();
if (load_dir == nullptr) { if (load_dirs.empty()) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return {}; return {};
} }
const auto& disabled = Settings::values.disabled_addons[title_id]; const auto& disabled = Settings::values.disabled_addons[application_id];
auto patch_dirs = load_dir->GetSubdirectories(); std::vector<VirtualDir> patch_dirs;
std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); }); 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 // <mod dir> / <folder> / cheats / <build id>.txt
std::vector<Core::Memory::CheatEntry> out; 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_') // Uncareless user-friendly loading of patches (must start with 'cheat_')
// <mod dir> / <cheat file>.txt // <mod dir> / <cheat file>.txt
for (auto const& f : load_dir->GetFiles()) { for (const auto& load_dir : load_dirs) {
auto const name = f->GetName(); for (auto const& f : load_dir->GetFiles()) {
if (name.starts_with("cheat_") && std::find(disabled.cbegin(), disabled.cend(), name) == disabled.cend()) { auto const name = f->GetName();
std::vector<u8> data(f->GetSize()); if (name.starts_with("cheat_") && std::find(disabled.cbegin(), disabled.cend(), name) == disabled.cend()) {
if (f->Read(data.data(), data.size()) == data.size()) { std::vector<u8> data(f->GetSize());
const Core::Memory::TextCheatParser parser; if (f->Read(data.data(), data.size()) == data.size()) {
auto const res = parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size())); const Core::Memory::TextCheatParser parser;
std::copy(res.begin(), res.end(), std::back_inserter(out)); auto const res = parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
} else { std::copy(res.begin(), res.end(), std::back_inserter(out));
LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}", title_id); } else {
LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}", title_id);
}
} }
} }
} }
return out; 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 Service::FileSystem::FileSystemController& fs_controller) {
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); std::vector<VirtualDir> load_dirs{fs_controller.GetModificationLoadRoot(title_id)};
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(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 && if ((type != ContentRecordType::Program && type != ContentRecordType::Data &&
type != ContentRecordType::HtmlDocument) || type != ContentRecordType::HtmlDocument) ||
(load_dir == nullptr && sdmc_load_dir == nullptr)) { (load_dirs.empty() && sdmc_load_dirs.empty())) {
return; return;
} }
const auto& disabled = Settings::values.disabled_addons[title_id]; const auto& disabled = Settings::values.disabled_addons[application_id];
std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories(); std::vector<VirtualDir> patch_dirs;
if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) { for (const auto& load_dir : load_dirs) {
patch_dirs.push_back(sdmc_load_dir); 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(), if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); 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;
std::vector<VirtualDir> layers_ext; std::vector<VirtualDir> layers_ext;
@@ -597,8 +657,8 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
auto romfs = base_romfs; auto romfs = base_romfs;
// Game Updates // Game Updates
const auto update_tid = GetUpdateTitleID(title_id); const auto update_tid = GetUpdateTitleIDForContent();
const auto& disabled = Settings::values.disabled_addons[title_id]; const auto& disabled = Settings::values.disabled_addons[application_id];
bool update_disabled = true; bool update_disabled = true;
std::optional<u32> enabled_version; std::optional<u32> enabled_version;
@@ -705,7 +765,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
// LayeredFS // LayeredFS
if (apply_layeredfs) { if (apply_layeredfs) {
ApplyLayeredFS(romfs, title_id, type, fs_controller); ApplyLayeredFS(romfs, title_id, application_id, type, fs_controller);
} }
return romfs; return romfs;
@@ -717,10 +777,10 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
} }
std::vector<Patch> out; std::vector<Patch> out;
const auto& disabled = Settings::values.disabled_addons[title_id]; const auto& disabled = Settings::values.disabled_addons[application_id];
// Game Updates // Game Updates
const auto update_tid = GetUpdateTitleID(title_id); const auto update_tid = GetUpdateTitleIDForContent();
std::vector<Patch> external_update_patches; std::vector<Patch> external_update_patches;
@@ -869,7 +929,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
.version = "", .version = "",
.type = PatchType::Update, .type = PatchType::Update,
.program_id = title_id, .program_id = title_id,
.title_id = title_id, .title_id = update_tid,
.source = PatchSource::Unknown, .source = PatchSource::Unknown,
.numeric_version = 0}; .numeric_version = 0};
@@ -895,8 +955,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
} }
// General Mods (LayeredFS and IPS) // General Mods (LayeredFS and IPS)
const auto mod_dir = fs_controller.GetModificationLoadRoot(title_id); for (const auto& mod_dir : GetModificationLoadRoots()) {
if (mod_dir != nullptr) {
for (auto const& f : mod_dir->GetFiles()) for (auto const& f : mod_dir->GetFiles())
if (auto const name = f->GetName(); name.starts_with("cheat_")) { if (auto const name = f->GetName(); name.starts_with("cheat_")) {
auto const mod_disabled = std::find(disabled.begin(), disabled.end(), name) != disabled.end(); 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) // SDMC mod directory (RomFS LayeredFS)
const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); for (const auto& sdmc_mod_dir : GetSDMCModificationLoadRoots()) {
if (sdmc_mod_dir != nullptr) {
std::string types; std::string types;
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs")))
AppendCommaIfNotEmpty(types, "LayeredExeFS"); AppendCommaIfNotEmpty(types, "LayeredExeFS");
@@ -999,10 +1057,10 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
dlc_match.reserve(dlc_entries_with_origin.size()); dlc_match.reserve(dlc_entries_with_origin.size());
for (const auto& [slot, entry] : dlc_entries_with_origin) { for (const auto& [slot, entry] : dlc_entries_with_origin) {
const auto base_tid = GetBaseTitleID(entry.title_id); 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) { if (!matches_base) {
LOG_DEBUG(Loader, "DLC {:016X} base {:016X} doesn't match title {:016X}", 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; continue;
} }
@@ -1077,16 +1135,22 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
} }
std::optional<u32> PatchManager::GetGameVersion() 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)) { if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
return content_provider.GetEntryVersion(update_tid); 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 { 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) { if (base_control_nca == nullptr) {
return {}; return {};
} }
@@ -1162,8 +1226,14 @@ PatchManager::Metadata PatchManager::ParseControlNCA(const NCA& nca) const {
auto metadata = pm.GetControlMetadata(); auto metadata = pm.GetControlMetadata();
if (metadata.first != nullptr) if (metadata.first != nullptr)
return metadata; return metadata;
const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(application_id), system.GetFileSystemController(), system.GetContentProvider()}; const auto update_id = FileSys::GetUpdateTitleID(application_id);
return pm_update.GetControlMetadata(); 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 } // namespace FileSys
+5
View File
@@ -10,6 +10,7 @@
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <string> #include <string>
#include <vector>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/file_sys/nca_metadata.h" #include "core/file_sys/nca_metadata.h"
#include "core/file_sys/vfs/vfs_types.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; [[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept;
private: 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, [[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
const std::string& build_id) const; const std::string& build_id) const;
u64 title_id; u64 title_id;
u64 application_id;
const Service::FileSystem::FileSystemController& fs_controller; const Service::FileSystem::FileSystemController& fs_controller;
const ContentProvider& content_provider; const ContentProvider& content_provider;
}; };
+10 -10
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -38,7 +38,7 @@ struct RomFSHeader {
}; };
static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size."); static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
struct RomFSDirectoryEntry { struct DirectoryEntry {
u32_le parent; u32_le parent;
u32_le sibling; u32_le sibling;
u32_le child_dir; u32_le child_dir;
@@ -46,9 +46,9 @@ struct RomFSDirectoryEntry {
u32_le hash; u32_le hash;
u32_le name_length; u32_le name_length;
}; };
static_assert(sizeof(RomFSDirectoryEntry) == 0x18, "RomFSDirectoryEntry has incorrect size."); static_assert(sizeof(DirectoryEntry) == 0x18, "DirectoryEntry has incorrect size.");
struct RomFSFileEntry { struct FileEntry {
u32_le parent; u32_le parent;
u32_le sibling; u32_le sibling;
u64_le offset; u64_le offset;
@@ -56,7 +56,7 @@ struct RomFSFileEntry {
u32_le hash; u32_le hash;
u32_le name_length; u32_le name_length;
}; };
static_assert(sizeof(RomFSFileEntry) == 0x20, "RomFSFileEntry has incorrect size."); static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size.");
struct RomFSTraversalContext { struct RomFSTraversalContext {
RomFSHeader header; RomFSHeader header;
@@ -84,14 +84,14 @@ std::pair<EntryType, std::string> GetEntry(const RomFSTraversalContext& ctx, siz
return {entry, std::move(name)}; return {entry, std::move(name)};
} }
std::pair<RomFSDirectoryEntry, std::string> GetDirectoryEntry(const RomFSTraversalContext& ctx, std::pair<DirectoryEntry, std::string> GetDirectoryEntry(const RomFSTraversalContext& ctx,
size_t directory_offset) { size_t directory_offset) {
return GetEntry<RomFSDirectoryEntry, &RomFSTraversalContext::directory_meta>(ctx, directory_offset); return GetEntry<DirectoryEntry, &RomFSTraversalContext::directory_meta>(ctx, directory_offset);
} }
std::pair<RomFSFileEntry, std::string> GetFileEntry(const RomFSTraversalContext& ctx, std::pair<FileEntry, std::string> GetFileEntry(const RomFSTraversalContext& ctx,
size_t file_offset) { size_t file_offset) {
return GetEntry<RomFSFileEntry, &RomFSTraversalContext::file_meta>(ctx, file_offset); return GetEntry<FileEntry, &RomFSTraversalContext::file_meta>(ctx, file_offset);
} }
void ProcessFile(const RomFSTraversalContext& ctx, u32 this_file_offset, void ProcessFile(const RomFSTraversalContext& ctx, u32 this_file_offset,
@@ -163,7 +163,7 @@ VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
if (dir == nullptr) if (dir == nullptr)
return nullptr; return nullptr;
RomFSBuilder::RomFSBuildContext ctx{dir, ext}; RomFSBuildContext ctx{dir, ext};
return ConcatenatedVfsFile::MakeConcatenatedFile(0, dir->GetName(), ctx.Build()); return ConcatenatedVfsFile::MakeConcatenatedFile(0, dir->GetName(), ctx.Build());
} }
+1 -7
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -10,12 +10,6 @@
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
#include "core/file_sys/vfs/vfs.h" #include "core/file_sys/vfs/vfs.h"
#undef CreateFile
#undef DeleteFile
#undef CreateDirectory
#undef CopyFile
#undef MoveFile
namespace FileSys { namespace FileSys {
VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {} VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {}
+1 -5
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -99,10 +99,6 @@ private:
std::string name; std::string name;
}; };
#undef CreateFile
#undef DeleteFile
#undef CreateDirectory
// An implementation of VfsDirectory that maintains two vectors for subdirectories and files. // An implementation of VfsDirectory that maintains two vectors for subdirectories and files.
// Vector data is supplied upon construction. // Vector data is supplied upon construction.
class VectorVfsDirectory : public VfsDirectory { class VectorVfsDirectory : public VfsDirectory {
+1 -5
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-late // SPDX-License-Identifier: GPL-2.0-or-late
@@ -13,10 +13,6 @@
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/svc.h" #include "core/hle/kernel/svc.h"
#undef OutputDebugString
#undef GetObject
#undef CreateProcess
namespace Kernel::Svc { namespace Kernel::Svc {
static uint32_t GetArg32(std::span<uint64_t, 8> args, int n) { static uint32_t GetArg32(std::span<uint64_t, 8> args, int n) {
+10 -10
View File
@@ -11,11 +11,7 @@
namespace Kernel::Svc { namespace Kernel::Svc {
namespace { namespace {
[[nodiscard]] inline constexpr bool IsValidSetAddressRange(u64 address, u64 size) { constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
return address + size > address;
}
[[nodiscard]] inline constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
switch (perm) { switch (perm) {
case MemoryPermission::None: case MemoryPermission::None:
case MemoryPermission::Read: case MemoryPermission::Read:
@@ -26,6 +22,13 @@ namespace {
} }
} }
// Checks if address + size is greater than the given address
// This can return false if the size causes an overflow of a 64-bit type
// or if the given size is zero.
constexpr bool IsValidAddressRange(u64 address, u64 size) {
return address + size > address;
}
// Helper function that performs the common sanity checks for svcMapMemory // Helper function that performs the common sanity checks for svcMapMemory
// and svcUnmapMemory. This is doable, as both functions perform their sanitizing // and svcUnmapMemory. This is doable, as both functions perform their sanitizing
// in the same order. // in the same order.
@@ -50,17 +53,14 @@ Result MapUnmapMemorySanityChecks(const KProcessPageTable& manager, u64 dst_addr
R_THROW(ResultInvalidSize); R_THROW(ResultInvalidSize);
} }
// Checks if address + size is greater than the given address if (!IsValidAddressRange(dst_addr, size)) {
// This can return false if the size causes an overflow of a 64-bit type
// or if the given size is zero.
if (!IsValidSetAddressRange(dst_addr, size)) {
LOG_ERROR(Kernel_SVC, LOG_ERROR(Kernel_SVC,
"Destination is not a valid address range, addr={:#016x}, size={:#016x}", "Destination is not a valid address range, addr={:#016x}, size={:#016x}",
dst_addr, size); dst_addr, size);
R_THROW(ResultInvalidCurrentMemory); R_THROW(ResultInvalidCurrentMemory);
} }
if (!IsValidSetAddressRange(src_addr, size)) { if (!IsValidAddressRange(src_addr, size)) {
LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr={:#016x}, size={:#016x}", LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr={:#016x}, size={:#016x}",
src_addr, size); src_addr, size);
R_THROW(ResultInvalidCurrentMemory); R_THROW(ResultInvalidCurrentMemory);
@@ -11,11 +11,11 @@
namespace Kernel::Svc { namespace Kernel::Svc {
namespace { namespace {
[[nodiscard]] inline constexpr bool IsValidAddressRange(u64 address, u64 size) { constexpr bool IsValidAddressRange(u64 address, u64 size) {
return address + size > address; return address + size > address;
} }
[[nodiscard]] inline constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) { constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
switch (perm) { switch (perm) {
case Svc::MemoryPermission::None: case Svc::MemoryPermission::None:
case Svc::MemoryPermission::Read: case Svc::MemoryPermission::Read:
+11 -2
View File
@@ -6,6 +6,7 @@
#include <optional> #include <optional>
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/common_funcs.h"
#include "core/file_sys/content_archive.h" #include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h" #include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.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 // TODO(DarkLordZach): When FSController/Game Card Support is added, if
// current_process_game_card use correct StorageId // current_process_game_card use correct StorageId
launch.base_game_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program)); auto base_slot = storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program);
launch.update_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(FileSys::GetUpdateTitleID(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); system.GetARPManager().Register(launch.title_id, launch, out_control);
return process; return process;
@@ -158,7 +158,7 @@ Result IApplicationFunctions::EnsureSaveData(Out<u64> out_size, Common::UUID use
LOG_INFO(Service_AM, "called, uid={}", user_id.FormattedString()); LOG_INFO(Service_AM, "called, uid={}", user_id.FormattedString());
FileSys::SaveDataAttribute attribute{}; 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.user_id = user_id.AsU128();
attribute.type = FileSys::SaveDataType::Account; attribute.type = FileSys::SaveDataType::Account;
@@ -238,7 +238,7 @@ Result IApplicationFunctions::ExtendSaveData(Out<u64> out_required_size, FileSys
static_cast<u8>(type), user_id.FormattedString(), normal_size, journal_size); static_cast<u8>(type), user_id.FormattedString(), normal_size, journal_size);
system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize( 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 // 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. // due to running out of space. Since we always succeed, this should be 0.
@@ -252,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()); LOG_DEBUG(Service_AM, "called with type={} user_id={}", type, user_id.FormattedString());
const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize( 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_normal_size = size.normal;
*out_journal_size = size.journal; *out_journal_size = size.journal;
+6 -5
View File
@@ -9,12 +9,13 @@
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
namespace Service::Audio { namespace Service::Audio {
using namespace AudioCore::AudioIn;
IAudioIn::IAudioIn(Core::System& system_, AudioCore::AudioIn::Manager& manager, size_t session_id, IAudioIn::IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
const std::string& device_name, const AudioCore::AudioIn::AudioInParameter& in_params, const std::string& device_name, const AudioInParameter& in_params,
Kernel::KProcess* handle, u64 applet_resource_user_id) Kernel::KProcess* handle, u64 applet_resource_user_id)
: ServiceFramework{system_, "IAudioIn"}, process{handle}, service_context{system_, "IAudioIn"}, : ServiceFramework{system_, "IAudioIn"}, process{handle}, service_context{system_, "IAudioIn"},
event{service_context.CreateEvent("AudioInEvent")}, impl{std::make_shared<AudioCore::AudioIn::In>(system_, event{service_context.CreateEvent("AudioInEvent")}, impl{std::make_shared<In>(system_,
manager, event, manager, event,
session_id)} { session_id)} {
// clang-format off // clang-format off
@@ -70,12 +71,12 @@ Result IAudioIn::Stop() {
R_RETURN(impl->StopSystem()); R_RETURN(impl->StopSystem());
} }
Result IAudioIn::AppendAudioInBuffer(InArray<AudioCore::AudioIn::AudioInBuffer, BufferAttr_HipcMapAlias> buffer, Result IAudioIn::AppendAudioInBuffer(InArray<AudioInBuffer, BufferAttr_HipcMapAlias> buffer,
u64 buffer_client_ptr) { u64 buffer_client_ptr) {
R_RETURN(this->AppendAudioInBufferAuto(buffer, buffer_client_ptr)); R_RETURN(this->AppendAudioInBufferAuto(buffer, buffer_client_ptr));
} }
Result IAudioIn::AppendAudioInBufferAuto(InArray<AudioCore::AudioIn::AudioInBuffer, BufferAttr_HipcAutoSelect> buffer, Result IAudioIn::AppendAudioInBufferAuto(InArray<AudioInBuffer, BufferAttr_HipcAutoSelect> buffer,
u64 buffer_client_ptr) { u64 buffer_client_ptr) {
if (buffer.empty()) { if (buffer.empty()) {
LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioInBuffer!"); LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioInBuffer!");
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -10,6 +7,7 @@
#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/cmif_serialization.h"
namespace Service::Audio { namespace Service::Audio {
using namespace AudioCore::AudioIn;
IAudioInManager::IAudioInManager(Core::System& system_) IAudioInManager::IAudioInManager(Core::System& system_)
: ServiceFramework{system_, "audin:u"}, impl{std::make_unique<AudioCore::AudioIn::Manager>( : ServiceFramework{system_, "audin:u"}, impl{std::make_unique<AudioCore::AudioIn::Manager>(
@@ -36,11 +34,11 @@ Result IAudioInManager::ListAudioIns(
R_RETURN(this->ListAudioInsAutoFiltered(out_audio_ins, out_count)); R_RETURN(this->ListAudioInsAutoFiltered(out_audio_ins, out_count));
} }
Result IAudioInManager::OpenAudioIn(Out<AudioCore::AudioIn::AudioInParameterInternal> out_parameter_internal, Result IAudioInManager::OpenAudioIn(Out<AudioInParameterInternal> out_parameter_internal,
Out<SharedPointer<IAudioIn>> out_audio_in, Out<SharedPointer<IAudioIn>> out_audio_in,
OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_name, OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_name,
InArray<AudioDeviceName, BufferAttr_HipcMapAlias> name, InArray<AudioDeviceName, BufferAttr_HipcMapAlias> name,
AudioCore::AudioIn::AudioInParameter parameter, AudioInParameter parameter,
InCopyHandle<Kernel::KProcess> process_handle, InCopyHandle<Kernel::KProcess> process_handle,
ClientAppletResourceUserId aruid) { ClientAppletResourceUserId aruid) {
LOG_DEBUG(Service_Audio, "called"); LOG_DEBUG(Service_Audio, "called");
@@ -55,9 +53,9 @@ Result IAudioInManager::ListAudioInsAuto(
} }
Result IAudioInManager::OpenAudioInAuto( Result IAudioInManager::OpenAudioInAuto(
Out<AudioCore::AudioIn::AudioInParameterInternal> out_parameter_internal, Out<SharedPointer<IAudioIn>> out_audio_in, Out<AudioInParameterInternal> out_parameter_internal, Out<SharedPointer<IAudioIn>> out_audio_in,
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name, OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name,
InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, AudioCore::AudioIn::AudioInParameter parameter, InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, AudioInParameter parameter,
InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid) { InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid) {
LOG_DEBUG(Service_Audio, "called"); LOG_DEBUG(Service_Audio, "called");
R_RETURN(this->OpenAudioInProtocolSpecified(out_parameter_internal, out_audio_in, out_name, R_RETURN(this->OpenAudioInProtocolSpecified(out_parameter_internal, out_audio_in, out_name,
@@ -72,10 +70,10 @@ Result IAudioInManager::ListAudioInsAutoFiltered(
} }
Result IAudioInManager::OpenAudioInProtocolSpecified( Result IAudioInManager::OpenAudioInProtocolSpecified(
Out<AudioCore::AudioIn::AudioInParameterInternal> out_parameter_internal, Out<SharedPointer<IAudioIn>> out_audio_in, Out<AudioInParameterInternal> out_parameter_internal, Out<SharedPointer<IAudioIn>> out_audio_in,
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name, OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name,
InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, Protocol protocol, InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, Protocol protocol,
AudioCore::AudioIn::AudioInParameter parameter, InCopyHandle<Kernel::KProcess> process_handle, AudioInParameter parameter, InCopyHandle<Kernel::KProcess> process_handle,
ClientAppletResourceUserId aruid) { ClientAppletResourceUserId aruid) {
LOG_DEBUG(Service_Audio, "called"); LOG_DEBUG(Service_Audio, "called");
@@ -106,7 +104,7 @@ Result IAudioInManager::OpenAudioInProtocolSpecified(
auto& out_system = impl->sessions[new_session_id]->GetSystem(); auto& out_system = impl->sessions[new_session_id]->GetSystem();
*out_parameter_internal = *out_parameter_internal =
AudioCore::AudioIn::AudioInParameterInternal{.sample_rate = out_system.GetSampleRate(), AudioInParameterInternal{.sample_rate = out_system.GetSampleRate(),
.channel_count = out_system.GetChannelCount(), .channel_count = out_system.GetChannelCount(),
.sample_format = static_cast<u32>(out_system.GetSampleFormat()), .sample_format = static_cast<u32>(out_system.GetSampleFormat()),
.state = static_cast<u32>(out_system.GetState())}; .state = static_cast<u32>(out_system.GetState())};
+5 -4
View File
@@ -13,9 +13,10 @@
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
namespace Service::Audio { namespace Service::Audio {
using namespace AudioCore::AudioOut;
IAudioOut::IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, size_t session_id, IAudioOut::IAudioOut(Core::System& system_, Manager& manager, size_t session_id,
const std::string& device_name, const AudioCore::AudioOut::AudioOutParameter& in_params, const std::string& device_name, const AudioOutParameter& in_params,
Kernel::KProcess* handle, u64 applet_resource_user_id) Kernel::KProcess* handle, u64 applet_resource_user_id)
: ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"}, : ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"},
event{service_context.CreateEvent("AudioOutEvent")}, process{handle}, event{service_context.CreateEvent("AudioOutEvent")}, process{handle},
@@ -67,12 +68,12 @@ Result IAudioOut::Stop() {
} }
Result IAudioOut::AppendAudioOutBuffer( Result IAudioOut::AppendAudioOutBuffer(
InArray<AudioCore::AudioOut::AudioOutBuffer, BufferAttr_HipcMapAlias> audio_out_buffer, u64 buffer_client_ptr) { InArray<AudioOutBuffer, BufferAttr_HipcMapAlias> audio_out_buffer, u64 buffer_client_ptr) {
R_RETURN(this->AppendAudioOutBufferAuto(audio_out_buffer, buffer_client_ptr)); R_RETURN(this->AppendAudioOutBufferAuto(audio_out_buffer, buffer_client_ptr));
} }
Result IAudioOut::AppendAudioOutBufferAuto( Result IAudioOut::AppendAudioOutBufferAuto(
InArray<AudioCore::AudioOut::AudioOutBuffer, BufferAttr_HipcAutoSelect> audio_out_buffer, u64 buffer_client_ptr) { InArray<AudioOutBuffer, BufferAttr_HipcAutoSelect> audio_out_buffer, u64 buffer_client_ptr) {
if (audio_out_buffer.empty()) { if (audio_out_buffer.empty()) {
LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioOutBuffer!"); LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioOutBuffer!");
R_THROW(Audio::ResultInsufficientBuffer); R_THROW(Audio::ResultInsufficientBuffer);
@@ -11,6 +11,7 @@
#include "core/memory.h" #include "core/memory.h"
namespace Service::Audio { namespace Service::Audio {
using namespace AudioCore::AudioOut;
IAudioOutManager::IAudioOutManager(Core::System& system_) IAudioOutManager::IAudioOutManager(Core::System& system_)
: ServiceFramework{system_, "audout:u"} : ServiceFramework{system_, "audout:u"}
@@ -35,11 +36,11 @@ Result IAudioOutManager::ListAudioOuts(
R_RETURN(this->ListAudioOutsAuto(out_audio_outs, out_count)); R_RETURN(this->ListAudioOutsAuto(out_audio_outs, out_count));
} }
Result IAudioOutManager::OpenAudioOut(Out<AudioCore::AudioOut::AudioOutParameterInternal> out_parameter_internal, Result IAudioOutManager::OpenAudioOut(Out<AudioOutParameterInternal> out_parameter_internal,
Out<SharedPointer<IAudioOut>> out_audio_out, Out<SharedPointer<IAudioOut>> out_audio_out,
OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_name, OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_name,
InArray<AudioDeviceName, BufferAttr_HipcMapAlias> name, InArray<AudioDeviceName, BufferAttr_HipcMapAlias> name,
AudioCore::AudioOut::AudioOutParameter parameter, AudioOutParameter parameter,
InCopyHandle<Kernel::KProcess> process_handle, InCopyHandle<Kernel::KProcess> process_handle,
ClientAppletResourceUserId aruid) { ClientAppletResourceUserId aruid) {
R_RETURN(this->OpenAudioOutAuto(out_parameter_internal, out_audio_out, out_name, name, R_RETURN(this->OpenAudioOutAuto(out_parameter_internal, out_audio_out, out_name, name,
@@ -61,10 +62,10 @@ Result IAudioOutManager::ListAudioOutsAuto(
} }
Result IAudioOutManager::OpenAudioOutAuto( Result IAudioOutManager::OpenAudioOutAuto(
Out<AudioCore::AudioOut::AudioOutParameterInternal> out_parameter_internal, Out<AudioOutParameterInternal> out_parameter_internal,
Out<SharedPointer<IAudioOut>> out_audio_out, Out<SharedPointer<IAudioOut>> out_audio_out,
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name, OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name,
InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, AudioCore::AudioOut::AudioOutParameter parameter, InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, AudioOutParameter parameter,
InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid) { InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid) {
if (!process_handle) { if (!process_handle) {
LOG_ERROR(Service_Audio, "Failed to get process handle"); LOG_ERROR(Service_Audio, "Failed to get process handle");
@@ -94,7 +95,7 @@ Result IAudioOutManager::OpenAudioOutAuto(
auto& out_system = impl->sessions[new_session_id]->GetSystem(); auto& out_system = impl->sessions[new_session_id]->GetSystem();
*out_parameter_internal = *out_parameter_internal =
AudioCore::AudioOut::AudioOutParameterInternal{.sample_rate = out_system.GetSampleRate(), AudioOutParameterInternal{.sample_rate = out_system.GetSampleRate(),
.channel_count = out_system.GetChannelCount(), .channel_count = out_system.GetChannelCount(),
.sample_format = static_cast<u32>(out_system.GetSampleFormat()), .sample_format = static_cast<u32>(out_system.GetSampleFormat()),
.state = static_cast<u32>(out_system.GetState())}; .state = static_cast<u32>(out_system.GetState())};
@@ -4,20 +4,21 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/audio_renderer.h"
#include "core/hle/service/audio/audio_renderer.h" #include "core/hle/service/audio/audio_renderer.h"
#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/cmif_serialization.h"
namespace Service::Audio { namespace Service::Audio {
using namespace AudioCore::Renderer;
IAudioRenderer::IAudioRenderer(Core::System& system_, AudioCore::Renderer::Manager& manager_, IAudioRenderer::IAudioRenderer(Core::System& system_, Manager& manager_,
AudioCore::AudioRendererParameterInternal& params, AudioCore::AudioRendererParameterInternal& params,
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
Kernel::KProcess* process_handle_, u64 applet_resource_user_id, Kernel::KProcess* process_handle_, u64 applet_resource_user_id,
s32 session_id) s32 session_id)
: ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"}, : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"},
rendered_event{service_context.CreateEvent("IAudioRendererEvent")}, manager{manager_}, rendered_event{service_context.CreateEvent("IAudioRendererEvent")}, manager{manager_},
impl{std::make_unique<AudioCore::Renderer::Renderer>(system_, manager, rendered_event)}, process_handle{process_handle_} { impl{std::make_unique<Renderer>(system_, manager, rendered_event)}, process_handle{
process_handle_} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, D<&IAudioRenderer::GetSampleRate>, "GetSampleRate"}, {0, D<&IAudioRenderer::GetSampleRate>, "GetSampleRate"},
@@ -14,13 +14,16 @@
#include <cstring> #include <cstring>
namespace Service::News { namespace Service::News {
namespace {
[[nodiscard]] inline std::string_view ToStringViewNDS(std::span<const char> buf) { std::string_view ToStringView(std::span<const char> buf) {
const std::string_view sv{buf.data(), buf.size()}; const std::string_view sv{buf.data(), buf.size()};
const auto nul = sv.find('\0'); const auto nul = sv.find('\0');
return nul == std::string_view::npos ? sv : sv.substr(0, nul); return nul == std::string_view::npos ? sv : sv.substr(0, nul);
} }
} // namespace
INewsDataService::INewsDataService(Core::System& system_) INewsDataService::INewsDataService(Core::System& system_)
: ServiceFramework{system_, "INewsDataService"} { : ServiceFramework{system_, "INewsDataService"} {
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
@@ -52,7 +55,7 @@ bool INewsDataService::TryOpen(std::string_view key, std::string_view user) {
const auto list = NewsStorage::Instance().ListAll(); const auto list = NewsStorage::Instance().ListAll();
if (!list.empty()) { if (!list.empty()) {
if (auto found = NewsStorage::Instance().FindByNewsId(ToStringViewNDS(list.front().news_id))) { if (auto found = NewsStorage::Instance().FindByNewsId(ToStringView(list.front().news_id))) {
opened_payload = std::move(found->payload); opened_payload = std::move(found->payload);
return true; return true;
} }
@@ -64,7 +67,7 @@ bool INewsDataService::TryOpen(std::string_view key, std::string_view user) {
Result INewsDataService::Open(InBuffer<BufferAttr_HipcMapAlias> name) { Result INewsDataService::Open(InBuffer<BufferAttr_HipcMapAlias> name) {
EnsureBuiltinNewsLoaded(); EnsureBuiltinNewsLoaded();
const auto key = ToStringViewNDS({reinterpret_cast<const char*>(name.data()), name.size()}); const auto key = ToStringView({reinterpret_cast<const char*>(name.data()), name.size()});
if (TryOpen(key, {})) { if (TryOpen(key, {})) {
R_SUCCEED(); R_SUCCEED();
@@ -76,8 +79,8 @@ Result INewsDataService::Open(InBuffer<BufferAttr_HipcMapAlias> name) {
Result INewsDataService::OpenWithNewsRecordV1(NewsRecordV1 record) { Result INewsDataService::OpenWithNewsRecordV1(NewsRecordV1 record) {
EnsureBuiltinNewsLoaded(); EnsureBuiltinNewsLoaded();
const auto key = ToStringViewNDS(record.news_id); const auto key = ToStringView(record.news_id);
const auto user = ToStringViewNDS(record.user_id); const auto user = ToStringView(record.user_id);
if (TryOpen(key, user)) { if (TryOpen(key, user)) {
R_SUCCEED(); R_SUCCEED();
@@ -89,8 +92,8 @@ Result INewsDataService::OpenWithNewsRecordV1(NewsRecordV1 record) {
Result INewsDataService::OpenWithNewsRecord(NewsRecord record) { Result INewsDataService::OpenWithNewsRecord(NewsRecord record) {
EnsureBuiltinNewsLoaded(); EnsureBuiltinNewsLoaded();
const auto key = ToStringViewNDS(record.news_id); const auto key = ToStringView(record.news_id);
const auto user = ToStringViewNDS(record.user_id); const auto user = ToStringView(record.user_id);
if (TryOpen(key, user)) { if (TryOpen(key, user)) {
R_SUCCEED(); R_SUCCEED();
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -15,13 +15,13 @@
namespace Service::News { namespace Service::News {
namespace { namespace {
[[nodiscard]] inline std::string_view ToStringView(std::span<const u8> buf) { std::string_view ToStringView(std::span<const u8> buf) {
if (buf.empty()) return {}; if (buf.empty()) return {};
auto data = reinterpret_cast<const char*>(buf.data()); auto data = reinterpret_cast<const char*>(buf.data());
return {data, strnlen(data, buf.size())}; return {data, strnlen(data, buf.size())};
} }
[[nodiscard]] inline std::string_view ToStringView(std::span<const char> buf) { std::string_view ToStringView(std::span<const char> buf) {
if (buf.empty()) return {}; if (buf.empty()) return {};
return {buf.data(), strnlen(buf.data(), buf.size())}; return {buf.data(), strnlen(buf.data(), buf.size())};
} }
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
@@ -9,8 +6,6 @@
namespace Service::News { namespace Service::News {
#undef CreateEvent
IOverwriteEventHolder::IOverwriteEventHolder(Core::System& system_) IOverwriteEventHolder::IOverwriteEventHolder(Core::System& system_)
: ServiceFramework{system_, "IOverwriteEventHolder"}, service_context{system_, : ServiceFramework{system_, "IOverwriteEventHolder"}, service_context{system_,
"IOverwriteEventHolder"} { "IOverwriteEventHolder"} {
@@ -18,8 +18,6 @@
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h" #include "core/hle/service/sm/sm.h"
#undef GetCurrentTime
namespace Service::Capture { namespace Service::Capture {
AlbumManager::AlbumManager(Core::System& system_) : system{system_} {} AlbumManager::AlbumManager(Core::System& system_) : system{system_} {}
-2
View File
@@ -19,8 +19,6 @@
#include "core/hle/service/server_manager.h" #include "core/hle/service/server_manager.h"
#include "core/reporter.h" #include "core/reporter.h"
#undef far
namespace Service::Fatal { namespace Service::Fatal {
Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
@@ -14,6 +14,7 @@
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/bis_factory.h" #include "core/file_sys/bis_factory.h"
#include "core/file_sys/card_image.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/control_metadata.h"
#include "core/file_sys/errors.h" #include "core/file_sys/errors.h"
#include "core/file_sys/patch_manager.h" #include "core/file_sys/patch_manager.h"
@@ -32,10 +33,6 @@
#include "core/hle/service/server_manager.h" #include "core/hle/service/server_manager.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#undef CreateFile
#undef DeleteFile
#undef CreateDirectory
namespace Service::FileSystem { namespace Service::FileSystem {
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
@@ -343,7 +340,7 @@ Result FileSystemController::RegisterProcess(
registrations.emplace(process_id, Registration{ registrations.emplace(process_id, Registration{
.program_id = program_id, .program_id = program_id,
.romfs_factory = std::move(romfs_factory), .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); LOG_DEBUG(Service_FS, "Registered for process {}", process_id);
@@ -18,6 +18,7 @@
#include "common/settings.h" #include "common/settings.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/common_funcs.h"
#include "core/file_sys/content_archive.h" #include "core/file_sys/content_archive.h"
#include "core/file_sys/errors.h" #include "core/file_sys/errors.h"
#include "core/file_sys/fs_directory.h" #include "core/file_sys/fs_directory.h"
@@ -313,7 +314,7 @@ Result FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId(OutInterface<IFileSyste
FileSys::ResultInvalidArgument); FileSys::ResultInvalidArgument);
if (attribute.program_id == 0) { if (attribute.program_id == 0) {
attribute.program_id = program_id; attribute.program_id = FileSys::GetBaseTitleID(program_id);
} }
FileSys::VirtualDir dir{}; FileSys::VirtualDir dir{};
-2
View File
@@ -27,8 +27,6 @@
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/memory.h" #include "core/memory.h"
#undef SendMessage
namespace Service { namespace Service {
SessionRequestHandler::SessionRequestHandler(Kernel::KernelCore& kernel_, const char* service_name_) SessionRequestHandler::SessionRequestHandler(Kernel::KernelCore& kernel_, const char* service_name_)
+1 -2
View File
@@ -212,9 +212,8 @@ struct NifmNetworkProfileData {
NifmWirelessSettingData wireless_setting_data{}; NifmWirelessSettingData wireless_setting_data{};
IpSettingData ip_setting_data{}; IpSettingData ip_setting_data{};
}; };
static_assert(sizeof(NifmNetworkProfileData) == 0x18E,
"NifmNetworkProfileData has incorrect size.");
#pragma pack(pop) #pragma pack(pop)
static_assert(sizeof(NifmNetworkProfileData) == 0x18E, "NifmNetworkProfileData has incorrect size.");
struct PendingProfile { struct PendingProfile {
std::array<char, 0x21> ssid{}; std::array<char, 0x21> ssid{};
@@ -8,6 +8,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/logging.h" #include "common/logging.h"
#include "core/core.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/core/container.h"
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h" #include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.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) { void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
LOG_INFO(Service_NVDRV, "NVDEC video stream started"); LOG_INFO(Service_NVDRV, "NVDEC video stream started");
system.SetNVDECActive(true);
sessions[fd] = session_id; 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); host1x.StartDevice(fd, Tegra::Host1x::ChannelType::NvDec, channel_syncpoint);
} }
void nvhost_nvdec::OnClose(DeviceFD fd) { void nvhost_nvdec::OnClose(DeviceFD fd) {
LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
host1x.StopDevice(fd, Tegra::Host1x::ChannelType::NvDec); host1x.StopDevice(fd, Tegra::Host1x::ChannelType::NvDec);
system.SetNVDECActive(false);
auto it = sessions.find(fd); auto it = sessions.find(fd);
if (it != sessions.end()) { 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); sessions.erase(it);
} }
} }
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -13,8 +10,6 @@
namespace Service::PSC::Time { namespace Service::PSC::Time {
class ContextWriter; class ContextWriter;
#undef GetCurrentTime
class SystemClockCore { class SystemClockCore {
public: public:
explicit SystemClockCore(SteadyClockCore& steady_clock) : m_steady_clock{steady_clock} {} explicit SystemClockCore(SteadyClockCore& steady_clock) : m_steady_clock{steady_clock} {}
@@ -19,8 +19,6 @@ class System;
namespace Service::PSC::Time { namespace Service::PSC::Time {
#undef GetCurrentTime
class SystemClock final : public ServiceFramework<SystemClock> { class SystemClock final : public ServiceFramework<SystemClock> {
public: public:
explicit SystemClock(Core::System& system, SystemClockCore& system_clock_core, bool can_write_clock, bool can_write_uninitialized_clock); explicit SystemClock(Core::System& system, SystemClockCore& system_clock_core, bool can_write_clock, bool can_write_uninitialized_clock);
+15 -1
View File
@@ -4,12 +4,16 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include <fmt/ranges.h> #include <fmt/ranges.h>
#include <string_view>
#include <thread>
#include "common/assert.h" #include "common/assert.h"
#include "common/logging.h" #include "common/logging.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/ipc.h" #include "core/hle/ipc.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
@@ -33,6 +37,7 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se
: SessionRequestHandler(system_.Kernel(), service_name_) : SessionRequestHandler(system_.Kernel(), service_name_)
, system{system_} , system{system_}
, service_name{service_name_} , service_name{service_name_}
, is_i_storage{std::string_view{service_name_} == "IStorage"}
, handler_invoker{handler_invoker_} , handler_invoker{handler_invoker_}
, max_sessions{max_sessions_} , max_sessions{max_sessions_}
{} {}
@@ -77,13 +82,22 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx,
} }
void ServiceFrameworkBase::InvokeRequest(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; FunctionInfoBase const* info = it == handlers.end() ? nullptr : &it->second;
if (info == nullptr || info->handler_callback == nullptr) if (info == nullptr || info->handler_callback == nullptr)
return ReportUnimplementedFunction(ctx, info); return ReportUnimplementedFunction(ctx, info);
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer())); LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
handler_invoker(this, info->handler_callback, ctx); 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) { void ServiceFrameworkBase::InvokeRequestTipc(HLERequestContext& ctx) {
+2
View File
@@ -107,6 +107,8 @@ protected:
Core::System& system; Core::System& system;
/// Identifier string used to connect to the service. /// Identifier string used to connect to the service.
const char* service_name; 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. /// Function used to safely up-cast pointers to the derived class before invoking a handler.
InvokerFn* handler_invoker; InvokerFn* handler_invoker;
/// Maximum number of concurrent sessions that this service can handle. /// Maximum number of concurrent sessions that this service can handle.
+90 -90
View File
@@ -54,11 +54,11 @@ void PutValue(std::span<u8> buffer, const T& t) {
} // Anonymous namespace } // Anonymous namespace
void NetworkBSD::PollWork::Execute(NetworkBSD* bsd) { void BSD::PollWork::Execute(BSD* bsd) {
std::tie(ret, bsd_errno) = bsd->PollImpl(write_buffer, read_buffer, nfds, timeout); std::tie(ret, bsd_errno) = bsd->PollImpl(write_buffer, read_buffer, nfds, timeout);
} }
void NetworkBSD::PollWork::Response(HLERequestContext& ctx) { void BSD::PollWork::Response(HLERequestContext& ctx) {
if (write_buffer.size() > 0) { if (write_buffer.size() > 0) {
ctx.WriteBuffer(write_buffer); ctx.WriteBuffer(write_buffer);
} }
@@ -69,11 +69,11 @@ void NetworkBSD::PollWork::Response(HLERequestContext& ctx) {
rb.PushEnum(bsd_errno); rb.PushEnum(bsd_errno);
} }
void NetworkBSD::AcceptWork::Execute(NetworkBSD* bsd) { void BSD::AcceptWork::Execute(BSD* bsd) {
std::tie(ret, bsd_errno) = bsd->AcceptImpl(fd, write_buffer); std::tie(ret, bsd_errno) = bsd->AcceptImpl(fd, write_buffer);
} }
void NetworkBSD::AcceptWork::Response(HLERequestContext& ctx) { void BSD::AcceptWork::Response(HLERequestContext& ctx) {
if (write_buffer.size() > 0) { if (write_buffer.size() > 0) {
ctx.WriteBuffer(write_buffer); ctx.WriteBuffer(write_buffer);
} }
@@ -85,22 +85,22 @@ void NetworkBSD::AcceptWork::Response(HLERequestContext& ctx) {
rb.Push<u32>(static_cast<u32>(write_buffer.size())); rb.Push<u32>(static_cast<u32>(write_buffer.size()));
} }
void NetworkBSD::ConnectWork::Execute(NetworkBSD* bsd) { void BSD::ConnectWork::Execute(BSD* bsd) {
bsd_errno = bsd->ConnectImpl(fd, addr); bsd_errno = bsd->ConnectImpl(fd, addr);
} }
void NetworkBSD::ConnectWork::Response(HLERequestContext& ctx) { void BSD::ConnectWork::Response(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1); rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
rb.PushEnum(bsd_errno); rb.PushEnum(bsd_errno);
} }
void NetworkBSD::RecvWork::Execute(NetworkBSD* bsd) { void BSD::RecvWork::Execute(BSD* bsd) {
std::tie(ret, bsd_errno) = bsd->RecvImpl(fd, flags, message); std::tie(ret, bsd_errno) = bsd->RecvImpl(fd, flags, message);
} }
void NetworkBSD::RecvWork::Response(HLERequestContext& ctx) { void BSD::RecvWork::Response(HLERequestContext& ctx) {
ctx.WriteBuffer(message); ctx.WriteBuffer(message);
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
@@ -109,11 +109,11 @@ void NetworkBSD::RecvWork::Response(HLERequestContext& ctx) {
rb.PushEnum(bsd_errno); rb.PushEnum(bsd_errno);
} }
void NetworkBSD::RecvFromWork::Execute(NetworkBSD* bsd) { void BSD::RecvFromWork::Execute(BSD* bsd) {
std::tie(ret, bsd_errno) = bsd->RecvFromImpl(fd, flags, message, addr); std::tie(ret, bsd_errno) = bsd->RecvFromImpl(fd, flags, message, addr);
} }
void NetworkBSD::RecvFromWork::Response(HLERequestContext& ctx) { void BSD::RecvFromWork::Response(HLERequestContext& ctx) {
ctx.WriteBuffer(message, 0); ctx.WriteBuffer(message, 0);
if (!addr.empty()) { if (!addr.empty()) {
ctx.WriteBuffer(addr, 1); ctx.WriteBuffer(addr, 1);
@@ -126,29 +126,29 @@ void NetworkBSD::RecvFromWork::Response(HLERequestContext& ctx) {
rb.Push<u32>(static_cast<u32>(addr.size())); rb.Push<u32>(static_cast<u32>(addr.size()));
} }
void NetworkBSD::SendWork::Execute(NetworkBSD* bsd) { void BSD::SendWork::Execute(BSD* bsd) {
std::tie(ret, bsd_errno) = bsd->SendImpl(fd, flags, message); std::tie(ret, bsd_errno) = bsd->SendImpl(fd, flags, message);
} }
void NetworkBSD::SendWork::Response(HLERequestContext& ctx) { void BSD::SendWork::Response(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push<s32>(ret); rb.Push<s32>(ret);
rb.PushEnum(bsd_errno); rb.PushEnum(bsd_errno);
} }
void NetworkBSD::SendToWork::Execute(NetworkBSD* bsd) { void BSD::SendToWork::Execute(BSD* bsd) {
std::tie(ret, bsd_errno) = bsd->SendToImpl(fd, flags, message, addr); std::tie(ret, bsd_errno) = bsd->SendToImpl(fd, flags, message, addr);
} }
void NetworkBSD::SendToWork::Response(HLERequestContext& ctx) { void BSD::SendToWork::Response(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.Push<s32>(ret); rb.Push<s32>(ret);
rb.PushEnum(bsd_errno); rb.PushEnum(bsd_errno);
} }
void NetworkBSD::RegisterClient(HLERequestContext& ctx) { void BSD::RegisterClient(HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called"); LOG_WARNING(Service, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
@@ -157,7 +157,7 @@ void NetworkBSD::RegisterClient(HLERequestContext& ctx) {
rb.Push<s32>(0); // bsd errno rb.Push<s32>(0); // bsd errno
} }
void NetworkBSD::StartMonitoring(HLERequestContext& ctx) { void BSD::StartMonitoring(HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called"); LOG_WARNING(Service, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
@@ -165,7 +165,7 @@ void NetworkBSD::StartMonitoring(HLERequestContext& ctx) {
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
} }
void NetworkBSD::Socket(HLERequestContext& ctx) { void BSD::Socket(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const u32 domain = rp.Pop<u32>(); const u32 domain = rp.Pop<u32>();
const u32 type = rp.Pop<u32>(); const u32 type = rp.Pop<u32>();
@@ -200,7 +200,7 @@ void BSD::SocketExempt(HLERequestContext& ctx) {
rb.PushEnum(bsd_errno); rb.PushEnum(bsd_errno);
} }
void NetworkBSD::Select(HLERequestContext& ctx) { void BSD::Select(HLERequestContext& ctx) {
LOG_DEBUG(Service, "(STUBBED) called"); LOG_DEBUG(Service, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
@@ -210,7 +210,7 @@ void NetworkBSD::Select(HLERequestContext& ctx) {
rb.Push<u32>(0); // bsd errno rb.Push<u32>(0); // bsd errno
} }
void NetworkBSD::Poll(HLERequestContext& ctx) { void BSD::Poll(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 nfds = rp.Pop<s32>(); const s32 nfds = rp.Pop<s32>();
const s32 timeout = rp.Pop<s32>(); const s32 timeout = rp.Pop<s32>();
@@ -225,7 +225,7 @@ void NetworkBSD::Poll(HLERequestContext& ctx) {
}); });
} }
void NetworkBSD::Accept(HLERequestContext& ctx) { void BSD::Accept(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
@@ -237,7 +237,7 @@ void NetworkBSD::Accept(HLERequestContext& ctx) {
}); });
} }
void NetworkBSD::Bind(HLERequestContext& ctx) { void BSD::Bind(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
@@ -245,7 +245,7 @@ void NetworkBSD::Bind(HLERequestContext& ctx) {
BuildErrnoResponse(ctx, BindImpl(fd, ctx.ReadBuffer())); BuildErrnoResponse(ctx, BindImpl(fd, ctx.ReadBuffer()));
} }
void NetworkBSD::Connect(HLERequestContext& ctx) { void BSD::Connect(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
@@ -257,7 +257,7 @@ void NetworkBSD::Connect(HLERequestContext& ctx) {
}); });
} }
void NetworkBSD::GetPeerName(HLERequestContext& ctx) { void BSD::GetPeerName(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
@@ -275,7 +275,7 @@ void NetworkBSD::GetPeerName(HLERequestContext& ctx) {
rb.Push<u32>(static_cast<u32>(write_buffer.size())); rb.Push<u32>(static_cast<u32>(write_buffer.size()));
} }
void NetworkBSD::GetSockName(HLERequestContext& ctx) { void BSD::GetSockName(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
@@ -293,7 +293,7 @@ void NetworkBSD::GetSockName(HLERequestContext& ctx) {
rb.Push<u32>(static_cast<u32>(write_buffer.size())); rb.Push<u32>(static_cast<u32>(write_buffer.size()));
} }
void NetworkBSD::GetSockOpt(HLERequestContext& ctx) { void BSD::GetSockOpt(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
const u32 level = rp.Pop<u32>(); const u32 level = rp.Pop<u32>();
@@ -315,7 +315,7 @@ void NetworkBSD::GetSockOpt(HLERequestContext& ctx) {
rb.Push<u32>(static_cast<u32>(optval.size())); rb.Push<u32>(static_cast<u32>(optval.size()));
} }
void NetworkBSD::Listen(HLERequestContext& ctx) { void BSD::Listen(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
const s32 backlog = rp.Pop<s32>(); const s32 backlog = rp.Pop<s32>();
@@ -325,7 +325,7 @@ void NetworkBSD::Listen(HLERequestContext& ctx) {
BuildErrnoResponse(ctx, ListenImpl(fd, backlog)); BuildErrnoResponse(ctx, ListenImpl(fd, backlog));
} }
void NetworkBSD::Fcntl(HLERequestContext& ctx) { void BSD::Fcntl(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
const s32 cmd = rp.Pop<s32>(); const s32 cmd = rp.Pop<s32>();
@@ -341,7 +341,7 @@ void NetworkBSD::Fcntl(HLERequestContext& ctx) {
rb.PushEnum(bsd_errno); rb.PushEnum(bsd_errno);
} }
void NetworkBSD::SetSockOpt(HLERequestContext& ctx) { void BSD::SetSockOpt(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
@@ -355,7 +355,7 @@ void NetworkBSD::SetSockOpt(HLERequestContext& ctx) {
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval)); BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
} }
void NetworkBSD::Shutdown(HLERequestContext& ctx) { void BSD::Shutdown(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
@@ -366,7 +366,7 @@ void NetworkBSD::Shutdown(HLERequestContext& ctx) {
BuildErrnoResponse(ctx, ShutdownImpl(fd, how)); BuildErrnoResponse(ctx, ShutdownImpl(fd, how));
} }
void NetworkBSD::Recv(HLERequestContext& ctx) { void BSD::Recv(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
@@ -381,7 +381,7 @@ void NetworkBSD::Recv(HLERequestContext& ctx) {
}); });
} }
void NetworkBSD::RecvFrom(HLERequestContext& ctx) { void BSD::RecvFrom(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
@@ -398,7 +398,7 @@ void NetworkBSD::RecvFrom(HLERequestContext& ctx) {
}); });
} }
void NetworkBSD::Send(HLERequestContext& ctx) { void BSD::Send(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
@@ -413,7 +413,7 @@ void NetworkBSD::Send(HLERequestContext& ctx) {
}); });
} }
void NetworkBSD::SendTo(HLERequestContext& ctx) { void BSD::SendTo(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
const u32 flags = rp.Pop<u32>(); const u32 flags = rp.Pop<u32>();
@@ -429,7 +429,7 @@ void NetworkBSD::SendTo(HLERequestContext& ctx) {
}); });
} }
void NetworkBSD::Write(HLERequestContext& ctx) { void BSD::Write(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
@@ -442,7 +442,7 @@ void NetworkBSD::Write(HLERequestContext& ctx) {
}); });
} }
void NetworkBSD::Read(HLERequestContext& ctx) { void BSD::Read(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
@@ -454,7 +454,7 @@ void NetworkBSD::Read(HLERequestContext& ctx) {
rb.Push<u32>(0); // bsd errno rb.Push<u32>(0); // bsd errno
} }
void NetworkBSD::Close(HLERequestContext& ctx) { void BSD::Close(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>(); const s32 fd = rp.Pop<s32>();
@@ -464,7 +464,7 @@ void NetworkBSD::Close(HLERequestContext& ctx) {
} }
/// @brief Only bsd:s is able to dup() /// @brief Only bsd:s is able to dup()
void NetworkBSD::DuplicateSocket(HLERequestContext& ctx) { void BSD::DuplicateSocket(HLERequestContext& ctx) {
struct InputParameters { struct InputParameters {
s32 fd; s32 fd;
u64 reserved; u64 reserved;
@@ -505,7 +505,7 @@ void NetworkBSD::DuplicateSocket(HLERequestContext& ctx) {
} }
} }
void NetworkBSD::EventFd(HLERequestContext& ctx) { void BSD::EventFd(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const u64 initval = rp.Pop<u64>(); const u64 initval = rp.Pop<u64>();
const u32 flags = rp.Pop<u32>(); const u32 flags = rp.Pop<u32>();
@@ -516,12 +516,12 @@ void NetworkBSD::EventFd(HLERequestContext& ctx) {
} }
template <typename Work> template <typename Work>
void NetworkBSD::ExecuteWork(HLERequestContext& ctx, Work work) { void BSD::ExecuteWork(HLERequestContext& ctx, Work work) {
work.Execute(this); work.Execute(this);
work.Response(ctx); work.Response(ctx);
} }
std::pair<s32, Errno> NetworkBSD::SocketImpl(Domain domain, Type type, Protocol protocol) { std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) {
// user bsd:u has restrictions on SOCK_SEQPACKET and SOCK_RAW // user bsd:u has restrictions on SOCK_SEQPACKET and SOCK_RAW
if (is_user && (type == Type::SEQPACKET || type == Type::RAW)) { if (is_user && (type == Type::SEQPACKET || type == Type::RAW)) {
if (type == Type::RAW && domain == Domain::INET && protocol == Protocol::ICMP) { if (type == Type::RAW && domain == Domain::INET && protocol == Protocol::ICMP) {
@@ -565,7 +565,7 @@ std::pair<s32, Errno> NetworkBSD::SocketImpl(Domain domain, Type type, Protocol
return {fd, Errno::SUCCESS}; return {fd, Errno::SUCCESS};
} }
std::pair<s32, Errno> NetworkBSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
s32 nfds, s32 timeout) { s32 nfds, s32 timeout) {
if (nfds <= 0) { if (nfds <= 0) {
// When no entries are provided, -1 is returned with errno zero // When no entries are provided, -1 is returned with errno zero
@@ -632,7 +632,7 @@ std::pair<s32, Errno> NetworkBSD::PollImpl(std::vector<u8>& write_buffer, std::s
return Translate(result); return Translate(result);
} }
std::pair<s32, Errno> NetworkBSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) { std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF}; return {-1, Errno::BADF};
} }
@@ -660,7 +660,7 @@ std::pair<s32, Errno> NetworkBSD::AcceptImpl(s32 fd, std::vector<u8>& write_buff
return {new_fd, Errno::SUCCESS}; return {new_fd, Errno::SUCCESS};
} }
Errno NetworkBSD::BindImpl(s32 fd, std::span<const u8> addr) { Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Errno::BADF;
} }
@@ -675,7 +675,7 @@ Errno NetworkBSD::BindImpl(s32 fd, std::span<const u8> addr) {
return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in))); return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
} }
Errno NetworkBSD::ConnectImpl(s32 fd, std::span<const u8> addr) { Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Errno::BADF;
} }
@@ -698,7 +698,7 @@ Errno NetworkBSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
return result; return result;
} }
Errno NetworkBSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) { Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Errno::BADF;
} }
@@ -720,7 +720,7 @@ Errno NetworkBSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
return Translate(bsd_errno); return Translate(bsd_errno);
} }
Errno NetworkBSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) { Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Errno::BADF;
} }
@@ -742,7 +742,7 @@ Errno NetworkBSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
return Translate(bsd_errno); return Translate(bsd_errno);
} }
Errno NetworkBSD::ListenImpl(s32 fd, s32 backlog) { Errno BSD::ListenImpl(s32 fd, s32 backlog) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Errno::BADF;
} }
@@ -753,7 +753,7 @@ Errno NetworkBSD::ListenImpl(s32 fd, s32 backlog) {
return Translate(file_descriptors[fd]->socket->Listen(backlog)); return Translate(file_descriptors[fd]->socket->Listen(backlog));
} }
std::pair<s32, Errno> NetworkBSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) { std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF}; return {-1, Errno::BADF};
} }
@@ -783,7 +783,7 @@ std::pair<s32, Errno> NetworkBSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
} }
} }
Errno NetworkBSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval) { Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Errno::BADF;
} }
@@ -818,7 +818,7 @@ Errno NetworkBSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector
} }
} }
Errno NetworkBSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) { Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Errno::BADF;
} }
@@ -872,7 +872,7 @@ Errno NetworkBSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<c
} }
} }
Errno NetworkBSD::ShutdownImpl(s32 fd, s32 how) { Errno BSD::ShutdownImpl(s32 fd, s32 how) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Errno::BADF;
} }
@@ -884,7 +884,7 @@ Errno NetworkBSD::ShutdownImpl(s32 fd, s32 how) {
return Translate(file_descriptors[fd]->socket->Shutdown(host_how)); return Translate(file_descriptors[fd]->socket->Shutdown(host_how));
} }
std::pair<s32, Errno> NetworkBSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) { std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF}; return {-1, Errno::BADF};
} }
@@ -911,7 +911,7 @@ std::pair<s32, Errno> NetworkBSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& m
return {ret, bsd_errno}; return {ret, bsd_errno};
} }
std::pair<s32, Errno> NetworkBSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
std::vector<u8>& addr) { std::vector<u8>& addr) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF}; return {-1, Errno::BADF};
@@ -958,7 +958,7 @@ std::pair<s32, Errno> NetworkBSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8
return {ret, bsd_errno}; return {ret, bsd_errno};
} }
std::pair<s32, Errno> NetworkBSD::SendImpl(s32 fd, u32 flags, std::span<const u8> message) { std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF}; return {-1, Errno::BADF};
} }
@@ -969,7 +969,7 @@ std::pair<s32, Errno> NetworkBSD::SendImpl(s32 fd, u32 flags, std::span<const u8
return Translate(file_descriptors[fd]->socket->Send(message, flags)); return Translate(file_descriptors[fd]->socket->Send(message, flags));
} }
std::pair<s32, Errno> NetworkBSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> message, std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
std::span<const u8> addr) { std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF}; return {-1, Errno::BADF};
@@ -991,7 +991,7 @@ std::pair<s32, Errno> NetworkBSD::SendToImpl(s32 fd, u32 flags, std::span<const
return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in)); return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in));
} }
Errno NetworkBSD::CloseImpl(s32 fd) { Errno BSD::CloseImpl(s32 fd) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Errno::BADF;
} }
@@ -1011,7 +1011,7 @@ Errno NetworkBSD::CloseImpl(s32 fd) {
return bsd_errno; return bsd_errno;
} }
std::variant<s32, Errno> NetworkBSD::DuplicateSocketImpl(s32 fd) { std::variant<s32, Errno> BSD::DuplicateSocketImpl(s32 fd) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return Errno::BADF; return Errno::BADF;
} }
@@ -1030,7 +1030,7 @@ std::variant<s32, Errno> NetworkBSD::DuplicateSocketImpl(s32 fd) {
return new_fd; return new_fd;
} }
std::optional<std::shared_ptr<Network::SocketBase>> NetworkBSD::GetSocket(s32 fd) { std::optional<std::shared_ptr<Network::SocketBase>> BSD::GetSocket(s32 fd) {
if (!IsFileDescriptorValid(fd)) { if (!IsFileDescriptorValid(fd)) {
return std::nullopt; return std::nullopt;
} }
@@ -1041,7 +1041,7 @@ std::optional<std::shared_ptr<Network::SocketBase>> NetworkBSD::GetSocket(s32 fd
return file_descriptors[fd]->socket; return file_descriptors[fd]->socket;
} }
s32 NetworkBSD::FindFreeFileDescriptorHandle() noexcept { s32 BSD::FindFreeFileDescriptorHandle() noexcept {
for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) { for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) {
if (!file_descriptors[fd]) { if (!file_descriptors[fd]) {
return fd; return fd;
@@ -1050,7 +1050,7 @@ s32 NetworkBSD::FindFreeFileDescriptorHandle() noexcept {
return -1; return -1;
} }
bool NetworkBSD::IsFileDescriptorValid(s32 fd) const noexcept { bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {
if (fd > static_cast<s32>(MAX_FD) || fd < 0) { if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
LOG_ERROR(Service, "Invalid file descriptor handle={}", fd); LOG_ERROR(Service, "Invalid file descriptor handle={}", fd);
return false; return false;
@@ -1062,7 +1062,7 @@ bool NetworkBSD::IsFileDescriptorValid(s32 fd) const noexcept {
return true; return true;
} }
void NetworkBSD::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept { void BSD::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept {
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@@ -1070,7 +1070,7 @@ void NetworkBSD::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) con
rb.PushEnum(bsd_errno); rb.PushEnum(bsd_errno);
} }
void NetworkBSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) { void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
for (auto& optional_descriptor : file_descriptors) { for (auto& optional_descriptor : file_descriptors) {
if (!optional_descriptor.has_value()) { if (!optional_descriptor.has_value()) {
continue; continue;
@@ -1080,43 +1080,43 @@ void NetworkBSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
} }
} }
NetworkBSD::NetworkBSD(Core::System& system_, const char* name, bool is_user_) BSD::BSD(Core::System& system_, const char* name, bool is_user_)
: ServiceFramework{system_, name} : ServiceFramework{system_, name}
, is_user{is_user_} { , is_user{is_user_} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &NetworkBSD::RegisterClient, "RegisterClient"}, {0, &BSD::RegisterClient, "RegisterClient"},
{1, &NetworkBSD::StartMonitoring, "StartMonitoring"}, {1, &BSD::StartMonitoring, "StartMonitoring"},
{2, &NetworkBSD::Socket, "Socket"}, {2, &BSD::Socket, "Socket"},
{3, &NetworkBSD::SocketExempt, "SocketExempt"}, {3, &BSD::SocketExempt, "SocketExempt"},
{4, nullptr, "Open"}, {4, nullptr, "Open"},
{5, &NetworkBSD::Select, "Select"}, {5, &BSD::Select, "Select"},
{6, &NetworkBSD::Poll, "Poll"}, {6, &BSD::Poll, "Poll"},
{7, nullptr, "Sysctl"}, {7, nullptr, "Sysctl"},
{8, &NetworkBSD::Recv, "Recv"}, {8, &BSD::Recv, "Recv"},
{9, &NetworkBSD::RecvFrom, "RecvFrom"}, {9, &BSD::RecvFrom, "RecvFrom"},
{10, &NetworkBSD::Send, "Send"}, {10, &BSD::Send, "Send"},
{11, &NetworkBSD::SendTo, "SendTo"}, {11, &BSD::SendTo, "SendTo"},
{12, &NetworkBSD::Accept, "Accept"}, {12, &BSD::Accept, "Accept"},
{13, &NetworkBSD::Bind, "Bind"}, {13, &BSD::Bind, "Bind"},
{14, &NetworkBSD::Connect, "Connect"}, {14, &BSD::Connect, "Connect"},
{15, &NetworkBSD::GetPeerName, "GetPeerName"}, {15, &BSD::GetPeerName, "GetPeerName"},
{16, &NetworkBSD::GetSockName, "GetSockName"}, {16, &BSD::GetSockName, "GetSockName"},
{17, &NetworkBSD::GetSockOpt, "GetSockOpt"}, {17, &BSD::GetSockOpt, "GetSockOpt"},
{18, &NetworkBSD::Listen, "Listen"}, {18, &BSD::Listen, "Listen"},
{19, nullptr, "Ioctl"}, {19, nullptr, "Ioctl"},
{20, &NetworkBSD::Fcntl, "Fcntl"}, {20, &BSD::Fcntl, "Fcntl"},
{21, &NetworkBSD::SetSockOpt, "SetSockOpt"}, {21, &BSD::SetSockOpt, "SetSockOpt"},
{22, &NetworkBSD::Shutdown, "Shutdown"}, {22, &BSD::Shutdown, "Shutdown"},
{23, nullptr, "ShutdownAllSockets"}, {23, nullptr, "ShutdownAllSockets"},
{24, &NetworkBSD::Write, "Write"}, {24, &BSD::Write, "Write"},
{25, &NetworkBSD::Read, "Read"}, {25, &BSD::Read, "Read"},
{26, &NetworkBSD::Close, "Close"}, {26, &BSD::Close, "Close"},
{27, &NetworkBSD::DuplicateSocket, "DuplicateSocket"}, {27, &BSD::DuplicateSocket, "DuplicateSocket"},
{28, nullptr, "GetResourceStatistics"}, {28, nullptr, "GetResourceStatistics"},
{29, nullptr, "RecvMMsg"}, //3.0.0+ {29, nullptr, "RecvMMsg"}, //3.0.0+
{30, nullptr, "SendMMsg"}, //3.0.0+ {30, nullptr, "SendMMsg"}, //3.0.0+
{31, &NetworkBSD::EventFd, "EventFd"}, //7.0.0+ {31, &BSD::EventFd, "EventFd"}, //7.0.0+
{32, nullptr, "RegisterResourceStatisticsName"}, //7.0.0+ {32, nullptr, "RegisterResourceStatisticsName"}, //7.0.0+
{33, nullptr, "RegisterClientShared"}, //10.0.0+ {33, nullptr, "RegisterClientShared"}, //10.0.0+
{34, nullptr, "GetSocketStatistics"}, //15.0.0+ {34, nullptr, "GetSocketStatistics"}, //15.0.0+
@@ -1144,13 +1144,13 @@ NetworkBSD::NetworkBSD(Core::System& system_, const char* name, bool is_user_)
} }
} }
NetworkBSD::~NetworkBSD() { BSD::~BSD() {
if (auto room_member = Network::GetRoomMember().lock()) { if (auto room_member = Network::GetRoomMember().lock()) {
room_member->Unbind(proxy_packet_received); room_member->Unbind(proxy_packet_received);
} }
} }
std::unique_lock<std::mutex> NetworkBSD::LockService() noexcept { std::unique_lock<std::mutex> BSD::LockService() noexcept {
return {}; return {};
} }
+10 -10
View File
@@ -27,10 +27,10 @@ class Socket;
namespace Service::Sockets { namespace Service::Sockets {
class NetworkBSD final : public ServiceFramework<NetworkBSD> { class BSD final : public ServiceFramework<BSD> {
public: public:
explicit NetworkBSD(Core::System& system_, const char* name, bool is_user); explicit BSD(Core::System& system_, const char* name, bool is_user);
~NetworkBSD() override; ~BSD() override;
// These methods are called from SSL; the first two are also called from // These methods are called from SSL; the first two are also called from
// this class for the corresponding IPC methods. // this class for the corresponding IPC methods.
@@ -50,7 +50,7 @@ private:
}; };
struct PollWork { struct PollWork {
void Execute(NetworkBSD* bsd); void Execute(BSD* bsd);
void Response(HLERequestContext& ctx); void Response(HLERequestContext& ctx);
s32 nfds; s32 nfds;
@@ -62,7 +62,7 @@ private:
}; };
struct AcceptWork { struct AcceptWork {
void Execute(NetworkBSD* bsd); void Execute(BSD* bsd);
void Response(HLERequestContext& ctx); void Response(HLERequestContext& ctx);
s32 fd; s32 fd;
@@ -72,7 +72,7 @@ private:
}; };
struct ConnectWork { struct ConnectWork {
void Execute(NetworkBSD* bsd); void Execute(BSD* bsd);
void Response(HLERequestContext& ctx); void Response(HLERequestContext& ctx);
s32 fd; s32 fd;
@@ -81,7 +81,7 @@ private:
}; };
struct RecvWork { struct RecvWork {
void Execute(NetworkBSD* bsd); void Execute(BSD* bsd);
void Response(HLERequestContext& ctx); void Response(HLERequestContext& ctx);
s32 fd; s32 fd;
@@ -92,7 +92,7 @@ private:
}; };
struct RecvFromWork { struct RecvFromWork {
void Execute(NetworkBSD* bsd); void Execute(BSD* bsd);
void Response(HLERequestContext& ctx); void Response(HLERequestContext& ctx);
s32 fd; s32 fd;
@@ -104,7 +104,7 @@ private:
}; };
struct SendWork { struct SendWork {
void Execute(NetworkBSD* bsd); void Execute(BSD* bsd);
void Response(HLERequestContext& ctx); void Response(HLERequestContext& ctx);
s32 fd; s32 fd;
@@ -115,7 +115,7 @@ private:
}; };
struct SendToWork { struct SendToWork {
void Execute(NetworkBSD* bsd); void Execute(BSD* bsd);
void Response(HLERequestContext& ctx); void Response(HLERequestContext& ctx);
s32 fd; s32 fd;
+3 -3
View File
@@ -66,9 +66,9 @@ void LoopProcess(Core::System& system) {
server_manager->RegisterNamedService("ethc:c", std::make_shared<ETHC_C>(system)); server_manager->RegisterNamedService("ethc:c", std::make_shared<ETHC_C>(system));
server_manager->RegisterNamedService("ethc:i", std::make_shared<ETHC_I>(system)); server_manager->RegisterNamedService("ethc:i", std::make_shared<ETHC_I>(system));
server_manager->RegisterNamedService("bsd:s", std::make_shared<NetworkBSD>(system, "bsd:s", false)); server_manager->RegisterNamedService("bsd:s", std::make_shared<BSD>(system, "bsd:s", false));
server_manager->RegisterNamedService("bsd:u", std::make_shared<NetworkBSD>(system, "bsd:u", true)); server_manager->RegisterNamedService("bsd:u", std::make_shared<BSD>(system, "bsd:u", true));
server_manager->RegisterNamedService("bsd:a", std::make_shared<NetworkBSD>(system, "bsd:a", true)); server_manager->RegisterNamedService("bsd:a", std::make_shared<BSD>(system, "bsd:a", true));
server_manager->RegisterNamedService("bsd:nu", std::make_shared<BSD_NU>(system)); server_manager->RegisterNamedService("bsd:nu", std::make_shared<BSD_NU>(system));
server_manager->RegisterNamedService("bsdcfg", std::make_shared<BSDCFG>(system, "bsdcfg")); server_manager->RegisterNamedService("bsdcfg", std::make_shared<BSDCFG>(system, "bsdcfg"));
server_manager->RegisterNamedService("ifcfg", std::make_shared<BSDCFG>(system, "ifcfg")); server_manager->RegisterNamedService("ifcfg", std::make_shared<BSDCFG>(system, "ifcfg"));
+2 -2
View File
@@ -129,7 +129,7 @@ public:
LOG_ERROR(Service_SSL, LOG_ERROR(Service_SSL,
"do_not_close_socket was changed after setting socket; is this right?"); "do_not_close_socket was changed after setting socket; is this right?");
} else { } else {
auto bsd = system.ServiceManager().GetService<Service::Sockets::NetworkBSD>("bsd:u"); auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD>("bsd:u");
if (bsd) { if (bsd) {
auto err = bsd->CloseImpl(fd); auto err = bsd->CloseImpl(fd);
if (err != Service::Sockets::Errno::SUCCESS) { if (err != Service::Sockets::Errno::SUCCESS) {
@@ -157,7 +157,7 @@ private:
Result SetSocketDescriptorImpl(s32* out_fd, s32 fd) { Result SetSocketDescriptorImpl(s32* out_fd, s32 fd) {
LOG_DEBUG(Service_SSL, "called, fd={}", fd); LOG_DEBUG(Service_SSL, "called, fd={}", fd);
ASSERT(!did_handshake); ASSERT(!did_handshake);
auto bsd = system.ServiceManager().GetService<Service::Sockets::NetworkBSD>("bsd:u"); auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD>("bsd:u");
ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; }); ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; });
auto const res_v = bsd->DuplicateSocketImpl(fd); auto const res_v = bsd->DuplicateSocketImpl(fd);
+6 -4
View File
@@ -17,9 +17,11 @@
namespace Loader { namespace Loader {
[[nodiscard]] inline constexpr u32 PageAlignSizeKIP(u32 size) { namespace {
return u32((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); constexpr u32 PageAlignSize(u32 size) {
return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
} }
} // Anonymous namespace
AppLoader_KIP::AppLoader_KIP(FileSys::VirtualFile file_) AppLoader_KIP::AppLoader_KIP(FileSys::VirtualFile file_)
: AppLoader(std::move(file_)), kip(std::make_unique<FileSys::KIP>(file)) {} : AppLoader(std::move(file_)), kip(std::make_unique<FileSys::KIP>(file)) {}
@@ -74,11 +76,11 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
kip->GetKernelCapabilities()); kip->GetKernelCapabilities());
Kernel::CodeSet codeset; Kernel::CodeSet codeset;
codeset.memory.resize(PageAlignSizeKIP(kip->GetBSSOffset()) + kip->GetBSSSize()); codeset.memory.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize());
const auto load_segment = [&codeset](Kernel::CodeSet::Segment& segment, std::span<const u8> data, u32 offset) { const auto load_segment = [&codeset](Kernel::CodeSet::Segment& segment, std::span<const u8> data, u32 offset) {
segment.addr = offset; segment.addr = offset;
segment.offset = offset; segment.offset = offset;
segment.size = PageAlignSizeKIP(u32(data.size())); segment.size = PageAlignSize(u32(data.size()));
std::memcpy(codeset.memory.data() + offset, data.data(), data.size()); std::memcpy(codeset.memory.data() + offset, data.data(), data.size());
}; };
load_segment(codeset.CodeSegment(), kip->GetTextSection(), kip->GetTextOffset()); load_segment(codeset.CodeSegment(), kip->GetTextSection(), kip->GetTextOffset());
+9
View File
@@ -70,6 +70,15 @@ std::optional<IndexedProgram> ResolveIndexedProgram(Core::System& system, u64 pr
return IndexedProgram{std::move(update), target_id, true}; return IndexedProgram{std::move(update), target_id, true};
} }
const auto application_update_id =
FileSys::GetUpdateTitleID(FileSys::GetBaseTitleID(target_id));
if (application_update_id != update_id) {
if (auto update = provider.GetEntryRaw(application_update_id, FileSys::ContentRecordType::Program)) {
LOG_INFO(Loader, "Program index {} has no base program, loading it from application update {:016X}", program_index, application_update_id);
return IndexedProgram{std::move(update), target_id, true};
}
}
LOG_WARNING(Loader, "No program NCA for {:016X} (index {}), falling back to the container", LOG_WARNING(Loader, "No program NCA for {:016X} (index {}), falling back to the container",
target_id, program_index); target_id, program_index);
return std::nullopt; return std::nullopt;
+8 -2
View File
@@ -11,6 +11,7 @@
#include "common/hex_util.h" #include "common/hex_util.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/common_funcs.h"
#include "core/file_sys/content_archive.h" #include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h" #include "core/file_sys/control_metadata.h"
#include "core/file_sys/nca_metadata.h" #include "core/file_sys/nca_metadata.h"
@@ -76,8 +77,13 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::KProcess& process, Core::S
LOG_INFO(Loader, "No ExeFS found in NCA, looking for ExeFS from update"); LOG_INFO(Loader, "No ExeFS found in NCA, looking for ExeFS from update");
const auto& installed = system.GetContentProvider(); const auto& installed = system.GetContentProvider();
const auto update_nca = installed.GetEntry(FileSys::GetUpdateTitleID(nca->GetTitleId()), const auto program_update_id = FileSys::GetUpdateTitleID(nca->GetTitleId());
FileSys::ContentRecordType::Program); auto update_nca = installed.GetEntry(program_update_id, FileSys::ContentRecordType::Program);
if (update_nca == nullptr) {
update_nca = installed.GetEntry(
FileSys::GetUpdateTitleID(FileSys::GetBaseTitleID(nca->GetTitleId())),
FileSys::ContentRecordType::Program);
}
if (update_nca) { if (update_nca) {
exefs = update_nca->GetExeFS(); exefs = update_nca->GetExeFS();
+7 -7
View File
@@ -148,8 +148,8 @@ bool AppLoader_NRO::IsHomebrew() {
nro_header.magic_ext2 == Common::MakeMagic('B', 'R', 'E', 'W'); nro_header.magic_ext2 == Common::MakeMagic('B', 'R', 'E', 'W');
} }
[[nodiscard]] inline constexpr u32 PageAlignSizeNRO(u32 size) { static constexpr u32 PageAlignSize(u32 size) {
return u32((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
} }
static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process, static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
@@ -166,9 +166,9 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
} }
// Build program image // Build program image
std::vector<u8> program_image(PageAlignSizeNRO(nro_header.file_size)); std::vector<u8> program_image(PageAlignSize(nro_header.file_size));
std::memcpy(program_image.data(), data.data(), program_image.size()); std::memcpy(program_image.data(), data.data(), program_image.size());
if (program_image.size() != PageAlignSizeNRO(nro_header.file_size)) { if (program_image.size() != PageAlignSize(nro_header.file_size)) {
return {}; return {};
} }
@@ -176,11 +176,11 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
for (std::size_t i = 0; i < nro_header.segments.size(); ++i) { for (std::size_t i = 0; i < nro_header.segments.size(); ++i) {
codeset.segments[i].addr = nro_header.segments[i].offset; codeset.segments[i].addr = nro_header.segments[i].offset;
codeset.segments[i].offset = nro_header.segments[i].offset; codeset.segments[i].offset = nro_header.segments[i].offset;
codeset.segments[i].size = PageAlignSizeNRO(nro_header.segments[i].size); codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size);
} }
// Default .bss to NRO header bss size if MOD0 section doesn't exist // Default .bss to NRO header bss size if MOD0 section doesn't exist
u32 bss_size{PageAlignSizeNRO(nro_header.bss_size)}; u32 bss_size{PageAlignSize(nro_header.bss_size)};
// Read MOD header // Read MOD header
ModHeader mod_header{}; ModHeader mod_header{};
@@ -190,7 +190,7 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
if (has_mod_header) { if (has_mod_header) {
// Resize program image to include .bss section and page align each section // Resize program image to include .bss section and page align each section
bss_size = PageAlignSizeNRO(mod_header.bss_end_offset - mod_header.bss_start_offset); bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
} }
codeset.DataSegment().size += bss_size; codeset.DataSegment().size += bss_size;
+4 -4
View File
@@ -41,8 +41,8 @@ struct MODHeader {
}; };
static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size."); static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size.");
[[nodiscard]] inline constexpr u32 PageAlignSizeNSO(u32 size) { constexpr u32 PageAlignSize(u32 size) {
return u32((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
} }
} // Anonymous namespace } // Anonymous namespace
@@ -128,11 +128,11 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
} }
codeset.DataSegment().size += nso_header.segments[2].bss_size; codeset.DataSegment().size += nso_header.segments[2].bss_size;
u32 image_size = PageAlignSizeNSO(u32(codeset.memory.size()) + nso_header.segments[2].bss_size); u32 image_size = PageAlignSize(u32(codeset.memory.size()) + nso_header.segments[2].bss_size);
codeset.memory.resize(image_size); codeset.memory.resize(image_size);
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
codeset.segments[i].size = PageAlignSizeNSO(codeset.segments[i].size); codeset.segments[i].size = PageAlignSize(codeset.segments[i].size);
} }
// Apply patches if necessary // Apply patches if necessary
+7 -2
View File
@@ -186,8 +186,13 @@ ResultStatus AppLoader_NSP::ReadUpdateRaw(FileSys::VirtualFile& out_file) {
return ResultStatus::ErrorNoPackedUpdate; return ResultStatus::ErrorNoPackedUpdate;
} }
const auto read = nsp->GetNCAFile(FileSys::GetUpdateTitleID(nsp->GetProgramTitleID()), const auto program_update_id = FileSys::GetUpdateTitleID(nsp->GetProgramTitleID());
FileSys::ContentRecordType::Program); auto read = nsp->GetNCAFile(program_update_id, FileSys::ContentRecordType::Program);
if (read == nullptr) {
read = nsp->GetNCAFile(
FileSys::GetUpdateTitleID(FileSys::GetBaseTitleID(nsp->GetProgramTitleID())),
FileSys::ContentRecordType::Program);
}
if (read == nullptr) { if (read == nullptr) {
return ResultStatus::ErrorNoPackedUpdate; return ResultStatus::ErrorNoPackedUpdate;
+8 -2
View File
@@ -9,6 +9,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/card_image.h" #include "core/file_sys/card_image.h"
#include "core/file_sys/common_funcs.h"
#include "core/file_sys/content_archive.h" #include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h" #include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h" #include "core/file_sys/patch_manager.h"
@@ -137,8 +138,13 @@ ResultStatus AppLoader_XCI::ReadUpdateRaw(FileSys::VirtualFile& out_file) {
return ResultStatus::ErrorXCIMissingProgramNCA; return ResultStatus::ErrorXCIMissingProgramNCA;
} }
const auto read = xci->GetSecurePartitionNSP()->GetNCAFile( const auto program_update_id = FileSys::GetUpdateTitleID(program_id);
FileSys::GetUpdateTitleID(program_id), FileSys::ContentRecordType::Program); auto read = xci->GetSecurePartitionNSP()->GetNCAFile(program_update_id, FileSys::ContentRecordType::Program);
if (read == nullptr) {
read = xci->GetSecurePartitionNSP()->GetNCAFile(
FileSys::GetUpdateTitleID(FileSys::GetBaseTitleID(program_id)),
FileSys::ContentRecordType::Program);
}
if (read == nullptr) { if (read == nullptr) {
return ResultStatus::ErrorNoPackedUpdate; return ResultStatus::ErrorNoPackedUpdate;
} }
-2
View File
@@ -21,8 +21,6 @@
#include "hid_core/resource_manager.h" #include "hid_core/resource_manager.h"
#include "hid_core/resources/npad/npad.h" #include "hid_core/resources/npad/npad.h"
#undef CreateEvent
namespace Core::Memory { namespace Core::Memory {
namespace { namespace {
constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12}; constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12};
-2
View File
@@ -28,8 +28,6 @@
#include "core/memory.h" #include "core/memory.h"
#include "core/reporter.h" #include "core/reporter.h"
#undef far
namespace { namespace {
std::filesystem::path GetPath(std::string_view type, u64 title_id, std::string_view timestamp) { std::filesystem::path GetPath(std::string_view type, u64 title_id, std::string_view timestamp) {
-2
View File
@@ -52,8 +52,6 @@ void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 v
} // Anonymous namespace } // Anonymous namespace
#undef CreateEvent
Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_) Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_)
: core_timing{core_timing_}, memory{memory_} { : core_timing{core_timing_}, memory{memory_} {
event = Core::Timing::CreateEvent("MemoryFreezer::FrameCallback", event = Core::Timing::CreateEvent("MemoryFreezer::FrameCallback",
@@ -31,7 +31,7 @@ using namespace oaknut::util;
namespace { namespace {
[[nodiscard]] inline bool IsOrdered(IR::AccType acctype) { bool IsOrdered(IR::AccType acctype) {
return acctype == IR::AccType::ORDERED || acctype == IR::AccType::ORDEREDRW || acctype == IR::AccType::LIMITEDORDERED; return acctype == IR::AccType::ORDERED || acctype == IR::AccType::ORDEREDRW || acctype == IR::AccType::LIMITEDORDERED;
} }
+31 -11
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project // SPDX-FileCopyrightText: 2024 yuzu Emulator Project
@@ -56,13 +56,14 @@ inline bool RemoveDLC(const Service::FileSystem::FileSystemController& fs_contro
*/ */
inline size_t RemoveAllDLC(Core::System& system, const u64 program_id) { inline size_t RemoveAllDLC(Core::System& system, const u64 program_id) {
size_t count{}; size_t count{};
const auto application_id = FileSys::GetBaseTitleID(program_id);
const auto& fs_controller = system.GetFileSystemController(); const auto& fs_controller = system.GetFileSystemController();
const auto dlc_entries = system.GetContentProvider().ListEntriesFilter( const auto dlc_entries = system.GetContentProvider().ListEntriesFilter(
FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
std::vector<u64> program_dlc_entries; std::vector<u64> program_dlc_entries;
for (const auto& entry : dlc_entries) { for (const auto& entry : dlc_entries) {
if (FileSys::GetBaseTitleID(entry.title_id) == program_id) { if (FileSys::GetBaseTitleID(entry.title_id) == application_id) {
program_dlc_entries.push_back(entry.title_id); program_dlc_entries.push_back(entry.title_id);
} }
} }
@@ -83,9 +84,17 @@ inline size_t RemoveAllDLC(Core::System& system, const u64 program_id) {
*/ */
inline bool RemoveUpdate(const Service::FileSystem::FileSystemController& fs_controller, inline bool RemoveUpdate(const Service::FileSystem::FileSystemController& fs_controller,
const u64 program_id) { const u64 program_id) {
const auto update_id = program_id | 0x800; const auto remove_update = [&fs_controller](u64 update_id) {
return fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) || return fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) ||
fs_controller.GetSDMCContents()->RemoveExistingEntry(update_id); fs_controller.GetSDMCContents()->RemoveExistingEntry(update_id);
};
const auto update_id = FileSys::GetUpdateTitleID(program_id);
const auto application_update_id =
FileSys::GetUpdateTitleID(FileSys::GetBaseTitleID(program_id));
if (update_id != application_update_id && remove_update(update_id)) {
return true;
}
return remove_update(application_update_id);
} }
/** /**
@@ -111,15 +120,26 @@ inline bool RemoveBaseContent(const Service::FileSystem::FileSystemController& f
inline bool RemoveMod(const Service::FileSystem::FileSystemController& fs_controller, inline bool RemoveMod(const Service::FileSystem::FileSystemController& fs_controller,
const u64 program_id, const std::string& mod_name) { const u64 program_id, const std::string& mod_name) {
// Check general Mods (LayeredFS and IPS) // Check general Mods (LayeredFS and IPS)
const auto mod_dir = fs_controller.GetModificationLoadRoot(program_id); const auto remove_from_root = [&mod_name](const auto& root) {
if (mod_dir != nullptr) { return root != nullptr && root->DeleteSubdirectoryRecursive(mod_name);
return mod_dir->DeleteSubdirectoryRecursive(mod_name); };
if (remove_from_root(fs_controller.GetModificationLoadRoot(program_id))) {
return true;
}
if (FileSys::GetBaseTitleID(program_id) != program_id &&
remove_from_root(
fs_controller.GetModificationLoadRoot(FileSys::GetBaseTitleID(program_id)))) {
return true;
} }
// Check SDMC mod directory (RomFS LayeredFS) // Check SDMC mod directory (RomFS LayeredFS)
const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(program_id); if (remove_from_root(fs_controller.GetSDMCModificationLoadRoot(program_id))) {
if (sdmc_mod_dir != nullptr) { return true;
return sdmc_mod_dir->DeleteSubdirectoryRecursive(mod_name); }
if (FileSys::GetBaseTitleID(program_id) != program_id &&
remove_from_root(
fs_controller.GetSDMCModificationLoadRoot(FileSys::GetBaseTitleID(program_id)))) {
return true;
} }
return false; return false;
+2 -1
View File
@@ -7,6 +7,7 @@
#include "common/fs/fs.h" #include "common/fs/fs.h"
#include "common/fs/fs_types.h" #include "common/fs/fs_types.h"
#include "common/logging.h" #include "common/logging.h"
#include "core/file_sys/common_funcs.h"
#include "frontend_common/data_manager.h" #include "frontend_common/data_manager.h"
#include "mod_manager.h" #include "mod_manager.h"
@@ -40,7 +41,7 @@ std::vector<std::filesystem::path> GetModFolder(const std::string& root) {
} }
ModInstallResult InstallMod(const std::filesystem::path& path, const u64 program_id, const bool copy) { ModInstallResult InstallMod(const std::filesystem::path& path, const u64 program_id, const bool copy) {
const auto program_id_string = fmt::format("{:016X}", program_id); const auto program_id_string = fmt::format("{:016X}", FileSys::GetBaseTitleID(program_id));
const auto mod_name = path.filename(); const auto mod_name = path.filename();
const auto mod_dir = const auto mod_dir =
DataManager::GetDataDir(DataManager::DataDir::Mods) / program_id_string / mod_name; DataManager::GetDataDir(DataManager::DataDir::Mods) / program_id_string / mod_name;
+2 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -9,6 +6,7 @@
#include "hid_core/hidbus/starlink.h" #include "hid_core/hidbus/starlink.h"
namespace Service::HID { namespace Service::HID {
constexpr u8 DEVICE_ID = 0x28;
Starlink::Starlink(Core::System& system_, KernelHelpers::ServiceContext& service_context_) Starlink::Starlink(Core::System& system_, KernelHelpers::ServiceContext& service_context_)
: HidbusBase(system_, service_context_) {} : HidbusBase(system_, service_context_) {}
@@ -37,7 +35,7 @@ void Starlink::OnUpdate() {
} }
u8 Starlink::GetDeviceId() const { u8 Starlink::GetDeviceId() const {
return 0x28; return DEVICE_ID;
} }
u64 Starlink::GetReply(std::span<u8> out_data) const { u64 Starlink::GetReply(std::span<u8> out_data) const {
+2 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -9,6 +6,7 @@
#include "hid_core/hidbus/stubbed.h" #include "hid_core/hidbus/stubbed.h"
namespace Service::HID { namespace Service::HID {
constexpr u8 DEVICE_ID = 0xFF;
HidbusStubbed::HidbusStubbed(Core::System& system_, KernelHelpers::ServiceContext& service_context_) HidbusStubbed::HidbusStubbed(Core::System& system_, KernelHelpers::ServiceContext& service_context_)
: HidbusBase(system_, service_context_) {} : HidbusBase(system_, service_context_) {}
@@ -37,7 +35,7 @@ void HidbusStubbed::OnUpdate() {
} }
u8 HidbusStubbed::GetDeviceId() const { u8 HidbusStubbed::GetDeviceId() const {
return 0xFF; return DEVICE_ID;
} }
u64 HidbusStubbed::GetReply(std::span<u8> out_data) const { u64 HidbusStubbed::GetReply(std::span<u8> out_data) const {
+9 -12
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
@@ -11,6 +8,9 @@
#include "hid_core/irsensor/moment_processor.h" #include "hid_core/irsensor/moment_processor.h"
namespace Service::IRS { namespace Service::IRS {
static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size40x30;
static constexpr std::size_t ImageWidth = 40;
static constexpr std::size_t ImageHeight = 30;
MomentProcessor::MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format, MomentProcessor::MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format,
std::size_t npad_index) std::size_t npad_index)
@@ -80,9 +80,9 @@ void MomentProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type)
} }
u8 MomentProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const { u8 MomentProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
constexpr std::size_t ImageWidth = 40; if ((y * ImageWidth) + x >= data.size()) {
if ((y * ImageWidth) + x >= data.size())
return 0; return 0;
}
return data[(y * ImageWidth) + x]; return data[(y * ImageWidth) + x];
} }
@@ -92,12 +92,9 @@ MomentProcessor::MomentStatistic MomentProcessor::GetStatistic(const std::vector
std::size_t width, std::size_t width,
std::size_t height) const { std::size_t height) const {
// The actual implementation is always 320x240 // The actual implementation is always 320x240
constexpr std::size_t RealWidth = 320; static constexpr std::size_t RealWidth = 320;
constexpr std::size_t RealHeight = 240; static constexpr std::size_t RealHeight = 240;
constexpr std::size_t Threshold = 30; static constexpr std::size_t Threshold = 30;
constexpr std::size_t ImageWidth = 40;
constexpr std::size_t ImageHeight = 30;
MomentStatistic statistic{}; MomentStatistic statistic{};
std::size_t active_points{}; std::size_t active_points{};
@@ -146,7 +143,7 @@ void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig conf
static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess); static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess);
current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold; current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold;
npad_device->SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat::Size40x30); npad_device->SetCameraFormat(format);
} }
} // namespace Service::IRS } // namespace Service::IRS
+3 -6
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -10,14 +7,14 @@
#include "input_common/drivers/camera.h" #include "input_common/drivers/camera.h"
namespace InputCommon { namespace InputCommon {
constexpr PadIdentifier camera_identifier = { constexpr PadIdentifier identifier = {
.guid = Common::UUID{}, .guid = Common::UUID{},
.port = 0, .port = 0,
.pad = 0, .pad = 0,
}; };
Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_)) { Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
PreSetController(camera_identifier); PreSetController(identifier);
} }
void Camera::SetCameraData(std::size_t width, std::size_t height, std::span<const u32> data) { void Camera::SetCameraData(std::size_t width, std::size_t height, std::span<const u32> data) {
@@ -36,7 +33,7 @@ void Camera::SetCameraData(std::size_t width, std::size_t height, std::span<cons
} }
} }
SetCamera(camera_identifier, status); SetCamera(identifier, status);
} }
std::size_t Camera::getImageWidth() const { std::size_t Camera::getImageWidth() const {
+16 -16
View File
@@ -26,7 +26,7 @@ constexpr int mouse_axis_x = 0;
constexpr int mouse_axis_y = 1; constexpr int mouse_axis_y = 1;
constexpr int wheel_axis_x = 2; constexpr int wheel_axis_x = 2;
constexpr int wheel_axis_y = 3; constexpr int wheel_axis_y = 3;
constexpr PadIdentifier mouse_identifier = { constexpr PadIdentifier identifier = {
.guid = Common::UUID{}, .guid = Common::UUID{},
.port = 0, .port = 0,
.pad = 0, .pad = 0,
@@ -51,16 +51,16 @@ constexpr PadIdentifier touch_identifier = {
}; };
Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) { Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
PreSetController(mouse_identifier); PreSetController(identifier);
PreSetController(real_mouse_identifier); PreSetController(real_mouse_identifier);
PreSetController(touch_identifier); PreSetController(touch_identifier);
PreSetController(motion_identifier); PreSetController(motion_identifier);
// Initialize all mouse axis // Initialize all mouse axis
PreSetAxis(mouse_identifier, mouse_axis_x); PreSetAxis(identifier, mouse_axis_x);
PreSetAxis(mouse_identifier, mouse_axis_y); PreSetAxis(identifier, mouse_axis_y);
PreSetAxis(mouse_identifier, wheel_axis_x); PreSetAxis(identifier, wheel_axis_x);
PreSetAxis(mouse_identifier, wheel_axis_y); PreSetAxis(identifier, wheel_axis_y);
PreSetAxis(real_mouse_identifier, mouse_axis_x); PreSetAxis(real_mouse_identifier, mouse_axis_x);
PreSetAxis(real_mouse_identifier, mouse_axis_y); PreSetAxis(real_mouse_identifier, mouse_axis_y);
PreSetAxis(touch_identifier, mouse_axis_x); PreSetAxis(touch_identifier, mouse_axis_x);
@@ -88,8 +88,8 @@ void Mouse::UpdateStickInput() {
last_mouse_change *= maximum_stick_range; last_mouse_change *= maximum_stick_range;
} }
SetAxis(mouse_identifier, mouse_axis_x, last_mouse_change.x); SetAxis(identifier, mouse_axis_x, last_mouse_change.x);
SetAxis(mouse_identifier, mouse_axis_y, -last_mouse_change.y); SetAxis(identifier, mouse_axis_y, -last_mouse_change.y);
// Decay input over time // Decay input over time
const float clamped_length = (std::min)(1.0f, length); const float clamped_length = (std::min)(1.0f, length);
@@ -165,8 +165,8 @@ void Mouse::Move(int x, int y, int center_x, int center_y) {
Settings::values.mouse_panning_x_sensitivity.GetValue() * default_stick_sensitivity; Settings::values.mouse_panning_x_sensitivity.GetValue() * default_stick_sensitivity;
const float y_sensitivity = const float y_sensitivity =
Settings::values.mouse_panning_y_sensitivity.GetValue() * default_stick_sensitivity; Settings::values.mouse_panning_y_sensitivity.GetValue() * default_stick_sensitivity;
SetAxis(mouse_identifier, mouse_axis_x, static_cast<float>(mouse_move.x) * x_sensitivity); SetAxis(identifier, mouse_axis_x, static_cast<float>(mouse_move.x) * x_sensitivity);
SetAxis(mouse_identifier, mouse_axis_y, static_cast<float>(-mouse_move.y) * y_sensitivity); SetAxis(identifier, mouse_axis_y, static_cast<float>(-mouse_move.y) * y_sensitivity);
last_motion_change = { last_motion_change = {
static_cast<float>(-mouse_move.y) * x_sensitivity, static_cast<float>(-mouse_move.y) * x_sensitivity,
@@ -192,7 +192,7 @@ void Mouse::TouchMove(f32 touch_x, f32 touch_y) {
} }
void Mouse::PressButton(int x, int y, MouseButton button) { void Mouse::PressButton(int x, int y, MouseButton button) {
SetButton(mouse_identifier, static_cast<int>(button), true); SetButton(identifier, static_cast<int>(button), true);
// Set initial analog parameters // Set initial analog parameters
mouse_origin = {x, y}; mouse_origin = {x, y};
@@ -211,13 +211,13 @@ void Mouse::PressTouchButton(f32 touch_x, f32 touch_y, MouseButton button) {
} }
void Mouse::ReleaseButton(MouseButton button) { void Mouse::ReleaseButton(MouseButton button) {
SetButton(mouse_identifier, static_cast<int>(button), false); SetButton(identifier, static_cast<int>(button), false);
SetButton(real_mouse_identifier, static_cast<int>(button), false); SetButton(real_mouse_identifier, static_cast<int>(button), false);
SetButton(touch_identifier, static_cast<int>(button), false); SetButton(touch_identifier, static_cast<int>(button), false);
if (!IsMousePanningEnabled()) { if (!IsMousePanningEnabled()) {
SetAxis(mouse_identifier, mouse_axis_x, 0); SetAxis(identifier, mouse_axis_x, 0);
SetAxis(mouse_identifier, mouse_axis_y, 0); SetAxis(identifier, mouse_axis_y, 0);
} }
last_motion_change.x = 0; last_motion_change.x = 0;
@@ -230,8 +230,8 @@ void Mouse::MouseWheelChange(int x, int y) {
wheel_position.x += x; wheel_position.x += x;
wheel_position.y += y; wheel_position.y += y;
last_motion_change.z += static_cast<f32>(y); last_motion_change.z += static_cast<f32>(y);
SetAxis(mouse_identifier, wheel_axis_x, static_cast<f32>(wheel_position.x)); SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x));
SetAxis(mouse_identifier, wheel_axis_y, static_cast<f32>(wheel_position.y)); SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y));
} }
void Mouse::ReleaseAllButtons() { void Mouse::ReleaseAllButtons() {
+8 -11
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -9,14 +6,14 @@
namespace InputCommon { namespace InputCommon {
constexpr PadIdentifier touch_screen_identifier = { constexpr PadIdentifier identifier = {
.guid = Common::UUID{}, .guid = Common::UUID{},
.port = 0, .port = 0,
.pad = 0, .pad = 0,
}; };
TouchScreen::TouchScreen(std::string input_engine_) : InputEngine(std::move(input_engine_)) { TouchScreen::TouchScreen(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
PreSetController(touch_screen_identifier); PreSetController(identifier);
ReleaseAllTouch(); ReleaseAllTouch();
} }
@@ -29,9 +26,9 @@ void TouchScreen::TouchMoved(float x, float y, std::size_t finger_id) {
} }
const auto i = index.value(); const auto i = index.value();
fingers[i].is_active = true; fingers[i].is_active = true;
SetButton(touch_screen_identifier, static_cast<int>(i), true); SetButton(identifier, static_cast<int>(i), true);
SetAxis(touch_screen_identifier, static_cast<int>(i * 2), x); SetAxis(identifier, static_cast<int>(i * 2), x);
SetAxis(touch_screen_identifier, static_cast<int>(i * 2 + 1), y); SetAxis(identifier, static_cast<int>(i * 2 + 1), y);
} }
void TouchScreen::TouchPressed(float x, float y, std::size_t finger_id) { void TouchScreen::TouchPressed(float x, float y, std::size_t finger_id) {
@@ -58,9 +55,9 @@ void TouchScreen::TouchReleased(std::size_t finger_id) {
} }
const auto i = index.value(); const auto i = index.value();
fingers[i].is_enabled = false; fingers[i].is_enabled = false;
SetButton(touch_screen_identifier, static_cast<int>(i), false); SetButton(identifier, static_cast<int>(i), false);
SetAxis(touch_screen_identifier, static_cast<int>(i * 2), 0.0f); SetAxis(identifier, static_cast<int>(i * 2), 0.0f);
SetAxis(touch_screen_identifier, static_cast<int>(i * 2 + 1), 0.0f); SetAxis(identifier, static_cast<int>(i * 2 + 1), 0.0f);
} }
std::optional<std::size_t> TouchScreen::GetIndexFromFingerId(std::size_t finger_id) const { std::optional<std::size_t> TouchScreen::GetIndexFromFingerId(std::size_t finger_id) const {
+7 -7
View File
@@ -600,7 +600,7 @@ void TestCommunication(const std::string& host, u16 port, const std::function<vo
} }
CalibrationConfigurationJob::CalibrationConfigurationJob( CalibrationConfigurationJob::CalibrationConfigurationJob(
const std::string& host, u16 port, std::function<void(CalibrationStatus)> status_callback, const std::string& host, u16 port, std::function<void(Status)> status_callback,
std::function<void(u16, u16, u16, u16)> data_callback) { std::function<void(u16, u16, u16, u16)> data_callback) {
std::thread([=, this] { std::thread([=, this] {
@@ -609,13 +609,13 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
u16 max_x{}; u16 max_x{};
u16 max_y{}; u16 max_y{};
auto current_status = CalibrationStatus::Initialized; Status current_status{Status::Initialized};
SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {}, [&](Response::PadData data) { SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {}, [&](Response::PadData data) {
constexpr u16 CALIBRATION_THRESHOLD = 100; constexpr u16 CALIBRATION_THRESHOLD = 100;
if (current_status == CalibrationStatus::Initialized) { if (current_status == Status::Initialized) {
// Receiving data means the communication is ready now // Receiving data means the communication is ready now
current_status = CalibrationStatus::Ready; current_status = Status::Ready;
status_callback(current_status); status_callback(current_status);
} }
if (data.touch[0].is_active == 0) { if (data.touch[0].is_active == 0) {
@@ -624,9 +624,9 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x, data.touch[0].y); LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x, data.touch[0].y);
min_x = (std::min)(min_x, u16(data.touch[0].x)); min_x = (std::min)(min_x, u16(data.touch[0].x));
min_y = (std::min)(min_y, u16(data.touch[0].y)); min_y = (std::min)(min_y, u16(data.touch[0].y));
if (current_status == CalibrationStatus::Ready) { if (current_status == Status::Ready) {
// First touch - min data (min_x/min_y) // First touch - min data (min_x/min_y)
current_status = CalibrationStatus::Stage1Completed; current_status = Status::Stage1Completed;
status_callback(current_status); status_callback(current_status);
} }
if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD && if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD &&
@@ -635,7 +635,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
// configuration // configuration
max_x = data.touch[0].x; max_x = data.touch[0].x;
max_y = data.touch[0].y; max_y = data.touch[0].y;
current_status = CalibrationStatus::Completed; current_status = Status::Completed;
data_callback(min_x, min_y, max_x, max_y); data_callback(min_x, min_y, max_x, max_y);
status_callback(current_status); status_callback(current_status);
+2 -5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2018 Citra Emulator Project // SPDX-FileCopyrightText: 2018 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -166,7 +163,7 @@ private:
/// An async job allowing configuration of the touchpad calibration. /// An async job allowing configuration of the touchpad calibration.
class CalibrationConfigurationJob { class CalibrationConfigurationJob {
public: public:
enum class CalibrationStatus { enum class Status {
Initialized, Initialized,
Ready, Ready,
Stage1Completed, Stage1Completed,
@@ -179,7 +176,7 @@ public:
* @param data_callback Called when calibration data is ready * @param data_callback Called when calibration data is ready
*/ */
explicit CalibrationConfigurationJob(const std::string& host, u16 port, explicit CalibrationConfigurationJob(const std::string& host, u16 port,
std::function<void(CalibrationStatus)> status_callback, std::function<void(Status)> status_callback,
std::function<void(u16, u16, u16, u16)> data_callback); std::function<void(u16, u16, u16, u16)> data_callback);
~CalibrationConfigurationJob(); ~CalibrationConfigurationJob();
void Stop(); void Stop();
+4 -4
View File
@@ -15,7 +15,7 @@
#include "input_common/drivers/virtual_amiibo.h" #include "input_common/drivers/virtual_amiibo.h"
namespace InputCommon { namespace InputCommon {
constexpr PadIdentifier virtual_amiibo_identifier = { constexpr PadIdentifier identifier = {
.guid = Common::UUID{}, .guid = Common::UUID{},
.port = 0, .port = 0,
.pad = 0, .pad = 0,
@@ -228,13 +228,13 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(std::span<u8> data) {
status.state = Common::Input::NfcState::NewAmiibo, status.state = Common::Input::NfcState::NewAmiibo,
memcpy(nfc_data.data(), data.data(), data.size_bytes()); memcpy(nfc_data.data(), data.data(), data.size_bytes());
memcpy(status.uuid.data(), nfc_data.data(), status.uuid_length); memcpy(status.uuid.data(), nfc_data.data(), status.uuid_length);
SetNfc(virtual_amiibo_identifier, status); SetNfc(identifier, status);
return Info::Success; return Info::Success;
} }
VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() { VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
if (state == State::TagNearby) { if (state == State::TagNearby) {
SetNfc(virtual_amiibo_identifier, status); SetNfc(identifier, status);
return Info::Success; return Info::Success;
} }
@@ -248,7 +248,7 @@ VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
state = State::WaitingForAmiibo; state = State::WaitingForAmiibo;
status.state = Common::Input::NfcState::AmiiboRemoved; status.state = Common::Input::NfcState::AmiiboRemoved;
SetNfc(virtual_amiibo_identifier, status); SetNfc(identifier, status);
status.tag_type = 0; status.tag_type = 0;
return Info::Success; return Info::Success;
} }
+10 -9
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project // SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -8,7 +5,6 @@
#include <array> #include <array>
#include <vector> #include <vector>
#include <string>
#include "common/common_types.h" #include "common/common_types.h"
namespace Network { namespace Network {
@@ -122,21 +118,26 @@ private:
template <typename T> template <typename T>
Packet& Packet::Read(std::vector<T>& out_data) { Packet& Packet::Read(std::vector<T>& out_data) {
// First extract the size
u32 size = 0; u32 size = 0;
Read(size); Read(size);
out_data.resize(size); out_data.resize(size);
for (auto& elem : out_data) { // Then extract the data
Read(elem); for (std::size_t i = 0; i < out_data.size(); ++i) {
T character;
Read(character);
out_data[i] = character;
} }
return *this; return *this;
} }
template <typename T, std::size_t S> template <typename T, std::size_t S>
Packet& Packet::Read(std::array<T, S>& out_data) { Packet& Packet::Read(std::array<T, S>& out_data) {
for (auto& elem : out_data) { for (std::size_t i = 0; i < out_data.size(); ++i) {
Read(elem); T character;
Read(character);
out_data[i] = character;
} }
return *this; return *this;
} }
+2 -2
View File
@@ -832,7 +832,7 @@ void Room::RoomImpl::HandleProxyPacket(const ENetEvent* event) {
in_packet.IgnoreBytes(sizeof(u8)); // Protocol in_packet.IgnoreBytes(sizeof(u8)); // Protocol
bool broadcast = false; bool broadcast;
in_packet.Read(broadcast); // Broadcast in_packet.Read(broadcast); // Broadcast
Packet out_packet; Packet out_packet;
@@ -886,7 +886,7 @@ void Room::RoomImpl::HandleLdnPacket(const ENetEvent* event) {
IPv4Address remote_ip; IPv4Address remote_ip;
in_packet.Read(remote_ip); // Remote IP in_packet.Read(remote_ip); // Remote IP
bool broadcast = false; bool broadcast;
in_packet.Read(broadcast); // Broadcast in_packet.Read(broadcast); // Broadcast
Packet out_packet; Packet out_packet;
+2
View File
@@ -5,6 +5,7 @@
#include "common/fs/fs.h" #include "common/fs/fs.h"
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
#include "core/file_sys/common_funcs.h"
#include "core/file_sys/savedata_factory.h" #include "core/file_sys/savedata_factory.h"
#include "core/hle/service/am/am_types.h" #include "core/hle/service/am/am_types.h"
#include "frontend_common/content_manager.h" #include "frontend_common/content_manager.h"
@@ -305,6 +306,7 @@ void RemoveAllTransferableShaderCaches(u64 program_id) {
} }
void RemoveCustomConfiguration(u64 program_id, const std::string& game_path) { void RemoveCustomConfiguration(u64 program_id, const std::string& game_path) {
program_id = FileSys::GetBaseTitleID(program_id);
const auto file_path = std::filesystem::path(Common::FS::ToU8String(game_path)); const auto file_path = std::filesystem::path(Common::FS::ToU8String(game_path));
const auto config_file_name = const auto config_file_name =
program_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()).append(".ini") program_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()).append(".ini")
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -48,6 +45,11 @@ void GetCbuf(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU
} }
} }
bool IsInputArray(Stage stage) {
return stage == Stage::Geometry || stage == Stage::TessellationControl ||
stage == Stage::TessellationEval;
}
std::string VertexIndex(EmitContext& ctx, ScalarU32 vertex) { std::string VertexIndex(EmitContext& ctx, ScalarU32 vertex) {
return IsInputArray(ctx.stage) ? fmt::format("[{}]", vertex) : ""; return IsInputArray(ctx.stage) ? fmt::format("[{}]", vertex) : "";
} }
@@ -7,7 +7,6 @@
#pragma once #pragma once
#include "common/common_types.h" #include "common/common_types.h"
#include "shader_recompiler/stage.h"
#include "shader_recompiler/backend/glasm/reg_alloc.h" #include "shader_recompiler/backend/glasm/reg_alloc.h"
namespace Shader::IR { namespace Shader::IR {
@@ -19,11 +18,6 @@ class Value;
namespace Shader::Backend::GLASM { namespace Shader::Backend::GLASM {
[[nodiscard]] inline bool IsInputArray(Stage stage) {
return stage == Stage::Geometry || stage == Stage::TessellationControl
|| stage == Stage::TessellationEval;
}
class EmitContext; class EmitContext;
// Microinstruction emitters // Microinstruction emitters
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -10,92 +7,94 @@
namespace Shader::Backend::GLASM { namespace Shader::Backend::GLASM {
#define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__)
void EmitGetRegister(EmitContext& ctx) { void EmitGetRegister(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitSetRegister(EmitContext& ctx) { void EmitSetRegister(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitGetPred(EmitContext& ctx) { void EmitGetPred(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitSetPred(EmitContext& ctx) { void EmitSetPred(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitSetGotoVariable(EmitContext& ctx) { void EmitSetGotoVariable(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitGetGotoVariable(EmitContext& ctx) { void EmitGetGotoVariable(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitSetIndirectBranchVariable(EmitContext& ctx) { void EmitSetIndirectBranchVariable(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitGetIndirectBranchVariable(EmitContext& ctx) { void EmitGetIndirectBranchVariable(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitGetZFlag(EmitContext& ctx) { void EmitGetZFlag(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitGetSFlag(EmitContext& ctx) { void EmitGetSFlag(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitGetCFlag(EmitContext& ctx) { void EmitGetCFlag(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitGetOFlag(EmitContext& ctx) { void EmitGetOFlag(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitSetZFlag(EmitContext& ctx) { void EmitSetZFlag(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitSetSFlag(EmitContext& ctx) { void EmitSetSFlag(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitSetCFlag(EmitContext& ctx) { void EmitSetCFlag(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitSetOFlag(EmitContext& ctx) { void EmitSetOFlag(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitGetZeroFromOp(EmitContext& ctx) { void EmitGetZeroFromOp(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitGetSignFromOp(EmitContext& ctx) { void EmitGetSignFromOp(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitGetCarryFromOp(EmitContext& ctx) { void EmitGetCarryFromOp(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitGetOverflowFromOp(EmitContext& ctx) { void EmitGetOverflowFromOp(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitGetSparseFromOp(EmitContext& ctx) { void EmitGetSparseFromOp(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
void EmitGetInBoundsFromOp(EmitContext& ctx) { void EmitGetInBoundsFromOp(EmitContext& ctx) {
throw NotImplementedException("GLASM instruction {}", __LINE__); NotImplemented();
} }
} // namespace Shader::Backend::GLASM } // namespace Shader::Backend::GLASM
@@ -1,12 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "shader_recompiler/backend/bindings.h" #include "shader_recompiler/backend/bindings.h"
#include "shader_recompiler/backend/glasm/emit_glasm.h" #include "shader_recompiler/backend/glasm/emit_glasm.h"
#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
#include "shader_recompiler/backend/glasm/glasm_emit_context.h" #include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/program.h" #include "shader_recompiler/frontend/ir/program.h"
#include "shader_recompiler/profile.h" #include "shader_recompiler/profile.h"
@@ -25,6 +21,11 @@ std::string_view InterpDecorator(Interpolation interp) {
} }
throw InvalidArgument("Invalid interpolation {}", interp); throw InvalidArgument("Invalid interpolation {}", interp);
} }
bool IsInputArray(Stage stage) {
return stage == Stage::Geometry || stage == Stage::TessellationControl ||
stage == Stage::TessellationEval;
}
} // Anonymous namespace } // Anonymous namespace
EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_, EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -12,13 +9,14 @@
namespace Shader::Backend::GLSL { namespace Shader::Backend::GLSL {
namespace { namespace {
constexpr std::string_view SWIZZLE{"xyzw"};
void CompositeInsert(EmitContext& ctx, std::string_view result, std::string_view composite, void CompositeInsert(EmitContext& ctx, std::string_view result, std::string_view composite,
std::string_view object, u32 index) { std::string_view object, u32 index) {
if (result == composite) { if (result == composite) {
// The result is aliased with the composite // The result is aliased with the composite
ctx.Add("{}.{}={};", composite, "xyzw"[index], object); ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
} else { } else {
ctx.Add("{}={};{}.{}={};", result, composite, result, "xyzw"[index], object); ctx.Add("{}={};{}.{}={};", result, composite, result, SWIZZLE[index], object);
} }
} }
} // Anonymous namespace } // Anonymous namespace
@@ -40,17 +38,17 @@ void EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst& inst, std::string_v
void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite, void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
u32 index) { u32 index) {
ctx.AddU32("{}={}.{};", inst, composite, "xyzw"[index]); ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
} }
void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite, void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
u32 index) { u32 index) {
ctx.AddU32("{}={}.{};", inst, composite, "xyzw"[index]); ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
} }
void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite, void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
u32 index) { u32 index) {
ctx.AddU32("{}={}.{};", inst, composite, "xyzw"[index]); ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
} }
void EmitCompositeInsertU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite, void EmitCompositeInsertU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
@@ -148,17 +146,17 @@ void EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst& inst, std::string_v
void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite, void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
u32 index) { u32 index) {
ctx.AddF32("{}={}.{};", inst, composite, "xyzw"[index]); ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
} }
void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite, void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
u32 index) { u32 index) {
ctx.AddF32("{}={}.{};", inst, composite, "xyzw"[index]); ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
} }
void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite, void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
u32 index) { u32 index) {
ctx.AddF32("{}={}.{};", inst, composite, "xyzw"[index]); ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
} }
void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite, void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
@@ -205,16 +203,16 @@ void EmitCompositeExtractF64x4([[maybe_unused]] EmitContext& ctx) {
void EmitCompositeInsertF64x2(EmitContext& ctx, std::string_view composite, std::string_view object, void EmitCompositeInsertF64x2(EmitContext& ctx, std::string_view composite, std::string_view object,
u32 index) { u32 index) {
ctx.Add("{}.{}={};", composite, "xyzw"[index], object); ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
} }
void EmitCompositeInsertF64x3(EmitContext& ctx, std::string_view composite, std::string_view object, void EmitCompositeInsertF64x3(EmitContext& ctx, std::string_view composite, std::string_view object,
u32 index) { u32 index) {
ctx.Add("{}.{}={};", composite, "xyzw"[index], object); ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
} }
void EmitCompositeInsertF64x4(EmitContext& ctx, std::string_view composite, std::string_view object, void EmitCompositeInsertF64x4(EmitContext& ctx, std::string_view composite, std::string_view object,
u32 index) { u32 index) {
ctx.Add("{}.{}={};", composite, "xyzw"[index], object); ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
} }
} // namespace Shader::Backend::GLSL } // namespace Shader::Backend::GLSL
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -14,13 +11,14 @@
namespace Shader::Backend::GLSL { namespace Shader::Backend::GLSL {
namespace { namespace {
constexpr char SWIZZLE[]{"xyzw"};
u32 CbufIndex(u32 offset) { u32 CbufIndex(u32 offset) {
return (offset / 4) % 4; return (offset / 4) % 4;
} }
char OffsetSwizzle(u32 offset) { char OffsetSwizzle(u32 offset) {
return "xyzw"[CbufIndex(offset)]; return SWIZZLE[CbufIndex(offset)];
} }
bool IsInputArray(Stage stage) { bool IsInputArray(Stage stage) {
@@ -32,6 +30,10 @@ std::string InputVertexIndex(EmitContext& ctx, std::string_view vertex) {
return IsInputArray(ctx.stage) ? fmt::format("[{}]", vertex) : ""; return IsInputArray(ctx.stage) ? fmt::format("[{}]", vertex) : "";
} }
std::string_view OutputVertexIndex(EmitContext& ctx) {
return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : "";
}
std::string ChooseCbuf(EmitContext& ctx, const IR::Value& binding, std::string_view index) { std::string ChooseCbuf(EmitContext& ctx, const IR::Value& binding, std::string_view index) {
if (binding.IsImmediate()) { if (binding.IsImmediate()) {
return fmt::format("{}_cbuf{}[{}]", ctx.stage_name, binding.U32(), index); return fmt::format("{}_cbuf{}[{}]", ctx.stage_name, binding.U32(), index);
@@ -277,7 +279,7 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view val
const u32 index{IR::GenericAttributeIndex(attr)}; const u32 index{IR::GenericAttributeIndex(attr)};
const u32 attr_element{IR::GenericAttributeElement(attr)}; const u32 attr_element{IR::GenericAttributeElement(attr)};
const GenericElementInfo& info{ctx.output_generics.at(index).at(attr_element)}; const GenericElementInfo& info{ctx.output_generics.at(index).at(attr_element)};
const auto output_decorator = ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : ""; const auto output_decorator{OutputVertexIndex(ctx)};
if (info.num_components == 1) { if (info.num_components == 1) {
ctx.Add("{}{}={};", info.name, output_decorator, value); ctx.Add("{}{}={};", info.name, output_decorator, value);
} else { } else {
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -13,13 +10,14 @@
namespace Shader::Backend::GLSL { namespace Shader::Backend::GLSL {
namespace { namespace {
constexpr char cas_loop[]{"for(;;){{uint old_value={};uint "
"cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
"if(cas_result==old_value){{break;}}}}"};
void SsboWriteCas(EmitContext& ctx, const IR::Value& binding, std::string_view offset_var, void SsboWriteCas(EmitContext& ctx, const IR::Value& binding, std::string_view offset_var,
std::string_view value, std::string_view bit_offset, u32 num_bits) { std::string_view value, std::string_view bit_offset, u32 num_bits) {
const auto ssbo{fmt::format("{}_ssbo{}[{}>>2]", ctx.stage_name, binding.U32(), offset_var)}; const auto ssbo{fmt::format("{}_ssbo{}[{}>>2]", ctx.stage_name, binding.U32(), offset_var)};
ctx.Add( ctx.Add(cas_loop, ssbo, ssbo, ssbo, value, bit_offset, num_bits);
"for(;;){{uint old_value={};uint "
"cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
"if(cas_result==old_value){{break;}}}}", ssbo, ssbo, ssbo, value, bit_offset, num_bits);
} }
} // Anonymous namespace } // Anonymous namespace
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -10,13 +7,14 @@
namespace Shader::Backend::GLSL { namespace Shader::Backend::GLSL {
namespace { namespace {
constexpr char cas_loop[]{"for(;;){{uint old_value={};uint "
"cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
"if(cas_result==old_value){{break;}}}}"};
void SharedWriteCas(EmitContext& ctx, std::string_view offset, std::string_view value, void SharedWriteCas(EmitContext& ctx, std::string_view offset, std::string_view value,
std::string_view bit_offset, u32 num_bits) { std::string_view bit_offset, u32 num_bits) {
const auto smem{fmt::format("smem[{}>>2]", offset)}; const auto smem{fmt::format("smem[{}>>2]", offset)};
ctx.Add( ctx.Add(cas_loop, smem, smem, smem, value, bit_offset, num_bits);
"for(;;){{uint old_value={};uint "
"cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
"if(cas_result==old_value){{break;}}}}", smem, smem, smem, value, bit_offset, num_bits);
} }
} // Anonymous namespace } // Anonymous namespace
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -12,6 +9,9 @@
namespace Shader::Backend::GLSL { namespace Shader::Backend::GLSL {
namespace { namespace {
std::string_view OutputVertexIndex(EmitContext& ctx) {
return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : "";
}
void InitializeOutputVaryings(EmitContext& ctx) { void InitializeOutputVaryings(EmitContext& ctx) {
if (ctx.uses_geometry_passthrough) { if (ctx.uses_geometry_passthrough) {
@@ -25,7 +25,7 @@ void InitializeOutputVaryings(EmitContext& ctx) {
continue; continue;
} }
const auto& info_array{ctx.output_generics.at(index)}; const auto& info_array{ctx.output_generics.at(index)};
const auto output_decorator = ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : ""; const auto output_decorator{OutputVertexIndex(ctx)};
size_t element{}; size_t element{};
while (element < info_array.size()) { while (element < info_array.size()) {
const auto& info{info_array.at(element)}; const auto& info{info_array.at(element)};
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -9,7 +9,7 @@
namespace Shader::Backend::SPIRV { namespace Shader::Backend::SPIRV {
namespace { namespace {
Id DecorateNoContraction(EmitContext& ctx, IR::Inst* inst, Id op) { Id Decorate(EmitContext& ctx, IR::Inst* inst, Id op) {
const auto flags{inst->Flags<IR::FpControl>()}; const auto flags{inst->Flags<IR::FpControl>()};
if (flags.no_contraction) { if (flags.no_contraction) {
ctx.Decorate(op, spv::Decoration::NoContraction); ctx.Decorate(op, spv::Decoration::NoContraction);
@@ -61,27 +61,27 @@ Id EmitFPAbs64(EmitContext& ctx, Id value) {
} }
Id EmitFPAdd16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { Id EmitFPAdd16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
return DecorateNoContraction(ctx, inst, ctx.OpFAdd(ctx.F16[1], a, b)); return Decorate(ctx, inst, ctx.OpFAdd(ctx.F16[1], a, b));
} }
Id EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { Id EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
return DecorateNoContraction(ctx, inst, ctx.OpFAdd(ctx.F32[1], a, b)); return Decorate(ctx, inst, ctx.OpFAdd(ctx.F32[1], a, b));
} }
Id EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { Id EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
return DecorateNoContraction(ctx, inst, ctx.OpFAdd(ctx.F64[1], a, b)); return Decorate(ctx, inst, ctx.OpFAdd(ctx.F64[1], a, b));
} }
Id EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { Id EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
return DecorateNoContraction(ctx, inst, ctx.OpFma(ctx.F16[1], a, b, c)); return Decorate(ctx, inst, ctx.OpFma(ctx.F16[1], a, b, c));
} }
Id EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { Id EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
return DecorateNoContraction(ctx, inst, ctx.OpFma(ctx.F32[1], a, b, c)); return Decorate(ctx, inst, ctx.OpFma(ctx.F32[1], a, b, c));
} }
Id EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { Id EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
return DecorateNoContraction(ctx, inst, ctx.OpFma(ctx.F64[1], a, b, c)); return Decorate(ctx, inst, ctx.OpFma(ctx.F64[1], a, b, c));
} }
Id EmitFPMax32(EmitContext& ctx, Id a, Id b) { Id EmitFPMax32(EmitContext& ctx, Id a, Id b) {
@@ -101,15 +101,15 @@ Id EmitFPMin64(EmitContext& ctx, Id a, Id b) {
} }
Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
return DecorateNoContraction(ctx, inst, ctx.OpFMul(ctx.F16[1], a, b)); return Decorate(ctx, inst, ctx.OpFMul(ctx.F16[1], a, b));
} }
Id EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { Id EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
return DecorateNoContraction(ctx, inst, ctx.OpFMul(ctx.F32[1], a, b)); return Decorate(ctx, inst, ctx.OpFMul(ctx.F32[1], a, b));
} }
Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
return DecorateNoContraction(ctx, inst, ctx.OpFMul(ctx.F64[1], a, b)); return Decorate(ctx, inst, ctx.OpFMul(ctx.F64[1], a, b));
} }
Id EmitFPNeg16(EmitContext& ctx, Id value) { Id EmitFPNeg16(EmitContext& ctx, Id value) {
@@ -338,7 +338,7 @@ bool IsTextureInteger(EmitContext& ctx, const IR::TextureInstInfo& info) {
return ctx.textures.at(info.descriptor_index).is_integer; return ctx.textures.at(info.descriptor_index).is_integer;
} }
Id DecorateRelaxedPrecision(EmitContext& ctx, IR::Inst* inst, Id sample) { Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
if (info.relaxed_precision != 0) { if (info.relaxed_precision != 0) {
ctx.Decorate(sample, spv::Decoration::RelaxedPrecision); ctx.Decorate(sample, spv::Decoration::RelaxedPrecision);
@@ -351,14 +351,14 @@ Id Emit(MethodPtrType sparse_ptr, MethodPtrType non_sparse_ptr, EmitContext& ctx
Id result_type, Args&&... args) { Id result_type, Args&&... args) {
IR::Inst* const sparse{inst->GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)}; IR::Inst* const sparse{inst->GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
if (!sparse) { if (!sparse) {
return DecorateRelaxedPrecision(ctx, inst, (ctx.*non_sparse_ptr)(result_type, std::forward<Args>(args)...)); return Decorate(ctx, inst, (ctx.*non_sparse_ptr)(result_type, std::forward<Args>(args)...));
} }
const Id struct_type{ctx.TypeStruct(ctx.U32[1], result_type)}; const Id struct_type{ctx.TypeStruct(ctx.U32[1], result_type)};
const Id sample{(ctx.*sparse_ptr)(struct_type, std::forward<Args>(args)...)}; const Id sample{(ctx.*sparse_ptr)(struct_type, std::forward<Args>(args)...)};
const Id resident_code{ctx.OpCompositeExtract(ctx.U32[1], sample, 0U)}; const Id resident_code{ctx.OpCompositeExtract(ctx.U32[1], sample, 0U)};
sparse->SetDefinition(ctx.OpImageSparseTexelsResident(ctx.U1, resident_code)); sparse->SetDefinition(ctx.OpImageSparseTexelsResident(ctx.U1, resident_code));
sparse->Invalidate(); sparse->Invalidate();
DecorateRelaxedPrecision(ctx, inst, sample); Decorate(ctx, inst, sample);
return ctx.OpCompositeExtract(result_type, sample, 1U); return ctx.OpCompositeExtract(result_type, sample, 1U);
} }

Some files were not shown because too many files have changed in this diff Show More