mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-11 22:32:29 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e0f8d196f1 | |||
| 5f142c7926 | |||
| c5f6f1ca5e | |||
| dcbcdf16ad | |||
| ed57836903 | |||
| 20f9aa4cfe | |||
| 6f6a8f6d67 | |||
| 2f787ddb8c | |||
| 23585820bc | |||
| e5b8b10188 | |||
| 8a10109e24 | |||
| a538cd9aff | |||
| ce202292cf | |||
| 87e5d0b6e4 | |||
| fdd8d4252c | |||
| 1036982d2f | |||
| e27650cf42 | |||
| ecb2ae4076 |
+4
-1
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
// SPDX-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 {
|
||||||
|
|||||||
+2
-2
@@ -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(
|
||||||
|
|||||||
@@ -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 =
|
||||||
@@ -341,7 +342,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(),
|
||||||
@@ -356,7 +357,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);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<!-- General application strings -->
|
<!-- General application strings -->
|
||||||
<string name="app_name" translatable="false">Eden</string>
|
<string name="app_name" translatable="false">Eden</string>
|
||||||
<string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.<br /><br />Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.<br /><br /><![CDATA[<a href=\"https://yuzu-mirror.github.io/help/quickstart\">Learn more</a>]]></string>
|
<string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.<br /><br />Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.<br /><br /><![CDATA[<a href=\"https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/QuickStart.md\">Learn more</a>]]></string>
|
||||||
<string name="notice_notification_channel_name">Notices and errors</string>
|
<string name="notice_notification_channel_name">Notices and errors</string>
|
||||||
<string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string>
|
<string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string>
|
||||||
<string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string>
|
<string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string>
|
||||||
@@ -933,7 +933,7 @@
|
|||||||
<string name="loader_error_file_not_found">ROM file does not exist</string>
|
<string name="loader_error_file_not_found">ROM file does not exist</string>
|
||||||
|
|
||||||
<string name="loader_requires_firmware">Game Requires Firmware</string>
|
<string name="loader_requires_firmware">Game Requires Firmware</string>
|
||||||
<string name="loader_requires_firmware_description"><![CDATA[The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href=\"https://yuzu-mirror.github.io/help/quickstart\"> dump and install firmware</a>, or press \"OK\" to launch anyways.]]></string>
|
<string name="loader_requires_firmware_description"><![CDATA[This game may require firmware to function properly, and you don\'t have any installed. Please install firmware before launching, or press \"OK\" to launch anyways.]]></string>
|
||||||
|
|
||||||
<!-- Intent Launch strings -->
|
<!-- Intent Launch strings -->
|
||||||
<string name="searching_for_game">Searching for game...</string>
|
<string name="searching_for_game">Searching for game...</string>
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ add_library(
|
|||||||
param_package.h
|
param_package.h
|
||||||
parent_of_member.h
|
parent_of_member.h
|
||||||
point.h
|
point.h
|
||||||
quaternion.h
|
|
||||||
range_map.h
|
range_map.h
|
||||||
range_mutex.h
|
range_mutex.h
|
||||||
range_sets.h
|
range_sets.h
|
||||||
@@ -109,6 +108,8 @@ add_library(
|
|||||||
settings_setting.h
|
settings_setting.h
|
||||||
slot_vector.h
|
slot_vector.h
|
||||||
socket_types.h
|
socket_types.h
|
||||||
|
sparse_large_vector.cpp
|
||||||
|
sparse_large_vector.h
|
||||||
spin_lock.h
|
spin_lock.h
|
||||||
stb.cpp
|
stb.cpp
|
||||||
stb.h
|
stb.h
|
||||||
@@ -138,8 +139,6 @@ add_library(
|
|||||||
uuid.cpp
|
uuid.cpp
|
||||||
uuid.h
|
uuid.h
|
||||||
vector_math.h
|
vector_math.h
|
||||||
virtual_buffer.cpp
|
|
||||||
virtual_buffer.h
|
|
||||||
zstd_compression.cpp
|
zstd_compression.cpp
|
||||||
zstd_compression.h
|
zstd_compression.h
|
||||||
fs/ryujinx_compat.h fs/ryujinx_compat.cpp
|
fs/ryujinx_compat.h fs/ryujinx_compat.cpp
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/fiber.h"
|
#include "common/fiber.h"
|
||||||
#include "common/virtual_buffer.h"
|
|
||||||
|
|
||||||
#include <boost/context/detail/fcontext.hpp>
|
#include <boost/context/detail/fcontext.hpp>
|
||||||
|
|
||||||
|
|||||||
+40
-13
@@ -117,7 +117,6 @@ template <typename T>
|
|||||||
static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn) {
|
static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn) {
|
||||||
if (!dll.GetSymbol(name, &pfn)) {
|
if (!dll.GetSymbol(name, &pfn)) {
|
||||||
LOG_CRITICAL(HW_Memory, "Failed to load {}", name);
|
LOG_CRITICAL(HW_Memory, "Failed to load {}", name);
|
||||||
throw std::bad_alloc{};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,6 +178,14 @@ public:
|
|||||||
Release();
|
Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* Allocate(size_t size) {
|
||||||
|
auto* ptr = VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||||
|
if (ptr == nullptr) {
|
||||||
|
LOG_CRITICAL(HW_Memory, "Failed to allocate fallback buffer with size {:#x}, error {}", size, GetLastError());
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
|
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
|
||||||
std::unique_lock lock{placeholder_mutex};
|
std::unique_lock lock{placeholder_mutex};
|
||||||
if (!IsNiechePlaceholder(virtual_offset, length)) {
|
if (!IsNiechePlaceholder(virtual_offset, length)) {
|
||||||
@@ -399,6 +406,10 @@ private:
|
|||||||
// For managarm: see https://github.com/managarm/managarm/issues/1370
|
// For managarm: see https://github.com/managarm/managarm/issues/1370
|
||||||
#else // ^^^ Windows ^^^ vvv POSIX vvv
|
#else // ^^^ Windows ^^^ vvv POSIX vvv
|
||||||
|
|
||||||
|
#ifndef MAP_NOCORE
|
||||||
|
#define MAP_NOCORE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef ARCHITECTURE_arm64
|
#ifdef ARCHITECTURE_arm64
|
||||||
|
|
||||||
static void* ChooseVirtualBase(size_t virtual_size) {
|
static void* ChooseVirtualBase(size_t virtual_size) {
|
||||||
@@ -423,7 +434,7 @@ static void* ChooseVirtualBase(size_t virtual_size) {
|
|||||||
// Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here.
|
// Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here.
|
||||||
void* map_pointer =
|
void* map_pointer =
|
||||||
mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE,
|
mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE,
|
||||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_NOCORE, -1, 0);
|
||||||
|
|
||||||
// If we successfully mapped, we're done.
|
// If we successfully mapped, we're done.
|
||||||
if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) {
|
if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) {
|
||||||
@@ -443,11 +454,11 @@ static void* ChooseVirtualBase(size_t virtual_size) {
|
|||||||
|
|
||||||
static void* ChooseVirtualBase(size_t virtual_size) {
|
static void* ChooseVirtualBase(size_t virtual_size) {
|
||||||
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__)
|
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__)
|
||||||
void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0);
|
void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER | MAP_NOCORE, -1, 0);
|
||||||
if (virtual_base != MAP_FAILED)
|
if (virtual_base != MAP_FAILED)
|
||||||
return virtual_base;
|
return virtual_base;
|
||||||
#endif
|
#endif
|
||||||
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_NOCORE, -1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -541,13 +552,13 @@ public:
|
|||||||
}
|
}
|
||||||
if (use_anon) {
|
if (use_anon) {
|
||||||
LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones");
|
LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones");
|
||||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
|
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NOCORE, -1, 0));
|
||||||
if (fd > 0) {
|
if (fd > 0) {
|
||||||
fd = -1;
|
fd = -1;
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
|
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NOCORE, fd, 0));
|
||||||
}
|
}
|
||||||
if (backing_base == MAP_FAILED) {
|
if (backing_base == MAP_FAILED) {
|
||||||
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
||||||
@@ -571,6 +582,14 @@ public:
|
|||||||
Release();
|
Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* Allocate(size_t size) {
|
||||||
|
auto* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||||
|
if (ptr == MAP_FAILED) {
|
||||||
|
LOG_CRITICAL(HW_Memory, "Failed to allocate fallback buffer with size {:#x}, {}", size, strerror(errno));
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
|
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
|
||||||
// Intersect the range with our address space.
|
// Intersect the range with our address space.
|
||||||
AdjustMap(&virtual_offset, &length);
|
AdjustMap(&virtual_offset, &length);
|
||||||
@@ -691,12 +710,10 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
|
|||||||
{
|
{
|
||||||
#if defined(__OPENORBIS__) || defined(__managarm__)
|
#if defined(__OPENORBIS__) || defined(__managarm__)
|
||||||
LOG_WARNING(HW_Memory, "Platform doesn't support fastmem");
|
LOG_WARNING(HW_Memory, "Platform doesn't support fastmem");
|
||||||
fallback_buffer.emplace(backing_size);
|
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||||
backing_base = fallback_buffer->data();
|
|
||||||
virtual_base = nullptr;
|
virtual_base = nullptr;
|
||||||
#else
|
#else
|
||||||
// Try to allocate a fastmem arena.
|
// Try to allocate a fastmem arena.
|
||||||
// The implementation will fail with std::bad_alloc on errors.
|
|
||||||
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize);
|
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize);
|
||||||
if (impl->Init()) {
|
if (impl->Init()) {
|
||||||
backing_base = impl->backing_base;
|
backing_base = impl->backing_base;
|
||||||
@@ -707,16 +724,26 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
|
|||||||
virtual_base_offset = virtual_base - impl->virtual_base;
|
virtual_base_offset = virtual_base - impl->virtual_base;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
impl.reset();
|
|
||||||
LOG_WARNING(HW_Memory, "Platform can support fastmem, but can't create it");
|
LOG_WARNING(HW_Memory, "Platform can support fastmem, but can't create it");
|
||||||
fallback_buffer.emplace(backing_size);
|
fallback_buffer = true;
|
||||||
backing_base = fallback_buffer->data();
|
backing_base = static_cast<u8*>(impl->Allocate(backing_size));
|
||||||
virtual_base = nullptr;
|
virtual_base = nullptr;
|
||||||
|
impl.reset();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
HostMemory::~HostMemory() = default;
|
HostMemory::~HostMemory() {
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (fallback_buffer) {
|
||||||
|
VirtualFree(backing_base, backing_size, MEM_RELEASE);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (fallback_buffer) {
|
||||||
|
munmap(backing_base, backing_size);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
HostMemory::HostMemory(HostMemory&&) noexcept = default;
|
HostMemory::HostMemory(HostMemory&&) noexcept = default;
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/virtual_buffer.h"
|
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
@@ -86,7 +85,7 @@ private:
|
|||||||
u8* virtual_base{};
|
u8* virtual_base{};
|
||||||
size_t virtual_base_offset{};
|
size_t virtual_base_offset{};
|
||||||
// Windows requires it for kernels whom lack proper support for some functions!
|
// Windows requires it for kernels whom lack proper support for some functions!
|
||||||
std::optional<Common::VirtualBuffer<u8>> fallback_buffer;
|
bool fallback_buffer{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@@ -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: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
@@ -13,39 +13,11 @@ PageTable::PageTable() = default;
|
|||||||
|
|
||||||
PageTable::~PageTable() noexcept = default;
|
PageTable::~PageTable() noexcept = default;
|
||||||
|
|
||||||
bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
|
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_bits) {
|
||||||
Common::ProcessAddress address) const {
|
auto const num_page_table_entries = 1ULL << (address_space_width_in_bits - page_bits);
|
||||||
out_context->next_offset = GetInteger(address);
|
entries.ResizeAndClear(num_page_table_entries);
|
||||||
out_context->next_page = address / page_size;
|
|
||||||
|
|
||||||
return this->ContinueTraversal(out_entry, out_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const {
|
|
||||||
// Setup invalid defaults.
|
|
||||||
out_entry->phys_addr = 0;
|
|
||||||
out_entry->block_size = page_size;
|
|
||||||
// Validate that we can read the actual entry.
|
|
||||||
if (auto const page = context->next_page; page < entries.size()) {
|
|
||||||
// Validate that the entry is mapped.
|
|
||||||
if (auto const paddr = entries[page].addr; paddr != 0) {
|
|
||||||
// Populate the results.
|
|
||||||
out_entry->phys_addr = paddr + context->next_offset;
|
|
||||||
context->next_page += 1;
|
|
||||||
context->next_offset += page_size;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
context->next_page += 1;
|
|
||||||
context->next_offset += page_size;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits) {
|
|
||||||
auto const num_page_table_entries = 1ULL << (address_space_width_in_bits - page_size_in_bits);
|
|
||||||
entries.resize(num_page_table_entries);
|
|
||||||
current_address_space_width_in_bits = address_space_width_in_bits;
|
current_address_space_width_in_bits = address_space_width_in_bits;
|
||||||
page_size = 1ULL << page_size_in_bits;
|
current_page_bits = page_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
+64
-56
@@ -9,22 +9,22 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "common/sparse_large_vector.h"
|
||||||
#include "common/typed_address.h"
|
#include "common/typed_address.h"
|
||||||
#include "common/virtual_buffer.h"
|
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
enum class PageType : u8 {
|
enum class PageType : u8 {
|
||||||
/// Page is unmapped and should cause an access error.
|
/// Page is unmapped and should cause an access error.
|
||||||
Unmapped,
|
Unmapped = 0b00,
|
||||||
/// Page is mapped to regular memory. This is the only type you can get pointers to.
|
/// Page is mapped to regular memory. This is the only type you can get pointers to.
|
||||||
Memory,
|
Memory = 0b01,
|
||||||
/// Page is mapped to regular memory, but inaccessible from CPU fastmem and must use
|
/// Page is mapped to regular memory, but inaccessible from CPU fastmem and must use
|
||||||
/// the callbacks.
|
/// the callbacks.
|
||||||
DebugMemory,
|
DebugMemory = 0b10,
|
||||||
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
||||||
/// invalidation
|
/// invalidation
|
||||||
RasterizerCachedMemory,
|
RasterizerCachedMemory = 0b11,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,57 +42,86 @@ struct PageTable {
|
|||||||
u64 next_offset{};
|
u64 next_offset{};
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Number of bits reserved for attribute tagging.
|
/// Masks out bits reserved for attribute tagging.
|
||||||
/// This can be at most the guaranteed alignment of the pointers in the page table.
|
static constexpr u64 ATTRIBUTE_MASK = ((1ULL << 44) - 1) << 12;
|
||||||
static constexpr int ATTRIBUTE_BITS = 2;
|
|
||||||
|
/// Specifies sign bit for page table entries.
|
||||||
|
static constexpr u64 SIGN_BIT = 45 + 12; // 44 bits of data + page offset
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pair of host pointer and page type attribute.
|
* Atomic tuple of host pointer, page type, and block id.
|
||||||
* This uses the lower bits of a given pointer to store the attribute tag.
|
* This uses the lower bits of a given pointer to store the attributes.
|
||||||
* Writing and reading the pointer attribute pair is guaranteed to be atomic for the same method
|
* Writing and reading the pointer attribute pair is guaranteed to be atomic for the same method
|
||||||
* call. In other words, they are guaranteed to be synchronized at all times.
|
* call. In other words, they are guaranteed to be synchronized at all times.
|
||||||
*/
|
*/
|
||||||
class PageInfo {
|
class PageEntryData {
|
||||||
public:
|
public:
|
||||||
|
struct Data {
|
||||||
|
Data(bool marked_, PageType type_, u16 block_, u64 page_)
|
||||||
|
: marked(static_cast<u64>(marked_) & 0b1)
|
||||||
|
, type(static_cast<u64>(type_) & ((1ULL << 2) - 1))
|
||||||
|
, block(static_cast<u64>(block_) & ((1ULL << 9) - 1))
|
||||||
|
, page((page_ >> 12) & ((1ULL << 45) - 1))
|
||||||
|
, block2((static_cast<u64>(block_) >> 9) & ((1ULL << 7) - 1)) {}
|
||||||
|
u64 marked : 1;
|
||||||
|
u64 type : 2;
|
||||||
|
u64 block : 9;
|
||||||
|
u64 page : 45; // 44 bits of actual data (64 - page offset (12) - reserved (8)) + a sign bit
|
||||||
|
u64 block2 : 7;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] Data Raw() const noexcept {
|
||||||
|
return std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed));
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the page pointer
|
/// Returns the page pointer
|
||||||
[[nodiscard]] uintptr_t Pointer() const noexcept {
|
[[nodiscard]] uintptr_t Pointer(bool ignored_marked = false) const noexcept {
|
||||||
return ExtractPointer(raw.load(std::memory_order_relaxed));
|
return ExtractPointer(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)), ignored_marked);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the page type attribute
|
/// Returns the page type attribute
|
||||||
[[nodiscard]] PageType Type() const noexcept {
|
[[nodiscard]] PageType Type() const noexcept {
|
||||||
return ExtractType(raw.load(std::memory_order_relaxed));
|
return static_cast<PageType>(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)).type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the block identifier.
|
||||||
|
[[nodiscard]] u16 Block() const noexcept {
|
||||||
|
return ExtractBlock(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the page pointer and attribute pair, extracted from the same atomic read
|
/// Returns the page pointer and attribute pair, extracted from the same atomic read
|
||||||
[[nodiscard]] std::pair<uintptr_t, PageType> PointerType() const noexcept {
|
[[nodiscard]] std::tuple<uintptr_t, PageType, u16> PointerTypeBlock(bool ignore_marked = false) const noexcept {
|
||||||
const uintptr_t non_atomic_raw = raw.load(std::memory_order_relaxed);
|
const auto non_atomic_raw = std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed));
|
||||||
return {ExtractPointer(non_atomic_raw), ExtractType(non_atomic_raw)};
|
return {ExtractPointer(non_atomic_raw, ignore_marked), static_cast<PageType>(non_atomic_raw.type), ExtractBlock(non_atomic_raw)};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the raw representation of the page information.
|
/// Write page info atomically
|
||||||
/// Use ExtractPointer and ExtractType to unpack the value.
|
constexpr void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
|
||||||
[[nodiscard]] uintptr_t Raw() const noexcept {
|
data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer}));
|
||||||
return raw.load(std::memory_order_relaxed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write a page pointer and type pair atomically
|
constexpr void MarkRasterizerCached() noexcept {
|
||||||
void Store(uintptr_t pointer, PageType type) noexcept {
|
data_raw.fetch_or(0b111);
|
||||||
raw.store(pointer | uintptr_t(type));
|
}
|
||||||
|
|
||||||
|
constexpr void MarkDebug(u64 ptr, u16 block) noexcept {
|
||||||
|
Store(true, PageType::DebugMemory, block, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unpack a pointer from a page info raw representation
|
/// Unpack a pointer from a page info raw representation
|
||||||
[[nodiscard]] static uintptr_t ExtractPointer(uintptr_t raw) noexcept {
|
[[nodiscard]] static uintptr_t ExtractPointer(Data raw, bool ignore_marked = false) noexcept {
|
||||||
return raw & (~uintptr_t{0} << ATTRIBUTE_BITS);
|
return raw.marked && !ignore_marked ? 0
|
||||||
|
// shift raw.page's fake sign bit to the actual sign bit, then sign extend
|
||||||
|
: ((s64)(raw.page << (64 - 44))) >> (64 - 44 - 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unpack a page type from a page info raw representation
|
[[nodiscard]] static u16 ExtractBlock(Data raw) noexcept {
|
||||||
[[nodiscard]] static PageType ExtractType(uintptr_t raw) noexcept {
|
return static_cast<u16>(raw.block | (raw.block2 << 9));
|
||||||
return static_cast<PageType>(raw & ((uintptr_t{1} << ATTRIBUTE_BITS) - 1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::atomic<uintptr_t> raw;
|
std::atomic<u64> data_raw;
|
||||||
|
static_assert(sizeof(Data) == sizeof(std::atomic<u64>));
|
||||||
};
|
};
|
||||||
|
|
||||||
PageTable();
|
PageTable();
|
||||||
@@ -100,13 +129,8 @@ struct PageTable {
|
|||||||
|
|
||||||
PageTable(const PageTable&) = delete;
|
PageTable(const PageTable&) = delete;
|
||||||
PageTable& operator=(const PageTable&) = delete;
|
PageTable& operator=(const PageTable&) = delete;
|
||||||
|
PageTable(PageTable&&) noexcept = delete;
|
||||||
PageTable(PageTable&&) noexcept = default;
|
PageTable& operator=(PageTable&&) noexcept = delete;
|
||||||
PageTable& operator=(PageTable&&) noexcept = default;
|
|
||||||
|
|
||||||
bool BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
|
|
||||||
Common::ProcessAddress address) const;
|
|
||||||
bool ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resizes the page table to be able to accommodate enough pages within
|
* Resizes the page table to be able to accommodate enough pages within
|
||||||
@@ -121,30 +145,14 @@ struct PageTable {
|
|||||||
return current_address_space_width_in_bits;
|
return current_address_space_width_in_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetPhysicalAddress(Common::PhysicalAddress* out_phys_addr,
|
|
||||||
Common::ProcessAddress virt_addr) const {
|
|
||||||
if (virt_addr > (1ULL << this->GetAddressSpaceBits())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*out_phys_addr = entries[virt_addr / page_size].addr + GetInteger(virt_addr);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Vector of memory pointers backing each page. An entry can only be non-null if the
|
/// Vector of memory pointers backing each page. An entry can only be non-null if the
|
||||||
/// corresponding attribute element is of type `Memory`.
|
/// corresponding attribute element is of type `Memory`.
|
||||||
struct PageEntryData {
|
SparseLargeVector<PageEntryData> entries;
|
||||||
PageInfo ptr;
|
static_assert(sizeof(PageEntryData) == 8);
|
||||||
u64 block;
|
|
||||||
u64 addr;
|
|
||||||
u64 padding;
|
|
||||||
};
|
|
||||||
VirtualBuffer<PageEntryData> entries;
|
|
||||||
static_assert(sizeof(PageEntryData) == 32);
|
|
||||||
|
|
||||||
u8* fastmem_arena{};
|
u8* fastmem_arena{};
|
||||||
std::size_t current_address_space_width_in_bits{};
|
std::size_t current_address_space_width_in_bits{};
|
||||||
std::size_t page_size{};
|
std::size_t current_page_bits{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "common/vector_math.h"
|
|
||||||
|
|
||||||
namespace Common {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class Quaternion {
|
|
||||||
public:
|
|
||||||
Vec3<T> xyz;
|
|
||||||
T w{};
|
|
||||||
|
|
||||||
[[nodiscard]] Quaternion<decltype(-T{})> Inverse() const {
|
|
||||||
return {-xyz, w};
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] Quaternion<decltype(T{} + T{})> operator+(const Quaternion& other) const {
|
|
||||||
return {xyz + other.xyz, w + other.w};
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] Quaternion<decltype(T{} - T{})> operator-(const Quaternion& other) const {
|
|
||||||
return {xyz - other.xyz, w - other.w};
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] Quaternion<decltype(T{} * T{} - T{} * T{})> operator*(
|
|
||||||
const Quaternion& other) const {
|
|
||||||
return {xyz * other.w + other.xyz * w + Cross(xyz, other.xyz),
|
|
||||||
w * other.w - Dot(xyz, other.xyz)};
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] Quaternion<T> Normalized() const {
|
|
||||||
T length = std::sqrt(xyz.Length2() + w * w);
|
|
||||||
return {xyz / length, w / length};
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] std::array<decltype(-T{}), 16> ToMatrix() const {
|
|
||||||
const T x2 = xyz[0] * xyz[0];
|
|
||||||
const T y2 = xyz[1] * xyz[1];
|
|
||||||
const T z2 = xyz[2] * xyz[2];
|
|
||||||
|
|
||||||
const T xy = xyz[0] * xyz[1];
|
|
||||||
const T wz = w * xyz[2];
|
|
||||||
const T xz = xyz[0] * xyz[2];
|
|
||||||
const T wy = w * xyz[1];
|
|
||||||
const T yz = xyz[1] * xyz[2];
|
|
||||||
const T wx = w * xyz[0];
|
|
||||||
|
|
||||||
return {1.0f - 2.0f * (y2 + z2),
|
|
||||||
2.0f * (xy + wz),
|
|
||||||
2.0f * (xz - wy),
|
|
||||||
0.0f,
|
|
||||||
2.0f * (xy - wz),
|
|
||||||
1.0f - 2.0f * (x2 + z2),
|
|
||||||
2.0f * (yz + wx),
|
|
||||||
0.0f,
|
|
||||||
2.0f * (xz + wy),
|
|
||||||
2.0f * (yz - wx),
|
|
||||||
1.0f - 2.0f * (x2 + y2),
|
|
||||||
0.0f,
|
|
||||||
0.0f,
|
|
||||||
0.0f,
|
|
||||||
0.0f,
|
|
||||||
1.0f};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] auto QuaternionRotate(const Quaternion<T>& q, const Vec3<T>& v) {
|
|
||||||
return v + 2 * Cross(q.xyz, Cross(q.xyz, v) + v * q.w);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] inline Quaternion<float> MakeQuaternion(const Vec3<float>& axis, float angle) {
|
|
||||||
return {axis * std::sin(angle / 2), std::cos(angle / 2)};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Common
|
|
||||||
@@ -858,7 +858,7 @@ struct Values {
|
|||||||
SwitchableSetting<std::string> program_args{linkage,
|
SwitchableSetting<std::string> program_args{linkage,
|
||||||
std::string(),
|
std::string(),
|
||||||
"program_args",
|
"program_args",
|
||||||
Category::System,
|
Category::Debugging,
|
||||||
Specialization::Default,
|
Specialization::Default,
|
||||||
true, // save_ - persist in config file
|
true, // save_ - persist in config file
|
||||||
false}; // runtime_modifiable_ - startup-only
|
false}; // runtime_modifiable_ - startup-only
|
||||||
@@ -904,7 +904,7 @@ struct Values {
|
|||||||
0,
|
0,
|
||||||
65535,
|
65535,
|
||||||
"debug_knobs",
|
"debug_knobs",
|
||||||
Category::System,
|
Category::Debugging,
|
||||||
Specialization::Countable,
|
Specialization::Countable,
|
||||||
true,
|
true,
|
||||||
true};
|
true};
|
||||||
|
|||||||
@@ -0,0 +1,143 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
/* virtual_buffer.cpp */
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#include <mutex>
|
||||||
|
#else
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "common/alignment.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/sparse_large_vector.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
static std::vector<std::pair<u64, u64>> vector_regions {};
|
||||||
|
|
||||||
|
// Workaround for handling non-commited memory accessed by Dynarmic; usually result of an error
|
||||||
|
static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
||||||
|
DWORD code = info->ExceptionRecord->ExceptionCode;
|
||||||
|
u64 exception_addr = reinterpret_cast<u64>(info->ExceptionRecord->ExceptionAddress);
|
||||||
|
|
||||||
|
if (code != EXCEPTION_ACCESS_VIOLATION) {
|
||||||
|
// Not our problem
|
||||||
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 addr = 0, addr2 = 0;
|
||||||
|
|
||||||
|
for (auto region: vector_regions) {
|
||||||
|
auto addr_shifted = exception_addr >> HostPageBits;
|
||||||
|
if (region.first <= addr_shifted && addr_shifted <= region.second) {
|
||||||
|
addr = addr_shifted;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Page-boundary accesses
|
||||||
|
if (auto addr_ = (exception_addr + 0x40) >> HostPageBits; addr_ != addr_shifted && region.first <= addr_ && addr_ <= region.second) {
|
||||||
|
addr2 = addr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr != 0 || addr2 != 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr == 0 && addr2 == 0) {
|
||||||
|
// Not our problem
|
||||||
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_ERROR(HW_Memory, "Accessing an unallocated region of a SparseLargeVector at {:#x}; this shouldn't happen and is likely a Dynarmic error!", exception_addr);
|
||||||
|
|
||||||
|
// Commit this region
|
||||||
|
if (addr != 0) {
|
||||||
|
if (!CommitVectorPage(addr << HostPageBits, false)) {
|
||||||
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Commit next region if needed
|
||||||
|
if (addr2 != 0) {
|
||||||
|
if (!CommitVectorPage(addr2 << HostPageBits, false)) {
|
||||||
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXCEPTION_CONTINUE_EXECUTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
||||||
|
MEMORY_BASIC_INFORMATION info {};
|
||||||
|
auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
|
||||||
|
if (res == 0) {
|
||||||
|
LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError());
|
||||||
|
} else if (info.State != MEM_RESERVE) {
|
||||||
|
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} that is not mapped or is already committed (state {:#x})", addr, info.State);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto perm = write ? PAGE_READWRITE : PAGE_READONLY;
|
||||||
|
void* res2 = VirtualAlloc(reinterpret_cast<LPVOID>(addr), HostPageSize, MEM_COMMIT, perm);
|
||||||
|
if (res2 == nullptr) {
|
||||||
|
LOG_ERROR(HW_Memory, "Failed to commit large buffer region at {:#x}, error {}", addr, GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MAP_NOCORE
|
||||||
|
#define MAP_NOCORE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||||
|
if (auto page = HostPageSize; size % page != 0) {
|
||||||
|
LOG_WARNING(HW_Memory, "Allocating unaligned large vector with size {:#x}; aligning to {} page size", size, page);
|
||||||
|
size = AlignUp(size, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// We will never use this memory entirely so instead of committing it up front let's just reserve it and commit each page individually
|
||||||
|
void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
||||||
|
|
||||||
|
if (base != nullptr) {
|
||||||
|
vector_regions.emplace_back(reinterpret_cast<u64>(base), reinterpret_cast<u64>(base) + size);
|
||||||
|
|
||||||
|
static std::once_flag flag;
|
||||||
|
std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); });
|
||||||
|
} else {
|
||||||
|
// Try committing everything instead??
|
||||||
|
LOG_WARNING(HW_Memory, "Failed to reserve large vector region with error {}, trying to commit instead..", GetLastError());
|
||||||
|
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||||
|
}
|
||||||
|
ASSERT_MSG(base, "Failed to reserve {:#x} sized region with error {}", size, GetLastError());
|
||||||
|
#else
|
||||||
|
void* base = mmap(nullptr, size, PROT_READ, MAP_ANON | MAP_PRIVATE | MAP_NOCORE, -1, 0);
|
||||||
|
if (base == MAP_FAILED)
|
||||||
|
base = nullptr;
|
||||||
|
ASSERT_MSG(base, "Failed to allocate {:#x} sized region with error {}", size, strerror(errno));
|
||||||
|
#endif
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
||||||
|
if (auto page = HostPageSize; size % page != 0) {
|
||||||
|
size = AlignUp(size, page);
|
||||||
|
}
|
||||||
|
if (!base)
|
||||||
|
return;
|
||||||
|
#ifdef _WIN32
|
||||||
|
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
||||||
|
#else
|
||||||
|
ASSERT(munmap(base, size) == 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Common
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
/* virtual_buffer.h */
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <bit>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "common/alignment.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
constexpr u64 HostPageSize = 0x1000;
|
||||||
|
constexpr u64 HostPageBits = 12;
|
||||||
|
constexpr u64 HostPageMask = ~(HostPageSize - 1);
|
||||||
|
bool CommitVectorPage(uintptr_t addr, bool write) noexcept;
|
||||||
|
#else
|
||||||
|
const u64 HostPageSize = sysconf(_SC_PAGESIZE);
|
||||||
|
const u64 HostPageBits = std::countr_zero(HostPageSize);
|
||||||
|
const u64 HostPageMask = ~(HostPageSize - 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void* AllocateMemoryPages(std::size_t size) noexcept;
|
||||||
|
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
||||||
|
|
||||||
|
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
|
||||||
|
template <typename T>
|
||||||
|
requires std::is_trivially_copyable_v<T>
|
||||||
|
class SparseLargeVector final {
|
||||||
|
public:
|
||||||
|
constexpr SparseLargeVector() = default;
|
||||||
|
|
||||||
|
explicit SparseLargeVector(std::size_t count) noexcept
|
||||||
|
: alloc_size{count * sizeof(T)}
|
||||||
|
{
|
||||||
|
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||||
|
|
||||||
|
// each item in vector holds information for 64 pages
|
||||||
|
auto denom = HostPageSize * 64;
|
||||||
|
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
|
||||||
|
}
|
||||||
|
|
||||||
|
~SparseLargeVector() noexcept {
|
||||||
|
FreeMemoryPages(base_ptr, alloc_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
SparseLargeVector(const SparseLargeVector&) = delete;
|
||||||
|
SparseLargeVector& operator=(const SparseLargeVector&) = delete;
|
||||||
|
SparseLargeVector(SparseLargeVector&& other) = delete;
|
||||||
|
SparseLargeVector& operator=(SparseLargeVector&& other) = delete;
|
||||||
|
|
||||||
|
void ResizeAndClear(std::size_t count) noexcept {
|
||||||
|
if (auto const new_size = count * sizeof(T); new_size != alloc_size) {
|
||||||
|
FreeMemoryPages(base_ptr, alloc_size);
|
||||||
|
alloc_size = new_size;
|
||||||
|
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||||
|
|
||||||
|
auto denom = HostPageSize * 64;
|
||||||
|
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the value of the requested index and allocates memory if needed.
|
||||||
|
T& GetAndFault(std::size_t index) noexcept {
|
||||||
|
if (index > alloc_size / sizeof(T)) {
|
||||||
|
UNREACHABLE_MSG("Out of bounds RW access on SparseLargeVector @ {}", index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsCommittedPage(index)) {
|
||||||
|
CommitPage(index);
|
||||||
|
}
|
||||||
|
return base_ptr[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the value of the requested index if initialized, or will otherwise return a zero-initialized object.
|
||||||
|
const T& GetOrDefault(std::size_t index) const {
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (!IsCommittedPage(index)) {
|
||||||
|
return *reinterpret_cast<const T*>(&default_val);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// On non-Windows, OS page table should optimize this by pointing to a zero page if unallocated.
|
||||||
|
return base_ptr[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Set(std::size_t index, const T& value) noexcept {
|
||||||
|
if (index > alloc_size / sizeof(T)) {
|
||||||
|
LOG_CRITICAL(Common_Memory, "Out of bounds write on SparseLargeVector @ {}", index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!IsCommittedPage(index))
|
||||||
|
CommitPage(index);
|
||||||
|
base_ptr[index] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZeroRegion(std::size_t start, std::size_t end_) noexcept {
|
||||||
|
u64 base = reinterpret_cast<u64>(&base_ptr[start]);
|
||||||
|
const u64 end = reinterpret_cast<u64>(&base_ptr[end_]);
|
||||||
|
|
||||||
|
const u64 end_page = AlignUp(base, HostPageSize);
|
||||||
|
const u64 first_size = (std::min)(end_page, end) - base;
|
||||||
|
|
||||||
|
if (IsCommittedPage(start / sizeof(T))) {
|
||||||
|
std::memset(reinterpret_cast<void*>(base), 0, first_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end <= end_page)
|
||||||
|
return;
|
||||||
|
|
||||||
|
base = end_page;
|
||||||
|
|
||||||
|
for (u64 page = base; page < end; page += HostPageSize) {
|
||||||
|
if (!IsCommittedPage((page - reinterpret_cast<u64>(base_ptr)) / sizeof(T))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memset(reinterpret_cast<void*>(page), 0, (std::min)( HostPageSize, end - page));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void CommitRegion(size_t index, size_t end_) {
|
||||||
|
const u64 base = static_cast<u64>(index) * sizeof(T);
|
||||||
|
const u64 end = static_cast<u64>(end_) * sizeof(T);
|
||||||
|
|
||||||
|
for (u64 page = AlignDown(base, HostPageSize); page < end; page += HostPageSize) {
|
||||||
|
if (!IsCommittedPage(page / sizeof(T))) {
|
||||||
|
CommitPage(page / sizeof(T));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr T& GetUnchecked(size_t index) {
|
||||||
|
return base_ptr[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept {
|
||||||
|
return GetOrDefault(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr const T* data() const noexcept {
|
||||||
|
return base_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr std::size_t size() const noexcept {
|
||||||
|
return alloc_size / sizeof(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
[[nodiscard]] constexpr bool IsCommittedPage(std::size_t index) const noexcept {
|
||||||
|
if (index > alloc_size / sizeof(T)) {
|
||||||
|
LOG_CRITICAL(Common_Memory, "Out of bounds access on large vector @ {}", index);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto page = (index * sizeof(T)) >> HostPageBits;
|
||||||
|
auto val = committed_pages[page >> 6].load(std::memory_order_acquire);
|
||||||
|
return (val >> (page & 63)) & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void CommitPage(std::size_t index) noexcept {
|
||||||
|
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
||||||
|
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
||||||
|
#if defined(_WIN32)
|
||||||
|
CommitVectorPage(page, true);
|
||||||
|
#else
|
||||||
|
mprotect(reinterpret_cast<void*>(page), HostPageSize, PROT_READ | PROT_WRITE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t alloc_size{};
|
||||||
|
T* base_ptr{};
|
||||||
|
|
||||||
|
std::vector<std::atomic<u64>> committed_pages{};
|
||||||
|
#ifdef _WIN32
|
||||||
|
const std::array<u8, sizeof(T)> default_val{};
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Common
|
||||||
+89
-713
@@ -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: 2014 Tony Wasserka
|
// SPDX-FileCopyrightText: 2014 Tony Wasserka
|
||||||
@@ -7,752 +7,128 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef __ARM_NEON
|
|
||||||
#include <arm_neon.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T, size_t N>
|
||||||
class Vec2;
|
class Vec {
|
||||||
template <typename T>
|
|
||||||
class Vec3;
|
|
||||||
template <typename T>
|
|
||||||
class Vec4;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class Vec2 {
|
|
||||||
public:
|
public:
|
||||||
T x{};
|
std::array<T, N> elems{};
|
||||||
T y{};
|
|
||||||
|
|
||||||
constexpr Vec2() = default;
|
constexpr Vec() = default;
|
||||||
constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {}
|
constexpr Vec(T e0) noexcept : elems{e0} {}
|
||||||
|
constexpr Vec(T e0, T e1) noexcept : elems{e0, e1} {}
|
||||||
|
constexpr Vec(T e0, T e1, T e2) noexcept : elems{e0, e1, e2} {}
|
||||||
|
constexpr Vec(T e0, T e1, T e2, T e4) noexcept : elems{e0, e1, e2, e4} {}
|
||||||
|
//explicit constexpr Vec(const std::initializer_list<T> elems_) noexcept : elems{elems_} {}
|
||||||
|
|
||||||
template <typename T2>
|
[[nodiscard]] constexpr Vec<decltype(T{} + T{}), N> operator+(const Vec o) const noexcept {
|
||||||
[[nodiscard]] constexpr Vec2<T2> Cast() const {
|
Vec<decltype(T{} + T{}), N> r{};
|
||||||
return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y));
|
for (size_t i = 0; i < N; ++i)
|
||||||
|
r.elems[i] = elems[i] + o.elems[i];
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
constexpr Vec<T, N> operator+=(const Vec<T, N> o) noexcept { return *this = *this + o; }
|
||||||
|
|
||||||
[[nodiscard]] static constexpr Vec2 AssignToAll(const T& f) {
|
[[nodiscard]] constexpr Vec<decltype(T{} - T{}), N> operator-(const Vec o) const noexcept {
|
||||||
return Vec2{f, f};
|
Vec<decltype(T{} - T{}), N> r{};
|
||||||
}
|
for (size_t i = 0; i < N; ++i)
|
||||||
|
r.elems[i] = elems[i] - o.elems[i];
|
||||||
[[nodiscard]] constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
|
return r;
|
||||||
return {x + other.x, y + other.y};
|
|
||||||
}
|
|
||||||
constexpr Vec2& operator+=(const Vec2& other) {
|
|
||||||
x += other.x;
|
|
||||||
y += other.y;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
|
|
||||||
return {x - other.x, y - other.y};
|
|
||||||
}
|
|
||||||
constexpr Vec2& operator-=(const Vec2& other) {
|
|
||||||
x -= other.x;
|
|
||||||
y -= other.y;
|
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
constexpr Vec<T, N> operator-=(const Vec<T, N> o) noexcept { return *this = *this - o; }
|
||||||
|
|
||||||
template <typename U = T>
|
template <typename U = T>
|
||||||
[[nodiscard]] constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
|
[[nodiscard]] constexpr Vec<std::enable_if_t<std::is_signed_v<U>, U>, N> operator-() const noexcept {
|
||||||
return {-x, -y};
|
Vec<U, N> r{};
|
||||||
}
|
for (size_t i = 0; i < N; ++i)
|
||||||
[[nodiscard]] constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
|
r.elems[i] = -elems[i];
|
||||||
return {x * other.x, y * other.y};
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr Vec<decltype(T{} * T{}), N> operator*(const Vec o) const noexcept {
|
||||||
|
Vec<decltype(T{} * T{}), N> r{};
|
||||||
|
for (size_t i = 0; i < N; ++i)
|
||||||
|
r.elems[i] = elems[i] * o.elems[i];
|
||||||
|
return r;
|
||||||
|
}
|
||||||
template <typename V>
|
template <typename V>
|
||||||
[[nodiscard]] constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const {
|
[[nodiscard]] constexpr Vec<decltype(T{} * V{}), N> operator*(const V f) const noexcept {
|
||||||
using TV = decltype(T{} * V{});
|
using TV = decltype(T{} * V{});
|
||||||
using C = std::common_type_t<T, V>;
|
using C = std::common_type_t<T, V>;
|
||||||
|
Vec<TV, N> r{};
|
||||||
return {
|
for (size_t i = 0; i < N; ++i)
|
||||||
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
|
r.elems[i] = TV(C(elems[i]) * C(f));
|
||||||
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
|
return r;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
template <typename V>
|
||||||
|
constexpr Vec<T, N> operator*=(const V f) noexcept { return *this = *this * f; }
|
||||||
|
|
||||||
template <typename V>
|
template <typename V>
|
||||||
constexpr Vec2& operator*=(const V& f) {
|
[[nodiscard]] constexpr Vec<decltype(T{} / V{}), N> operator/(const V f) const noexcept {
|
||||||
*this = *this * f;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename V>
|
|
||||||
[[nodiscard]] constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
|
|
||||||
using TV = decltype(T{} / V{});
|
using TV = decltype(T{} / V{});
|
||||||
using C = std::common_type_t<T, V>;
|
using C = std::common_type_t<T, V>;
|
||||||
|
Vec<TV, N> r{};
|
||||||
return {
|
for (size_t i = 0; i < N; ++i)
|
||||||
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
|
r.elems[i] = TV(C(elems[i]) / C(f));
|
||||||
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
|
return r;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename V>
|
template <typename V>
|
||||||
constexpr Vec2& operator/=(const V& f) {
|
constexpr Vec<T, N> operator/=(const V f) noexcept { return *this = *this / f; }
|
||||||
*this = *this / f;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr T Length2() const {
|
[[nodiscard]] constexpr T Length2() const noexcept {
|
||||||
return x * x + y * y;
|
T r{};
|
||||||
|
for (size_t i = 0; i < N; ++i)
|
||||||
|
r += elems[i] * elems[i];
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only implemented for T=float
|
// Only implemented for T=float
|
||||||
[[nodiscard]] float Length() const;
|
[[nodiscard]] T Length() const { return T(std::sqrt(float(Length2()))); }
|
||||||
[[nodiscard]] float Normalize(); // returns the previous length, which is often useful
|
[[nodiscard]] Vec<T, N> Normalized() const { return *this / Length(); }
|
||||||
|
[[nodiscard]] constexpr T& operator[](std::size_t i) noexcept { return elems[i]; }
|
||||||
|
[[nodiscard]] constexpr const T& operator[](std::size_t i) const noexcept { return elems[i]; }
|
||||||
|
|
||||||
[[nodiscard]] constexpr T& operator[](std::size_t i) {
|
[[nodiscard]] std::array<decltype(-T{}), 16> ToMatrix() const {
|
||||||
return *((&x) + i);
|
const T x2 = elems[0] * elems[0];
|
||||||
}
|
const T y2 = elems[1] * elems[1];
|
||||||
[[nodiscard]] constexpr const T& operator[](std::size_t i) const {
|
const T z2 = elems[2] * elems[2];
|
||||||
return *((&x) + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr void SetZero() {
|
const T xy = elems[0] * elems[1];
|
||||||
x = 0;
|
const T wz = elems[3] * elems[2];
|
||||||
y = 0;
|
const T xz = elems[0] * elems[2];
|
||||||
}
|
const T wy = elems[3] * elems[1];
|
||||||
|
const T yz = elems[1] * elems[2];
|
||||||
// Common aliases: UV (texel coordinates), ST (texture coordinates)
|
const T wx = elems[3] * elems[0];
|
||||||
[[nodiscard]] constexpr T& u() {
|
return {
|
||||||
return x;
|
1.0f - 2.0f * (y2 + z2),
|
||||||
}
|
2.0f * (xy + wz),
|
||||||
[[nodiscard]] constexpr T& v() {
|
2.0f * (xz - wy),
|
||||||
return y;
|
0.0f,
|
||||||
}
|
2.0f * (xy - wz),
|
||||||
[[nodiscard]] constexpr T& s() {
|
1.0f - 2.0f * (x2 + z2),
|
||||||
return x;
|
2.0f * (yz + wx),
|
||||||
}
|
0.0f,
|
||||||
[[nodiscard]] constexpr T& t() {
|
2.0f * (xz + wy),
|
||||||
return y;
|
2.0f * (yz - wx),
|
||||||
}
|
1.0f - 2.0f * (x2 + y2),
|
||||||
|
0.0f,
|
||||||
[[nodiscard]] constexpr const T& u() const {
|
0.0f,
|
||||||
return x;
|
0.0f,
|
||||||
}
|
0.0f,
|
||||||
[[nodiscard]] constexpr const T& v() const {
|
1.0f
|
||||||
return y;
|
};
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr const T& s() const {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr const T& t() const {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
|
|
||||||
// swizzlers - create a subvector of specific components
|
|
||||||
[[nodiscard]] constexpr Vec2 yx() const {
|
|
||||||
return Vec2(y, x);
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr Vec2 vu() const {
|
|
||||||
return Vec2(y, x);
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr Vec2 ts() const {
|
|
||||||
return Vec2(y, x);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename V>
|
template <typename T, size_t N, typename V>
|
||||||
[[nodiscard]] constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
|
[[nodiscard]] constexpr Vec<T, N> operator*(const V f, const Vec<T, N> v) noexcept {
|
||||||
using C = std::common_type_t<T, V>;
|
using C = std::common_type_t<T, V>;
|
||||||
|
Vec<T, N> r{};
|
||||||
return Vec2<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
|
for (size_t i = 0; i < N; ++i)
|
||||||
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)));
|
r.elems[i] = T(C(f) * C(v.elems[i]));
|
||||||
}
|
return r;
|
||||||
|
|
||||||
using Vec2f = Vec2<float>;
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline float Vec2<float>::Length() const {
|
|
||||||
return std::sqrt(x * x + y * y);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline float Vec2<float>::Normalize() {
|
|
||||||
float length = Length();
|
|
||||||
*this /= length;
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class Vec3 {
|
|
||||||
public:
|
|
||||||
T x{};
|
|
||||||
T y{};
|
|
||||||
T z{};
|
|
||||||
|
|
||||||
constexpr Vec3() = default;
|
|
||||||
constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {}
|
|
||||||
|
|
||||||
template <typename T2>
|
|
||||||
[[nodiscard]] constexpr Vec3<T2> Cast() const {
|
|
||||||
return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z));
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static constexpr Vec3 AssignToAll(const T& f) {
|
|
||||||
return Vec3(f, f, f);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
|
|
||||||
return {x + other.x, y + other.y, z + other.z};
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr Vec3& operator+=(const Vec3& other) {
|
|
||||||
x += other.x;
|
|
||||||
y += other.y;
|
|
||||||
z += other.z;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
|
|
||||||
return {x - other.x, y - other.y, z - other.z};
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr Vec3& operator-=(const Vec3& other) {
|
|
||||||
x -= other.x;
|
|
||||||
y -= other.y;
|
|
||||||
z -= other.z;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename U = T>
|
|
||||||
[[nodiscard]] constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
|
|
||||||
return {-x, -y, -z};
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
|
|
||||||
return {x * other.x, y * other.y, z * other.z};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename V>
|
|
||||||
[[nodiscard]] constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
|
|
||||||
using TV = decltype(T{} * V{});
|
|
||||||
using C = std::common_type_t<T, V>;
|
|
||||||
|
|
||||||
return {
|
|
||||||
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
|
|
||||||
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
|
|
||||||
static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename V>
|
|
||||||
constexpr Vec3& operator*=(const V& f) {
|
|
||||||
*this = *this * f;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
template <typename V>
|
|
||||||
[[nodiscard]] constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
|
|
||||||
using TV = decltype(T{} / V{});
|
|
||||||
using C = std::common_type_t<T, V>;
|
|
||||||
|
|
||||||
return {
|
|
||||||
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
|
|
||||||
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
|
|
||||||
static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename V>
|
|
||||||
constexpr Vec3& operator/=(const V& f) {
|
|
||||||
*this = *this / f;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RotateFromOrigin(float roll, float pitch, float yaw) {
|
|
||||||
float temp = y;
|
|
||||||
y = std::cos(roll) * y - std::sin(roll) * z;
|
|
||||||
z = std::sin(roll) * temp + std::cos(roll) * z;
|
|
||||||
|
|
||||||
temp = x;
|
|
||||||
x = std::cos(pitch) * x + std::sin(pitch) * z;
|
|
||||||
z = -std::sin(pitch) * temp + std::cos(pitch) * z;
|
|
||||||
|
|
||||||
temp = x;
|
|
||||||
x = std::cos(yaw) * x - std::sin(yaw) * y;
|
|
||||||
y = std::sin(yaw) * temp + std::cos(yaw) * y;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr T Length2() const {
|
|
||||||
return x * x + y * y + z * z;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only implemented for T=float
|
|
||||||
[[nodiscard]] float Length() const;
|
|
||||||
[[nodiscard]] Vec3 Normalized() const;
|
|
||||||
[[nodiscard]] float Normalize(); // returns the previous length, which is often useful
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr T& operator[](std::size_t i) {
|
|
||||||
return *((&x) + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr const T& operator[](std::size_t i) const {
|
|
||||||
return *((&x) + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr void SetZero() {
|
|
||||||
x = 0;
|
|
||||||
y = 0;
|
|
||||||
z = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates)
|
|
||||||
[[nodiscard]] constexpr T& u() {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr T& v() {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr T& w() {
|
|
||||||
return z;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr T& r() {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr T& g() {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr T& b() {
|
|
||||||
return z;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr T& s() {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr T& t() {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr T& q() {
|
|
||||||
return z;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr const T& u() const {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr const T& v() const {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr const T& w() const {
|
|
||||||
return z;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr const T& r() const {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr const T& g() const {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr const T& b() const {
|
|
||||||
return z;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr const T& s() const {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr const T& t() const {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr const T& q() const {
|
|
||||||
return z;
|
|
||||||
}
|
|
||||||
|
|
||||||
// swizzlers - create a subvector of specific components
|
|
||||||
// e.g. Vec2 uv() { return Vec2(x,y); }
|
|
||||||
// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
|
|
||||||
// component names (x<->r) and permutations (xy<->yx)
|
|
||||||
#define _DEFINE_SWIZZLER2(a, b, name) \
|
|
||||||
[[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
|
|
||||||
#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
|
|
||||||
_DEFINE_SWIZZLER2(a, b, a##b); \
|
|
||||||
_DEFINE_SWIZZLER2(a, b, a2##b2); \
|
|
||||||
_DEFINE_SWIZZLER2(a, b, a3##b3); \
|
|
||||||
_DEFINE_SWIZZLER2(a, b, a4##b4); \
|
|
||||||
_DEFINE_SWIZZLER2(b, a, b##a); \
|
|
||||||
_DEFINE_SWIZZLER2(b, a, b2##a2); \
|
|
||||||
_DEFINE_SWIZZLER2(b, a, b3##a3); \
|
|
||||||
_DEFINE_SWIZZLER2(b, a, b4##a4)
|
|
||||||
|
|
||||||
DEFINE_SWIZZLER2(x, y, r, g, u, v, s, t);
|
|
||||||
DEFINE_SWIZZLER2(x, z, r, b, u, w, s, q);
|
|
||||||
DEFINE_SWIZZLER2(y, z, g, b, v, w, t, q);
|
|
||||||
#undef DEFINE_SWIZZLER2
|
|
||||||
#undef _DEFINE_SWIZZLER2
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, typename V>
|
|
||||||
[[nodiscard]] constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
|
|
||||||
using C = std::common_type_t<T, V>;
|
|
||||||
|
|
||||||
return Vec3<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
|
|
||||||
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)),
|
|
||||||
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.z)));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline float Vec3<float>::Length() const {
|
|
||||||
return std::sqrt(x * x + y * y + z * z);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline Vec3<float> Vec3<float>::Normalized() const {
|
|
||||||
return *this / Length();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline float Vec3<float>::Normalize() {
|
|
||||||
float length = Length();
|
|
||||||
*this /= length;
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
using Vec3f = Vec3<float>;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class Vec4 {
|
|
||||||
public:
|
|
||||||
T x{};
|
|
||||||
T y{};
|
|
||||||
T z{};
|
|
||||||
T w{};
|
|
||||||
|
|
||||||
constexpr Vec4() = default;
|
|
||||||
constexpr Vec4(const T& x_, const T& y_, const T& z_, const T& w_)
|
|
||||||
: x(x_), y(y_), z(z_), w(w_) {}
|
|
||||||
|
|
||||||
template <typename T2>
|
|
||||||
[[nodiscard]] constexpr Vec4<T2> Cast() const {
|
|
||||||
return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z),
|
|
||||||
static_cast<T2>(w));
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static constexpr Vec4 AssignToAll(const T& f) {
|
|
||||||
return Vec4(f, f, f, f);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
|
|
||||||
return {x + other.x, y + other.y, z + other.z, w + other.w};
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr Vec4& operator+=(const Vec4& other) {
|
|
||||||
x += other.x;
|
|
||||||
y += other.y;
|
|
||||||
z += other.z;
|
|
||||||
w += other.w;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
|
|
||||||
return {x - other.x, y - other.y, z - other.z, w - other.w};
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr Vec4& operator-=(const Vec4& other) {
|
|
||||||
x -= other.x;
|
|
||||||
y -= other.y;
|
|
||||||
z -= other.z;
|
|
||||||
w -= other.w;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename U = T>
|
|
||||||
[[nodiscard]] constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
|
|
||||||
return {-x, -y, -z, -w};
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
|
|
||||||
return {x * other.x, y * other.y, z * other.z, w * other.w};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename V>
|
|
||||||
[[nodiscard]] constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
|
|
||||||
using TV = decltype(T{} * V{});
|
|
||||||
using C = std::common_type_t<T, V>;
|
|
||||||
|
|
||||||
return {
|
|
||||||
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
|
|
||||||
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
|
|
||||||
static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
|
|
||||||
static_cast<TV>(static_cast<C>(w) * static_cast<C>(f)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename V>
|
|
||||||
constexpr Vec4& operator*=(const V& f) {
|
|
||||||
*this = *this * f;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename V>
|
|
||||||
[[nodiscard]] constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
|
|
||||||
using TV = decltype(T{} / V{});
|
|
||||||
using C = std::common_type_t<T, V>;
|
|
||||||
|
|
||||||
return {
|
|
||||||
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
|
|
||||||
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
|
|
||||||
static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
|
|
||||||
static_cast<TV>(static_cast<C>(w) / static_cast<C>(f)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename V>
|
|
||||||
constexpr Vec4& operator/=(const V& f) {
|
|
||||||
*this = *this / f;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr T Length2() const {
|
|
||||||
return x * x + y * y + z * z + w * w;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr T& operator[](std::size_t i) {
|
|
||||||
return *((&x) + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr const T& operator[](std::size_t i) const {
|
|
||||||
return *((&x) + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr void SetZero() {
|
|
||||||
x = 0;
|
|
||||||
y = 0;
|
|
||||||
z = 0;
|
|
||||||
w = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Common alias: RGBA (colors)
|
|
||||||
[[nodiscard]] constexpr T& r() {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr T& g() {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr T& b() {
|
|
||||||
return z;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr T& a() {
|
|
||||||
return w;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr const T& r() const {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr const T& g() const {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr const T& b() const {
|
|
||||||
return z;
|
|
||||||
}
|
|
||||||
[[nodiscard]] constexpr const T& a() const {
|
|
||||||
return w;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swizzlers - Create a subvector of specific components
|
|
||||||
// e.g. Vec2 uv() { return Vec2(x,y); }
|
|
||||||
|
|
||||||
// _DEFINE_SWIZZLER2 defines a single such function
|
|
||||||
// DEFINE_SWIZZLER2_COMP1 defines one-component functions for all component names (x<->r)
|
|
||||||
// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
|
|
||||||
// permutations (xy<->yx)
|
|
||||||
#define _DEFINE_SWIZZLER2(a, b, name) \
|
|
||||||
[[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
|
|
||||||
#define DEFINE_SWIZZLER2_COMP1(a, a2) \
|
|
||||||
_DEFINE_SWIZZLER2(a, a, a##a); \
|
|
||||||
_DEFINE_SWIZZLER2(a, a, a2##a2)
|
|
||||||
#define DEFINE_SWIZZLER2_COMP2(a, b, a2, b2) \
|
|
||||||
_DEFINE_SWIZZLER2(a, b, a##b); \
|
|
||||||
_DEFINE_SWIZZLER2(a, b, a2##b2); \
|
|
||||||
_DEFINE_SWIZZLER2(b, a, b##a); \
|
|
||||||
_DEFINE_SWIZZLER2(b, a, b2##a2)
|
|
||||||
|
|
||||||
DEFINE_SWIZZLER2_COMP2(x, y, r, g);
|
|
||||||
DEFINE_SWIZZLER2_COMP2(x, z, r, b);
|
|
||||||
DEFINE_SWIZZLER2_COMP2(x, w, r, a);
|
|
||||||
DEFINE_SWIZZLER2_COMP2(y, z, g, b);
|
|
||||||
DEFINE_SWIZZLER2_COMP2(y, w, g, a);
|
|
||||||
DEFINE_SWIZZLER2_COMP2(z, w, b, a);
|
|
||||||
DEFINE_SWIZZLER2_COMP1(x, r);
|
|
||||||
DEFINE_SWIZZLER2_COMP1(y, g);
|
|
||||||
DEFINE_SWIZZLER2_COMP1(z, b);
|
|
||||||
DEFINE_SWIZZLER2_COMP1(w, a);
|
|
||||||
#undef DEFINE_SWIZZLER2_COMP1
|
|
||||||
#undef DEFINE_SWIZZLER2_COMP2
|
|
||||||
#undef _DEFINE_SWIZZLER2
|
|
||||||
|
|
||||||
#define _DEFINE_SWIZZLER3(a, b, c, name) \
|
|
||||||
[[nodiscard]] constexpr Vec3<T> name() const { return Vec3<T>(a, b, c); }
|
|
||||||
#define DEFINE_SWIZZLER3_COMP1(a, a2) \
|
|
||||||
_DEFINE_SWIZZLER3(a, a, a, a##a##a); \
|
|
||||||
_DEFINE_SWIZZLER3(a, a, a, a2##a2##a2)
|
|
||||||
#define DEFINE_SWIZZLER3_COMP3(a, b, c, a2, b2, c2) \
|
|
||||||
_DEFINE_SWIZZLER3(a, b, c, a##b##c); \
|
|
||||||
_DEFINE_SWIZZLER3(a, c, b, a##c##b); \
|
|
||||||
_DEFINE_SWIZZLER3(b, a, c, b##a##c); \
|
|
||||||
_DEFINE_SWIZZLER3(b, c, a, b##c##a); \
|
|
||||||
_DEFINE_SWIZZLER3(c, a, b, c##a##b); \
|
|
||||||
_DEFINE_SWIZZLER3(c, b, a, c##b##a); \
|
|
||||||
_DEFINE_SWIZZLER3(a, b, c, a2##b2##c2); \
|
|
||||||
_DEFINE_SWIZZLER3(a, c, b, a2##c2##b2); \
|
|
||||||
_DEFINE_SWIZZLER3(b, a, c, b2##a2##c2); \
|
|
||||||
_DEFINE_SWIZZLER3(b, c, a, b2##c2##a2); \
|
|
||||||
_DEFINE_SWIZZLER3(c, a, b, c2##a2##b2); \
|
|
||||||
_DEFINE_SWIZZLER3(c, b, a, c2##b2##a2)
|
|
||||||
|
|
||||||
DEFINE_SWIZZLER3_COMP3(x, y, z, r, g, b);
|
|
||||||
DEFINE_SWIZZLER3_COMP3(x, y, w, r, g, a);
|
|
||||||
DEFINE_SWIZZLER3_COMP3(x, z, w, r, b, a);
|
|
||||||
DEFINE_SWIZZLER3_COMP3(y, z, w, g, b, a);
|
|
||||||
DEFINE_SWIZZLER3_COMP1(x, r);
|
|
||||||
DEFINE_SWIZZLER3_COMP1(y, g);
|
|
||||||
DEFINE_SWIZZLER3_COMP1(z, b);
|
|
||||||
DEFINE_SWIZZLER3_COMP1(w, a);
|
|
||||||
#undef DEFINE_SWIZZLER3_COMP1
|
|
||||||
#undef DEFINE_SWIZZLER3_COMP3
|
|
||||||
#undef _DEFINE_SWIZZLER3
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, typename V>
|
|
||||||
[[nodiscard]] constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
|
|
||||||
using TV = decltype(V{} * T{});
|
|
||||||
using C = std::common_type_t<T, V>;
|
|
||||||
|
|
||||||
return {
|
|
||||||
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.x)),
|
|
||||||
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.y)),
|
|
||||||
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.z)),
|
|
||||||
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.w)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
using Vec4f = Vec4<float>;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b) {
|
|
||||||
return a.x * b.x + a.y * b.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
|
|
||||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
|
|
||||||
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
[[nodiscard]] inline float Dot(const Vec4<float>& a, const Vec4<float>& b) {
|
|
||||||
#ifdef __ARM_NEON
|
|
||||||
float32x4_t va = vld1q_f32(&a.x);
|
|
||||||
float32x4_t vb = vld1q_f32(&b.x);
|
|
||||||
float32x4_t result = vmulq_f32(va, vb);
|
|
||||||
#if defined(__aarch64__) // Use vaddvq_f32 in ARMv8 architectures
|
|
||||||
return vaddvq_f32(result);
|
|
||||||
#else // Use manual addition for older architectures
|
|
||||||
float32x2_t sum2 = vadd_f32(vget_high_f32(result), vget_low_f32(result));
|
|
||||||
return vget_lane_f32(vpadd_f32(sum2, sum2), 0);
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a,
|
|
||||||
const Vec3<T>& b) {
|
|
||||||
return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
|
|
||||||
}
|
|
||||||
|
|
||||||
// linear interpolation via float: 0.0=begin, 1.0=end
|
|
||||||
template <typename X>
|
|
||||||
[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
|
|
||||||
const float t) {
|
|
||||||
return begin * (1.f - t) + end * t;
|
|
||||||
}
|
|
||||||
|
|
||||||
// linear interpolation via int: 0=begin, base=end
|
|
||||||
template <typename X, int base>
|
|
||||||
[[nodiscard]] constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin,
|
|
||||||
const X& end,
|
|
||||||
const int t) {
|
|
||||||
return (begin * (base - t) + end * t) / base;
|
|
||||||
}
|
|
||||||
|
|
||||||
// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second
|
|
||||||
// interpolation.
|
|
||||||
template <typename X>
|
|
||||||
[[nodiscard]] constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11,
|
|
||||||
const float s, const float t) {
|
|
||||||
auto y0 = Lerp(x00, x01, s);
|
|
||||||
auto y1 = Lerp(x10, x11, s);
|
|
||||||
return Lerp(y0, y1, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utility vector factories
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] constexpr Vec2<T> MakeVec(const T& x, const T& y) {
|
|
||||||
return Vec2<T>{x, y};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
|
|
||||||
return Vec3<T>{x, y, z};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
|
|
||||||
return MakeVec(x, y, zw[0], zw[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
|
|
||||||
return MakeVec(xy[0], xy[1], z);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
|
|
||||||
return MakeVec(x, yz[0], yz[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
|
|
||||||
return Vec4<T>{x, y, z, w};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
|
|
||||||
return MakeVec(xy[0], xy[1], z, w);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
|
|
||||||
return MakeVec(x, yz[0], yz[1], w);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: This has priority over "Vec2<Vec2<T>> MakeVec(const Vec2<T>& x, const Vec2<T>& y)".
|
|
||||||
// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error
|
|
||||||
// out soon enough due to misuse of the returned structure.
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
|
|
||||||
return MakeVec(xy[0], xy[1], zw[0], zw[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
|
|
||||||
return MakeVec(xyz[0], xyz[1], xyz[2], w);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
|
|
||||||
return MakeVec(x, yzw[0], yzw[1], yzw[2]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/virtual_buffer.h"
|
|
||||||
|
|
||||||
namespace Common {
|
|
||||||
|
|
||||||
void* AllocateMemoryPages(std::size_t size) noexcept {
|
|
||||||
#ifdef _WIN32
|
|
||||||
void* base = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
||||||
if (base == nullptr) {
|
|
||||||
// Probably failing to reserve is less likely than failing to commit
|
|
||||||
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
void* base = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
|
||||||
if (base == MAP_FAILED)
|
|
||||||
base = nullptr;
|
|
||||||
#endif
|
|
||||||
ASSERT(base);
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
|
||||||
if (!base)
|
|
||||||
return;
|
|
||||||
#ifdef _WIN32
|
|
||||||
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
|
||||||
#else
|
|
||||||
ASSERT(munmap(base, size) == 0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Common
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace Common {
|
|
||||||
|
|
||||||
void* AllocateMemoryPages(std::size_t size) noexcept;
|
|
||||||
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class VirtualBuffer final {
|
|
||||||
public:
|
|
||||||
// TODO: Uncomment this and change Common::PageTable::PageInfo to be trivially constructible
|
|
||||||
// using std::atomic_ref once libc++ has support for it
|
|
||||||
// static_assert(
|
|
||||||
// std::is_trivially_constructible_v<T>,
|
|
||||||
// "T must be trivially constructible, as non-trivial constructors will not be executed "
|
|
||||||
// "with the current allocator");
|
|
||||||
|
|
||||||
constexpr VirtualBuffer() = default;
|
|
||||||
explicit VirtualBuffer(std::size_t count) noexcept
|
|
||||||
: alloc_size{count * sizeof(T)}
|
|
||||||
{
|
|
||||||
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
|
|
||||||
}
|
|
||||||
|
|
||||||
~VirtualBuffer() noexcept {
|
|
||||||
FreeMemoryPages(base_ptr, alloc_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
VirtualBuffer(const VirtualBuffer&) = delete;
|
|
||||||
VirtualBuffer& operator=(const VirtualBuffer&) = delete;
|
|
||||||
|
|
||||||
VirtualBuffer(VirtualBuffer&& other) noexcept
|
|
||||||
: alloc_size{std::exchange(other.alloc_size, 0)}
|
|
||||||
, base_ptr{std::exchange(other.base_ptr, nullptr)}
|
|
||||||
{}
|
|
||||||
|
|
||||||
VirtualBuffer& operator=(VirtualBuffer&& other) noexcept {
|
|
||||||
alloc_size = std::exchange(other.alloc_size, 0);
|
|
||||||
base_ptr = std::exchange(other.base_ptr, nullptr);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void resize(std::size_t count) noexcept {
|
|
||||||
if (auto const new_size = count * sizeof(T); new_size != alloc_size) {
|
|
||||||
FreeMemoryPages(base_ptr, alloc_size);
|
|
||||||
alloc_size = new_size;
|
|
||||||
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept {
|
|
||||||
return base_ptr[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr T& operator[](std::size_t index) noexcept {
|
|
||||||
return base_ptr[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr T* data() noexcept {
|
|
||||||
return base_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr const T* data() const noexcept {
|
|
||||||
return base_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr std::size_t size() const noexcept {
|
|
||||||
return alloc_size / sizeof(T);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::size_t alloc_size{};
|
|
||||||
T* base_ptr{};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Common
|
|
||||||
@@ -711,8 +711,6 @@ add_library(core STATIC
|
|||||||
hle/service/kernel_helpers.h
|
hle/service/kernel_helpers.h
|
||||||
hle/service/lbl/lbl.cpp
|
hle/service/lbl/lbl.cpp
|
||||||
hle/service/lbl/lbl.h
|
hle/service/lbl/lbl.h
|
||||||
hle/service/ldn/client_process_monitor.cpp
|
|
||||||
hle/service/ldn/client_process_monitor.h
|
|
||||||
hle/service/ldn/lan_discovery.cpp
|
hle/service/ldn/lan_discovery.cpp
|
||||||
hle/service/ldn/lan_discovery.h
|
hle/service/ldn/lan_discovery.h
|
||||||
hle/service/ldn/ldn.cpp
|
hle/service/ldn/ldn.cpp
|
||||||
@@ -832,8 +830,6 @@ add_library(core STATIC
|
|||||||
hle/service/ns/system_update_control.h
|
hle/service/ns/system_update_control.h
|
||||||
hle/service/ns/system_update_interface.cpp
|
hle/service/ns/system_update_interface.cpp
|
||||||
hle/service/ns/system_update_interface.h
|
hle/service/ns/system_update_interface.h
|
||||||
hle/service/ns/vulnerability_manager_interface.cpp
|
|
||||||
hle/service/ns/vulnerability_manager_interface.h
|
|
||||||
hle/service/nvdrv/core/container.cpp
|
hle/service/nvdrv/core/container.cpp
|
||||||
hle/service/nvdrv/core/container.h
|
hle/service/nvdrv/core/container.h
|
||||||
hle/service/nvdrv/core/heap_mapper.cpp
|
hle/service/nvdrv/core/heap_mapper.cpp
|
||||||
@@ -1067,8 +1063,6 @@ add_library(core STATIC
|
|||||||
hle/service/sockets/sockets.h
|
hle/service/sockets/sockets.h
|
||||||
hle/service/sockets/sockets_translate.cpp
|
hle/service/sockets/sockets_translate.cpp
|
||||||
hle/service/sockets/sockets_translate.h
|
hle/service/sockets/sockets_translate.h
|
||||||
hle/service/spl/csrng.cpp
|
|
||||||
hle/service/spl/csrng.h
|
|
||||||
hle/service/spl/spl.cpp
|
hle/service/spl/spl.cpp
|
||||||
hle/service/spl/spl.h
|
hle/service/spl/spl.h
|
||||||
hle/service/spl/spl_module.cpp
|
hle/service/spl/spl_module.cpp
|
||||||
@@ -1083,8 +1077,6 @@ add_library(core STATIC
|
|||||||
hle/service/ssl/ssl_types.h
|
hle/service/ssl/ssl_types.h
|
||||||
hle/service/tma/tma.cpp
|
hle/service/tma/tma.cpp
|
||||||
hle/service/tma/tma.h
|
hle/service/tma/tma.h
|
||||||
hle/service/ulsf/ulsf.cpp
|
|
||||||
hle/service/ulsf/ulsf.h
|
|
||||||
hle/service/usb/usb.cpp
|
hle/service/usb/usb.cpp
|
||||||
hle/service/usb/usb.h
|
hle/service/usb/usb.h
|
||||||
hle/service/vi/application_display_service.cpp
|
hle/service/vi/application_display_service.cpp
|
||||||
|
|||||||
@@ -172,12 +172,12 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
|||||||
if (page_table) {
|
if (page_table) {
|
||||||
constexpr size_t PageBits = 12;
|
constexpr size_t PageBits = 12;
|
||||||
constexpr size_t NumPageTableEntries = 1 << (32 - PageBits);
|
constexpr size_t NumPageTableEntries = 1 << (32 - PageBits);
|
||||||
constexpr size_t PageLog2Stride = 5;
|
|
||||||
static_assert(1 << PageLog2Stride == sizeof(Common::PageTable::PageEntryData));
|
|
||||||
|
|
||||||
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(page_table->entries.data());
|
// Dynarmic will not write to the page table, const_cast is safe here
|
||||||
config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
|
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(
|
||||||
config.page_table_log2_stride = PageLog2Stride;
|
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
||||||
|
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
||||||
|
config.page_table_marked_bit = 0;
|
||||||
config.absolute_offset_page_table = true;
|
config.absolute_offset_page_table = true;
|
||||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||||
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
||||||
@@ -188,6 +188,13 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
|||||||
|
|
||||||
config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
|
config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
|
||||||
config.recompile_on_exclusive_fastmem_failure = true;
|
config.recompile_on_exclusive_fastmem_failure = true;
|
||||||
|
|
||||||
|
if (reinterpret_cast<u64>(m_system.DeviceMemory().buffer.BackingBasePointer() +
|
||||||
|
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
|
||||||
|
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
|
||||||
|
// we have to manually sign extend when our actual pointer is negative.
|
||||||
|
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multi-process state
|
// Multi-process state
|
||||||
@@ -420,6 +427,7 @@ void ArmDynarmic32::SignalInterrupt(Kernel::KThread* thread) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ArmDynarmic32::ClearInstructionCache() {
|
void ArmDynarmic32::ClearInstructionCache() {
|
||||||
|
m_cb->last_code_addr = u64(-1);
|
||||||
m_jit->ClearCache();
|
m_jit->ClearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -211,13 +211,12 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
|||||||
|
|
||||||
// Memory
|
// Memory
|
||||||
if (page_table) {
|
if (page_table) {
|
||||||
constexpr size_t PageLog2Stride = 5;
|
// Dynarmic will not write to the page table, const_cast is safe here
|
||||||
static_assert(1 << PageLog2Stride == sizeof(Common::PageTable::PageEntryData));
|
config.page_table = reinterpret_cast<void**>(
|
||||||
|
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
||||||
config.page_table = reinterpret_cast<void**>(page_table->entries.data());
|
|
||||||
config.page_table_address_space_bits = std::uint32_t(address_space_bits);
|
config.page_table_address_space_bits = std::uint32_t(address_space_bits);
|
||||||
config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
|
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
||||||
config.page_table_log2_stride = PageLog2Stride;
|
config.page_table_marked_bit = 0;
|
||||||
config.silently_mirror_page_table = false;
|
config.silently_mirror_page_table = false;
|
||||||
config.absolute_offset_page_table = true;
|
config.absolute_offset_page_table = true;
|
||||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||||
@@ -231,6 +230,13 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
|||||||
|
|
||||||
config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
|
config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
|
||||||
config.recompile_on_exclusive_fastmem_failure = true;
|
config.recompile_on_exclusive_fastmem_failure = true;
|
||||||
|
|
||||||
|
if (reinterpret_cast<u64>(m_system.DeviceMemory().buffer.BackingBasePointer() +
|
||||||
|
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
|
||||||
|
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
|
||||||
|
// we have to manually sign extend when our actual pointer is negative.
|
||||||
|
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multi-process state
|
// Multi-process state
|
||||||
@@ -447,6 +453,7 @@ void ArmDynarmic64::SignalInterrupt(Kernel::KThread* thread) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ArmDynarmic64::ClearInstructionCache() {
|
void ArmDynarmic64::ClearInstructionCache() {
|
||||||
|
m_cb->last_code_addr = u64(-1);
|
||||||
m_jit->ClearCache();
|
m_jit->ClearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,10 @@ union Exclusive {
|
|||||||
constexpr explicit Exclusive(u32 raw_) : raw{raw_} {}
|
constexpr explicit Exclusive(u32 raw_) : raw{raw_} {}
|
||||||
|
|
||||||
constexpr bool Verify() {
|
constexpr bool Verify() {
|
||||||
return this->GetSig() == 0x10;
|
if (this->GetSig() != 0x10) return false;
|
||||||
|
const bool pair = decltype(l)::ExtractValue(raw) & 1;
|
||||||
|
const bool fixed_rt2 = decltype(rt2)::ExtractValue(raw) == 0b11111;
|
||||||
|
return pair || fixed_rt2;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr u32 GetSig() {
|
constexpr u32 GetSig() {
|
||||||
|
|||||||
+2
-1
@@ -17,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"
|
||||||
@@ -396,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;
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -26,8 +29,11 @@ public:
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
Common::PhysicalAddress GetPhysicalAddr(const T* ptr) const {
|
Common::PhysicalAddress GetPhysicalAddr(const T* ptr) const {
|
||||||
return (reinterpret_cast<uintptr_t>(ptr) -
|
return GetPhysicalAddr(reinterpret_cast<uintptr_t>(ptr));
|
||||||
reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())) +
|
}
|
||||||
|
|
||||||
|
Common::PhysicalAddress GetPhysicalAddr(uintptr_t ptr) const {
|
||||||
|
return (ptr - reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())) +
|
||||||
DramMemoryMap::Base;
|
DramMemoryMap::Base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/range_mutex.h"
|
#include "common/range_mutex.h"
|
||||||
#include "common/scratch_buffer.h"
|
#include "common/scratch_buffer.h"
|
||||||
#include "common/virtual_buffer.h"
|
#include "common/sparse_large_vector.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
@@ -178,8 +178,8 @@ private:
|
|||||||
u32 continuity_tracker;
|
u32 continuity_tracker;
|
||||||
u32 compressed_physical_ptr;
|
u32 compressed_physical_ptr;
|
||||||
};
|
};
|
||||||
Common::VirtualBuffer<u32> compressed_device_addr;
|
Common::SparseLargeVector<u32> compressed_device_addr;
|
||||||
Common::VirtualBuffer<TrackedEntry> tracked_entries;
|
Common::SparseLargeVector<TrackedEntry> tracked_entries;
|
||||||
|
|
||||||
// Process memory interfaces
|
// Process memory interfaces
|
||||||
|
|
||||||
@@ -200,8 +200,8 @@ private:
|
|||||||
return std::make_pair(asid, address);
|
return std::make_pair(asid, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) {
|
constexpr void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) {
|
||||||
tracked_entries[page_index].cpu_backing_address = address | (asid.id << asid_start_bit);
|
tracked_entries.GetUnchecked(page_index).cpu_backing_address = address | (asid.id << asid_start_bit);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<TranslationEntry, 4> t_slot{};
|
std::array<TranslationEntry, 4> t_slot{};
|
||||||
|
|||||||
@@ -177,17 +177,6 @@ DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memo
|
|||||||
{
|
{
|
||||||
impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>();
|
impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>();
|
||||||
cached_pages = std::make_unique<CachedPages>();
|
cached_pages = std::make_unique<CachedPages>();
|
||||||
|
|
||||||
const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS;
|
|
||||||
for (size_t i = 0; i < total_virtual; i++) {
|
|
||||||
tracked_entries[i].compressed_physical_ptr = 0;
|
|
||||||
tracked_entries[i].continuity_tracker = 1;
|
|
||||||
tracked_entries[i].cpu_backing_address = 0;
|
|
||||||
}
|
|
||||||
const size_t total_phys = 1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS);
|
|
||||||
for (size_t i = 0; i < total_phys; i++) {
|
|
||||||
compressed_device_addr[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
@@ -220,26 +209,28 @@ void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size
|
|||||||
size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
|
size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
|
||||||
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
||||||
std::scoped_lock lk(mapping_guard);
|
std::scoped_lock lk(mapping_guard);
|
||||||
|
|
||||||
|
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages);
|
||||||
for (size_t i = 0; i < num_pages; i++) {
|
for (size_t i = 0; i < num_pages; i++) {
|
||||||
const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE;
|
const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE;
|
||||||
auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress));
|
auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress));
|
||||||
if (ptr == nullptr) [[unlikely]] {
|
if (ptr == nullptr) [[unlikely]] {
|
||||||
tracked_entries[start_page_d + i].compressed_physical_ptr = 0;
|
tracked_entries.GetUnchecked(start_page_d + i).compressed_physical_ptr = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto phys_addr = static_cast<u32>(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U;
|
auto phys_addr = static_cast<u32>(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U;
|
||||||
tracked_entries[start_page_d + i].compressed_physical_ptr = phys_addr;
|
tracked_entries.GetUnchecked(start_page_d + i).compressed_physical_ptr = phys_addr;
|
||||||
InsertCPUBacking(start_page_d + i, new_vaddress, asid);
|
InsertCPUBacking(start_page_d + i, new_vaddress, asid);
|
||||||
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
|
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
|
||||||
const u32 new_dev = static_cast<u32>(start_page_d + i);
|
const u32 new_dev = static_cast<u32>(start_page_d + i);
|
||||||
if (base_dev == 0) [[likely]] {
|
if (base_dev == 0) [[likely]] {
|
||||||
compressed_device_addr[phys_addr - 1U] = new_dev;
|
compressed_device_addr.GetAndFault(phys_addr - 1U) = new_dev;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
u32 start_id = base_dev & MULTI_MASK;
|
u32 start_id = base_dev & MULTI_MASK;
|
||||||
if ((base_dev >> MULTI_FLAG_BITS) == 0) {
|
if ((base_dev >> MULTI_FLAG_BITS) == 0) {
|
||||||
start_id = impl->multi_dev_address.Register(base_dev);
|
start_id = impl->multi_dev_address.Register(base_dev);
|
||||||
compressed_device_addr[phys_addr - 1U] = MULTI_FLAG | start_id;
|
compressed_device_addr.GetAndFault(phys_addr - 1U) = MULTI_FLAG | start_id;
|
||||||
}
|
}
|
||||||
impl->multi_dev_address.Register(new_dev, start_id);
|
impl->multi_dev_address.Register(new_dev, start_id);
|
||||||
}
|
}
|
||||||
@@ -255,24 +246,26 @@ void DeviceMemoryManager<Traits>::Unmap(DAddr address, size_t size) {
|
|||||||
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
||||||
device_inter->InvalidateRegion(address, size);
|
device_inter->InvalidateRegion(address, size);
|
||||||
std::scoped_lock lk(mapping_guard);
|
std::scoped_lock lk(mapping_guard);
|
||||||
|
|
||||||
|
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages); // should already be committed, but just in case
|
||||||
for (size_t i = 0; i < num_pages; i++) {
|
for (size_t i = 0; i < num_pages; i++) {
|
||||||
auto phys_addr = tracked_entries[start_page_d + i].compressed_physical_ptr;
|
auto& entry = tracked_entries.GetUnchecked(start_page_d + i);
|
||||||
tracked_entries[start_page_d + i].compressed_physical_ptr = 0;
|
auto phys_addr = entry.compressed_physical_ptr;
|
||||||
tracked_entries[start_page_d + i].cpu_backing_address = 0;
|
entry.compressed_physical_ptr = 0;
|
||||||
|
entry.cpu_backing_address = 0;
|
||||||
if (phys_addr != 0) [[likely]] {
|
if (phys_addr != 0) [[likely]] {
|
||||||
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
|
u32& base_dev = compressed_device_addr.GetAndFault(phys_addr - 1U);
|
||||||
if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] {
|
if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] {
|
||||||
compressed_device_addr[phys_addr - 1] = 0;
|
base_dev = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto [more_entries, new_start] = impl->multi_dev_address.Unregister(
|
const auto [more_entries, new_start] = impl->multi_dev_address.Unregister(
|
||||||
static_cast<u32>(start_page_d + i), base_dev & MULTI_MASK);
|
static_cast<u32>(start_page_d + i), base_dev & MULTI_MASK);
|
||||||
if (!more_entries) {
|
if (!more_entries) {
|
||||||
compressed_device_addr[phys_addr - 1] =
|
base_dev = impl->multi_dev_address.ReleaseEntry(new_start);
|
||||||
impl->multi_dev_address.ReleaseEntry(new_start);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
compressed_device_addr[phys_addr - 1] = new_start | MULTI_FLAG;
|
base_dev = new_start | MULTI_FLAG;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t_slot = {};
|
t_slot = {};
|
||||||
@@ -285,6 +278,8 @@ void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtu
|
|||||||
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
||||||
uintptr_t last_ptr = 0;
|
uintptr_t last_ptr = 0;
|
||||||
size_t page_count = 1;
|
size_t page_count = 1;
|
||||||
|
|
||||||
|
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages);
|
||||||
for (size_t i = num_pages; i > 0; i--) {
|
for (size_t i = num_pages; i > 0; i--) {
|
||||||
size_t index = i - 1;
|
size_t index = i - 1;
|
||||||
const VAddr new_vaddress = virtual_address + index * Memory::YUZU_PAGESIZE;
|
const VAddr new_vaddress = virtual_address + index * Memory::YUZU_PAGESIZE;
|
||||||
@@ -296,14 +291,14 @@ void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtu
|
|||||||
page_count = 1;
|
page_count = 1;
|
||||||
}
|
}
|
||||||
last_ptr = new_ptr;
|
last_ptr = new_ptr;
|
||||||
tracked_entries[start_page_d + index].continuity_tracker = static_cast<u32>(page_count);
|
tracked_entries.GetUnchecked(start_page_d + index).continuity_tracker = static_cast<u32>(page_count) - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) {
|
u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) {
|
||||||
size_t page_index = src_addr >> page_bits;
|
size_t page_index = src_addr >> page_bits;
|
||||||
size_t subbits = src_addr & page_mask;
|
size_t subbits = src_addr & page_mask;
|
||||||
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker) << page_bits) >= size + subbits) {
|
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker+1) << page_bits) >= size + subbits) {
|
||||||
return GetPointer<u8>(src_addr);
|
return GetPointer<u8>(src_addr);
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -313,7 +308,7 @@ template <typename Traits>
|
|||||||
const u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) const {
|
const u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) const {
|
||||||
size_t page_index = src_addr >> page_bits;
|
size_t page_index = src_addr >> page_bits;
|
||||||
size_t subbits = src_addr & page_mask;
|
size_t subbits = src_addr & page_mask;
|
||||||
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker) << page_bits) >= size + subbits) {
|
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker+1) << page_bits) >= size + subbits) {
|
||||||
return GetPointer<u8>(src_addr);
|
return GetPointer<u8>(src_addr);
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -383,7 +378,7 @@ void DeviceMemoryManager<Traits>::WalkBlock(DAddr addr, std::size_t size, auto o
|
|||||||
std::size_t page_index = addr >> Memory::YUZU_PAGEBITS;
|
std::size_t page_index = addr >> Memory::YUZU_PAGEBITS;
|
||||||
std::size_t page_offset = addr & Memory::YUZU_PAGEMASK;
|
std::size_t page_offset = addr & Memory::YUZU_PAGEMASK;
|
||||||
while (remaining_size) {
|
while (remaining_size) {
|
||||||
const size_t next_pages = std::size_t(tracked_entries[page_index].continuity_tracker);
|
const size_t next_pages = std::size_t(tracked_entries[page_index].continuity_tracker+1);
|
||||||
const std::size_t copy_amount = (std::min)((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size);
|
const std::size_t copy_amount = (std::min)((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size);
|
||||||
const auto current_vaddr = u64((page_index << Memory::YUZU_PAGEBITS) + page_offset);
|
const auto current_vaddr = u64((page_index << Memory::YUZU_PAGEBITS) + page_offset);
|
||||||
SCOPE_EXIT{
|
SCOPE_EXIT{
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -121,6 +124,7 @@ size_t HierarchicalIntegrityVerificationStorage::Read(u8* buffer, size_t size,
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t HierarchicalIntegrityVerificationStorage::GetSize() const {
|
size_t HierarchicalIntegrityVerificationStorage::GetSize() const {
|
||||||
|
ASSERT(m_data_size >= 0);
|
||||||
return m_data_size;
|
return m_data_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ std::array<u8, 32> IPSwitchCompiler::GetBuildID() const {
|
|||||||
|
|
||||||
static IPSwitchRecord EscapeStringSequences(std::string_view sv) {
|
static IPSwitchRecord EscapeStringSequences(std::string_view sv) {
|
||||||
IPSwitchRecord r{};
|
IPSwitchRecord r{};
|
||||||
for (auto it = sv.cbegin(); it != sv.cend(); ) {
|
for (auto it = sv.cbegin(); it < sv.cend(); ) {
|
||||||
if (*it == '\\' && it + 1 < sv.cend()) {
|
if (*it == '\\' && it + 1 < sv.cend()) {
|
||||||
switch (it[1]) {
|
switch (it[1]) {
|
||||||
case 'a': r.data[r.count] = '\a'; break;
|
case 'a': r.data[r.count] = '\a'; break;
|
||||||
@@ -198,6 +198,8 @@ void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
|
|||||||
LOG_WARNING(Loader, "Unknown flag {}", line);
|
LOG_WARNING(Loader, "Unknown flag {}", line);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else if (patches.empty()) {
|
||||||
|
LOG_WARNING(Loader, "Invalid line not in a patch {}", line);
|
||||||
} else {
|
} else {
|
||||||
size_t offset = size_t(std::strtoul(line.data(), nullptr, 16));
|
size_t offset = size_t(std::strtoul(line.data(), nullptr, 16));
|
||||||
offset += size_t(offset_shift);
|
offset += size_t(offset_shift);
|
||||||
@@ -253,25 +255,30 @@ void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
|
|||||||
// now make a nominal preprocessed line: remove comments
|
// now make a nominal preprocessed line: remove comments
|
||||||
char quote = '\0';
|
char quote = '\0';
|
||||||
auto const sline_start = p;
|
auto const sline_start = p;
|
||||||
|
auto last_char = p;
|
||||||
for (; p < sline.cend(); ) {
|
for (; p < sline.cend(); ) {
|
||||||
// we dont check for "//", IPS checks for '/' only...
|
// we dont check for "//", IPS checks for '/' only...
|
||||||
if ((!quote && p[0] == '/')
|
if (std::isspace(*p)) {
|
||||||
|
++p;
|
||||||
|
} else if ((!quote && p[0] == '/')
|
||||||
|| (!quote && p[0] == '#')) {
|
|| (!quote && p[0] == '#')) {
|
||||||
break;
|
break;
|
||||||
} else if (p[0] == '\"' || p[0] == '\'') {
|
} else if (p[0] == '\"' || p[0] == '\'') {
|
||||||
quote = (p[0] == quote) ? '\0' : p[0];
|
quote = (p[0] == quote) ? '\0' : p[0];
|
||||||
++p;
|
++p;
|
||||||
|
last_char = p;
|
||||||
} else if (p + 1 < sline.cend() && p[0] == '\\') {
|
} else if (p + 1 < sline.cend() && p[0] == '\\') {
|
||||||
p += 2;
|
p += 2;
|
||||||
|
last_char = p;
|
||||||
} else {
|
} else {
|
||||||
++p;
|
++p;
|
||||||
|
last_char = p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// now we have the preprocessed string ;)
|
// now we have the preprocessed string ;)
|
||||||
std::string_view pp_str(sline_start, p);
|
std::string_view const pp_str(sline_start, last_char);
|
||||||
if (pp_str.size() > 0 && !parse_line(pp_str)) {
|
if (pp_str.size() > 0 && !parse_line(pp_str))
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -346,7 +378,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const {
|
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const {
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto& disabled = Settings::values.disabled_addons[application_id];
|
||||||
const auto nso_build_id = fmt::format("{:0<64}", build_id);
|
const auto nso_build_id = fmt::format("{:0<64}", build_id);
|
||||||
|
|
||||||
std::vector<VirtualFile> out;
|
std::vector<VirtualFile> out;
|
||||||
@@ -405,15 +437,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;
|
||||||
@@ -448,29 +485,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;
|
||||||
@@ -486,39 +533,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;
|
||||||
@@ -590,8 +650,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;
|
||||||
@@ -698,7 +758,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;
|
||||||
@@ -710,10 +770,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;
|
||||||
|
|
||||||
@@ -862,7 +922,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};
|
||||||
|
|
||||||
@@ -888,8 +948,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();
|
||||||
@@ -956,8 +1015,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");
|
||||||
@@ -992,10 +1050,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1070,16 +1128,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 {};
|
||||||
}
|
}
|
||||||
@@ -1155,8 +1219,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
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -86,8 +86,7 @@ constexpr size_t SlabCountKDeviceAddressSpace = 300;
|
|||||||
constexpr size_t SlabCountKSession = 1133;
|
constexpr size_t SlabCountKSession = 1133;
|
||||||
constexpr size_t SlabCountKLightSession = 100;
|
constexpr size_t SlabCountKLightSession = 100;
|
||||||
constexpr size_t SlabCountKObjectName = 7;
|
constexpr size_t SlabCountKObjectName = 7;
|
||||||
// TODO(lizzie): divergence that allows ulauncher to work
|
constexpr size_t SlabCountKResourceLimit = 5;
|
||||||
constexpr size_t SlabCountKResourceLimit = 5 + 1;
|
|
||||||
constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES;
|
constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES;
|
||||||
constexpr size_t SlabCountKIoPool = 1;
|
constexpr size_t SlabCountKIoPool = 1;
|
||||||
constexpr size_t SlabCountKIoRegion = 6;
|
constexpr size_t SlabCountKIoRegion = 6;
|
||||||
|
|||||||
@@ -635,6 +635,36 @@ Result KPageTableBase::CheckMemoryState(const KMemoryInfo& info, KMemoryState st
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool KPageTableBase::BeginTraversal(const Common::PageTable &impl, TraversalEntry *out_entry, TraversalContext *out_context,
|
||||||
|
Common::ProcessAddress address) const {
|
||||||
|
out_context->next_offset = GetInteger(address);
|
||||||
|
out_context->next_page = GetInteger(address) >> PageBits;
|
||||||
|
|
||||||
|
return ContinueTraversal(impl, out_entry, out_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KPageTableBase::ContinueTraversal(const Common::PageTable &impl, TraversalEntry *out_entry,
|
||||||
|
TraversalContext *context) const {
|
||||||
|
// Setup invalid defaults.
|
||||||
|
out_entry->phys_addr = 0;
|
||||||
|
out_entry->block_size = PageSize;
|
||||||
|
// Validate that we can read the actual entry.
|
||||||
|
if (auto const page = context->next_page; page < impl.entries.size()) {
|
||||||
|
// Validate that the entry is mapped.
|
||||||
|
if (auto const paddr = impl.entries[page].Pointer(true); paddr != 0) {
|
||||||
|
// Populate the results and return true
|
||||||
|
out_entry->phys_addr = GetInteger(m_system.DeviceMemory().GetPhysicalAddr(paddr + context->next_offset));
|
||||||
|
context->next_page += 1;
|
||||||
|
context->next_offset += PageSize;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context->next_page += 1;
|
||||||
|
context->next_offset += PageSize;
|
||||||
|
// Otherwise return false
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Result KPageTableBase::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr,
|
Result KPageTableBase::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr,
|
||||||
size_t size, KMemoryState state_mask,
|
size_t size, KMemoryState state_mask,
|
||||||
KMemoryState state, KMemoryPermission perm_mask,
|
KMemoryState state, KMemoryPermission perm_mask,
|
||||||
@@ -940,7 +970,7 @@ Result KPageTableBase::QueryMappingImpl(KProcessAddress* out, KPhysicalAddress a
|
|||||||
size_t tot_size = 0;
|
size_t tot_size = 0;
|
||||||
|
|
||||||
next_valid =
|
next_valid =
|
||||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), region_start);
|
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), region_start);
|
||||||
next_entry.block_size =
|
next_entry.block_size =
|
||||||
(next_entry.block_size - (GetInteger(region_start) & (next_entry.block_size - 1)));
|
(next_entry.block_size - (GetInteger(region_start) & (next_entry.block_size - 1)));
|
||||||
|
|
||||||
@@ -976,7 +1006,7 @@ Result KPageTableBase::QueryMappingImpl(KProcessAddress* out, KPhysicalAddress a
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
next_valid = impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
next_valid = ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the last entry.
|
// Check the last entry.
|
||||||
@@ -1754,7 +1784,7 @@ Result KPageTableBase::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_
|
|||||||
// Begin traversal.
|
// Begin traversal.
|
||||||
TraversalContext context;
|
TraversalContext context;
|
||||||
TraversalEntry next_entry;
|
TraversalEntry next_entry;
|
||||||
R_UNLESS(impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), addr),
|
R_UNLESS(BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), addr),
|
||||||
ResultInvalidCurrentMemory);
|
ResultInvalidCurrentMemory);
|
||||||
|
|
||||||
// Prepare tracking variables.
|
// Prepare tracking variables.
|
||||||
@@ -1764,7 +1794,7 @@ Result KPageTableBase::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_
|
|||||||
|
|
||||||
// Iterate, adding to group as we go.
|
// Iterate, adding to group as we go.
|
||||||
while (tot_size < size) {
|
while (tot_size < size) {
|
||||||
R_UNLESS(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)),
|
R_UNLESS(ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)),
|
||||||
ResultInvalidCurrentMemory);
|
ResultInvalidCurrentMemory);
|
||||||
|
|
||||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||||
@@ -1828,7 +1858,7 @@ bool KPageTableBase::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr
|
|||||||
// Begin traversal.
|
// Begin traversal.
|
||||||
TraversalContext context;
|
TraversalContext context;
|
||||||
TraversalEntry next_entry;
|
TraversalEntry next_entry;
|
||||||
if (!impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), addr)) {
|
if (!BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), addr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1839,7 +1869,7 @@ bool KPageTableBase::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr
|
|||||||
|
|
||||||
// Iterate, comparing expected to actual.
|
// Iterate, comparing expected to actual.
|
||||||
while (tot_size < size) {
|
while (tot_size < size) {
|
||||||
if (!impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context))) {
|
if (!ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1896,7 +1926,7 @@ Result KPageTableBase::GetContiguousMemoryRangeWithState(
|
|||||||
// Begin a traversal.
|
// Begin a traversal.
|
||||||
TraversalContext context;
|
TraversalContext context;
|
||||||
TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0};
|
TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0};
|
||||||
R_UNLESS(impl.BeginTraversal(std::addressof(cur_entry), std::addressof(context), address),
|
R_UNLESS(BeginTraversal(impl, std::addressof(cur_entry), std::addressof(context), address),
|
||||||
ResultInvalidCurrentMemory);
|
ResultInvalidCurrentMemory);
|
||||||
|
|
||||||
// Traverse until we have enough size or we aren't contiguous any more.
|
// Traverse until we have enough size or we aren't contiguous any more.
|
||||||
@@ -1905,7 +1935,7 @@ Result KPageTableBase::GetContiguousMemoryRangeWithState(
|
|||||||
for (contig_size =
|
for (contig_size =
|
||||||
cur_entry.block_size - (GetInteger(phys_address) & (cur_entry.block_size - 1));
|
cur_entry.block_size - (GetInteger(phys_address) & (cur_entry.block_size - 1));
|
||||||
contig_size < size; contig_size += cur_entry.block_size) {
|
contig_size < size; contig_size += cur_entry.block_size) {
|
||||||
if (!impl.ContinueTraversal(std::addressof(cur_entry), std::addressof(context))) {
|
if (!ContinueTraversal(impl, std::addressof(cur_entry), std::addressof(context))) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (cur_entry.phys_addr != phys_address + contig_size) {
|
if (cur_entry.phys_addr != phys_address + contig_size) {
|
||||||
@@ -2334,7 +2364,7 @@ Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out,
|
|||||||
TraversalContext context;
|
TraversalContext context;
|
||||||
TraversalEntry next_entry;
|
TraversalEntry next_entry;
|
||||||
bool traverse_valid =
|
bool traverse_valid =
|
||||||
m_impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), virt_addr);
|
BeginTraversal(m_impl, std::addressof(next_entry), std::addressof(context), virt_addr);
|
||||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||||
|
|
||||||
// Set tracking variables.
|
// Set tracking variables.
|
||||||
@@ -2345,7 +2375,7 @@ Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out,
|
|||||||
while (true) {
|
while (true) {
|
||||||
// Continue the traversal.
|
// Continue the traversal.
|
||||||
traverse_valid =
|
traverse_valid =
|
||||||
m_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
ContinueTraversal(m_impl, std::addressof(next_entry), std::addressof(context));
|
||||||
if (!traverse_valid) {
|
if (!traverse_valid) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2567,7 +2597,7 @@ Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddre
|
|||||||
TraversalContext context;
|
TraversalContext context;
|
||||||
TraversalEntry next_entry;
|
TraversalEntry next_entry;
|
||||||
ASSERT(
|
ASSERT(
|
||||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_address));
|
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_address));
|
||||||
|
|
||||||
// Check that the physical region matches.
|
// Check that the physical region matches.
|
||||||
R_UNLESS(next_entry.phys_addr == phys_addr, ResultInvalidMemoryRegion);
|
R_UNLESS(next_entry.phys_addr == phys_addr, ResultInvalidMemoryRegion);
|
||||||
@@ -2577,7 +2607,7 @@ Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddre
|
|||||||
next_entry.block_size - (GetInteger(phys_addr) & (next_entry.block_size - 1));
|
next_entry.block_size - (GetInteger(phys_addr) & (next_entry.block_size - 1));
|
||||||
checked_size < size; checked_size += next_entry.block_size) {
|
checked_size < size; checked_size += next_entry.block_size) {
|
||||||
// Continue the traversal.
|
// Continue the traversal.
|
||||||
ASSERT(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)));
|
ASSERT(ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)));
|
||||||
|
|
||||||
// Check that the physical region matches.
|
// Check that the physical region matches.
|
||||||
R_UNLESS(next_entry.phys_addr == phys_addr + checked_size, ResultInvalidMemoryRegion);
|
R_UNLESS(next_entry.phys_addr == phys_addr + checked_size, ResultInvalidMemoryRegion);
|
||||||
@@ -3029,7 +3059,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_
|
|||||||
TraversalContext context;
|
TraversalContext context;
|
||||||
TraversalEntry next_entry;
|
TraversalEntry next_entry;
|
||||||
bool traverse_valid =
|
bool traverse_valid =
|
||||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), address);
|
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), address);
|
||||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||||
|
|
||||||
// Prepare tracking variables.
|
// Prepare tracking variables.
|
||||||
@@ -3041,7 +3071,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_
|
|||||||
while (tot_size < size) {
|
while (tot_size < size) {
|
||||||
// Continue the traversal.
|
// Continue the traversal.
|
||||||
traverse_valid =
|
traverse_valid =
|
||||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||||
|
|
||||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||||
@@ -3129,7 +3159,7 @@ Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddr
|
|||||||
TraversalContext context;
|
TraversalContext context;
|
||||||
TraversalEntry next_entry;
|
TraversalEntry next_entry;
|
||||||
bool traverse_valid =
|
bool traverse_valid =
|
||||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_address);
|
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), src_address);
|
||||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||||
|
|
||||||
// Prepare tracking variables.
|
// Prepare tracking variables.
|
||||||
@@ -3167,7 +3197,7 @@ Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddr
|
|||||||
while (tot_size < size) {
|
while (tot_size < size) {
|
||||||
// Continue the traversal.
|
// Continue the traversal.
|
||||||
traverse_valid =
|
traverse_valid =
|
||||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||||
@@ -3225,7 +3255,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd
|
|||||||
TraversalContext context;
|
TraversalContext context;
|
||||||
TraversalEntry next_entry;
|
TraversalEntry next_entry;
|
||||||
bool traverse_valid =
|
bool traverse_valid =
|
||||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_address);
|
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_address);
|
||||||
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
|
||||||
|
|
||||||
// Prepare tracking variables.
|
// Prepare tracking variables.
|
||||||
@@ -3267,7 +3297,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd
|
|||||||
while (tot_size < size) {
|
while (tot_size < size) {
|
||||||
// Continue the traversal.
|
// Continue the traversal.
|
||||||
traverse_valid =
|
traverse_valid =
|
||||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||||
@@ -3728,7 +3758,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser(
|
|||||||
TraversalContext context;
|
TraversalContext context;
|
||||||
TraversalEntry next_entry;
|
TraversalEntry next_entry;
|
||||||
bool traverse_valid =
|
bool traverse_valid =
|
||||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_addr);
|
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), src_addr);
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
// Prepare tracking variables.
|
// Prepare tracking variables.
|
||||||
@@ -3768,7 +3798,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser(
|
|||||||
while (tot_size < size) {
|
while (tot_size < size) {
|
||||||
// Continue the traversal.
|
// Continue the traversal.
|
||||||
traverse_valid =
|
traverse_valid =
|
||||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||||
@@ -3822,7 +3852,7 @@ Result KPageTableBase::CopyMemoryFromLinearToKernel(
|
|||||||
TraversalContext context;
|
TraversalContext context;
|
||||||
TraversalEntry next_entry;
|
TraversalEntry next_entry;
|
||||||
bool traverse_valid =
|
bool traverse_valid =
|
||||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_addr);
|
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), src_addr);
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
// Prepare tracking variables.
|
// Prepare tracking variables.
|
||||||
@@ -3845,7 +3875,7 @@ Result KPageTableBase::CopyMemoryFromLinearToKernel(
|
|||||||
while (tot_size < size) {
|
while (tot_size < size) {
|
||||||
// Continue the traversal.
|
// Continue the traversal.
|
||||||
traverse_valid =
|
traverse_valid =
|
||||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||||
@@ -3902,7 +3932,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear(
|
|||||||
TraversalContext context;
|
TraversalContext context;
|
||||||
TraversalEntry next_entry;
|
TraversalEntry next_entry;
|
||||||
bool traverse_valid =
|
bool traverse_valid =
|
||||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_addr);
|
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_addr);
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
// Prepare tracking variables.
|
// Prepare tracking variables.
|
||||||
@@ -3941,7 +3971,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear(
|
|||||||
while (tot_size < size) {
|
while (tot_size < size) {
|
||||||
// Continue the traversal.
|
// Continue the traversal.
|
||||||
traverse_valid =
|
traverse_valid =
|
||||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||||
@@ -3997,7 +4027,7 @@ Result KPageTableBase::CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, si
|
|||||||
TraversalContext context;
|
TraversalContext context;
|
||||||
TraversalEntry next_entry;
|
TraversalEntry next_entry;
|
||||||
bool traverse_valid =
|
bool traverse_valid =
|
||||||
impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_addr);
|
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_addr);
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
// Prepare tracking variables.
|
// Prepare tracking variables.
|
||||||
@@ -4020,7 +4050,7 @@ Result KPageTableBase::CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, si
|
|||||||
while (tot_size < size) {
|
while (tot_size < size) {
|
||||||
// Continue the traversal.
|
// Continue the traversal.
|
||||||
traverse_valid =
|
traverse_valid =
|
||||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context));
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||||
@@ -4089,10 +4119,10 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap(
|
|||||||
bool traverse_valid;
|
bool traverse_valid;
|
||||||
|
|
||||||
// Begin traversal.
|
// Begin traversal.
|
||||||
traverse_valid = src_impl.BeginTraversal(std::addressof(src_next_entry),
|
traverse_valid = BeginTraversal(src_impl, std::addressof(src_next_entry),
|
||||||
std::addressof(src_context), src_addr);
|
std::addressof(src_context), src_addr);
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
traverse_valid = dst_impl.BeginTraversal(std::addressof(dst_next_entry),
|
traverse_valid = BeginTraversal(dst_impl, std::addressof(dst_next_entry),
|
||||||
std::addressof(dst_context), dst_addr);
|
std::addressof(dst_context), dst_addr);
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
@@ -4127,7 +4157,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap(
|
|||||||
if (ofs + cur_copy_size != size) {
|
if (ofs + cur_copy_size != size) {
|
||||||
if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) {
|
if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) {
|
||||||
// Continue the src traversal.
|
// Continue the src traversal.
|
||||||
traverse_valid = src_impl.ContinueTraversal(std::addressof(src_next_entry),
|
traverse_valid = ContinueTraversal(src_impl, std::addressof(src_next_entry),
|
||||||
std::addressof(src_context));
|
std::addressof(src_context));
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
@@ -4138,7 +4168,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap(
|
|||||||
if (cur_dst_addr + cur_min_size ==
|
if (cur_dst_addr + cur_min_size ==
|
||||||
dst_next_entry.phys_addr + dst_next_entry.block_size) {
|
dst_next_entry.phys_addr + dst_next_entry.block_size) {
|
||||||
// Continue the dst traversal.
|
// Continue the dst traversal.
|
||||||
traverse_valid = dst_impl.ContinueTraversal(std::addressof(dst_next_entry),
|
traverse_valid = ContinueTraversal(dst_impl, std::addressof(dst_next_entry),
|
||||||
std::addressof(dst_context));
|
std::addressof(dst_context));
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
@@ -4223,10 +4253,10 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
|
|||||||
bool traverse_valid;
|
bool traverse_valid;
|
||||||
|
|
||||||
// Begin traversal.
|
// Begin traversal.
|
||||||
traverse_valid = src_impl.BeginTraversal(std::addressof(src_next_entry),
|
traverse_valid = BeginTraversal(src_impl, std::addressof(src_next_entry),
|
||||||
std::addressof(src_context), src_addr);
|
std::addressof(src_context), src_addr);
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
traverse_valid = dst_impl.BeginTraversal(std::addressof(dst_next_entry),
|
traverse_valid = BeginTraversal(dst_impl, std::addressof(dst_next_entry),
|
||||||
std::addressof(dst_context), dst_addr);
|
std::addressof(dst_context), dst_addr);
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
@@ -4261,7 +4291,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
|
|||||||
if (ofs + cur_copy_size != size) {
|
if (ofs + cur_copy_size != size) {
|
||||||
if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) {
|
if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) {
|
||||||
// Continue the src traversal.
|
// Continue the src traversal.
|
||||||
traverse_valid = src_impl.ContinueTraversal(std::addressof(src_next_entry),
|
traverse_valid = ContinueTraversal(src_impl, std::addressof(src_next_entry),
|
||||||
std::addressof(src_context));
|
std::addressof(src_context));
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
@@ -4272,7 +4302,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
|
|||||||
if (cur_dst_addr + cur_min_size ==
|
if (cur_dst_addr + cur_min_size ==
|
||||||
dst_next_entry.phys_addr + dst_next_entry.block_size) {
|
dst_next_entry.phys_addr + dst_next_entry.block_size) {
|
||||||
// Continue the dst traversal.
|
// Continue the dst traversal.
|
||||||
traverse_valid = dst_impl.ContinueTraversal(std::addressof(dst_next_entry),
|
traverse_valid = ContinueTraversal(dst_impl, std::addressof(dst_next_entry),
|
||||||
std::addressof(dst_context));
|
std::addressof(dst_context));
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
@@ -4547,7 +4577,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
|
|||||||
// Begin traversal.
|
// Begin traversal.
|
||||||
TraversalContext context;
|
TraversalContext context;
|
||||||
TraversalEntry next_entry;
|
TraversalEntry next_entry;
|
||||||
bool traverse_valid = src_impl.BeginTraversal(std::addressof(next_entry),
|
bool traverse_valid = BeginTraversal(src_impl, std::addressof(next_entry),
|
||||||
std::addressof(context), aligned_src_start);
|
std::addressof(context), aligned_src_start);
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
@@ -4597,7 +4627,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
|
|||||||
// If the block's size was one page, we may need to continue traversal.
|
// If the block's size was one page, we may need to continue traversal.
|
||||||
if (cur_block_size == 0 && aligned_src_size > PageSize) {
|
if (cur_block_size == 0 && aligned_src_size > PageSize) {
|
||||||
traverse_valid =
|
traverse_valid =
|
||||||
src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
ContinueTraversal(src_impl, std::addressof(next_entry), std::addressof(context));
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
cur_block_addr = next_entry.phys_addr;
|
cur_block_addr = next_entry.phys_addr;
|
||||||
@@ -4610,7 +4640,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
|
|||||||
while (aligned_src_start + tot_block_size < mapping_src_end) {
|
while (aligned_src_start + tot_block_size < mapping_src_end) {
|
||||||
// Continue the traversal.
|
// Continue the traversal.
|
||||||
traverse_valid =
|
traverse_valid =
|
||||||
src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
ContinueTraversal(src_impl, std::addressof(next_entry), std::addressof(context));
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
// Process the block.
|
// Process the block.
|
||||||
@@ -4653,7 +4683,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
|
|||||||
if (mapped_block_end + cur_block_size < aligned_src_end &&
|
if (mapped_block_end + cur_block_size < aligned_src_end &&
|
||||||
cur_block_size == last_block_size) {
|
cur_block_size == last_block_size) {
|
||||||
traverse_valid =
|
traverse_valid =
|
||||||
src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
ContinueTraversal(src_impl, std::addressof(next_entry), std::addressof(context));
|
||||||
ASSERT(traverse_valid);
|
ASSERT(traverse_valid);
|
||||||
|
|
||||||
cur_block_addr = next_entry.phys_addr;
|
cur_block_addr = next_entry.phys_addr;
|
||||||
@@ -5601,7 +5631,7 @@ Result KPageTableBase::UnmapProcessMemory(KProcessAddress dst_address, size_t si
|
|||||||
ContiguousRangeInfo(KPageTableBase& pt, KProcessAddress address, size_t size)
|
ContiguousRangeInfo(KPageTableBase& pt, KProcessAddress address, size_t size)
|
||||||
: m_pt(pt), m_remaining_size(size) {
|
: m_pt(pt), m_remaining_size(size) {
|
||||||
// Begin a traversal.
|
// Begin a traversal.
|
||||||
ASSERT(m_pt.GetImpl().BeginTraversal(std::addressof(m_entry),
|
ASSERT(m_pt.BeginTraversal(m_pt.GetImpl(), std::addressof(m_entry),
|
||||||
std::addressof(m_context), address));
|
std::addressof(m_context), address));
|
||||||
|
|
||||||
// Setup tracking fields.
|
// Setup tracking fields.
|
||||||
@@ -5632,7 +5662,7 @@ Result KPageTableBase::UnmapProcessMemory(KProcessAddress dst_address, size_t si
|
|||||||
void DetermineContiguousBlockExtents() {
|
void DetermineContiguousBlockExtents() {
|
||||||
// Continue traversing until we're not contiguous, or we have enough.
|
// Continue traversing until we're not contiguous, or we have enough.
|
||||||
while (m_cur_size < m_remaining_size) {
|
while (m_cur_size < m_remaining_size) {
|
||||||
ASSERT(m_pt.GetImpl().ContinueTraversal(std::addressof(m_entry),
|
ASSERT(m_pt.ContinueTraversal(m_pt.GetImpl(), std::addressof(m_entry),
|
||||||
std::addressof(m_context)));
|
std::addressof(m_context)));
|
||||||
|
|
||||||
// If we're not contiguous, we're done.
|
// If we're not contiguous, we're done.
|
||||||
|
|||||||
@@ -370,6 +370,10 @@ private:
|
|||||||
size_t num_pages, size_t alignment, size_t offset,
|
size_t num_pages, size_t alignment, size_t offset,
|
||||||
size_t guard_pages) const;
|
size_t guard_pages) const;
|
||||||
|
|
||||||
|
bool BeginTraversal(const Common::PageTable& impl, TraversalEntry* out_entry, TraversalContext* out_context,
|
||||||
|
Common::ProcessAddress address) const;
|
||||||
|
bool ContinueTraversal(const Common::PageTable& impl, TraversalEntry* out_entry, TraversalContext* context) const;
|
||||||
|
|
||||||
Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
|
Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
|
||||||
KMemoryState state_mask, KMemoryState state,
|
KMemoryState state_mask, KMemoryState state,
|
||||||
KMemoryPermission perm_mask, KMemoryPermission perm,
|
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||||
@@ -474,7 +478,14 @@ private:
|
|||||||
// Validate pre-conditions.
|
// Validate pre-conditions.
|
||||||
ASSERT(this->IsLockedByCurrentThread());
|
ASSERT(this->IsLockedByCurrentThread());
|
||||||
|
|
||||||
return this->GetImpl().GetPhysicalAddress(out, virt_addr);
|
if (virt_addr > (1ULL << m_address_space_width)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = m_system.DeviceMemory().GetPhysicalAddr(
|
||||||
|
this->GetImpl().entries[GetInteger(virt_addr) >> PageBits].Pointer(true) + GetInteger(virt_addr));
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -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: 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
|
||||||
@@ -2501,14 +2501,15 @@ void Call(Core::System& system, u32 imm) {
|
|||||||
auto& process = GetCurrentProcess(kernel);
|
auto& process = GetCurrentProcess(kernel);
|
||||||
std::array<uint64_t, 8> args;
|
std::array<uint64_t, 8> args;
|
||||||
kernel.CurrentPhysicalCore().SaveSvcArguments(process, args);
|
kernel.CurrentPhysicalCore().SaveSvcArguments(process, args);
|
||||||
LOG_TRACE(Kernel_SVC, "{} [0]={:#x} [1]={:#x} [2]={:#x} [3]={:#x} [4]={:#x} [5]={:#x} [6]={:#x}",
|
|
||||||
imm, GetArg32(args, 0), GetArg32(args, 1), GetArg32(args, 2),
|
|
||||||
GetArg32(args, 3), GetArg32(args, 4), GetArg32(args, 5), GetArg32(args, 6));
|
|
||||||
//kernel.EnterSVCProfile();
|
//kernel.EnterSVCProfile();
|
||||||
if (process.Is64Bit())
|
LOG_TRACE(Kernel_SVC, "{} [0]={:#x} [1]={:#x} [2]={:#x} [3]={:#x} [4]={:#x} [5]={:#x} [6]={:#x}",
|
||||||
|
imm, GetArg64(args, 0), GetArg64(args, 1), GetArg64(args, 2),
|
||||||
|
GetArg64(args, 3), GetArg64(args, 4), GetArg64(args, 5), GetArg64(args, 6));
|
||||||
|
if (process.Is64Bit()) {
|
||||||
Call64(system, imm, args);
|
Call64(system, imm, args);
|
||||||
else
|
} else {
|
||||||
Call32(system, imm, args);
|
Call32(system, imm, args);
|
||||||
|
}
|
||||||
//kernel.ExitSVCProfile();
|
//kernel.ExitSVCProfile();
|
||||||
kernel.CurrentPhysicalCore().LoadSvcArguments(process, args);
|
kernel.CurrentPhysicalCore().LoadSvcArguments(process, args);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,17 +10,15 @@
|
|||||||
#include "core/hle/kernel/k_process.h"
|
#include "core/hle/kernel/k_process.h"
|
||||||
#include "core/hle/kernel/k_resource_limit.h"
|
#include "core/hle/kernel/k_resource_limit.h"
|
||||||
#include "core/hle/kernel/svc.h"
|
#include "core/hle/kernel/svc.h"
|
||||||
|
#include "core/hle/kernel/svc_results.h"
|
||||||
|
#include "core/hle/kernel/svc_version.h"
|
||||||
|
|
||||||
namespace Kernel::Svc {
|
namespace Kernel::Svc {
|
||||||
|
|
||||||
/// Gets system/memory information for the current process
|
/// Gets system/memory information for the current process
|
||||||
Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle handle,
|
Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle handle, u64 info_sub_id) {
|
||||||
u64 info_sub_id) {
|
LOG_TRACE(Kernel_SVC, "called info_id={:#x}, info_sub_id={:#x}, handle={:#08x}", info_id_type, info_sub_id, handle);
|
||||||
LOG_TRACE(Kernel_SVC, "called info_id={:#x}, info_sub_id={:#x}, handle={:#08x}",
|
u32 info_id = u32(info_id_type);
|
||||||
info_id_type, info_sub_id, handle);
|
|
||||||
|
|
||||||
u32 info_id = static_cast<u32>(info_id_type);
|
|
||||||
|
|
||||||
switch (info_id_type) {
|
switch (info_id_type) {
|
||||||
case InfoType::CoreMask:
|
case InfoType::CoreMask:
|
||||||
case InfoType::PriorityMask:
|
case InfoType::PriorityMask:
|
||||||
@@ -53,152 +51,123 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
|
|||||||
case InfoType::CoreMask:
|
case InfoType::CoreMask:
|
||||||
*result = process->GetCoreMask();
|
*result = process->GetCoreMask();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::PriorityMask:
|
case InfoType::PriorityMask:
|
||||||
*result = process->GetPriorityMask();
|
*result = process->GetPriorityMask();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::AliasRegionAddress:
|
case InfoType::AliasRegionAddress:
|
||||||
*result = GetInteger(process->GetPageTable().GetAliasRegionStart());
|
*result = GetInteger(process->GetPageTable().GetAliasRegionStart());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::AliasRegionSize:
|
case InfoType::AliasRegionSize:
|
||||||
*result = process->GetPageTable().GetAliasRegionSize();
|
*result = process->GetPageTable().GetAliasRegionSize();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
case InfoType::HeapRegionAddress:
|
case InfoType::HeapRegionAddress:
|
||||||
*result = GetInteger(process->GetPageTable().GetHeapRegionStart());
|
*result = GetInteger(process->GetPageTable().GetHeapRegionStart());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::HeapRegionSize:
|
case InfoType::HeapRegionSize:
|
||||||
*result = process->GetPageTable().GetHeapRegionSize();
|
*result = process->GetPageTable().GetHeapRegionSize();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::AslrRegionAddress:
|
case InfoType::AslrRegionAddress:
|
||||||
*result = GetInteger(process->GetPageTable().GetAliasCodeRegionStart());
|
*result = GetInteger(process->GetPageTable().GetAliasCodeRegionStart());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::AslrRegionSize:
|
case InfoType::AslrRegionSize:
|
||||||
*result = process->GetPageTable().GetAliasCodeRegionSize();
|
*result = process->GetPageTable().GetAliasCodeRegionSize();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::StackRegionAddress:
|
case InfoType::StackRegionAddress:
|
||||||
*result = GetInteger(process->GetPageTable().GetStackRegionStart());
|
*result = GetInteger(process->GetPageTable().GetStackRegionStart());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::StackRegionSize:
|
case InfoType::StackRegionSize:
|
||||||
*result = process->GetPageTable().GetStackRegionSize();
|
*result = process->GetPageTable().GetStackRegionSize();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::TotalMemorySize:
|
case InfoType::TotalMemorySize:
|
||||||
*result = process->GetTotalUserPhysicalMemorySize(system.Kernel());
|
*result = process->GetTotalUserPhysicalMemorySize(system.Kernel());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::UsedMemorySize:
|
case InfoType::UsedMemorySize:
|
||||||
*result = process->GetUsedUserPhysicalMemorySize(system.Kernel());
|
*result = process->GetUsedUserPhysicalMemorySize(system.Kernel());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::SystemResourceSizeTotal:
|
case InfoType::SystemResourceSizeTotal:
|
||||||
*result = process->GetTotalSystemResourceSize();
|
*result = process->GetTotalSystemResourceSize();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::SystemResourceSizeUsed:
|
case InfoType::SystemResourceSizeUsed:
|
||||||
*result = process->GetUsedSystemResourceSize();
|
*result = process->GetUsedSystemResourceSize();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::ProgramId:
|
case InfoType::ProgramId:
|
||||||
*result = process->GetProgramId();
|
*result = process->GetProgramId();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::UserExceptionContextAddress:
|
case InfoType::UserExceptionContextAddress:
|
||||||
*result = GetInteger(process->GetProcessLocalRegionAddress());
|
*result = GetInteger(process->GetProcessLocalRegionAddress());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::TotalNonSystemMemorySize:
|
case InfoType::TotalNonSystemMemorySize:
|
||||||
*result = process->GetTotalNonSystemUserPhysicalMemorySize(system.Kernel());
|
*result = process->GetTotalNonSystemUserPhysicalMemorySize(system.Kernel());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::UsedNonSystemMemorySize:
|
case InfoType::UsedNonSystemMemorySize:
|
||||||
*result = process->GetUsedNonSystemUserPhysicalMemorySize(system.Kernel());
|
*result = process->GetUsedNonSystemUserPhysicalMemorySize(system.Kernel());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::IsApplication:
|
case InfoType::IsApplication:
|
||||||
*result = process->IsApplication();
|
*result = process->IsApplication();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::FreeThreadCount:
|
case InfoType::FreeThreadCount:
|
||||||
if (KResourceLimit* resource_limit = process->GetResourceLimit();
|
if (KResourceLimit* resource_limit = process->GetResourceLimit();
|
||||||
resource_limit != nullptr) {
|
resource_limit != nullptr) {
|
||||||
const auto current_value =
|
const auto current_value = resource_limit->GetCurrentValue(Svc::LimitableResource::ThreadCountMax);
|
||||||
resource_limit->GetCurrentValue(Svc::LimitableResource::ThreadCountMax);
|
const auto limit_value = resource_limit->GetLimitValue(Svc::LimitableResource::ThreadCountMax);
|
||||||
const auto limit_value =
|
|
||||||
resource_limit->GetLimitValue(Svc::LimitableResource::ThreadCountMax);
|
|
||||||
*result = limit_value - current_value;
|
*result = limit_value - current_value;
|
||||||
} else {
|
} else {
|
||||||
*result = 0;
|
*result = 0;
|
||||||
}
|
}
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::AliasRegionExtraSize: {
|
case InfoType::AliasRegionExtraSize: {
|
||||||
if (info_sub_id != 0) {
|
R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
|
||||||
return ResultInvalidCombination;
|
|
||||||
}
|
|
||||||
|
|
||||||
KProcess* current_process = GetCurrentProcessPointer(system.Kernel());
|
KProcess* current_process = GetCurrentProcessPointer(system.Kernel());
|
||||||
*result = current_process->GetPageTable().GetAliasRegionExtraSize();
|
*result = current_process->GetPageTable().GetAliasRegionExtraSize();
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id={:#016x}", info_id);
|
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id={:#016x}", info_id);
|
||||||
R_THROW(ResultInvalidEnumValue);
|
R_THROW(ResultInvalidEnumValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
case InfoType::DebuggerAttached:
|
case InfoType::DebuggerAttached:
|
||||||
*result = 0;
|
*result = 0;
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|
||||||
case InfoType::ResourceLimit: {
|
case InfoType::ResourceLimit: {
|
||||||
R_UNLESS(handle == 0, ResultInvalidHandle);
|
R_UNLESS(handle == 0, ResultInvalidHandle);
|
||||||
R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
|
R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
|
||||||
|
|
||||||
KProcess* const current_process = GetCurrentProcessPointer(system.Kernel());
|
KProcess* const current_process = GetCurrentProcessPointer(system.Kernel());
|
||||||
KHandleTable& handle_table = current_process->GetHandleTable();
|
KHandleTable& handle_table = current_process->GetHandleTable();
|
||||||
const auto resource_limit = current_process->GetResourceLimit();
|
if (auto const resource_limit = current_process->GetResourceLimit(); resource_limit) {
|
||||||
if (!resource_limit) {
|
Handle resource_handle{};
|
||||||
|
R_TRY(handle_table.Add(system.Kernel(), std::addressof(resource_handle), resource_limit));
|
||||||
|
*result = resource_handle;
|
||||||
|
} else {
|
||||||
*result = Svc::InvalidHandle;
|
*result = Svc::InvalidHandle;
|
||||||
// Yes, the kernel considers this a successful operation.
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
}
|
||||||
|
// Yes, the kernel considers this a successful operation either way.
|
||||||
Handle resource_handle{};
|
|
||||||
R_TRY(handle_table.Add(system.Kernel(), std::addressof(resource_handle), resource_limit));
|
|
||||||
|
|
||||||
*result = resource_handle;
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
case InfoType::RandomEntropy:
|
case InfoType::RandomEntropy:
|
||||||
R_UNLESS(handle == 0, ResultInvalidHandle);
|
R_UNLESS(handle == 0, ResultInvalidHandle);
|
||||||
R_UNLESS(info_sub_id < 4, ResultInvalidCombination);
|
R_UNLESS(info_sub_id < 4, ResultInvalidCombination);
|
||||||
|
|
||||||
*result = GetCurrentProcess(system.Kernel()).GetRandomEntropy(info_sub_id);
|
*result = GetCurrentProcess(system.Kernel()).GetRandomEntropy(info_sub_id);
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
case InfoType::InitialProcessIdRange: {
|
||||||
case InfoType::InitialProcessIdRange:
|
LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query privileged process id bounds, returned 0/64");
|
||||||
LOG_WARNING(Kernel_SVC,
|
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
||||||
"(STUBBED) Attempted to query privileged process id bounds, returned 0");
|
switch (InitialProcessIdRangeInfo(info_sub_id)) {
|
||||||
*result = 0;
|
case InitialProcessIdRangeInfo::Minimum:
|
||||||
R_SUCCEED();
|
*result = 0; //todo
|
||||||
|
R_SUCCEED();
|
||||||
|
case InitialProcessIdRangeInfo::Maximum:
|
||||||
|
*result = 64; //todo
|
||||||
|
R_SUCCEED();
|
||||||
|
default:
|
||||||
|
R_THROW(ResultInvalidCombination);
|
||||||
|
}
|
||||||
|
}
|
||||||
case InfoType::ThreadTickCount: {
|
case InfoType::ThreadTickCount: {
|
||||||
constexpr u64 num_cpus = 4;
|
constexpr u64 num_cpus = 4;
|
||||||
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
|
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
|
||||||
LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus,
|
LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus, info_sub_id);
|
||||||
info_sub_id);
|
|
||||||
R_THROW(ResultInvalidCombination);
|
R_THROW(ResultInvalidCombination);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,8 +175,7 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
|
|||||||
.GetHandleTable()
|
.GetHandleTable()
|
||||||
.GetObject<KThread>(system.Kernel(), Handle(handle));
|
.GetObject<KThread>(system.Kernel(), Handle(handle));
|
||||||
if (thread.IsNull()) {
|
if (thread.IsNull()) {
|
||||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle={:#08x}",
|
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle={:#08x}", Handle(handle));
|
||||||
static_cast<Handle>(handle));
|
|
||||||
R_THROW(ResultInvalidHandle);
|
R_THROW(ResultInvalidHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,7 +188,6 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
|
|||||||
u64 out_ticks = 0;
|
u64 out_ticks = 0;
|
||||||
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
|
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
|
||||||
const u64 thread_ticks = current_thread->GetCpuTime();
|
const u64 thread_ticks = current_thread->GetCpuTime();
|
||||||
|
|
||||||
out_ticks = thread_ticks + (core_timing.GetClockTicks() - prev_ctx_ticks);
|
out_ticks = thread_ticks + (core_timing.GetClockTicks() - prev_ctx_ticks);
|
||||||
} else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
|
} else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
|
||||||
out_ticks = core_timing.GetClockTicks() - prev_ctx_ticks;
|
out_ticks = core_timing.GetClockTicks() - prev_ctx_ticks;
|
||||||
@@ -230,19 +197,40 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
case InfoType::IdleTickCount: {
|
case InfoType::IdleTickCount: {
|
||||||
// Verify the input handle is invalid.
|
|
||||||
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
|
||||||
|
|
||||||
// Verify the requested core is valid.
|
// Verify the requested core is valid.
|
||||||
const bool core_valid =
|
const bool core_valid =
|
||||||
(info_sub_id == 0xFFFFFFFFFFFFFFFF) ||
|
(info_sub_id == 0xFFFFFFFFFFFFFFFF)
|
||||||
(info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex()));
|
|| (info_sub_id == u64(system.Kernel().CurrentPhysicalCoreIndex()));
|
||||||
R_UNLESS(core_valid, ResultInvalidCombination);
|
R_UNLESS(core_valid, ResultInvalidCombination);
|
||||||
|
|
||||||
|
// Verify the input handle is invalid.
|
||||||
|
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
||||||
|
|
||||||
// Get the idle tick count.
|
// Get the idle tick count.
|
||||||
*result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
|
*result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
case InfoType::MesosphereMeta: {
|
||||||
|
enum MesosphereMetaInfo : u64 {
|
||||||
|
KernelVersion = 0,
|
||||||
|
IsKTraceEnabled = 1,
|
||||||
|
IsSingleStepEnabled = 2,
|
||||||
|
};
|
||||||
|
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
||||||
|
switch (MesosphereMetaInfo(info_sub_id)) {
|
||||||
|
case MesosphereMetaInfo::KernelVersion:
|
||||||
|
*result = Kernel::Svc::SupportedKernelVersion;
|
||||||
|
R_SUCCEED();
|
||||||
|
case MesosphereMetaInfo::IsKTraceEnabled:
|
||||||
|
*result = 0;
|
||||||
|
R_SUCCEED();
|
||||||
|
case MesosphereMetaInfo::IsSingleStepEnabled:
|
||||||
|
*result = 0;
|
||||||
|
R_SUCCEED();
|
||||||
|
default:
|
||||||
|
R_THROW(ResultInvalidCombination);
|
||||||
|
}
|
||||||
|
}
|
||||||
case InfoType::MesosphereCurrentProcess: {
|
case InfoType::MesosphereCurrentProcess: {
|
||||||
// Verify the input handle is invalid.
|
// Verify the input handle is invalid.
|
||||||
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
||||||
@@ -255,13 +243,11 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
|
|||||||
KHandleTable& handle_table = current_process->GetHandleTable();
|
KHandleTable& handle_table = current_process->GetHandleTable();
|
||||||
|
|
||||||
// Get a new handle for the current process.
|
// Get a new handle for the current process.
|
||||||
Handle tmp;
|
Handle tmp{};
|
||||||
R_TRY(handle_table.Add(system.Kernel(), std::addressof(tmp), current_process));
|
R_TRY(handle_table.Add(system.Kernel(), std::addressof(tmp), current_process));
|
||||||
|
|
||||||
// Set the output.
|
// Set the output.
|
||||||
*result = tmp;
|
*result = tmp;
|
||||||
|
|
||||||
// We succeeded.
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1387,7 +1387,8 @@ public:
|
|||||||
{5, &ACC_U1::GetProfile, "GetProfile"},
|
{5, &ACC_U1::GetProfile, "GetProfile"},
|
||||||
{6, nullptr, "GetProfileDigest"},
|
{6, nullptr, "GetProfileDigest"},
|
||||||
{50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
|
{50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
|
||||||
{51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
|
{51, &ACC_U1::TrySelectUserWithoutInteractionDeprecated, "TrySelectUserWithoutInteractionDeprecated"},
|
||||||
|
{52, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
|
||||||
{60, &ACC_U1::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"},
|
{60, &ACC_U1::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"},
|
||||||
{99, nullptr, "DebugActivateOpenContextRetention"},
|
{99, nullptr, "DebugActivateOpenContextRetention"},
|
||||||
{100, nullptr, "GetUserRegistrationNotifier"},
|
{100, nullptr, "GetUserRegistrationNotifier"},
|
||||||
|
|||||||
@@ -94,9 +94,7 @@ enum class AppletId : u32 {
|
|||||||
LoginShare = 0x18,
|
LoginShare = 0x18,
|
||||||
WebAuth = 0x19,
|
WebAuth = 0x19,
|
||||||
MyPage = 0x1A,
|
MyPage = 0x1A,
|
||||||
Lhub = 0x35,
|
Lhub = 0x35
|
||||||
// Homebrew -- uses same ProgramId as qlaunch
|
|
||||||
UlauncherUmenu = 0xF000'0000,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class AppletProgramId : u64 {
|
enum class AppletProgramId : u64 {
|
||||||
@@ -156,6 +154,7 @@ enum class AppletMessage : u32 {
|
|||||||
DetectLongPressingCaptureButton = 91,
|
DetectLongPressingCaptureButton = 91,
|
||||||
AlbumScreenShotTaken = 92,
|
AlbumScreenShotTaken = 92,
|
||||||
AlbumRecordingSaved = 93,
|
AlbumRecordingSaved = 93,
|
||||||
|
StartupLogoDisappeared = 95,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class LibraryAppletMode : u32 {
|
enum class LibraryAppletMode : u32 {
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ struct Applet {
|
|||||||
// Common state
|
// Common state
|
||||||
bool sleep_lock_enabled{};
|
bool sleep_lock_enabled{};
|
||||||
bool vr_mode_enabled{};
|
bool vr_mode_enabled{};
|
||||||
|
bool vr_mode_enabled_3d{};
|
||||||
bool lcd_backlight_off_enabled{};
|
bool lcd_backlight_off_enabled{};
|
||||||
APM::CpuBoostMode boost_mode{};
|
APM::CpuBoostMode boost_mode{};
|
||||||
bool request_exit_to_library_applet_at_execute_next_program_enabled{};
|
bool request_exit_to_library_applet_at_execute_next_program_enabled{};
|
||||||
|
|||||||
@@ -24,20 +24,6 @@ namespace Service::AM {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr size_t NroPathSize = 512;
|
|
||||||
constexpr size_t NroArgvSize = 2048;
|
|
||||||
constexpr size_t MenuCaptionSize = 1024;
|
|
||||||
struct UloaderTargetInput {
|
|
||||||
u32 magic;
|
|
||||||
bool target_once;
|
|
||||||
bool is_auto_game_recording;
|
|
||||||
std::array<u8, 2> unused;
|
|
||||||
std::array<char, NroPathSize> nro_path;
|
|
||||||
std::array<char, NroArgvSize> nro_argv;
|
|
||||||
std::array<char, MenuCaptionSize> menu_caption;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(UloaderTargetInput) == 3592);
|
|
||||||
|
|
||||||
constexpr u32 LaunchParameterAccountPreselectedUserMagic = 0xC79497CA;
|
constexpr u32 LaunchParameterAccountPreselectedUserMagic = 0xC79497CA;
|
||||||
|
|
||||||
struct LaunchParameterAccountPreselectedUser {
|
struct LaunchParameterAccountPreselectedUser {
|
||||||
@@ -134,44 +120,6 @@ void PushInShowController(Core::System& system, AppletStorageChannel& channel) {
|
|||||||
channel.Push(system.Kernel(), std::make_shared<IStorage>(system, std::move(user_args_data)));
|
channel.Push(system.Kernel(), std::make_shared<IStorage>(system, std::move(user_args_data)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PushInShowUlauncherUmenu(Core::System& system, AppletStorageChannel& channel) {
|
|
||||||
typedef std::array<u64, 2> AccountUid;
|
|
||||||
const CommonArguments common_args = {
|
|
||||||
.arguments_version = CommonArgumentVersion::Version3,
|
|
||||||
.size = CommonArgumentSize::Version3,
|
|
||||||
.library_version = 2,
|
|
||||||
.theme_color = ThemeColor::BasicBlack,
|
|
||||||
.play_startup_sound = true,
|
|
||||||
.system_tick = system.CoreTiming().GetClockTicks(),
|
|
||||||
};
|
|
||||||
struct UmenuInput {
|
|
||||||
AccountUid selected_user;
|
|
||||||
UloaderTargetInput suspended_hb_target_ipt; // Set if homebrew (launched as an application) is currently suspended
|
|
||||||
u64 suspended_app_id; // Set if any normal application is suspended
|
|
||||||
std::array<char, 0x301> last_menu_fs_path; //FS_MAX_PATH
|
|
||||||
std::array<char, 0x301> last_menu_path;
|
|
||||||
u32 last_menu_index;
|
|
||||||
bool reload_theme_cache;
|
|
||||||
bool warned_about_outdated_theme;
|
|
||||||
u32 last_added_app_count;
|
|
||||||
u32 last_deleted_app_count;
|
|
||||||
u32 in_verify_app_count;
|
|
||||||
} user_args = {};
|
|
||||||
static_assert(sizeof(UmenuInput) == 5176);
|
|
||||||
|
|
||||||
auto const user = system.GetProfileManager().GetUser(0);
|
|
||||||
user_args.selected_user[0] = user->uuid[0];
|
|
||||||
user_args.selected_user[1] = user->uuid[1];
|
|
||||||
user_args.warned_about_outdated_theme = true;
|
|
||||||
|
|
||||||
std::vector<u8> common_args_data(sizeof(common_args));
|
|
||||||
std::vector<u8> user_args_data(sizeof(user_args));
|
|
||||||
std::memcpy(common_args_data.data(), std::addressof(common_args), sizeof(common_args));
|
|
||||||
std::memcpy(user_args_data.data(), std::addressof(user_args), sizeof(user_args));
|
|
||||||
channel.Push(std::make_shared<IStorage>(system, std::move(common_args_data)));
|
|
||||||
channel.Push(std::make_shared<IStorage>(system, std::move(user_args_data)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PushInShowCabinetData(Core::System& system, AppletStorageChannel& channel) {
|
void PushInShowCabinetData(Core::System& system, AppletStorageChannel& channel) {
|
||||||
const CommonArguments arguments{
|
const CommonArguments arguments{
|
||||||
.arguments_version = CommonArgumentVersion::Version3,
|
.arguments_version = CommonArgumentVersion::Version3,
|
||||||
@@ -343,18 +291,8 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
|
|||||||
applet->previous_program_index = params.previous_program_index;
|
applet->previous_program_index = params.previous_program_index;
|
||||||
|
|
||||||
// Push UserChannel data from previous application
|
// Push UserChannel data from previous application
|
||||||
// Or ulaunch initialization where we push parameters willingly!
|
|
||||||
if (params.launch_type == LaunchType::ApplicationInitiated) {
|
if (params.launch_type == LaunchType::ApplicationInitiated) {
|
||||||
applet->user_channel_launch_parameter.swap(m_system.GetUserChannel());
|
applet->user_channel_launch_parameter.swap(m_system.GetUserChannel());
|
||||||
} else if (params.launch_type == LaunchType::FrontendUlaunchInitiated) {
|
|
||||||
UloaderTargetInput target_ipt = {};
|
|
||||||
target_ipt.magic = 0x49444C55; // "ULDI"
|
|
||||||
target_ipt.nro_path = {"sdmc:/hbmenu.nro"};
|
|
||||||
target_ipt.menu_caption = {"Loaded by uLoader v1.2.4 - uLaunch's custom hbloader replacement ;)"};
|
|
||||||
std::vector<u8> v(sizeof(target_ipt));
|
|
||||||
std::memcpy(v.data(), std::addressof(target_ipt), sizeof(target_ipt));
|
|
||||||
applet->user_channel_launch_parameter.clear();
|
|
||||||
applet->user_channel_launch_parameter.push_back(std::move(v));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Read whether we need a preselected user from NACP?
|
// TODO: Read whether we need a preselected user from NACP?
|
||||||
@@ -396,9 +334,6 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
|
|||||||
case AppletId::Controller:
|
case AppletId::Controller:
|
||||||
PushInShowController(m_system, InitializeFakeCallerApplet(m_system, applet));
|
PushInShowController(m_system, InitializeFakeCallerApplet(m_system, applet));
|
||||||
break;
|
break;
|
||||||
case AppletId::UlauncherUmenu:
|
|
||||||
PushInShowUlauncherUmenu(m_system, InitializeFakeCallerApplet(m_system, applet));
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -406,7 +341,7 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
|
|||||||
// Applet was started by frontend, so it is foreground.
|
// Applet was started by frontend, so it is foreground.
|
||||||
applet->lifecycle_manager.SetFocusState(m_system.Kernel(), FocusState::InFocus);
|
applet->lifecycle_manager.SetFocusState(m_system.Kernel(), FocusState::InFocus);
|
||||||
|
|
||||||
if (applet->applet_id == AppletId::QLaunch || applet->applet_id == AppletId::UlauncherUmenu) {
|
if (applet->applet_id == AppletId::QLaunch) {
|
||||||
applet->lifecycle_manager.SetFocusHandlingMode(false);
|
applet->lifecycle_manager.SetFocusHandlingMode(false);
|
||||||
applet->lifecycle_manager.SetOutOfFocusSuspendingEnabled(false);
|
applet->lifecycle_manager.SetOutOfFocusSuspendingEnabled(false);
|
||||||
m_window_system->TrackApplet(applet, false);
|
m_window_system->TrackApplet(applet, false);
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -26,9 +26,6 @@ class WindowSystem;
|
|||||||
enum class LaunchType {
|
enum class LaunchType {
|
||||||
FrontendInitiated,
|
FrontendInitiated,
|
||||||
ApplicationInitiated,
|
ApplicationInitiated,
|
||||||
// Special masquerade for AMS + uLaunch CFW
|
|
||||||
FrontendUlaunchInitiated = 0x800,
|
|
||||||
FrontendUmenuInitiated,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FrontendAppletParameters {
|
struct FrontendAppletParameters {
|
||||||
|
|||||||
@@ -73,20 +73,16 @@ Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
|
|||||||
R_TRY(m_manager_display_service->CreateManagedLayer(
|
R_TRY(m_manager_display_service->CreateManagedLayer(
|
||||||
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
|
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
|
||||||
|
|
||||||
m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
|
|
||||||
(void)m_display_service->GetContainer()->SetLayerStackMask(*out_layer_id,
|
|
||||||
this->GetLayerStackMask());
|
|
||||||
|
|
||||||
if (m_applet_id != AppletId::Application) {
|
if (m_applet_id != AppletId::Application) {
|
||||||
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
|
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
|
||||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||||
(void)m_manager_display_service->SetLayerZIndex(Overlay, *out_layer_id);
|
(void)m_manager_display_service->SetLayerZIndex(-1, *out_layer_id);
|
||||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true);
|
(void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true);
|
||||||
} else {
|
} else {
|
||||||
(void)m_manager_display_service->SetLayerZIndex(Foreground, *out_layer_id);
|
(void)m_manager_display_service->SetLayerZIndex(1, *out_layer_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
(void)m_display_service->GetContainer()->SetLayerZIndex(*out_layer_id, true);
|
||||||
m_managed_display_layers.emplace(*out_layer_id);
|
m_managed_display_layers.emplace(*out_layer_id);
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
@@ -126,12 +122,11 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
|
|||||||
|
|
||||||
// Ensure the overlay layer is visible
|
// Ensure the overlay layer is visible
|
||||||
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
||||||
(void)m_display_service->GetContainer()->SetLayerStackMask(m_system_shared_layer_id,
|
|
||||||
this->GetLayerStackMask());
|
|
||||||
m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id);
|
m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id);
|
||||||
s32 initial_z = Foreground;
|
s32 initial_z = 1;
|
||||||
|
(void)m_display_service->GetContainer()->SetLayerZIndex(m_system_shared_layer_id, true);
|
||||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||||
initial_z = Overlay;
|
initial_z = -1;
|
||||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true);
|
(void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true);
|
||||||
}
|
}
|
||||||
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
|
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
@@ -73,8 +73,7 @@ Result IAllSystemAppletProxiesService::OpenLibraryAppletProxy(
|
|||||||
|
|
||||||
Result IAllSystemAppletProxiesService::OpenOverlayAppletProxy(
|
Result IAllSystemAppletProxiesService::OpenOverlayAppletProxy(
|
||||||
Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy, ClientProcessId pid,
|
Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy, ClientProcessId pid,
|
||||||
InCopyHandle<Kernel::KProcess> process_handle,
|
InCopyHandle<Kernel::KProcess> process_handle) {
|
||||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute) {
|
|
||||||
LOG_WARNING(Service_AM, "called");
|
LOG_WARNING(Service_AM, "called");
|
||||||
|
|
||||||
if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
|
if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
|
||||||
@@ -89,8 +88,7 @@ Result IAllSystemAppletProxiesService::OpenOverlayAppletProxy(
|
|||||||
|
|
||||||
Result IAllSystemAppletProxiesService::OpenSystemApplicationProxy(
|
Result IAllSystemAppletProxiesService::OpenSystemApplicationProxy(
|
||||||
Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid,
|
Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid,
|
||||||
InCopyHandle<Kernel::KProcess> process_handle,
|
InCopyHandle<Kernel::KProcess> process_handle) {
|
||||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute) {
|
|
||||||
LOG_DEBUG(Service_AM, "called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
|
|
||||||
if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
|
if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
|
||||||
|
|||||||
@@ -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: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
@@ -36,15 +36,13 @@ private:
|
|||||||
InCopyHandle<Kernel::KProcess> process_handle,
|
InCopyHandle<Kernel::KProcess> process_handle,
|
||||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
|
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
|
||||||
Result OpenOverlayAppletProxy(Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy,
|
Result OpenOverlayAppletProxy(Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy,
|
||||||
ClientProcessId pid, InCopyHandle<Kernel::KProcess> process_handle,
|
ClientProcessId pid, InCopyHandle<Kernel::KProcess> process_handle);
|
||||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
|
|
||||||
Result OpenLibraryAppletProxyOld(
|
Result OpenLibraryAppletProxyOld(
|
||||||
Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid,
|
Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid,
|
||||||
InCopyHandle<Kernel::KProcess> process_handle);
|
InCopyHandle<Kernel::KProcess> process_handle);
|
||||||
Result OpenSystemApplicationProxy(
|
Result OpenSystemApplicationProxy(
|
||||||
Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid,
|
Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid,
|
||||||
InCopyHandle<Kernel::KProcess> process_handle,
|
InCopyHandle<Kernel::KProcess> process_handle);
|
||||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
|
|
||||||
Result GetSystemProcessCommonFunctions();
|
Result GetSystemProcessCommonFunctions();
|
||||||
Result GetAppletAlternativeFunctions();
|
Result GetAppletAlternativeFunctions();
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
|
|||||||
{14, nullptr, "GetWakeupCount"}, //11.0.0+
|
{14, nullptr, "GetWakeupCount"}, //11.0.0+
|
||||||
{15, nullptr, "Unknown15"}, //19.0.0+
|
{15, nullptr, "Unknown15"}, //19.0.0+
|
||||||
{20, D<&ICommonStateGetter::PushToGeneralChannel>, "PushToGeneralChannel"},
|
{20, D<&ICommonStateGetter::PushToGeneralChannel>, "PushToGeneralChannel"},
|
||||||
{30, nullptr, "GetHomeButtonReaderLockAccessor"},
|
{30, D<&ICommonStateGetter::GetHomeButtonReaderLockAccessor>, "GetHomeButtonReaderLockAccessor"},
|
||||||
{31, D<&ICommonStateGetter::GetReaderLockAccessorEx>, "GetReaderLockAccessorEx"}, //2.0.0+
|
{31, D<&ICommonStateGetter::GetReaderLockAccessorEx>, "GetReaderLockAccessorEx"}, //2.0.0+
|
||||||
{32, D<&ICommonStateGetter::GetWriterLockAccessorEx>, "GetWriterLockAccessorEx"}, //7.0.0+
|
{32, D<&ICommonStateGetter::GetWriterLockAccessorEx>, "GetWriterLockAccessorEx"}, //7.0.0+
|
||||||
{40, nullptr, "GetCradleFwVersion"}, //2.0.0+
|
{40, nullptr, "GetCradleFwVersion"}, //2.0.0+
|
||||||
@@ -65,7 +65,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
|
|||||||
{100, D<&ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
|
{100, D<&ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
|
||||||
{110, nullptr, "OpenMyGpuErrorHandler"},
|
{110, nullptr, "OpenMyGpuErrorHandler"},
|
||||||
{120, D<&ICommonStateGetter::GetAppletLaunchedHistory>, "GetAppletLaunchedHistory"}, //13.0.0+
|
{120, D<&ICommonStateGetter::GetAppletLaunchedHistory>, "GetAppletLaunchedHistory"}, //13.0.0+
|
||||||
{130, nullptr, "Unknown130"}, //21.0.0+
|
{130, D<&ICommonStateGetter::EnableStartupLogoDisappearedMessage>, "EnableStartupLogoDisappearedMessage"}, //21.0.0+
|
||||||
{200, D<&ICommonStateGetter::GetOperationModeSystemInfo>, "GetOperationModeSystemInfo"},
|
{200, D<&ICommonStateGetter::GetOperationModeSystemInfo>, "GetOperationModeSystemInfo"},
|
||||||
{300, D<&ICommonStateGetter::GetSettingsPlatformRegion>, "GetSettingsPlatformRegion"},
|
{300, D<&ICommonStateGetter::GetSettingsPlatformRegion>, "GetSettingsPlatformRegion"},
|
||||||
{400, nullptr, "ActivateMigrationService"},
|
{400, nullptr, "ActivateMigrationService"},
|
||||||
@@ -74,14 +74,17 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
|
|||||||
{501, nullptr, "SuppressDisablingSleepTemporarily"},
|
{501, nullptr, "SuppressDisablingSleepTemporarily"},
|
||||||
{502, nullptr, "IsSleepEnabled"},
|
{502, nullptr, "IsSleepEnabled"},
|
||||||
{503, nullptr, "IsDisablingSleepSuppressed"},
|
{503, nullptr, "IsDisablingSleepSuppressed"},
|
||||||
{600, nullptr, "Unknown600"}, //20.0.0+
|
{600, nullptr, "SetHidInputMagnificationForApplication"}, //20.0.0+
|
||||||
{610, D<&ICommonStateGetter::Unknown610>, "Unknown610"}, //21.0.0+
|
{610, D<&ICommonStateGetter::Unknown610>, "Unknown610"}, //21.0.0+
|
||||||
{611, D<&ICommonStateGetter::Unknown611>, "Unknown611"}, //22.0.0+
|
{611, D<&ICommonStateGetter::Unknown611>, "Unknown611"}, //22.0.0+
|
||||||
{900, D<&ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled>, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"}, //11.0.0+
|
{900, D<&ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled>, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"}, //11.0.0+
|
||||||
{910, nullptr, "GetLaunchRequiredTick"}, //17.0.0+
|
{910, nullptr, "GetLaunchRequiredTick"}, //17.0.0+
|
||||||
{1000, nullptr, "BeginVrMode3d"}, //19.0.0+
|
{1000, D<&ICommonStateGetter::BeginVrMode3d>, "BeginVrMode3d"}, //19.0.0+
|
||||||
{1001, nullptr, "EndVrMode3d"}, //19.0.0+
|
{1001, D<&ICommonStateGetter::EndVrMode3d>, "EndVrMode3d"}, //19.0.0+
|
||||||
{1002, nullptr, "IsVrModeEnabled3d"}, //19.0.0+
|
{1002, D<&ICommonStateGetter::IsVrModeEnabled3d>, "IsVrModeEnabled3d"}, //19.0.0+
|
||||||
|
{1003, D<&ICommonStateGetter::GetVrLaboGoggleViewport>, "GetVrLaboGoggleViewport"}, //21.0.0+
|
||||||
|
{1004, D<&ICommonStateGetter::GetPanelPhysicalSizeForSpecificTitle>, "GetPanelPhysicalSizeForSpecificTitle"}, //21.0.0+
|
||||||
|
{1005, D<&ICommonStateGetter::GetPanelResolutionForSpecificTitle>, "GetPanelResolutionForSpecificTitle"}, //21.0.0+
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
@@ -311,6 +314,12 @@ Result ICommonStateGetter::PerformSystemButtonPressingIfInFocus(SystemButtonType
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result ICommonStateGetter::EnableStartupLogoDisappearedMessage() {
|
||||||
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
|
m_applet->lifecycle_manager.PushUnorderedMessage(system.Kernel(), AppletMessage::StartupLogoDisappeared);
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
Result ICommonStateGetter::GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info) {
|
Result ICommonStateGetter::GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info) {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
*out_operation_mode_system_info = 0;
|
*out_operation_mode_system_info = 0;
|
||||||
@@ -355,6 +364,12 @@ Result ICommonStateGetter::PushToGeneralChannel(SharedPointer<IStorage> storage)
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result ICommonStateGetter::GetHomeButtonReaderLockAccessor(Out<SharedPointer<ILockAccessor>> out_lock_accessor) {
|
||||||
|
LOG_DEBUG(Service_AM, "called");
|
||||||
|
*out_lock_accessor = std::make_shared<ILockAccessor>(system);
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
Result ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled(bool enabled) {
|
Result ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled(bool enabled) {
|
||||||
LOG_DEBUG(Service_AM, "called, enabled={} applet_id={}", enabled, m_applet->applet_id);
|
LOG_DEBUG(Service_AM, "called, enabled={} applet_id={}", enabled, m_applet->applet_id);
|
||||||
|
|
||||||
@@ -363,14 +378,58 @@ Result ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled(bool enabled
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ICommonStateGetter::Unknown610() {
|
Result ICommonStateGetter::Unknown610(u64 unk) {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ICommonStateGetter::Unknown611() {
|
Result ICommonStateGetter::Unknown611(u8 unk) {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result ICommonStateGetter::BeginVrMode3d() {
|
||||||
|
std::scoped_lock lk{m_applet->lock};
|
||||||
|
m_applet->vr_mode_enabled_3d = true;
|
||||||
|
LOG_WARNING(Service_AM, "VR Mode is {}", m_applet->vr_mode_enabled_3d ? "on" : "off");
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ICommonStateGetter::EndVrMode3d() {
|
||||||
|
std::scoped_lock lk{m_applet->lock};
|
||||||
|
m_applet->vr_mode_enabled_3d = false;
|
||||||
|
LOG_WARNING(Service_AM, "VR Mode is {}", m_applet->vr_mode_enabled_3d ? "on" : "off");
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ICommonStateGetter::IsVrModeEnabled3d(Out<bool> out_is_vr_mode_enabled_3d) {
|
||||||
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
|
std::scoped_lock lk{m_applet->lock};
|
||||||
|
*out_is_vr_mode_enabled_3d = m_applet->vr_mode_enabled_3d;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ICommonStateGetter::GetVrLaboGoggleViewport(Out<s32> out_x, Out<s32> out_y, Out<s32> out_width, Out<s32> out_height) {
|
||||||
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
|
*out_x = 0;
|
||||||
|
*out_y = 0;
|
||||||
|
*out_width = 1280;
|
||||||
|
*out_height = 720;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ICommonStateGetter::GetPanelPhysicalSizeForSpecificTitle(Out<f32> out_width, Out<f32> out_height) {
|
||||||
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
|
*out_width = 137250.0f / 1000.0f;
|
||||||
|
*out_height = 77200.0f / 1000.0f;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ICommonStateGetter::GetPanelResolutionForSpecificTitle(Out<s32> out_width, Out<s32> out_height) {
|
||||||
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
|
*out_width = 1280;
|
||||||
|
*out_height = 720;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Service::AM
|
} // namespace Service::AM
|
||||||
|
|||||||
@@ -56,15 +56,23 @@ private:
|
|||||||
Result GetDefaultDisplayResolution(Out<s32> out_width, Out<s32> out_height);
|
Result GetDefaultDisplayResolution(Out<s32> out_width, Out<s32> out_height);
|
||||||
Result GetBuiltInDisplayType(Out<s32> out_display_type);
|
Result GetBuiltInDisplayType(Out<s32> out_display_type);
|
||||||
Result PerformSystemButtonPressingIfInFocus(SystemButtonType type);
|
Result PerformSystemButtonPressingIfInFocus(SystemButtonType type);
|
||||||
|
Result EnableStartupLogoDisappearedMessage();
|
||||||
Result GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info);
|
Result GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info);
|
||||||
Result GetAppletLaunchedHistory(Out<s32> out_count,
|
Result GetAppletLaunchedHistory(Out<s32> out_count,
|
||||||
OutArray<AppletId, BufferAttr_HipcMapAlias> out_applet_ids);
|
OutArray<AppletId, BufferAttr_HipcMapAlias> out_applet_ids);
|
||||||
Result GetSettingsPlatformRegion(Out<Set::PlatformRegion> out_settings_platform_region);
|
Result GetSettingsPlatformRegion(Out<Set::PlatformRegion> out_settings_platform_region);
|
||||||
Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled();
|
Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled();
|
||||||
Result PushToGeneralChannel(SharedPointer<IStorage> storage); // cmd 20
|
Result PushToGeneralChannel(SharedPointer<IStorage> storage); // cmd 20
|
||||||
|
Result GetHomeButtonReaderLockAccessor(Out<SharedPointer<ILockAccessor>> out_lock_accessor);
|
||||||
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
|
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
|
||||||
Result Unknown610();
|
Result Unknown610(u64 unk);
|
||||||
Result Unknown611();
|
Result Unknown611(u8 unk);
|
||||||
|
Result BeginVrMode3d();
|
||||||
|
Result EndVrMode3d();
|
||||||
|
Result IsVrModeEnabled3d(Out<bool> out_is_vr_mode_enabled_3d);
|
||||||
|
Result GetVrLaboGoggleViewport(Out<s32> out_x, Out<s32> out_y, Out<s32> out_width, Out<s32> out_height);
|
||||||
|
Result GetPanelPhysicalSizeForSpecificTitle(Out<f32> out_width, Out<f32> out_height);
|
||||||
|
Result GetPanelResolutionForSpecificTitle(Out<s32> out_width, Out<s32> out_height);
|
||||||
|
|
||||||
void SetCpuBoostMode(HLERequestContext& ctx);
|
void SetCpuBoostMode(HLERequestContext& ctx);
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) {
|
|||||||
case AppletId::OverlayDisplay:
|
case AppletId::OverlayDisplay:
|
||||||
return AppletProgramId::OverlayDisplay;
|
return AppletProgramId::OverlayDisplay;
|
||||||
case AppletId::QLaunch:
|
case AppletId::QLaunch:
|
||||||
case AppletId::UlauncherUmenu: //reuses same id as Qlaunch
|
|
||||||
return AppletProgramId::QLaunch;
|
return AppletProgramId::QLaunch;
|
||||||
case AppletId::Starter:
|
case AppletId::Starter:
|
||||||
return AppletProgramId::Starter;
|
return AppletProgramId::Starter;
|
||||||
|
|||||||
@@ -8,23 +8,18 @@
|
|||||||
#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"
|
||||||
#include "core/file_sys/registered_cache.h"
|
#include "core/file_sys/registered_cache.h"
|
||||||
#include "core/hle/result.h"
|
|
||||||
#include "core/hle/service/acc/profile_manager.h"
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
#include "core/hle/service/am/applet_data_broker.h"
|
#include "core/hle/service/am/applet_data_broker.h"
|
||||||
#include "core/hle/service/am/applet_manager.h"
|
#include "core/hle/service/am/applet_manager.h"
|
||||||
#include "core/hle/service/am/frontend/applets.h"
|
#include "core/hle/service/am/frontend/applets.h"
|
||||||
#include "core/hle/service/am/process_creation.h"
|
|
||||||
#include "core/hle/service/am/service/library_applet_self_accessor.h"
|
#include "core/hle/service/am/service/library_applet_self_accessor.h"
|
||||||
#include "core/hle/service/am/service/storage.h"
|
#include "core/hle/service/am/service/storage.h"
|
||||||
#include "core/hle/service/am/window_system.h"
|
|
||||||
#include "core/hle/service/cmif_serialization.h"
|
#include "core/hle/service/cmif_serialization.h"
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
#include "core/hle/service/glue/glue_manager.h"
|
#include "core/hle/service/glue/glue_manager.h"
|
||||||
#include "core/hle/service/ns/application_manager_interface.h"
|
#include "core/hle/service/ns/application_manager_interface.h"
|
||||||
#include "core/hle/service/ns/service_getter_interface.h"
|
#include "core/hle/service/ns/service_getter_interface.h"
|
||||||
#include "core/hle/service/server_manager.h"
|
|
||||||
#include "core/hle/service/sm/sm.h"
|
#include "core/hle/service/sm/sm.h"
|
||||||
#include "core/loader/loader.h"
|
|
||||||
|
|
||||||
namespace Service::AM {
|
namespace Service::AM {
|
||||||
|
|
||||||
@@ -49,9 +44,8 @@ AppletIdentityInfo GetCallerIdentity(Applet& applet) {
|
|||||||
|
|
||||||
ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_,
|
ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_,
|
||||||
std::shared_ptr<Applet> applet)
|
std::shared_ptr<Applet> applet)
|
||||||
: ServiceFramework{system_, "ILibraryAppletSelfAccessor"}, m_applet{std::move(applet)}
|
: ServiceFramework{system_, "ILibraryAppletSelfAccessor"}, m_applet{std::move(applet)},
|
||||||
, m_broker{m_applet->caller_applet_broker}
|
m_broker{m_applet->caller_applet_broker} {
|
||||||
{
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, D<&ILibraryAppletSelfAccessor::PopInData>, "PopInData"},
|
{0, D<&ILibraryAppletSelfAccessor::PopInData>, "PopInData"},
|
||||||
@@ -102,128 +96,9 @@ Result ILibraryAppletSelfAccessor::PopInData(Out<SharedPointer<IStorage>> out_st
|
|||||||
R_RETURN(m_broker->GetInData().Pop(system.Kernel(), out_storage));
|
R_RETURN(m_broker->GetInData().Pop(system.Kernel(), out_storage));
|
||||||
}
|
}
|
||||||
|
|
||||||
// uLauncher emulation
|
|
||||||
static Result UloaderCreateApplication(Core::System& system, u64 program_id, std::shared_ptr<Applet> caller_applet) {
|
|
||||||
// Get the program NCA from storage.
|
|
||||||
auto& storage = system.GetContentProviderUnion();
|
|
||||||
FileSys::VirtualFile nca_raw = storage.GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
|
|
||||||
// Ensure we retrieved a program NCA.
|
|
||||||
R_UNLESS(nca_raw != nullptr, ResultUnknown);
|
|
||||||
std::vector<u8> control;
|
|
||||||
std::unique_ptr<Loader::AppLoader> loader;
|
|
||||||
Loader::ResultStatus result;
|
|
||||||
auto process = CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0);
|
|
||||||
R_UNLESS(process != nullptr, ResultUnknown);
|
|
||||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
|
||||||
applet->program_id = program_id;
|
|
||||||
applet->applet_id = AppletId::Application;
|
|
||||||
applet->type = AppletType::Application;
|
|
||||||
applet->library_applet_mode = LibraryAppletMode::AllForeground;
|
|
||||||
|
|
||||||
applet->caller_applet = caller_applet;
|
|
||||||
applet->caller_applet_broker = std::make_shared<AppletDataBroker>(system);
|
|
||||||
applet->frontend = caller_applet->frontend;
|
|
||||||
caller_applet->child_applets.push_back(applet);
|
|
||||||
|
|
||||||
system.GetAppletManager().GetWindowSystem()->TrackApplet(applet, true);
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ILibraryAppletSelfAccessor::PushOutData(SharedPointer<IStorage> storage) {
|
Result ILibraryAppletSelfAccessor::PushOutData(SharedPointer<IStorage> storage) {
|
||||||
LOG_INFO(Service_AM, "called");
|
LOG_INFO(Service_AM, "called");
|
||||||
m_broker->GetOutData().Push(system.Kernel(), storage);
|
m_broker->GetOutData().Push(system.Kernel(), storage);
|
||||||
if (m_applet->applet_id == AppletId::UlauncherUmenu) {
|
|
||||||
enum class SystemMessage : u32 {
|
|
||||||
Invalid,
|
|
||||||
SetSelectedUser,
|
|
||||||
LaunchApplication,
|
|
||||||
ResumeApplication,
|
|
||||||
TerminateApplication,
|
|
||||||
LaunchHomebrewLibraryApplet,
|
|
||||||
LaunchHomebrewApplication,
|
|
||||||
ChooseHomebrew,
|
|
||||||
OpenWebPage,
|
|
||||||
OpenAlbum,
|
|
||||||
RestartMenu,
|
|
||||||
ReloadConfig,
|
|
||||||
UpdateMenuPaths,
|
|
||||||
UpdateMenuIndex,
|
|
||||||
OpenUserPage,
|
|
||||||
OpenMiiEdit,
|
|
||||||
OpenAddUser,
|
|
||||||
OpenNetConnect,
|
|
||||||
ListAddedApplications,
|
|
||||||
ListDeletedApplications,
|
|
||||||
OpenCabinet,
|
|
||||||
StartVerifyApplication,
|
|
||||||
ListInVerifyApplications,
|
|
||||||
NotifyWarnedAboutOutdatedTheme,
|
|
||||||
TerminateMenu,
|
|
||||||
OpenControllerKeyRemapping
|
|
||||||
};
|
|
||||||
struct CommandCommonHeader {
|
|
||||||
u32 magic;
|
|
||||||
u32 val;
|
|
||||||
};
|
|
||||||
std::shared_ptr<IStorage> req_storage;
|
|
||||||
m_broker->GetOutData().Pop(&req_storage);
|
|
||||||
auto req_data = req_storage->GetData();
|
|
||||||
CommandCommonHeader req_cmd{};
|
|
||||||
std::memcpy(&req_cmd, req_data.data(), sizeof(req_cmd));
|
|
||||||
|
|
||||||
LOG_WARNING(Service_AM, "uLauncher:IPC {}, {}", req_cmd.magic, req_cmd.val);
|
|
||||||
switch (SystemMessage(req_cmd.val)) {
|
|
||||||
case SystemMessage::SetSelectedUser:
|
|
||||||
break;
|
|
||||||
case SystemMessage::LaunchApplication: {
|
|
||||||
// all applet proxies OpenSystemAppletProxy
|
|
||||||
// applet proxy GetApplicationCreator
|
|
||||||
// application creator CreateApplication
|
|
||||||
u64 args_value{};
|
|
||||||
std::memcpy(std::addressof(args_value), req_data.data() + sizeof(req_cmd), sizeof(args_value));
|
|
||||||
LOG_WARNING(Service_AM, "program_id={:016x}", args_value);
|
|
||||||
UloaderCreateApplication(system, args_value, m_applet);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SystemMessage::ResumeApplication:
|
|
||||||
case SystemMessage::TerminateApplication:
|
|
||||||
case SystemMessage::LaunchHomebrewLibraryApplet:
|
|
||||||
case SystemMessage::LaunchHomebrewApplication:
|
|
||||||
case SystemMessage::ChooseHomebrew:
|
|
||||||
case SystemMessage::OpenWebPage:
|
|
||||||
case SystemMessage::OpenAlbum:
|
|
||||||
case SystemMessage::RestartMenu:
|
|
||||||
case SystemMessage::ReloadConfig:
|
|
||||||
case SystemMessage::UpdateMenuPaths:
|
|
||||||
case SystemMessage::UpdateMenuIndex:
|
|
||||||
case SystemMessage::OpenUserPage:
|
|
||||||
case SystemMessage::OpenMiiEdit:
|
|
||||||
case SystemMessage::OpenAddUser:
|
|
||||||
case SystemMessage::OpenNetConnect:
|
|
||||||
case SystemMessage::ListAddedApplications:
|
|
||||||
case SystemMessage::ListDeletedApplications:
|
|
||||||
case SystemMessage::OpenCabinet:
|
|
||||||
case SystemMessage::StartVerifyApplication:
|
|
||||||
case SystemMessage::ListInVerifyApplications:
|
|
||||||
case SystemMessage::NotifyWarnedAboutOutdatedTheme:
|
|
||||||
break;
|
|
||||||
case SystemMessage::TerminateMenu:
|
|
||||||
system.GetUserChannel() = m_applet->user_channel_launch_parameter;
|
|
||||||
system.ExecuteProgram(0);
|
|
||||||
break;
|
|
||||||
case SystemMessage::OpenControllerKeyRemapping:
|
|
||||||
break;
|
|
||||||
case SystemMessage::Invalid:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
CommandCommonHeader res_cmd{};
|
|
||||||
std::vector<u8> res_data(0x8000);
|
|
||||||
res_cmd.magic = 0x21494D53;
|
|
||||||
res_cmd.val = u32(ResultSuccess.raw);
|
|
||||||
std::memcpy(res_data.data(), &res_cmd, sizeof(res_cmd));
|
|
||||||
m_broker->GetInData().Push(std::make_shared<IStorage>(system, std::move(res_data)));
|
|
||||||
}
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -26,17 +29,14 @@ IStorage::~IStorage() = default;
|
|||||||
|
|
||||||
Result IStorage::Open(Out<SharedPointer<IStorageAccessor>> out_storage_accessor) {
|
Result IStorage::Open(Out<SharedPointer<IStorageAccessor>> out_storage_accessor) {
|
||||||
LOG_DEBUG(Service_AM, "called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
|
|
||||||
R_UNLESS(m_impl->GetHandle() == nullptr, AM::ResultInvalidStorageType);
|
R_UNLESS(m_impl->GetHandle() == nullptr, AM::ResultInvalidStorageType);
|
||||||
|
|
||||||
*out_storage_accessor = std::make_shared<IStorageAccessor>(system, m_impl);
|
*out_storage_accessor = std::make_shared<IStorageAccessor>(system, m_impl);
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IStorage::OpenTransferStorage(
|
Result IStorage::OpenTransferStorage(Out<SharedPointer<ITransferStorageAccessor>> out_transfer_storage_accessor) {
|
||||||
Out<SharedPointer<ITransferStorageAccessor>> out_transfer_storage_accessor) {
|
LOG_DEBUG(Service_AM, "called");
|
||||||
R_UNLESS(m_impl->GetHandle() != nullptr, AM::ResultInvalidStorageType);
|
R_UNLESS(m_impl->GetHandle() != nullptr, AM::ResultInvalidStorageType);
|
||||||
|
|
||||||
*out_transfer_storage_accessor = std::make_shared<ITransferStorageAccessor>(system, m_impl);
|
*out_transfer_storage_accessor = std::make_shared<ITransferStorageAccessor>(system, m_impl);
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ void WindowSystem::TrackApplet(std::shared_ptr<Applet> applet, bool is_applicati
|
|||||||
{
|
{
|
||||||
std::scoped_lock lk{m_lock};
|
std::scoped_lock lk{m_lock};
|
||||||
|
|
||||||
if (applet->applet_id == AppletId::QLaunch || applet->applet_id == AppletId::UlauncherUmenu)) {
|
if (applet->applet_id == AppletId::QLaunch) {
|
||||||
ASSERT(m_home_menu == nullptr);
|
ASSERT(m_home_menu == nullptr);
|
||||||
m_home_menu = applet.get();
|
m_home_menu = applet.get();
|
||||||
} else if (applet->applet_id == AppletId::OverlayDisplay) {
|
} else if (applet->applet_id == AppletId::OverlayDisplay) {
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -340,7 +341,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);
|
||||||
@@ -798,7 +799,7 @@ void LoopProcess(Core::System& system) {
|
|||||||
const auto FileSystemProxyFactory = [&] { return std::make_shared<FSP_SRV>(system); };
|
const auto FileSystemProxyFactory = [&] { return std::make_shared<FSP_SRV>(system); };
|
||||||
|
|
||||||
server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system));
|
server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system));
|
||||||
server_manager->RegisterNamedService("fsp:pr", std::make_shared<FSP_PR>(system));
|
server_manager->RegisterNamedService("fsp-pr", std::make_shared<FSP_PR>(system));
|
||||||
server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory));
|
server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory));
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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{};
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ public:
|
|||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
}
|
}
|
||||||
~I2CSession() override = default;
|
~I2CSession() override = default;
|
||||||
|
|
||||||
Result Send(InBuffer<BufferAttr_HipcMapAlias> in_data, u32 transaction_option) {
|
Result Send(InBuffer<BufferAttr_HipcMapAlias> in_data, u32 transaction_option) {
|
||||||
LOG_WARNING(Service, "(stubbed) topt={}", transaction_option);
|
LOG_WARNING(Service, "(stubbed) topt={}", transaction_option);
|
||||||
R_THROW(ResultUnknown);
|
R_THROW(ResultUnknown);
|
||||||
@@ -50,21 +49,40 @@ public:
|
|||||||
: ServiceFramework{system_, "i2c"}
|
: ServiceFramework{system_, "i2c"}
|
||||||
{
|
{
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, nullptr, "OpenSessionForDev"},
|
{0, C<&I2C::OpenSessionForDev>, "OpenSessionForDev"},
|
||||||
{1, C<&I2C::OpenSession>, "OpenSession"},
|
{1, C<&I2C::OpenSession>, "OpenSession"},
|
||||||
{2, nullptr, "HasDevice"},
|
{2, C<&I2C::HasDevice>, "HasDevice"},
|
||||||
{3, nullptr, "HasDeviceForDev"},
|
{3, C<&I2C::HasDeviceForDev>, "HasDeviceForDev"},
|
||||||
{4, nullptr, "OpenSession2"},
|
{4, C<&I2C::OpenSession2>, "OpenSession2"},
|
||||||
};
|
};
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
}
|
}
|
||||||
~I2C() override = default;
|
~I2C() override = default;
|
||||||
|
Result OpenSessionForDev(OutInterface<I2CSession> out_session, s32 bus_idx, u32 slave_address, u32 addressing_mode, u32 speed_mode) {
|
||||||
Result OpenSession(I2CDevice device, OutInterface<I2CSession> out_session) {
|
|
||||||
LOG_DEBUG(Service, "(stubbed)");
|
LOG_DEBUG(Service, "(stubbed)");
|
||||||
*out_session = std::make_shared<I2CSession>(system);
|
*out_session = std::make_shared<I2CSession>(system);
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
Result OpenSession(OutInterface<I2CSession> out_session, I2CDevice device) {
|
||||||
|
LOG_DEBUG(Service, "(stubbed)");
|
||||||
|
*out_session = std::make_shared<I2CSession>(system);
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
Result HasDevice(Out<bool> out_has_device, I2CDevice device) {
|
||||||
|
LOG_DEBUG(Service, "(stubbed)");
|
||||||
|
*out_has_device = false;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
Result HasDeviceForDev(Out<bool> out_has_device, I2CDevice device) {
|
||||||
|
LOG_DEBUG(Service, "(stubbed)");
|
||||||
|
*out_has_device = false;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
Result OpenSession2(OutInterface<I2CSession> out_session, u32 device_code) {
|
||||||
|
LOG_DEBUG(Service, "(stubbed) device_code={}", device_code);
|
||||||
|
*out_session = std::make_shared<I2CSession>(system);
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include "core/hle/service/cmif_serialization.h"
|
|
||||||
#include "core/hle/service/ldn/ldn_results.h"
|
|
||||||
#include "core/hle/service/ldn/client_process_monitor.h"
|
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
|
||||||
|
|
||||||
namespace Service::LDN {
|
|
||||||
|
|
||||||
IClientProcessMonitor::IClientProcessMonitor(Core::System& system_)
|
|
||||||
: ServiceFramework{system_, "IClientProcessMonitor"} {
|
|
||||||
static const FunctionInfo functions[] = {
|
|
||||||
{0, D<&IClientProcessMonitor::RegisterClient>, "RegisterClient"},
|
|
||||||
};
|
|
||||||
RegisterHandlers(functions);
|
|
||||||
}
|
|
||||||
|
|
||||||
IClientProcessMonitor::~IClientProcessMonitor() = default;
|
|
||||||
|
|
||||||
Result IClientProcessMonitor::RegisterClient() {
|
|
||||||
LOG_WARNING(Service_LDN, "(STUBBED) called");
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Service::LDN
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "core/hle/service/cmif_types.h"
|
|
||||||
#include "core/hle/service/service.h"
|
|
||||||
|
|
||||||
namespace Core {
|
|
||||||
class System;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Service::LDN {
|
|
||||||
|
|
||||||
class IClientProcessMonitor final
|
|
||||||
: public ServiceFramework<IClientProcessMonitor> {
|
|
||||||
public:
|
|
||||||
explicit IClientProcessMonitor(Core::System& system_);
|
|
||||||
~IClientProcessMonitor() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Result RegisterClient();
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Service::LDN
|
|
||||||
@@ -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: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
@@ -7,7 +7,6 @@
|
|||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/service/cmif_serialization.h"
|
#include "core/hle/service/cmif_serialization.h"
|
||||||
#include "core/hle/service/ldn/ldn.h"
|
#include "core/hle/service/ldn/ldn.h"
|
||||||
#include "core/hle/service/ldn/client_process_monitor.h"
|
|
||||||
#include "core/hle/service/ldn/monitor_service.h"
|
#include "core/hle/service/ldn/monitor_service.h"
|
||||||
#include "core/hle/service/ldn/sf_monitor_service.h"
|
#include "core/hle/service/ldn/sf_monitor_service.h"
|
||||||
#include "core/hle/service/ldn/sf_service.h"
|
#include "core/hle/service/ldn/sf_service.h"
|
||||||
@@ -17,6 +16,26 @@
|
|||||||
|
|
||||||
namespace Service::LDN {
|
namespace Service::LDN {
|
||||||
|
|
||||||
|
class IClientProcessMonitor final
|
||||||
|
: public ServiceFramework<IClientProcessMonitor> {
|
||||||
|
public:
|
||||||
|
explicit IClientProcessMonitor(Core::System& system_)
|
||||||
|
: ServiceFramework{system_, "IClientProcessMonitor"} {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, D<&IClientProcessMonitor::RegisterClient>, "RegisterClient"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
~IClientProcessMonitor() override = default;
|
||||||
|
private:
|
||||||
|
Result RegisterClient(ClientProcessId pid) {
|
||||||
|
LOG_WARNING(Service_LDN, "(STUBBED) called");
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class IMonitorServiceCreator final : public ServiceFramework<IMonitorServiceCreator> {
|
class IMonitorServiceCreator final : public ServiceFramework<IMonitorServiceCreator> {
|
||||||
public:
|
public:
|
||||||
explicit IMonitorServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:m"} {
|
explicit IMonitorServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:m"} {
|
||||||
@@ -25,7 +44,6 @@ public:
|
|||||||
{0, C<&IMonitorServiceCreator::CreateMonitorService>, "CreateMonitorService"}
|
{0, C<&IMonitorServiceCreator::CreateMonitorService>, "CreateMonitorService"}
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
|||||||
{405, nullptr, "ListApplicationControlCacheEntryInfo"},
|
{405, nullptr, "ListApplicationControlCacheEntryInfo"},
|
||||||
{406, nullptr, "GetApplicationControlProperty"},
|
{406, nullptr, "GetApplicationControlProperty"},
|
||||||
{407, &IApplicationManagerInterface::ListApplicationTitle, "ListApplicationTitle"},
|
{407, &IApplicationManagerInterface::ListApplicationTitle, "ListApplicationTitle"},
|
||||||
{408, nullptr, "ListApplicationIcon"},
|
{408, &IApplicationManagerInterface::ListApplicationIcon, "ListApplicationIcon"},
|
||||||
{411, nullptr, "Unknown411"}, //19.0.0+
|
{411, nullptr, "Unknown411"}, //19.0.0+
|
||||||
{412, nullptr, "Unknown412"}, //19.0.0+
|
{412, nullptr, "Unknown412"}, //19.0.0+
|
||||||
{413, nullptr, "Unknown413"}, //19.0.0+
|
{413, nullptr, "Unknown413"}, //19.0.0+
|
||||||
@@ -894,4 +894,9 @@ void IApplicationManagerInterface::ListApplicationTitle(HLERequestContext& ctx)
|
|||||||
IReadOnlyApplicationControlDataInterface(system).ListApplicationTitle(ctx);
|
IReadOnlyApplicationControlDataInterface(system).ListApplicationTitle(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IApplicationManagerInterface::ListApplicationIcon(HLERequestContext& ctx) {
|
||||||
|
LOG_DEBUG(Service_NS, "called");
|
||||||
|
IReadOnlyApplicationControlDataInterface(system).ListApplicationIcon(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Service::NS
|
} // namespace Service::NS
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ public:
|
|||||||
|
|
||||||
void PushApplicationRecord(HLERequestContext& ctx);
|
void PushApplicationRecord(HLERequestContext& ctx);
|
||||||
void ListApplicationTitle(HLERequestContext& ctx);
|
void ListApplicationTitle(HLERequestContext& ctx);
|
||||||
|
void ListApplicationIcon(HLERequestContext& ctx);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
KernelHelpers::ServiceContext service_context;
|
KernelHelpers::ServiceContext service_context;
|
||||||
|
|||||||
@@ -10,7 +10,9 @@
|
|||||||
#include "core/hle/service/ns/query_service.h"
|
#include "core/hle/service/ns/query_service.h"
|
||||||
#include "core/hle/service/ns/service_getter_interface.h"
|
#include "core/hle/service/ns/service_getter_interface.h"
|
||||||
#include "core/hle/service/ns/system_update_interface.h"
|
#include "core/hle/service/ns/system_update_interface.h"
|
||||||
#include "core/hle/service/ns/vulnerability_manager_interface.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
|
#include "core/hle/service/cmif_serialization.h"
|
||||||
|
#include "core/hle/service/cmif_types.h"
|
||||||
#include "core/hle/service/server_manager.h"
|
#include "core/hle/service/server_manager.h"
|
||||||
|
|
||||||
namespace Service::NS {
|
namespace Service::NS {
|
||||||
@@ -39,6 +41,43 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IVulnerabilityManagerInterface final
|
||||||
|
: public ServiceFramework<IVulnerabilityManagerInterface> {
|
||||||
|
public:
|
||||||
|
explicit IVulnerabilityManagerInterface(Core::System& system_)
|
||||||
|
: ServiceFramework{system_, "ns:vm"} {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{1200, D<&IVulnerabilityManagerInterface::NeedsUpdateVulnerability>, "NeedsUpdateVulnerability"},
|
||||||
|
{1201, nullptr, "UpdateSafeSystemVersionForDebug"},
|
||||||
|
{1202, nullptr, "GetSafeSystemVersion"},
|
||||||
|
{3100, D<&IVulnerabilityManagerInterface::GetSafeSystemVersionCheckInfo>, "GetSafeSystemVersionCheckInfo"},
|
||||||
|
{3101, nullptr, "RequestUpdateSafeSystemVersionCheckInfo"},
|
||||||
|
{3102, D<&IVulnerabilityManagerInterface::ResetSafeSystemVersionCheckInfo>, "ResetSafeSystemVersionCheckInfo"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
~IVulnerabilityManagerInterface() override = default;
|
||||||
|
|
||||||
|
Result NeedsUpdateVulnerability(Out<bool> out_needs_update_vulnerability) {
|
||||||
|
LOG_WARNING(Service_NS, "(STUBBED)");
|
||||||
|
*out_needs_update_vulnerability = false;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetSafeSystemVersionCheckInfo(Out<std::array<u8, 0x20>> out_system_version_check_info) {
|
||||||
|
LOG_WARNING(Service_NS, "(STUBBED)");
|
||||||
|
*out_system_version_check_info = {};
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ResetSafeSystemVersionCheckInfo() {
|
||||||
|
LOG_WARNING(Service_NS, "(STUBBED)");
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,17 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "common/stb.h"
|
#include "common/stb.h"
|
||||||
|
#include "common/logging.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.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"
|
||||||
#include "core/file_sys/vfs/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
#include "core/hle/kernel/k_process.h"
|
#include "core/hle/kernel/k_process.h"
|
||||||
#include "core/hle/kernel/k_transfer_memory.h"
|
#include "core/hle/kernel/k_transfer_memory.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
#include "core/hle/service/cmif_serialization.h"
|
#include "core/hle/service/cmif_serialization.h"
|
||||||
|
#include "core/hle/service/cmif_types.h"
|
||||||
|
#include "core/hle/service/hle_ipc.h"
|
||||||
#include "core/hle/service/ns/language.h"
|
#include "core/hle/service/ns/language.h"
|
||||||
#include "core/hle/service/ns/ns_types.h"
|
#include "core/hle/service/ns/ns_types.h"
|
||||||
#include "core/hle/service/ns/ns_results.h"
|
#include "core/hle/service/ns/ns_results.h"
|
||||||
@@ -72,24 +76,26 @@ void SanitizeJPEGImageSize(std::vector<u8>& image) {
|
|||||||
|
|
||||||
// IAsyncValue implementation for ListApplicationTitle
|
// IAsyncValue implementation for ListApplicationTitle
|
||||||
// https://switchbrew.org/wiki/NS_services#ListApplicationTitle
|
// https://switchbrew.org/wiki/NS_services#ListApplicationTitle
|
||||||
class IAsyncValueForListApplicationTitle final : public ServiceFramework<IAsyncValueForListApplicationTitle> {
|
class IAsyncValue final : public ServiceFramework<IAsyncValue> {
|
||||||
public:
|
public:
|
||||||
explicit IAsyncValueForListApplicationTitle(Core::System& system_, s32 offset, s32 size)
|
explicit IAsyncValue(Core::System& system_, s32 offset, s32 size)
|
||||||
: ServiceFramework{system_, "IAsyncValue"}, service_context{system_, "IAsyncValue"},
|
: ServiceFramework{system_, "IAsyncValue"}
|
||||||
data_offset{offset}, data_size{size} {
|
, service_context{system_, "IAsyncValue"}
|
||||||
|
, data_offset{offset}
|
||||||
|
, data_size{size}
|
||||||
|
{
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &IAsyncValueForListApplicationTitle::GetSize, "GetSize"},
|
{0, D<&IAsyncValue::GetSize>, "GetSize"},
|
||||||
{1, &IAsyncValueForListApplicationTitle::Get, "Get"},
|
{1, D<&IAsyncValue::Get>, "Get"},
|
||||||
{2, &IAsyncValueForListApplicationTitle::Cancel, "Cancel"},
|
{2, D<&IAsyncValue::Cancel>, "Cancel"},
|
||||||
{3, &IAsyncValueForListApplicationTitle::GetErrorContext, "GetErrorContext"},
|
{3, D<&IAsyncValue::GetErrorContext>, "GetErrorContext"},
|
||||||
};
|
};
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
|
|
||||||
completion_event = service_context.CreateEvent("IAsyncValue:Completion");
|
completion_event = service_context.CreateEvent("IAsyncValue:Completion");
|
||||||
completion_event->GetReadableEvent().Signal(system.Kernel());
|
completion_event->GetReadableEvent().Signal(system.Kernel());
|
||||||
}
|
}
|
||||||
|
|
||||||
~IAsyncValueForListApplicationTitle() override {
|
~IAsyncValue() override {
|
||||||
service_context.CloseEvent(completion_event);
|
service_context.CloseEvent(completion_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,35 +104,24 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void GetSize(HLERequestContext& ctx) {
|
Result GetSize(Out<s64> out_data_size) {
|
||||||
LOG_DEBUG(Service_NS, "called");
|
LOG_DEBUG(Service_NS, "called");
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
*out_data_size = data_size;
|
||||||
rb.Push(ResultSuccess);
|
R_SUCCEED();
|
||||||
rb.Push<s64>(data_size);
|
|
||||||
}
|
}
|
||||||
|
Result Get(OutBuffer<BufferAttr_HipcMapAlias> out_data_offset) {
|
||||||
void Get(HLERequestContext& ctx) {
|
|
||||||
LOG_DEBUG(Service_NS, "called");
|
LOG_DEBUG(Service_NS, "called");
|
||||||
std::vector<u8> buffer(sizeof(s32));
|
std::memcpy(out_data_offset.data(), &data_offset, sizeof(s32));
|
||||||
std::memcpy(buffer.data(), &data_offset, sizeof(s32));
|
R_SUCCEED();
|
||||||
ctx.WriteBuffer(buffer);
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
}
|
}
|
||||||
|
Result Cancel() {
|
||||||
void Cancel(HLERequestContext& ctx) {
|
|
||||||
LOG_DEBUG(Service_NS, "called");
|
LOG_DEBUG(Service_NS, "called");
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
R_SUCCEED();
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
}
|
}
|
||||||
|
Result GetErrorContext() {
|
||||||
void GetErrorContext(HLERequestContext& ctx) {
|
|
||||||
LOG_DEBUG(Service_NS, "called");
|
LOG_DEBUG(Service_NS, "called");
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
R_SUCCEED();
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KernelHelpers::ServiceContext service_context;
|
KernelHelpers::ServiceContext service_context;
|
||||||
Kernel::KEvent* completion_event{};
|
Kernel::KEvent* completion_event{};
|
||||||
s32 data_offset;
|
s32 data_offset;
|
||||||
@@ -144,6 +139,7 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
|
|||||||
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
|
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
|
||||||
{4, nullptr, "SelectApplicationDesiredLanguage"},
|
{4, nullptr, "SelectApplicationDesiredLanguage"},
|
||||||
{5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData2>, "GetApplicationControlData"},
|
{5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData2>, "GetApplicationControlData"},
|
||||||
|
{10, &IReadOnlyApplicationControlDataInterface::ListApplicationIcon, "ListApplicationIcon"},
|
||||||
{13, &IReadOnlyApplicationControlDataInterface::ListApplicationTitle, "ListApplicationTitle"},
|
{13, &IReadOnlyApplicationControlDataInterface::ListApplicationTitle, "ListApplicationTitle"},
|
||||||
{19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData3>, "GetApplicationControlData"},
|
{19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData3>, "GetApplicationControlData"},
|
||||||
};
|
};
|
||||||
@@ -160,8 +156,7 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
|
|||||||
LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}",
|
LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}",
|
||||||
application_control_source, application_id);
|
application_control_source, application_id);
|
||||||
|
|
||||||
const FileSys::PatchManager pm{application_id, system.GetFileSystemController(),
|
const FileSys::PatchManager pm{application_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||||
system.GetContentProvider()};
|
|
||||||
const auto control = pm.GetControlMetadata();
|
const auto control = pm.GetControlMetadata();
|
||||||
const auto size = out_buffer.size();
|
const auto size = out_buffer.size();
|
||||||
|
|
||||||
@@ -169,8 +164,7 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
|
|||||||
const auto total_size = sizeof(FileSys::RawNACP) + icon_size;
|
const auto total_size = sizeof(FileSys::RawNACP) + icon_size;
|
||||||
|
|
||||||
if (size < total_size) {
|
if (size < total_size) {
|
||||||
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min=0x4000)",
|
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min=0x4000)", size);
|
||||||
size);
|
|
||||||
R_THROW(ResultUnknown);
|
R_THROW(ResultUnknown);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,8 +172,7 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
|
|||||||
const auto bytes = control.first->GetRawBytes();
|
const auto bytes = control.first->GetRawBytes();
|
||||||
std::memcpy(out_buffer.data(), bytes.data(), bytes.size());
|
std::memcpy(out_buffer.data(), bytes.data(), bytes.size());
|
||||||
} else {
|
} else {
|
||||||
LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}, defaulting to zero",
|
LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}, defaulting to zero", application_id);
|
||||||
application_id);
|
|
||||||
std::memset(out_buffer.data(), 0, sizeof(FileSys::RawNACP));
|
std::memset(out_buffer.data(), 0, sizeof(FileSys::RawNACP));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,15 +197,12 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationDesiredLanguage(
|
|||||||
// Convert to application language, get priority list
|
// Convert to application language, get priority list
|
||||||
const auto application_language = ConvertToApplicationLanguage(language_code);
|
const auto application_language = ConvertToApplicationLanguage(language_code);
|
||||||
if (application_language == std::nullopt) {
|
if (application_language == std::nullopt) {
|
||||||
LOG_ERROR(Service_NS, "Could not convert application language! language_code={}",
|
LOG_ERROR(Service_NS, "Could not convert application language! language_code={}", language_code);
|
||||||
language_code);
|
|
||||||
R_THROW(Service::NS::ResultApplicationLanguageNotFound);
|
R_THROW(Service::NS::ResultApplicationLanguageNotFound);
|
||||||
}
|
}
|
||||||
const auto priority_list = GetApplicationLanguagePriorityList(*application_language);
|
const auto priority_list = GetApplicationLanguagePriorityList(*application_language);
|
||||||
if (!priority_list) {
|
if (!priority_list) {
|
||||||
LOG_ERROR(Service_NS,
|
LOG_ERROR(Service_NS, "Could not find application language priorities! application_language={}", *application_language);
|
||||||
"Could not find application language priorities! application_language={}",
|
|
||||||
*application_language);
|
|
||||||
R_THROW(Service::NS::ResultApplicationLanguageNotFound);
|
R_THROW(Service::NS::ResultApplicationLanguageNotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,8 +246,7 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData2(
|
|||||||
const auto nacp_size = sizeof(FileSys::RawNACP);
|
const auto nacp_size = sizeof(FileSys::RawNACP);
|
||||||
|
|
||||||
if (size < nacp_size) {
|
if (size < nacp_size) {
|
||||||
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min={:08X})",
|
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min={:08X})", size, nacp_size);
|
||||||
size, nacp_size);
|
|
||||||
R_THROW(ResultUnknown);
|
R_THROW(ResultUnknown);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,63 +297,81 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData2(
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IReadOnlyApplicationControlDataInterface::ListApplicationIcon(HLERequestContext& ctx) {
|
||||||
void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) {
|
LOG_WARNING(Service_NS, "(stubbed)");
|
||||||
/*
|
|
||||||
IPC::RequestParser rp{ctx};
|
|
||||||
auto control_source = rp.PopRaw<u8>();
|
|
||||||
rp.Skip(7, false);
|
|
||||||
auto transfer_memory_size = rp.Pop<u64>();
|
|
||||||
*/
|
|
||||||
|
|
||||||
const auto app_ids_buffer = ctx.ReadBuffer();
|
const auto app_ids_buffer = ctx.ReadBuffer();
|
||||||
const size_t app_count = app_ids_buffer.size() / sizeof(u64);
|
const u64 app_count = app_ids_buffer.size() / sizeof(u64);
|
||||||
|
|
||||||
std::vector<u64> application_ids(app_count);
|
|
||||||
if (app_count > 0) {
|
|
||||||
std::memcpy(application_ids.data(), app_ids_buffer.data(), app_count * sizeof(u64));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto t_mem_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0));
|
auto t_mem_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0));
|
||||||
auto* t_mem = t_mem_obj.GetPointerUnsafe();
|
auto* t_mem = t_mem_obj.GetPointerUnsafe();
|
||||||
|
size_t out_length = 0;
|
||||||
constexpr size_t title_entry_size = sizeof(FileSys::LanguageEntry);
|
|
||||||
const size_t total_data_size = app_count * title_entry_size;
|
|
||||||
|
|
||||||
constexpr s32 data_offset = 0;
|
|
||||||
|
|
||||||
if (t_mem != nullptr && t_mem->GetOwner() != nullptr && app_count > 0) {
|
if (t_mem != nullptr && t_mem->GetOwner() != nullptr && app_count > 0) {
|
||||||
auto& memory = t_mem->GetOwner()->GetMemory();
|
auto& memory = t_mem->GetOwner()->GetMemory();
|
||||||
const auto t_mem_address = t_mem->GetSourceAddress();
|
const auto t_mem_address = t_mem->GetSourceAddress();
|
||||||
|
// u64 - app count
|
||||||
|
memory.WriteBlock(t_mem_address + out_length, &app_count, sizeof(u64));
|
||||||
|
out_length += sizeof(u64);
|
||||||
|
// [list of u64] - size of icons
|
||||||
for (size_t i = 0; i < app_count; ++i) {
|
for (size_t i = 0; i < app_count; ++i) {
|
||||||
const u64 app_id = application_ids[i];
|
const u64 app_id = app_ids_buffer[i];
|
||||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(),
|
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||||
system.GetContentProvider()};
|
|
||||||
const auto control = pm.GetControlMetadata();
|
const auto control = pm.GetControlMetadata();
|
||||||
|
u64 full_size = control.second->GetSize();
|
||||||
FileSys::LanguageEntry entry{};
|
memory.WriteBlock(t_mem_address + out_length, &full_size, sizeof(u64));
|
||||||
if (control.first != nullptr) {
|
out_length += sizeof(u64);
|
||||||
entry = control.first->GetLanguageEntry();
|
}
|
||||||
|
// [list of raw icon data]
|
||||||
|
std::vector<u8> full_icon_data;
|
||||||
|
for (size_t i = 0; i < app_count; ++i) {
|
||||||
|
const u64 app_id = app_ids_buffer[i];
|
||||||
|
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||||
|
const auto control = pm.GetControlMetadata();
|
||||||
|
auto const full_size = control.second->GetSize();
|
||||||
|
if (full_size > 0) {
|
||||||
|
full_icon_data.resize(full_size);
|
||||||
|
control.second->Read(full_icon_data.data(), full_size, 0);
|
||||||
|
memory.WriteBlock(t_mem_address + out_length, full_icon_data.data(), full_size);
|
||||||
|
out_length += full_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t offset = i * title_entry_size;
|
|
||||||
memory.WriteBlock(t_mem_address + offset, &entry, title_entry_size);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
auto async_value = std::make_shared<IAsyncValue>(system, 0, s32(out_length));
|
||||||
auto async_value = std::make_shared<IAsyncValueForListApplicationTitle>(
|
|
||||||
system, data_offset, static_cast<s32>(total_data_size));
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 1, 1};
|
IPC::ResponseBuilder rb{ctx, 2, 1, 1};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.PushCopyObjects(ctx, async_value->ReadableEvent());
|
rb.PushCopyObjects(ctx, async_value->ReadableEvent());
|
||||||
rb.PushIpcInterface(ctx, std::move(async_value));
|
rb.PushIpcInterface(ctx, std::move(async_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3(
|
void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) {
|
||||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_flags_a, Out<u32> out_flags_b,
|
const auto app_ids_buffer = ctx.ReadBuffer();
|
||||||
Out<u32> out_actual_size, ApplicationControlSource application_control_source, u8 flag1, u8 flag2, u64 application_id) {
|
const size_t app_count = app_ids_buffer.size() / sizeof(u64);
|
||||||
|
auto t_mem_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0));
|
||||||
|
auto* t_mem = t_mem_obj.GetPointerUnsafe();
|
||||||
|
constexpr size_t title_entry_size = sizeof(FileSys::LanguageEntry);
|
||||||
|
const size_t total_data_size = app_count * title_entry_size;
|
||||||
|
constexpr s32 data_offset = 0;
|
||||||
|
if (t_mem != nullptr && app_count > 0) {
|
||||||
|
auto& memory = system.ApplicationMemory();
|
||||||
|
const auto t_mem_address = t_mem->GetSourceAddress();
|
||||||
|
for (size_t i = 0; i < app_count; ++i) {
|
||||||
|
const u64 app_id = app_ids_buffer[i];
|
||||||
|
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||||
|
const auto control = pm.GetControlMetadata();
|
||||||
|
FileSys::LanguageEntry entry{};
|
||||||
|
if (control.first != nullptr) {
|
||||||
|
entry = control.first->GetLanguageEntry();
|
||||||
|
}
|
||||||
|
const size_t offset = i * title_entry_size;
|
||||||
|
memory.WriteBlock(t_mem_address + offset, &entry, title_entry_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto async_value = std::make_shared<IAsyncValue>(system, data_offset, s32(total_data_size));
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2, 1, 1};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.PushCopyObjects(ctx, async_value->ReadableEvent());
|
||||||
|
rb.PushIpcInterface(ctx, std::move(async_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3(OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_flags_a, Out<u32> out_flags_b, Out<u32> out_actual_size, ApplicationControlSource application_control_source, u8 flag1, u8 flag2, u64 application_id) {
|
||||||
LOG_INFO(Service_NS, "called with control_source={}, flags=({:02X},{:02X}), application_id={:016X}",
|
LOG_INFO(Service_NS, "called with control_source={}, flags=({:02X},{:02X}), application_id={:016X}",
|
||||||
application_control_source, flag1, flag2, application_id);
|
application_control_source, flag1, flag2, application_id);
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ public:
|
|||||||
u8 flag1,
|
u8 flag1,
|
||||||
u8 flag2,
|
u8 flag2,
|
||||||
u64 application_id);
|
u64 application_id);
|
||||||
|
void ListApplicationIcon(HLERequestContext& ctx);
|
||||||
void ListApplicationTitle(HLERequestContext& ctx);
|
void ListApplicationTitle(HLERequestContext& ctx);
|
||||||
Result GetApplicationControlData3(
|
Result GetApplicationControlData3(
|
||||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||||
|
|||||||
@@ -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: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
@@ -16,10 +16,8 @@ IReadOnlyApplicationRecordInterface::IReadOnlyApplicationRecordInterface(Core::S
|
|||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, D<&IReadOnlyApplicationRecordInterface::HasApplicationRecord>, "HasApplicationRecord"},
|
{0, D<&IReadOnlyApplicationRecordInterface::HasApplicationRecord>, "HasApplicationRecord"},
|
||||||
{1, nullptr, "NotifyApplicationFailure"},
|
{1, nullptr, "NotifyApplicationFailure"},
|
||||||
{2, D<&IReadOnlyApplicationRecordInterface::IsDataCorruptedResult>,
|
{2, D<&IReadOnlyApplicationRecordInterface::IsDataCorruptedResult>, "IsDataCorruptedResult"},
|
||||||
"IsDataCorruptedResult"},
|
{3, D<&IReadOnlyApplicationRecordInterface::ListApplicationRecord>, "ListApplicationRecord"},
|
||||||
{3, D<&IReadOnlyApplicationRecordInterface::ListApplicationRecord>,
|
|
||||||
"ListApplicationRecord"},
|
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#include "core/hle/service/cmif_serialization.h"
|
|
||||||
#include "core/hle/service/ns/vulnerability_manager_interface.h"
|
|
||||||
|
|
||||||
namespace Service::NS {
|
|
||||||
|
|
||||||
IVulnerabilityManagerInterface::IVulnerabilityManagerInterface(Core::System& system_)
|
|
||||||
: ServiceFramework{system_, "ns:vm"} {
|
|
||||||
// clang-format off
|
|
||||||
static const FunctionInfo functions[] = {
|
|
||||||
{1200, D<&IVulnerabilityManagerInterface::NeedsUpdateVulnerability>, "NeedsUpdateVulnerability"},
|
|
||||||
{1201, nullptr, "UpdateSafeSystemVersionForDebug"},
|
|
||||||
{1202, nullptr, "GetSafeSystemVersion"},
|
|
||||||
};
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
RegisterHandlers(functions);
|
|
||||||
}
|
|
||||||
|
|
||||||
IVulnerabilityManagerInterface::~IVulnerabilityManagerInterface() = default;
|
|
||||||
|
|
||||||
Result IVulnerabilityManagerInterface::NeedsUpdateVulnerability(
|
|
||||||
Out<bool> out_needs_update_vulnerability) {
|
|
||||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
|
||||||
*out_needs_update_vulnerability = false;
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Service::NS
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "core/hle/service/cmif_types.h"
|
|
||||||
#include "core/hle/service/service.h"
|
|
||||||
|
|
||||||
namespace Service::NS {
|
|
||||||
|
|
||||||
class IVulnerabilityManagerInterface final
|
|
||||||
: public ServiceFramework<IVulnerabilityManagerInterface> {
|
|
||||||
public:
|
|
||||||
explicit IVulnerabilityManagerInterface(Core::System& system_);
|
|
||||||
~IVulnerabilityManagerInterface() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Result NeedsUpdateVulnerability(Out<bool> out_needs_update_vulnerability);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Service::NS
|
|
||||||
@@ -66,7 +66,6 @@
|
|||||||
#include "core/hle/service/ssl/ssl.h"
|
#include "core/hle/service/ssl/ssl.h"
|
||||||
#include "core/hle/service/wlan/wlan.h"
|
#include "core/hle/service/wlan/wlan.h"
|
||||||
#include "core/hle/service/tma/tma.h"
|
#include "core/hle/service/tma/tma.h"
|
||||||
#include "core/hle/service/ulsf/ulsf.h"
|
|
||||||
#include "core/hle/service/usb/usb.h"
|
#include "core/hle/service/usb/usb.h"
|
||||||
#include "core/hle/service/vi/vi.h"
|
#include "core/hle/service/vi/vi.h"
|
||||||
|
|
||||||
@@ -153,8 +152,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
|
|||||||
{"tma", &TMA::LoopProcess},
|
{"tma", &TMA::LoopProcess},
|
||||||
{"usb", &USB::LoopProcess},
|
{"usb", &USB::LoopProcess},
|
||||||
{"i2c", &I2C::LoopProcess},
|
{"i2c", &I2C::LoopProcess},
|
||||||
{"gpio", &GPIO::LoopProcess},
|
{"gpio", &GPIO::LoopProcess},
|
||||||
{"ulsf", &ULSF::LoopProcess},
|
|
||||||
})
|
})
|
||||||
kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); });
|
kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -30,15 +33,15 @@ struct DeviceSettings {
|
|||||||
INSERT_PADDING_BYTES(0x20); // Reserved
|
INSERT_PADDING_BYTES(0x20); // Reserved
|
||||||
|
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAccelerationBias
|
// nn::settings::system::ConsoleSixAxisSensorAccelerationBias
|
||||||
Common::Vec3<f32> console_six_axis_sensor_acceleration_bias;
|
Common::Vec<f32, 3> console_six_axis_sensor_acceleration_bias;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias
|
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias
|
||||||
Common::Vec3<f32> console_six_axis_sensor_angular_velocity_bias;
|
Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_bias;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAccelerationGain
|
// nn::settings::system::ConsoleSixAxisSensorAccelerationGain
|
||||||
std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
|
std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
|
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
|
||||||
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
|
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias
|
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias
|
||||||
Common::Vec3<f32> console_six_axis_sensor_angular_velocity_time_bias;
|
Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_time_bias;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
|
// nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
|
||||||
std::array<u8, 0x24> console_six_axis_sensor_angular_acceleration;
|
std::array<u8, 0x24> console_six_axis_sensor_angular_acceleration;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
@@ -153,15 +153,15 @@ struct SystemSettings {
|
|||||||
INSERT_PADDING_BYTES(0x7FF8); // Reserved
|
INSERT_PADDING_BYTES(0x7FF8); // Reserved
|
||||||
|
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAccelerationBias
|
// nn::settings::system::ConsoleSixAxisSensorAccelerationBias
|
||||||
Common::Vec3<f32> console_six_axis_sensor_acceleration_bias;
|
Common::Vec<f32, 3> console_six_axis_sensor_acceleration_bias;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias
|
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias
|
||||||
Common::Vec3<f32> console_six_axis_sensor_angular_velocity_bias;
|
Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_bias;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAccelerationGain
|
// nn::settings::system::ConsoleSixAxisSensorAccelerationGain
|
||||||
std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
|
std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
|
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
|
||||||
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
|
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias
|
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias
|
||||||
Common::Vec3<f32> console_six_axis_sensor_angular_velocity_time_bias;
|
Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_time_bias;
|
||||||
// nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
|
// nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
|
||||||
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_acceleration;
|
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_acceleration;
|
||||||
INSERT_PADDING_BYTES(0x70); // Reserved
|
INSERT_PADDING_BYTES(0x70); // Reserved
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#include "core/hle/service/spl/csrng.h"
|
|
||||||
|
|
||||||
namespace Service::SPL {
|
|
||||||
|
|
||||||
CSRNG::CSRNG(Core::System& system_, std::shared_ptr<Module> module_)
|
|
||||||
: Interface(system_, std::move(module_), "csrng") {
|
|
||||||
static const FunctionInfo functions[] = {
|
|
||||||
{0, &CSRNG::GenerateRandomBytes, "GenerateRandomBytes"},
|
|
||||||
};
|
|
||||||
RegisterHandlers(functions);
|
|
||||||
}
|
|
||||||
|
|
||||||
CSRNG::~CSRNG() = default;
|
|
||||||
|
|
||||||
} // namespace Service::SPL
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "core/hle/service/spl/spl_module.h"
|
|
||||||
|
|
||||||
namespace Core {
|
|
||||||
class System;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Service::SPL {
|
|
||||||
|
|
||||||
class CSRNG final : public Module::Interface {
|
|
||||||
public:
|
|
||||||
explicit CSRNG(Core::System& system_, std::shared_ptr<Module> module_);
|
|
||||||
~CSRNG() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Service::SPL
|
|
||||||
@@ -13,7 +13,6 @@
|
|||||||
#include "core/hle/api_version.h"
|
#include "core/hle/api_version.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
#include "core/hle/service/server_manager.h"
|
#include "core/hle/service/server_manager.h"
|
||||||
#include "core/hle/service/spl/csrng.h"
|
|
||||||
#include "core/hle/service/spl/spl.h"
|
#include "core/hle/service/spl/spl.h"
|
||||||
#include "core/hle/service/spl/spl_module.h"
|
#include "core/hle/service/spl/spl_module.h"
|
||||||
|
|
||||||
@@ -203,6 +202,18 @@ Result Module::Interface::GetConfigImpl(u64* out_config, ConfigItem config_item)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CSRNG final : public Module::Interface {
|
||||||
|
public:
|
||||||
|
explicit CSRNG(Core::System& system_, std::shared_ptr<Module> module_)
|
||||||
|
: Interface(system_, std::move(module_), "csrng") {
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, &CSRNG::GenerateRandomBytes, "GenerateRandomBytes"},
|
||||||
|
};
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
~CSRNG() override = default;
|
||||||
|
};
|
||||||
|
|
||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
auto module = std::make_shared<Module>();
|
auto module = std::make_shared<Module>();
|
||||||
|
|||||||
@@ -1,137 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include "core/hle/result.h"
|
|
||||||
#include "core/hle/service/cmif_serialization.h"
|
|
||||||
#include "core/hle/service/cmif_types.h"
|
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
|
||||||
#include "core/hle/service/ulsf/ulsf.h"
|
|
||||||
#include "core/hle/service/server_manager.h"
|
|
||||||
|
|
||||||
namespace Service::ULSF {
|
|
||||||
|
|
||||||
ULSF_U::ULSF_U(Core::System& system_) : ServiceFramework{system_, "ulsf:u"} {
|
|
||||||
static const FunctionInfo functions[] = {
|
|
||||||
{0, &ULSF_U::GetVersion, "GetVersion"},
|
|
||||||
};
|
|
||||||
RegisterHandlers(functions);
|
|
||||||
}
|
|
||||||
ULSF_U::~ULSF_U() = default;
|
|
||||||
|
|
||||||
// Result ULSF_U::GetVersion(Out<ULauncherVersion> out_version) {
|
|
||||||
// LOG_WARNING(Service_SM, "(stubbed)");
|
|
||||||
// *out_version = {};
|
|
||||||
// R_SUCCEED();
|
|
||||||
// }
|
|
||||||
|
|
||||||
void ULSF_U::GetVersion(HLERequestContext& ctx) {
|
|
||||||
LOG_WARNING(Service_SM, "(stubbed)");
|
|
||||||
ULauncherVersion version{1, 6, 7};
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
rb.PushRaw(version);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class MenuMessage : u32 {
|
|
||||||
Invalid,
|
|
||||||
HomeRequest,
|
|
||||||
SdCardEjected,
|
|
||||||
GameCardMountFailure,
|
|
||||||
PreviousLaunchFailure,
|
|
||||||
ChosenHomebrew,
|
|
||||||
FinishedSleep,
|
|
||||||
ApplicationRecordsChanged,
|
|
||||||
ApplicationVerifyProgress,
|
|
||||||
ApplicationVerifyResult
|
|
||||||
};
|
|
||||||
struct MenuMessageContext {
|
|
||||||
MenuMessage msg;
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
Result mount_rc;
|
|
||||||
} gc_mount_failure;
|
|
||||||
struct {
|
|
||||||
char nro_path[0x301];
|
|
||||||
} chosen_hb;
|
|
||||||
struct {
|
|
||||||
bool records_added_or_deleted;
|
|
||||||
} app_records_changed;
|
|
||||||
struct {
|
|
||||||
u64 app_id;
|
|
||||||
u64 done;
|
|
||||||
u64 total;
|
|
||||||
} app_verify_progress;
|
|
||||||
struct {
|
|
||||||
u64 app_id;
|
|
||||||
Result rc;
|
|
||||||
Result detail_rc;
|
|
||||||
} app_verify_rc;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class ULSF_P final : public ServiceFramework<ULSF_P> {
|
|
||||||
public:
|
|
||||||
explicit ULSF_P(Core::System& system_) : ServiceFramework{system_, "ulsf:p"} {
|
|
||||||
static const FunctionInfo functions[] = {
|
|
||||||
{0, C<&ULSF_P::Initialize>, "Initialize"},
|
|
||||||
{1, C<&ULSF_P::TryPopMessageContext>, "TryPopMessageContext"},
|
|
||||||
};
|
|
||||||
RegisterHandlers(functions);
|
|
||||||
}
|
|
||||||
~ULSF_P() = default;
|
|
||||||
|
|
||||||
Result Initialize(u64 pid) {
|
|
||||||
LOG_WARNING(Service_SM, "(stubbed) pid={}", pid);
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
Result TryPopMessageContext(OutLargeData<MenuMessageContext, BufferAttr_HipcMapAlias> out_menu_message) {
|
|
||||||
//LOG_WARNING(Service_SM, "(stubbed)");
|
|
||||||
// *out_menu_message = {};
|
|
||||||
// R_SUCCEED();
|
|
||||||
R_THROW(Kernel::ResultInvalidAddress);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class AVM final : public ServiceFramework<AVM> {
|
|
||||||
public:
|
|
||||||
explicit AVM(Core::System& system_): ServiceFramework{system_, "avm"} {
|
|
||||||
static const FunctionInfo functions[] = {
|
|
||||||
{0, &AVM::Cmd0, "Cmd0"},
|
|
||||||
};
|
|
||||||
RegisterHandlers(functions);
|
|
||||||
}
|
|
||||||
~AVM() = default;
|
|
||||||
void Cmd0(HLERequestContext& ctx) {
|
|
||||||
LOG_WARNING(Service_SM, "(stubbed)");
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
class WLAN final : public ServiceFramework<WLAN> {
|
|
||||||
public:
|
|
||||||
explicit WLAN(Core::System& system_): ServiceFramework{system_, "wlan"} {
|
|
||||||
static const FunctionInfo functions[] = {
|
|
||||||
{0, &WLAN::Cmd0, "Cmd0"},
|
|
||||||
};
|
|
||||||
RegisterHandlers(functions);
|
|
||||||
}
|
|
||||||
~WLAN() = default;
|
|
||||||
void Cmd0(HLERequestContext& ctx) {
|
|
||||||
LOG_WARNING(Service_SM, "(stubbed)");
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void LoopProcess(Core::System& system) {
|
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
|
||||||
server_manager->RegisterNamedService("ulsf:u", std::make_shared<ULSF_U>(system));
|
|
||||||
server_manager->RegisterNamedService("ulsf:p", std::make_shared<ULSF_P>(system));
|
|
||||||
|
|
||||||
server_manager->RegisterNamedService("avm", std::make_shared<AVM>(system));
|
|
||||||
server_manager->RegisterNamedService("wlan", std::make_shared<WLAN>(system));
|
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
#include "core/hle/service/cmif_types.h"
|
|
||||||
#include "core/hle/service/service.h"
|
|
||||||
|
|
||||||
namespace Service::ULSF {
|
|
||||||
|
|
||||||
struct ULauncherVersion {
|
|
||||||
u8 major;
|
|
||||||
u8 minor;
|
|
||||||
u8 micro;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ULSF_U final : public ServiceFramework<ULSF_U> {
|
|
||||||
public:
|
|
||||||
explicit ULSF_U(Core::System& system_);
|
|
||||||
~ULSF_U();
|
|
||||||
//Result GetVersion(Out<ULauncherVersion> out_version);
|
|
||||||
void GetVersion(HLERequestContext& ctx);
|
|
||||||
};
|
|
||||||
|
|
||||||
void LoopProcess(Core::System& system);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "core/hle/service/vi/manager_display_service.h"
|
#include "core/hle/service/vi/manager_display_service.h"
|
||||||
#include "core/hle/service/vi/system_display_service.h"
|
#include "core/hle/service/vi/system_display_service.h"
|
||||||
#include "core/hle/service/vi/vi_results.h"
|
#include "core/hle/service/vi/vi_results.h"
|
||||||
|
#include "service_creator.h"
|
||||||
|
|
||||||
namespace Service::VI {
|
namespace Service::VI {
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ IApplicationDisplayService::IApplicationDisplayService(Core::System& system_,
|
|||||||
{2031, C<&IApplicationDisplayService::DestroyStrayLayer>, "DestroyStrayLayer"},
|
{2031, C<&IApplicationDisplayService::DestroyStrayLayer>, "DestroyStrayLayer"},
|
||||||
{2101, C<&IApplicationDisplayService::SetLayerScalingMode>, "SetLayerScalingMode"},
|
{2101, C<&IApplicationDisplayService::SetLayerScalingMode>, "SetLayerScalingMode"},
|
||||||
{2102, C<&IApplicationDisplayService::ConvertScalingMode>, "ConvertScalingMode"},
|
{2102, C<&IApplicationDisplayService::ConvertScalingMode>, "ConvertScalingMode"},
|
||||||
|
{2103, C<&IApplicationDisplayService::Cmd2103>, "Cmd2103"},
|
||||||
{2450, C<&IApplicationDisplayService::GetIndirectLayerImageMap>, "GetIndirectLayerImageMap"},
|
{2450, C<&IApplicationDisplayService::GetIndirectLayerImageMap>, "GetIndirectLayerImageMap"},
|
||||||
{2451, nullptr, "GetIndirectLayerImageCropMap"},
|
{2451, nullptr, "GetIndirectLayerImageCropMap"},
|
||||||
{2460, C<&IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo>, "GetIndirectLayerImageRequiredMemoryInfo"},
|
{2460, C<&IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo>, "GetIndirectLayerImageRequiredMemoryInfo"},
|
||||||
@@ -289,6 +291,11 @@ Result IApplicationDisplayService::ConvertScalingMode(Out<ConvertedScaleMode> ou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result IApplicationDisplayService::Cmd2103(Out<std::array<u8, 0x18>> out_unk18) {
|
||||||
|
LOG_WARNING(Service_VI, "(stubbed)");
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
Result IApplicationDisplayService::GetIndirectLayerImageMap(
|
Result IApplicationDisplayService::GetIndirectLayerImageMap(
|
||||||
Out<u64> out_size, Out<u64> out_stride,
|
Out<u64> out_size, Out<u64> out_stride,
|
||||||
OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer,
|
OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer,
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ public:
|
|||||||
Result GetDisplayVsyncEvent(OutCopyHandle<Kernel::KReadableEvent> out_vsync_event,
|
Result GetDisplayVsyncEvent(OutCopyHandle<Kernel::KReadableEvent> out_vsync_event,
|
||||||
u64 display_id);
|
u64 display_id);
|
||||||
Result ConvertScalingMode(Out<ConvertedScaleMode> out_scaling_mode, NintendoScaleMode mode);
|
Result ConvertScalingMode(Out<ConvertedScaleMode> out_scaling_mode, NintendoScaleMode mode);
|
||||||
|
Result Cmd2103(Out<std::array<u8, 0x18>> out_unk18);
|
||||||
Result GetIndirectLayerImageMap(
|
Result GetIndirectLayerImageMap(
|
||||||
Out<u64> out_size, Out<u64> out_stride,
|
Out<u64> out_size, Out<u64> out_stride,
|
||||||
OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer,
|
OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer,
|
||||||
|
|||||||
@@ -300,28 +300,6 @@ Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64*
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Claim a presentation slot range.
|
|
||||||
u32 slot_base = 0;
|
|
||||||
|
|
||||||
std::array<bool, SharedBufferMaxSessions> in_use{};
|
|
||||||
for (const auto& [existing_aruid, existing] : m_sessions) {
|
|
||||||
const u32 index = existing.presentation_slot_base / SharedBufferSlotsPerSession;
|
|
||||||
|
|
||||||
if (index < in_use.size())
|
|
||||||
in_use[index] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 index = 0;
|
|
||||||
while (index < in_use.size() && in_use[index])
|
|
||||||
index++;
|
|
||||||
|
|
||||||
if (index >= in_use.size()) {
|
|
||||||
LOG_ERROR(Service_VI, "Out of shared buffer presentation slots ({} sessions)", SharedBufferMaxSessions);
|
|
||||||
R_THROW(VI::ResultOperationFailed);
|
|
||||||
}
|
|
||||||
|
|
||||||
slot_base = index * SharedBufferSlotsPerSession;
|
|
||||||
|
|
||||||
// Map into process.
|
// Map into process.
|
||||||
Common::ProcessAddress map_address{};
|
Common::ProcessAddress map_address{};
|
||||||
R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group,
|
R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group,
|
||||||
@@ -330,7 +308,6 @@ Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64*
|
|||||||
// Create new session.
|
// Create new session.
|
||||||
auto [it, was_emplaced] = m_sessions.emplace(aruid, SharedBufferSession{});
|
auto [it, was_emplaced] = m_sessions.emplace(aruid, SharedBufferSession{});
|
||||||
auto& session = it->second;
|
auto& session = it->second;
|
||||||
session.presentation_slot_base = slot_base;
|
|
||||||
|
|
||||||
auto& container = m_nvdrv->GetContainer();
|
auto& container = m_nvdrv->GetContainer();
|
||||||
session.session_id = container.OpenSession(owner_process);
|
session.session_id = container.OpenSession(owner_process);
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
#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"
|
||||||
#include "core/file_sys/program_metadata.h"
|
|
||||||
#include "core/file_sys/romfs_factory.h"
|
#include "core/file_sys/romfs_factory.h"
|
||||||
#include "core/hle/kernel/k_page_table.h"
|
#include "core/hle/kernel/k_page_table.h"
|
||||||
#include "core/hle/kernel/k_process.h"
|
#include "core/hle/kernel/k_process.h"
|
||||||
@@ -208,7 +207,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
|
|||||||
|
|
||||||
const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
|
const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
|
||||||
const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
|
const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
|
||||||
process, system, *module_file, code_size, should_pass_arguments, false, nullptr, metadata, {},
|
process, system, *module_file, code_size, should_pass_arguments, false, {},
|
||||||
patch_ctx.GetPatchers(), patch_ctx.GetLastIndex());
|
patch_ctx.GetPatchers(), patch_ctx.GetLastIndex());
|
||||||
if (!tentative_next_load_addr) {
|
if (!tentative_next_load_addr) {
|
||||||
return {ResultStatus::ErrorLoadingNSO, {}};
|
return {ResultStatus::ErrorLoadingNSO, {}};
|
||||||
@@ -255,9 +254,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
|
|||||||
|
|
||||||
const VAddr load_addr{next_load_addr};
|
const VAddr load_addr{next_load_addr};
|
||||||
const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
|
const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
|
||||||
FileSys::ProgramMetadata tmp_metadata{};
|
|
||||||
const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
|
const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
|
||||||
process, system, *module_file, load_addr, should_pass_arguments, true, nullptr, metadata, pm,
|
process, system, *module_file, load_addr, should_pass_arguments, true, pm,
|
||||||
patch_ctx.GetPatchers(), patch_ctx.GetIndex(i));
|
patch_ctx.GetPatchers(), patch_ctx.GetIndex(i));
|
||||||
if (!tentative_next_load_addr) {
|
if (!tentative_next_load_addr) {
|
||||||
return {ResultStatus::ErrorLoadingNSO, {}};
|
return {ResultStatus::ErrorLoadingNSO, {}};
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
+8
-50
@@ -17,14 +17,10 @@
|
|||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
#include "core/file_sys/romfs_factory.h"
|
|
||||||
#include "core/file_sys/vfs/vfs_types.h"
|
|
||||||
#include "core/hle/kernel/code_set.h"
|
#include "core/hle/kernel/code_set.h"
|
||||||
#include "core/hle/kernel/k_page_table.h"
|
#include "core/hle/kernel/k_page_table.h"
|
||||||
#include "core/hle/kernel/k_process.h"
|
#include "core/hle/kernel/k_process.h"
|
||||||
#include "core/hle/kernel/k_thread.h"
|
#include "core/hle/kernel/k_thread.h"
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
|
||||||
#include "core/loader/loader.h"
|
|
||||||
#include "core/loader/nso.h"
|
#include "core/loader/nso.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
|
||||||
@@ -70,7 +66,7 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& in_file) {
|
|||||||
return FileType::NSO;
|
return FileType::NSO;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system, const FileSys::VfsFile& nso_file, const VAddr load_base, const bool should_pass_arguments, const bool load_into_process, VAddr* out_load_base, FileSys::ProgramMetadata metadata, std::optional<FileSys::PatchManager> pm, std::vector<Core::NCE::Patcher>* patches, s32 patch_index) {
|
std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system, const FileSys::VfsFile& nso_file, VAddr load_base, bool should_pass_arguments, bool load_into_process, std::optional<FileSys::PatchManager> pm, std::vector<Core::NCE::Patcher>* patches, s32 patch_index) {
|
||||||
if (nso_file.GetSize() < sizeof(NSOHeader))
|
if (nso_file.GetSize() < sizeof(NSOHeader))
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
NSOHeader nso_header{};
|
NSOHeader nso_header{};
|
||||||
@@ -223,19 +219,9 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool is_hbl = true;
|
|
||||||
if (process
|
|
||||||
.LoadFromMetadata(metadata, image_size, 0, 0, is_hbl)
|
|
||||||
.IsError()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto const new_load_base = process.GetEntryPoint().GetValue();
|
|
||||||
if (out_load_base)
|
|
||||||
*out_load_base = new_load_base; // no change
|
|
||||||
// Load codeset for current process
|
// Load codeset for current process
|
||||||
process.LoadModule(system.Kernel(), std::move(codeset), new_load_base);
|
process.LoadModule(system.Kernel(), std::move(codeset), load_base);
|
||||||
return new_load_base + image_size;
|
return load_base + image_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::KProcess& process, Core::System& system) {
|
AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::KProcess& process, Core::System& system) {
|
||||||
@@ -243,34 +229,20 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::KProcess& process, Core::S
|
|||||||
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
FileSys::VirtualFile npdm_file{};
|
|
||||||
metadata = FileSys::ProgramMetadata::GetDefault();
|
|
||||||
if (auto const dir = file->GetContainingDirectory()) {
|
|
||||||
npdm_file = dir->GetFile("main.npdm");
|
|
||||||
if (npdm_file) {
|
|
||||||
metadata.Load(npdm_file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
modules.clear();
|
modules.clear();
|
||||||
|
|
||||||
// Load module
|
// Load module
|
||||||
VAddr base_address = GetInteger(process.GetEntryPoint());
|
const VAddr base_address = GetInteger(process.GetEntryPoint());
|
||||||
if (!LoadModule(process, system, *file, base_address, true, true, &base_address, metadata)) {
|
if (!LoadModule(process, system, *file, base_address, true, true)) {
|
||||||
return {ResultStatus::ErrorLoadingNSO, {}};
|
return {ResultStatus::ErrorLoadingNSO, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
modules.insert_or_assign(base_address, file->GetName());
|
modules.insert_or_assign(base_address, file->GetName());
|
||||||
LOG_DEBUG(Loader, "loaded module {} @ {:#x}", file->GetName(), base_address);
|
LOG_DEBUG(Loader, "loaded module {} @ {:#x}", file->GetName(), base_address);
|
||||||
|
|
||||||
if (npdm_file) {
|
|
||||||
LOG_WARNING(Loader, "creating associated rom-fs factories for likely standalone NSO");
|
|
||||||
u64 program_id{};
|
|
||||||
ReadProgramId(program_id);
|
|
||||||
system.GetFileSystemController().RegisterProcess(process.GetProcessId(), program_id, std::make_unique<FileSys::RomFSFactory>(*this, system.GetContentProvider(), system.GetFileSystemController()));
|
|
||||||
}
|
|
||||||
|
|
||||||
is_loaded = true;
|
is_loaded = true;
|
||||||
return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority, Core::Memory::DEFAULT_STACK_SIZE}};
|
return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority,
|
||||||
|
Core::Memory::DEFAULT_STACK_SIZE}};
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultStatus AppLoader_NSO::ReadNSOModules(Modules& out_modules) {
|
ResultStatus AppLoader_NSO::ReadNSOModules(Modules& out_modules) {
|
||||||
@@ -278,18 +250,4 @@ ResultStatus AppLoader_NSO::ReadNSOModules(Modules& out_modules) {
|
|||||||
return ResultStatus::Success;
|
return ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultStatus AppLoader_NSO::ReadProgramId(u64& out_program_id) {
|
|
||||||
if (metadata.GetTitleID() == 0)
|
|
||||||
return ResultStatus::ErrorNoControl;
|
|
||||||
out_program_id = metadata.GetTitleID();
|
|
||||||
return ResultStatus::Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultStatus AppLoader_NSO::ReadTitle(std::string& out_title) {
|
|
||||||
auto const raw_name = metadata.GetName();
|
|
||||||
out_title.resize(raw_name.size());
|
|
||||||
std::memcpy(out_title.data(), raw_name.data(), raw_name.size());
|
|
||||||
return ResultStatus::Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Loader
|
} // namespace Loader
|
||||||
|
|||||||
+5
-11
@@ -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,6 @@
|
|||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
#include "core/file_sys/program_metadata.h"
|
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
@@ -94,20 +90,18 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::optional<VAddr> LoadModule(Kernel::KProcess& process, Core::System& system,
|
static std::optional<VAddr> LoadModule(Kernel::KProcess& process, Core::System& system,
|
||||||
const FileSys::VfsFile& nso_file, const VAddr load_base,
|
const FileSys::VfsFile& nso_file, VAddr load_base,
|
||||||
const bool should_pass_arguments, const bool load_into_process, VAddr* out_load_base,
|
bool should_pass_arguments, bool load_into_process,
|
||||||
FileSys::ProgramMetadata metadata,
|
std::optional<FileSys::PatchManager> pm = {},
|
||||||
std::optional<FileSys::PatchManager> pm = {}, std::vector<Core::NCE::Patcher>* patches = nullptr, s32 patch_index = -1);
|
std::vector<Core::NCE::Patcher>* patches = nullptr,
|
||||||
|
s32 patch_index = -1);
|
||||||
|
|
||||||
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
|
||||||
|
|
||||||
ResultStatus ReadNSOModules(Modules& out_modules) override;
|
ResultStatus ReadNSOModules(Modules& out_modules) override;
|
||||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
|
||||||
ResultStatus ReadTitle(std::string& title) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Modules modules;
|
Modules modules;
|
||||||
FileSys::ProgramMetadata metadata;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Loader
|
} // namespace Loader
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-39
@@ -101,8 +101,10 @@ struct Memory::Impl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u64 protect_bytes = 0, protect_begin = 0;
|
u64 protect_bytes = 0, protect_begin = 0;
|
||||||
|
|
||||||
|
current_page_table->entries.CommitRegion(vaddr >> YUZU_PAGEBITS, (vaddr + size) >> YUZU_PAGEBITS);
|
||||||
for (u64 addr = vaddr; addr < vaddr + size; addr += YUZU_PAGESIZE) {
|
for (u64 addr = vaddr; addr < vaddr + size; addr += YUZU_PAGESIZE) {
|
||||||
const Common::PageType page_type = current_page_table->entries[addr >> YUZU_PAGEBITS].ptr.Type();
|
const Common::PageType page_type = current_page_table->entries.GetUnchecked(addr >> YUZU_PAGEBITS).Type();
|
||||||
switch (page_type) {
|
switch (page_type) {
|
||||||
case Common::PageType::RasterizerCachedMemory:
|
case Common::PageType::RasterizerCachedMemory:
|
||||||
if (protect_bytes > 0) {
|
if (protect_bytes > 0) {
|
||||||
@@ -123,16 +125,14 @@ struct Memory::Impl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(u64 vaddr) const {
|
[[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(u64 vaddr) const {
|
||||||
Common::PhysicalAddress const paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].addr;
|
if (u64 paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Pointer(true); paddr)
|
||||||
if (paddr)
|
return reinterpret_cast<u8*>(paddr) + vaddr;
|
||||||
return system.DeviceMemory().GetPointer<u8>(paddr + vaddr);
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] u8* GetPointerFromDebugMemory(u64 vaddr) const {
|
[[nodiscard]] u8* GetPointerFromDebugMemory(u64 vaddr) const {
|
||||||
const Common::PhysicalAddress paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].addr;
|
if (u64 paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Pointer(true); paddr)
|
||||||
if (paddr != 0)
|
return reinterpret_cast<u8*>(paddr) + vaddr;
|
||||||
return system.DeviceMemory().GetPointer<u8>(paddr + vaddr);
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,10 +243,12 @@ struct Memory::Impl {
|
|||||||
std::size_t page_index = addr >> YUZU_PAGEBITS;
|
std::size_t page_index = addr >> YUZU_PAGEBITS;
|
||||||
std::size_t page_offset = addr & YUZU_PAGEMASK;
|
std::size_t page_offset = addr & YUZU_PAGEMASK;
|
||||||
bool user_accessible = true;
|
bool user_accessible = true;
|
||||||
|
|
||||||
|
current_page_table->entries.CommitRegion(page_index, page_index + (size >> YUZU_PAGEBITS) + 1);
|
||||||
while (remaining_size != 0) {
|
while (remaining_size != 0) {
|
||||||
const std::size_t copy_amount = (std::min)(std::size_t(YUZU_PAGESIZE) - page_offset, remaining_size);
|
const std::size_t copy_amount = (std::min)(std::size_t(YUZU_PAGESIZE) - page_offset, remaining_size);
|
||||||
const auto current_vaddr = u64((page_index << YUZU_PAGEBITS) + page_offset);
|
const auto current_vaddr = u64((page_index << YUZU_PAGEBITS) + page_offset);
|
||||||
const auto [pointer, type] = current_page_table->entries[page_index].ptr.PointerType();
|
const auto [pointer, type, _] = current_page_table->entries.GetUnchecked(page_index).PointerTypeBlock();
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Common::PageType::Unmapped: {
|
case Common::PageType::Unmapped: {
|
||||||
user_accessible = false;
|
user_accessible = false;
|
||||||
@@ -297,10 +299,10 @@ struct Memory::Impl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline const u8* GetSpan(const VAddr addr, const std::size_t size) const noexcept {
|
[[nodiscard]] inline const u8* GetSpan(const VAddr addr, const std::size_t size) const noexcept {
|
||||||
return (current_page_table->entries[addr >> YUZU_PAGEBITS].block == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].block) ? GetPointerSilent(addr) : nullptr;
|
return (current_page_table->entries[addr >> YUZU_PAGEBITS].Block() == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].Block()) ? GetPointerSilent(addr) : nullptr;
|
||||||
}
|
}
|
||||||
[[nodiscard]] inline u8* GetSpan(const VAddr addr, const std::size_t size) noexcept {
|
[[nodiscard]] inline u8* GetSpan(const VAddr addr, const std::size_t size) noexcept {
|
||||||
return (current_page_table->entries[addr >> YUZU_PAGEBITS].block == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].block) ? GetPointerSilent(addr) : nullptr;
|
return (current_page_table->entries[addr >> YUZU_PAGEBITS].Block() == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].Block()) ? GetPointerSilent(addr) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WriteBlockImpl(const Common::ProcessAddress addr, const void* buffer, const std::size_t size, bool unsafe) {
|
bool WriteBlockImpl(const Common::ProcessAddress addr, const void* buffer, const std::size_t size, bool unsafe) {
|
||||||
@@ -404,11 +406,14 @@ struct Memory::Impl {
|
|||||||
// The region is at a granularity of CPU pages.
|
// The region is at a granularity of CPU pages.
|
||||||
|
|
||||||
const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
|
const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
|
||||||
|
|
||||||
|
current_page_table->entries.CommitRegion(vaddr >> YUZU_PAGEBITS, (vaddr >> YUZU_PAGEBITS) + num_pages);
|
||||||
for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
|
for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
|
||||||
const Common::PageType page_type = current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Type();
|
auto& entry = current_page_table->entries.GetUnchecked(vaddr >> YUZU_PAGEBITS);
|
||||||
|
const auto [pointer, type, block] = entry.PointerTypeBlock(true);
|
||||||
if (debug) {
|
if (debug) {
|
||||||
// Switch page type to debug if now debug
|
// Switch page type to debug if now debug
|
||||||
switch (page_type) {
|
switch (type) {
|
||||||
case Common::PageType::Unmapped:
|
case Common::PageType::Unmapped:
|
||||||
ASSERT(false && "Attempted to mark unmapped pages as debug");
|
ASSERT(false && "Attempted to mark unmapped pages as debug");
|
||||||
break;
|
break;
|
||||||
@@ -417,14 +422,14 @@ struct Memory::Impl {
|
|||||||
// Page is already marked.
|
// Page is already marked.
|
||||||
break;
|
break;
|
||||||
case Common::PageType::Memory:
|
case Common::PageType::Memory:
|
||||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::DebugMemory);
|
entry.MarkDebug(pointer, block);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Switch page type to non-debug if now non-debug
|
// Switch page type to non-debug if now non-debug
|
||||||
switch (page_type) {
|
switch (type) {
|
||||||
case Common::PageType::Unmapped:
|
case Common::PageType::Unmapped:
|
||||||
ASSERT(false && "Attempted to mark unmapped pages as non-debug");
|
ASSERT(false && "Attempted to mark unmapped pages as non-debug");
|
||||||
break;
|
break;
|
||||||
@@ -433,8 +438,7 @@ struct Memory::Impl {
|
|||||||
// Don't mess with already non-debug or rasterizer memory.
|
// Don't mess with already non-debug or rasterizer memory.
|
||||||
break;
|
break;
|
||||||
case Common::PageType::DebugMemory: {
|
case Common::PageType::DebugMemory: {
|
||||||
u8* const pointer = GetPointerFromDebugMemory(vaddr & ~YUZU_PAGEMASK);
|
entry.Store(false, Common::PageType::Memory, block, pointer);
|
||||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(uintptr_t(pointer) - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -466,8 +470,10 @@ struct Memory::Impl {
|
|||||||
// is different). This assumes the specified GPU address region is contiguous as well.
|
// is different). This assumes the specified GPU address region is contiguous as well.
|
||||||
|
|
||||||
const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
|
const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
|
||||||
|
current_page_table->entries.CommitRegion(vaddr >> YUZU_PAGEBITS, (vaddr >> YUZU_PAGEBITS) + num_pages);
|
||||||
for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
|
for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
|
||||||
const Common::PageType page_type= current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Type();
|
auto& entry = current_page_table->entries.GetUnchecked(vaddr >> YUZU_PAGEBITS);
|
||||||
|
const Common::PageType page_type = entry.Type();
|
||||||
if (cached) {
|
if (cached) {
|
||||||
// Switch page type to cached if now cached
|
// Switch page type to cached if now cached
|
||||||
switch (page_type) {
|
switch (page_type) {
|
||||||
@@ -477,7 +483,7 @@ struct Memory::Impl {
|
|||||||
break;
|
break;
|
||||||
case Common::PageType::DebugMemory:
|
case Common::PageType::DebugMemory:
|
||||||
case Common::PageType::Memory:
|
case Common::PageType::Memory:
|
||||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::RasterizerCachedMemory);
|
entry.MarkRasterizerCached();
|
||||||
break;
|
break;
|
||||||
case Common::PageType::RasterizerCachedMemory:
|
case Common::PageType::RasterizerCachedMemory:
|
||||||
// There can be more than one GPU region mapped per CPU region, so it's common
|
// There can be more than one GPU region mapped per CPU region, so it's common
|
||||||
@@ -499,13 +505,13 @@ struct Memory::Impl {
|
|||||||
// that this area is already unmarked as cached.
|
// that this area is already unmarked as cached.
|
||||||
break;
|
break;
|
||||||
case Common::PageType::RasterizerCachedMemory: {
|
case Common::PageType::RasterizerCachedMemory: {
|
||||||
if (u8* const pointer = GetPointerFromRasterizerCachedMemory(vaddr & ~YUZU_PAGEMASK); pointer == nullptr) {
|
if (auto [ptr, _, block] = entry.PointerTypeBlock(true); ptr == 0) {
|
||||||
// It's possible that this function has been called while updating the
|
// It's possible that this function has been called while updating the
|
||||||
// pagetable after unmapping a VMA. In that case the underlying VMA will no
|
// pagetable after unmapping a VMA. In that case the underlying VMA will no
|
||||||
// longer exist, and we should just leave the pagetable entry blank.
|
// longer exist, and we should just leave the pagetable entry blank.
|
||||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::Unmapped);
|
entry.Store(false, Common::PageType::Unmapped, block, 0);
|
||||||
} else {
|
} else {
|
||||||
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(uintptr_t(pointer) - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory);
|
entry.Store(false, Common::PageType::Memory, block, ptr);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -539,22 +545,18 @@ struct Memory::Impl {
|
|||||||
ASSERT_MSG(type != Common::PageType::Memory,
|
ASSERT_MSG(type != Common::PageType::Memory,
|
||||||
"Mapping memory page without a pointer @ {:016x}", base * YUZU_PAGESIZE);
|
"Mapping memory page without a pointer @ {:016x}", base * YUZU_PAGESIZE);
|
||||||
|
|
||||||
while (base != end) {
|
page_table.entries.ZeroRegion(base, end);
|
||||||
page_table.entries[base].ptr.Store(0, type);
|
|
||||||
page_table.entries[base].addr = 0;
|
|
||||||
page_table.entries[base].block = 0;
|
|
||||||
base += 1;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
auto orig_base = base;
|
auto current_block = block_count.fetch_add(1, std::memory_order_relaxed);
|
||||||
while (base != end) {
|
ASSERT(current_block != 65535);
|
||||||
auto host_ptr = uintptr_t(system.DeviceMemory().GetPointer<u8>(target)) - (base << YUZU_PAGEBITS);
|
|
||||||
auto backing = GetInteger(target) - (base << YUZU_PAGEBITS);
|
|
||||||
page_table.entries[base].ptr.Store(host_ptr, type);
|
|
||||||
page_table.entries[base].addr = backing;
|
|
||||||
page_table.entries[base].block = orig_base << YUZU_PAGEBITS;
|
|
||||||
|
|
||||||
ASSERT_MSG(page_table.entries[base].ptr.Pointer(),
|
page_table.entries.CommitRegion(base, end);
|
||||||
|
while (base != end) {
|
||||||
|
auto host_ptr = reinterpret_cast<u64>(system.DeviceMemory().GetPointer<u8>(target)) - (base << YUZU_PAGEBITS);;
|
||||||
|
auto& entry = page_table.entries.GetUnchecked(base);
|
||||||
|
|
||||||
|
entry.Store(false, type, current_block, host_ptr);
|
||||||
|
ASSERT_MSG(page_table.entries[base].Pointer(),
|
||||||
"memory mapping base yield a nullptr within the table");
|
"memory mapping base yield a nullptr within the table");
|
||||||
|
|
||||||
base += 1;
|
base += 1;
|
||||||
@@ -569,11 +571,11 @@ struct Memory::Impl {
|
|||||||
vaddr &= 0xffffffffffffULL;
|
vaddr &= 0xffffffffffffULL;
|
||||||
if (AddressSpaceContains(*current_page_table, vaddr, 1)) [[likely]] {
|
if (AddressSpaceContains(*current_page_table, vaddr, 1)) [[likely]] {
|
||||||
// Avoid adding any extra logic to this fast-path block
|
// Avoid adding any extra logic to this fast-path block
|
||||||
const uintptr_t raw_pointer = current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Raw();
|
const auto raw = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Raw();
|
||||||
if (const uintptr_t pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) [[likely]] {
|
if (auto pointer = Common::PageTable::PageEntryData::ExtractPointer(raw); pointer) [[likely]] {
|
||||||
return reinterpret_cast<u8*>(pointer + vaddr);
|
return reinterpret_cast<u8*>(pointer + vaddr);
|
||||||
} else {
|
} else {
|
||||||
switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
|
switch (static_cast<Common::PageType>(raw.type)) {
|
||||||
case Common::PageType::Memory:
|
case Common::PageType::Memory:
|
||||||
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:#016x}", vaddr);
|
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:#016x}", vaddr);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -773,6 +775,7 @@ struct Memory::Impl {
|
|||||||
#else
|
#else
|
||||||
Common::HostMemory* host_buffer{};
|
Common::HostMemory* host_buffer{};
|
||||||
#endif
|
#endif
|
||||||
|
std::atomic<u16> block_count = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory::Memory(Core::System& system_) : system{system_} {
|
Memory::Memory(Core::System& system_) : system{system_} {
|
||||||
@@ -811,7 +814,7 @@ bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const {
|
|||||||
if (page >= page_table.entries.size()) {
|
if (page >= page_table.entries.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto [pointer, type] = page_table.entries[page].ptr.PointerType();
|
const auto [pointer, type, _] = page_table.entries[page].PointerTypeBlock();
|
||||||
return pointer != 0 || type == Common::PageType::RasterizerCachedMemory ||
|
return pointer != 0 || type == Common::PageType::RasterizerCachedMemory ||
|
||||||
type == Common::PageType::DebugMemory;
|
type == Common::PageType::DebugMemory;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -371,8 +371,10 @@ EmitConfig A32AddressSpace::GetEmitConfig() {
|
|||||||
|
|
||||||
.page_table_pointer = std::bit_cast<u64>(conf.page_table),
|
.page_table_pointer = std::bit_cast<u64>(conf.page_table),
|
||||||
.page_table_address_space_bits = 32,
|
.page_table_address_space_bits = 32,
|
||||||
.page_table_pointer_mask_bits = conf.page_table_pointer_mask_bits,
|
.page_table_pointer_mask = conf.page_table_pointer_mask,
|
||||||
.page_table_log2_stride = conf.page_table_log2_stride,
|
.page_table_log2_stride = conf.page_table_log2_stride,
|
||||||
|
.page_table_marked_bit = conf.page_table_marked_bit,
|
||||||
|
.page_table_sign_extension = conf.page_table_sign_extension,
|
||||||
.silently_mirror_page_table = true,
|
.silently_mirror_page_table = true,
|
||||||
.absolute_offset_page_table = conf.absolute_offset_page_table,
|
.absolute_offset_page_table = conf.absolute_offset_page_table,
|
||||||
.detect_misaligned_access_via_page_table = conf.detect_misaligned_access_via_page_table,
|
.detect_misaligned_access_via_page_table = conf.detect_misaligned_access_via_page_table,
|
||||||
|
|||||||
@@ -545,8 +545,10 @@ EmitConfig A64AddressSpace::GetEmitConfig() {
|
|||||||
|
|
||||||
.page_table_pointer = std::bit_cast<u64>(conf.page_table),
|
.page_table_pointer = std::bit_cast<u64>(conf.page_table),
|
||||||
.page_table_address_space_bits = conf.page_table_address_space_bits,
|
.page_table_address_space_bits = conf.page_table_address_space_bits,
|
||||||
.page_table_pointer_mask_bits = conf.page_table_pointer_mask_bits,
|
.page_table_pointer_mask = conf.page_table_pointer_mask,
|
||||||
.page_table_log2_stride = conf.page_table_log2_stride,
|
.page_table_log2_stride = conf.page_table_log2_stride,
|
||||||
|
.page_table_marked_bit = conf.page_table_marked_bit,
|
||||||
|
.page_table_sign_extension = conf.page_table_sign_extension,
|
||||||
.silently_mirror_page_table = conf.silently_mirror_page_table,
|
.silently_mirror_page_table = conf.silently_mirror_page_table,
|
||||||
.absolute_offset_page_table = conf.absolute_offset_page_table,
|
.absolute_offset_page_table = conf.absolute_offset_page_table,
|
||||||
.detect_misaligned_access_via_page_table = conf.detect_misaligned_access_via_page_table,
|
.detect_misaligned_access_via_page_table = conf.detect_misaligned_access_via_page_table,
|
||||||
|
|||||||
@@ -128,8 +128,10 @@ struct EmitConfig {
|
|||||||
// Page table
|
// Page table
|
||||||
u64 page_table_pointer;
|
u64 page_table_pointer;
|
||||||
std::size_t page_table_address_space_bits;
|
std::size_t page_table_address_space_bits;
|
||||||
int page_table_pointer_mask_bits;
|
u64 page_table_pointer_mask;
|
||||||
std::size_t page_table_log2_stride;
|
std::size_t page_table_log2_stride;
|
||||||
|
std::optional<std::uint8_t> page_table_marked_bit;
|
||||||
|
std::optional<std::uint8_t> page_table_sign_extension;
|
||||||
bool silently_mirror_page_table;
|
bool silently_mirror_page_table;
|
||||||
bool absolute_offset_page_table;
|
bool absolute_offset_page_table;
|
||||||
u8 detect_misaligned_access_via_page_table;
|
u8 detect_misaligned_access_via_page_table;
|
||||||
|
|||||||
@@ -273,9 +273,18 @@ std::pair<oaknut::XReg, oaknut::XReg> InlinePageTableEmitVAddrLookup(oaknut::Cod
|
|||||||
// load x0 = *<(u8*)pagetable + index>
|
// load x0 = *<(u8*)pagetable + index>
|
||||||
code.LDR(Xscratch0, Xpagetable, Xscratch0);
|
code.LDR(Xscratch0, Xpagetable, Xscratch0);
|
||||||
|
|
||||||
if (ctx.conf.page_table_pointer_mask_bits != 0) {
|
if (ctx.conf.page_table_marked_bit) {
|
||||||
const u64 mask = u64(~u64(0)) << ctx.conf.page_table_pointer_mask_bits;
|
// check for marked bit
|
||||||
code.AND(Xscratch0, Xscratch0, mask);
|
code.TBNZ(Xscratch0, *ctx.conf.page_table_marked_bit, *fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.conf.page_table_pointer_mask != 0) {
|
||||||
|
code.AND(Xscratch0, Xscratch0, ctx.conf.page_table_pointer_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: combine this with page_table_pointer_mask
|
||||||
|
if (ctx.conf.page_table_sign_extension) {
|
||||||
|
code.SBFM(Xscratch0, Xscratch0, 0, *ctx.conf.page_table_sign_extension);
|
||||||
}
|
}
|
||||||
|
|
||||||
code.CBZ(Xscratch0, *fallback);
|
code.CBZ(Xscratch0, *fallback);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <bit>
|
#include <bit>
|
||||||
|
#include <utility>
|
||||||
#include "dynarmic/backend/x64/xbyak.h"
|
#include "dynarmic/backend/x64/xbyak.h"
|
||||||
|
|
||||||
#include "dynarmic/backend/x64/a32_emit_x64.h"
|
#include "dynarmic/backend/x64/a32_emit_x64.h"
|
||||||
@@ -78,27 +79,40 @@ Xbyak::RegExp EmitVAddrLookup(BlockOfCode& code, EmitContext& ctx, size_t bitsiz
|
|||||||
template<>
|
template<>
|
||||||
[[maybe_unused]] Xbyak::RegExp EmitVAddrLookup<A32EmitContext>(BlockOfCode& code, A32EmitContext& ctx, size_t bitsize, Xbyak::Label& abort, Xbyak::Reg64 vaddr) {
|
[[maybe_unused]] Xbyak::RegExp EmitVAddrLookup<A32EmitContext>(BlockOfCode& code, A32EmitContext& ctx, size_t bitsize, Xbyak::Label& abort, Xbyak::Reg64 vaddr) {
|
||||||
const Xbyak::Reg64 page = ctx.reg_alloc.ScratchGpr(code);
|
const Xbyak::Reg64 page = ctx.reg_alloc.ScratchGpr(code);
|
||||||
const Xbyak::Reg32 tmp = ctx.conf.absolute_offset_page_table ? page.cvt32() : ctx.reg_alloc.ScratchGpr(code).cvt32();
|
const Xbyak::Reg64 tmp = ctx.conf.absolute_offset_page_table && ctx.conf.page_table_pointer_mask == 0 ? page : ctx.reg_alloc.ScratchGpr(code);
|
||||||
|
|
||||||
EmitDetectMisalignedVAddr(code, ctx, bitsize, abort, vaddr, tmp.cvt64());
|
EmitDetectMisalignedVAddr(code, ctx, bitsize, abort, vaddr, tmp);
|
||||||
|
|
||||||
// TODO: This code assumes vaddr has been zext from 32-bits to 64-bits.
|
code.mov(tmp, vaddr);
|
||||||
|
|
||||||
code.mov(tmp, vaddr.cvt32());
|
|
||||||
code.shr(tmp, int(page_table_const_bits));
|
code.shr(tmp, int(page_table_const_bits));
|
||||||
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
||||||
code.mov(page, qword[r14 + tmp.cvt64()]);
|
code.mov(page, qword[r14 + tmp.cvt64()]);
|
||||||
if (ctx.conf.page_table_pointer_mask_bits == 0) {
|
|
||||||
|
// check for marked bit, use as unmapped if marked
|
||||||
|
if (ctx.conf.page_table_marked_bit) {
|
||||||
|
code.bt(page, *ctx.conf.page_table_marked_bit);
|
||||||
|
code.jc(abort, code.T_NEAR);
|
||||||
|
}
|
||||||
|
// mask away attributes
|
||||||
|
if (ctx.conf.page_table_pointer_mask == 0) {
|
||||||
code.test(page, page);
|
code.test(page, page);
|
||||||
|
} else if (std::in_range<s32>(ctx.conf.page_table_pointer_mask)) {
|
||||||
|
code.and_(page, ctx.conf.page_table_pointer_mask);
|
||||||
} else {
|
} else {
|
||||||
code.and_(page, ~u32(0) << ctx.conf.page_table_pointer_mask_bits);
|
code.mov(tmp, ctx.conf.page_table_pointer_mask);
|
||||||
|
code.and_(page, tmp);
|
||||||
}
|
}
|
||||||
|
if (ctx.conf.page_table_sign_extension) {
|
||||||
|
code.shl(page, *ctx.conf.page_table_sign_extension);
|
||||||
|
code.sar(page, *ctx.conf.page_table_sign_extension);
|
||||||
|
}
|
||||||
|
|
||||||
code.jz(abort, code.T_NEAR);
|
code.jz(abort, code.T_NEAR);
|
||||||
if (ctx.conf.absolute_offset_page_table) {
|
if (ctx.conf.absolute_offset_page_table) {
|
||||||
return page + vaddr;
|
return page + vaddr;
|
||||||
}
|
}
|
||||||
code.mov(tmp, vaddr.cvt32());
|
code.mov(tmp, vaddr);
|
||||||
code.and_(tmp, static_cast<u32>(page_table_const_mask));
|
code.and_(tmp, u32(page_table_const_mask));
|
||||||
return page + tmp.cvt64();
|
return page + tmp.cvt64();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +122,7 @@ template<>
|
|||||||
const size_t unused_top_bits = 64 - ctx.conf.page_table_address_space_bits;
|
const size_t unused_top_bits = 64 - ctx.conf.page_table_address_space_bits;
|
||||||
|
|
||||||
const Xbyak::Reg64 page = ctx.reg_alloc.ScratchGpr(code);
|
const Xbyak::Reg64 page = ctx.reg_alloc.ScratchGpr(code);
|
||||||
const Xbyak::Reg64 tmp = ctx.conf.absolute_offset_page_table ? page : ctx.reg_alloc.ScratchGpr(code);
|
const Xbyak::Reg64 tmp = ctx.conf.absolute_offset_page_table && ctx.conf.page_table_pointer_mask == 0 ? page : ctx.reg_alloc.ScratchGpr(code);
|
||||||
|
|
||||||
EmitDetectMisalignedVAddr(code, ctx, bitsize, abort, vaddr, tmp);
|
EmitDetectMisalignedVAddr(code, ctx, bitsize, abort, vaddr, tmp);
|
||||||
|
|
||||||
@@ -143,11 +157,26 @@ template<>
|
|||||||
|
|
||||||
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
||||||
code.mov(page, qword[r14 + tmp]);
|
code.mov(page, qword[r14 + tmp]);
|
||||||
if (ctx.conf.page_table_pointer_mask_bits == 0) {
|
|
||||||
code.test(page, page);
|
// check for marked bit, use as unmapped if marked
|
||||||
} else {
|
if (ctx.conf.page_table_marked_bit) {
|
||||||
code.and_(page, ~u32(0) << ctx.conf.page_table_pointer_mask_bits);
|
code.bt(page, *ctx.conf.page_table_marked_bit);
|
||||||
|
code.jc(abort, code.T_NEAR);
|
||||||
}
|
}
|
||||||
|
// mask away attributes
|
||||||
|
if (ctx.conf.page_table_pointer_mask == 0) {
|
||||||
|
code.test(page, page);
|
||||||
|
} else if (std::in_range<s32>(ctx.conf.page_table_pointer_mask)) {
|
||||||
|
code.and_(page, ctx.conf.page_table_pointer_mask);
|
||||||
|
} else {
|
||||||
|
code.mov(tmp, ctx.conf.page_table_pointer_mask);
|
||||||
|
code.and_(page, tmp);
|
||||||
|
}
|
||||||
|
if (ctx.conf.page_table_sign_extension) {
|
||||||
|
code.shl(page, *ctx.conf.page_table_sign_extension);
|
||||||
|
code.sar(page, *ctx.conf.page_table_sign_extension);
|
||||||
|
}
|
||||||
|
|
||||||
code.jz(abort, code.T_NEAR);
|
code.jz(abort, code.T_NEAR);
|
||||||
if (ctx.conf.absolute_offset_page_table) {
|
if (ctx.conf.absolute_offset_page_table) {
|
||||||
return page + vaddr;
|
return page + vaddr;
|
||||||
|
|||||||
@@ -159,14 +159,23 @@ struct UserConfig {
|
|||||||
/// Maximum size is limited by the maximum length of a x86_64 / arm64 jump.
|
/// Maximum size is limited by the maximum length of a x86_64 / arm64 jump.
|
||||||
std::uint32_t code_cache_size = 128 * 1024 * 1024; // bytes
|
std::uint32_t code_cache_size = 128 * 1024 * 1024; // bytes
|
||||||
|
|
||||||
/// Masks out the first N bits in host pointers from the page table.
|
/// Applies a bit mask to the bits in host pointers from the page table.
|
||||||
/// The intention behind this is to allow users of Dynarmic to pack attributes in the
|
/// The intention behind this is to allow users of Dynarmic to pack attributes in the
|
||||||
/// same integer and update the pointer attribute pair atomically.
|
/// same integer and update the pointer attribute pair atomically.
|
||||||
/// If the configured value is 3, all pointers will be forcefully aligned to 8 bytes.
|
/// If the configured value is ~(0b111ULL), all pointers will be forcefully aligned to 8 bytes.
|
||||||
std::int32_t page_table_pointer_mask_bits = 0;
|
std::uint64_t page_table_pointer_mask = 0;
|
||||||
|
|
||||||
// Log2 of the size per page entry, value should be either 3 or 4
|
/// Log2 of the size per page entry, value should be either 3 or 4
|
||||||
std::size_t page_table_log2_stride = 3;
|
std::uint32_t page_table_log2_stride = 3;
|
||||||
|
|
||||||
|
/// Setting this value has Dynarmic check the specified bit of the page pointer provided by page table.
|
||||||
|
/// If the bit is set to 1, Dynarmic will treat it as unmapped.
|
||||||
|
/// This bit should be included as part of `page_table_pointer_mask_bits`.
|
||||||
|
std::optional<std::uint8_t> page_table_marked_bit = std::nullopt;
|
||||||
|
|
||||||
|
/// If this value is set, Dynarmic will sign extend the page table pointer by this bit.
|
||||||
|
/// Useful for compacting bits into the page table and should be used as part of `page_table_pointer_mask`.
|
||||||
|
std::optional<std::uint8_t> page_table_sign_extension = std::nullopt;
|
||||||
|
|
||||||
/// Select the architecture version to use.
|
/// Select the architecture version to use.
|
||||||
/// There are minor behavioural differences between versions.
|
/// There are minor behavioural differences between versions.
|
||||||
|
|||||||
@@ -173,14 +173,23 @@ struct UserConfig {
|
|||||||
/// This is only used if page_table is not nullptr.
|
/// This is only used if page_table is not nullptr.
|
||||||
std::uint32_t page_table_address_space_bits = 36;
|
std::uint32_t page_table_address_space_bits = 36;
|
||||||
|
|
||||||
/// Masks out the first N bits in host pointers from the page table.
|
/// Applies a bit mask to the bits in host pointers from the page table.
|
||||||
/// The intention behind this is to allow users of Dynarmic to pack attributes in the
|
/// The intention behind this is to allow users of Dynarmic to pack attributes in the
|
||||||
/// same integer and update the pointer attribute pair atomically.
|
/// same integer and update the pointer attribute pair atomically.
|
||||||
/// If the configured value is 3, all pointers will be forcefully aligned to 8 bytes.
|
/// If the configured value is ~(0b111ULL), all pointers will be forcefully aligned to 8 bytes.
|
||||||
std::int32_t page_table_pointer_mask_bits = 0;
|
std::uint64_t page_table_pointer_mask = 0;
|
||||||
|
|
||||||
// Log2 of the size per page entry, value should be either 3 or 4
|
/// Log2 of the size per page entry, value should be either 3 or 4
|
||||||
std::size_t page_table_log2_stride = 3;
|
std::uint32_t page_table_log2_stride = 3;
|
||||||
|
|
||||||
|
/// Setting this value has Dynarmic check the specified bit of the page pointer provided by page table.
|
||||||
|
/// If the bit is set to 1, Dynarmic will treat it as unmapped.
|
||||||
|
/// This bit should be included as part of `page_table_pointer_mask`.
|
||||||
|
std::optional<std::uint8_t> page_table_marked_bit = std::nullopt;
|
||||||
|
|
||||||
|
/// If this value is set, Dynarmic will sign extend the page table pointer by this bit.
|
||||||
|
/// Useful for compacting bits into the page table and should be used as part of `page_table_pointer_mask`.
|
||||||
|
std::optional<std::uint8_t> page_table_sign_extension = std::nullopt;
|
||||||
|
|
||||||
/// Counter-timer frequency register. The value of the register is not interpreted by
|
/// Counter-timer frequency register. The value of the register is not interpreted by
|
||||||
/// dynarmic.
|
/// dynarmic.
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user