mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-29 18:08:08 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aa0878b636 | |||
| c0a85d0e53 |
+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 =
|
||||||
@@ -322,7 +323,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_addGameDir(JNIEnv* env, jobject
|
|||||||
|
|
||||||
jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDisabledAddons(JNIEnv* env, jobject obj,
|
jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDisabledAddons(JNIEnv* env, jobject obj,
|
||||||
jstring jprogramId) {
|
jstring jprogramId) {
|
||||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
const auto program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
|
||||||
auto& disabledAddons = Settings::values.disabled_addons[program_id];
|
auto& disabledAddons = Settings::values.disabled_addons[program_id];
|
||||||
jobjectArray jdisabledAddonsArray =
|
jobjectArray jdisabledAddonsArray =
|
||||||
env->NewObjectArray(disabledAddons.size(), Common::Android::GetStringClass(),
|
env->NewObjectArray(disabledAddons.size(), Common::Android::GetStringClass(),
|
||||||
@@ -337,7 +338,7 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDisabledAddons(JNIEnv
|
|||||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, jobject obj,
|
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, jobject obj,
|
||||||
jstring jprogramId,
|
jstring jprogramId,
|
||||||
jobjectArray jdisabledAddons) {
|
jobjectArray jdisabledAddons) {
|
||||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
const auto program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
|
||||||
Settings::values.disabled_addons[program_id].clear();
|
Settings::values.disabled_addons[program_id].clear();
|
||||||
std::vector<std::string> disabled_addons;
|
std::vector<std::string> disabled_addons;
|
||||||
const int size = env->GetArrayLength(jdisabledAddons);
|
const int size = env->GetArrayLength(jdisabledAddons);
|
||||||
|
|||||||
+36
-7
@@ -4,6 +4,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "game_settings.h"
|
#include "game_settings.h"
|
||||||
@@ -16,6 +17,7 @@
|
|||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "core/arm/exclusive_monitor.h"
|
#include "core/arm/exclusive_monitor.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
#include "core/file_sys/common_funcs.h"
|
||||||
|
|
||||||
#include "launch_timestamp_cache.h"
|
#include "launch_timestamp_cache.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
@@ -248,12 +250,30 @@ struct System::Impl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetNVDECActive(bool is_nvdec_active) {
|
void NotifyNVDECChannelOpen(u64 process_id) {
|
||||||
nvdec_active = is_nvdec_active;
|
std::scoped_lock lock{nvdec_active_mutex};
|
||||||
|
++nvdec_active_channels[process_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotifyNVDECChannelClose(u64 process_id) {
|
||||||
|
std::scoped_lock lock{nvdec_active_mutex};
|
||||||
|
const auto it = nvdec_active_channels.find(process_id);
|
||||||
|
if (it == nvdec_active_channels.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (--it->second == 0) {
|
||||||
|
nvdec_active_channels.erase(it);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetNVDECActive() {
|
bool GetNVDECActive() {
|
||||||
return nvdec_active;
|
std::scoped_lock lock{nvdec_active_mutex};
|
||||||
|
return !nvdec_active_channels.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsNVDECActiveForProcess(u64 process_id) {
|
||||||
|
std::scoped_lock lock{nvdec_active_mutex};
|
||||||
|
return nvdec_active_channels.contains(process_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeDebugger(System& system, u16 port) {
|
void InitializeDebugger(System& system, u16 port) {
|
||||||
@@ -377,7 +397,7 @@ struct System::Impl {
|
|||||||
LOG_ERROR(Core, "Failed to find program id for ROM");
|
LOG_ERROR(Core, "Failed to find program id for ROM");
|
||||||
}
|
}
|
||||||
|
|
||||||
GameSettings::LoadOverrides(program_id, gpu_core->Renderer());
|
GameSettings::LoadOverrides(FileSys::GetBaseTitleID(program_id), gpu_core->Renderer());
|
||||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||||
Network::GameInfo game_info;
|
Network::GameInfo game_info;
|
||||||
game_info.name = name;
|
game_info.name = name;
|
||||||
@@ -505,6 +525,8 @@ struct System::Impl {
|
|||||||
|
|
||||||
mutable std::mutex suspend_guard;
|
mutable std::mutex suspend_guard;
|
||||||
std::mutex general_channel_mutex;
|
std::mutex general_channel_mutex;
|
||||||
|
std::mutex nvdec_active_mutex;
|
||||||
|
std::unordered_map<u64, u32> nvdec_active_channels;
|
||||||
std::atomic_bool is_paused{};
|
std::atomic_bool is_paused{};
|
||||||
std::atomic_bool is_shutting_down{};
|
std::atomic_bool is_shutting_down{};
|
||||||
std::atomic_bool is_powered_on{};
|
std::atomic_bool is_powered_on{};
|
||||||
@@ -512,7 +534,6 @@ struct System::Impl {
|
|||||||
bool extended_memory_layout : 1 = false;
|
bool extended_memory_layout : 1 = false;
|
||||||
bool exit_locked : 1 = false;
|
bool exit_locked : 1 = false;
|
||||||
bool exit_requested : 1 = false;
|
bool exit_requested : 1 = false;
|
||||||
bool nvdec_active : 1 = false;
|
|
||||||
|
|
||||||
void EnsureGeneralChannelInitialized(System& system) {
|
void EnsureGeneralChannelInitialized(System& system) {
|
||||||
if (!general_channel_event) {
|
if (!general_channel_event) {
|
||||||
@@ -576,14 +597,22 @@ void System::UnstallApplication() {
|
|||||||
impl->UnstallApplication();
|
impl->UnstallApplication();
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::SetNVDECActive(bool is_nvdec_active) {
|
void System::NotifyNVDECChannelOpen(u64 process_id) {
|
||||||
impl->SetNVDECActive(is_nvdec_active);
|
impl->NotifyNVDECChannelOpen(process_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::NotifyNVDECChannelClose(u64 process_id) {
|
||||||
|
impl->NotifyNVDECChannelClose(process_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::GetNVDECActive() {
|
bool System::GetNVDECActive() {
|
||||||
return impl->GetNVDECActive();
|
return impl->GetNVDECActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool System::IsNVDECActiveForProcess(u64 process_id) {
|
||||||
|
return impl->IsNVDECActiveForProcess(process_id);
|
||||||
|
}
|
||||||
|
|
||||||
void System::InitializeDebugger() {
|
void System::InitializeDebugger() {
|
||||||
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -191,8 +191,10 @@ public:
|
|||||||
std::unique_lock<std::mutex> StallApplication();
|
std::unique_lock<std::mutex> StallApplication();
|
||||||
void UnstallApplication();
|
void UnstallApplication();
|
||||||
|
|
||||||
void SetNVDECActive(bool is_nvdec_active);
|
void NotifyNVDECChannelOpen(u64 process_id);
|
||||||
|
void NotifyNVDECChannelClose(u64 process_id);
|
||||||
[[nodiscard]] bool GetNVDECActive();
|
[[nodiscard]] bool GetNVDECActive();
|
||||||
|
[[nodiscard]] bool IsNVDECActiveForProcess(u64 process_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the debugger.
|
* Initialize the debugger.
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ std::string GetUpdateVersionStringFromSlot(const ContentProvider* provider, u64
|
|||||||
PatchManager::PatchManager(u64 title_id_,
|
PatchManager::PatchManager(u64 title_id_,
|
||||||
const Service::FileSystem::FileSystemController& fs_controller_,
|
const Service::FileSystem::FileSystemController& fs_controller_,
|
||||||
const ContentProvider& content_provider_)
|
const ContentProvider& content_provider_)
|
||||||
: title_id{title_id_}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
|
: title_id{title_id_}, application_id{GetBaseTitleID(title_id_)}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
|
||||||
|
|
||||||
PatchManager::~PatchManager() = default;
|
PatchManager::~PatchManager() = default;
|
||||||
|
|
||||||
@@ -169,13 +169,41 @@ u64 PatchManager::GetTitleID() const {
|
|||||||
return title_id;
|
return title_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u64 PatchManager::GetUpdateTitleIDForContent() const {
|
||||||
|
const auto program_update_id = GetUpdateTitleID(title_id);
|
||||||
|
if (program_update_id == GetUpdateTitleID(application_id) || content_provider.HasEntry(program_update_id, ContentRecordType::Program)) {
|
||||||
|
return program_update_id;
|
||||||
|
}
|
||||||
|
return GetUpdateTitleID(application_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VirtualDir> PatchManager::GetModificationLoadRoots() const {
|
||||||
|
std::vector<VirtualDir> roots;
|
||||||
|
roots.push_back(fs_controller.GetModificationLoadRoot(title_id));
|
||||||
|
if (application_id != title_id) {
|
||||||
|
roots.push_back(fs_controller.GetModificationLoadRoot(application_id));
|
||||||
|
}
|
||||||
|
std::erase(roots, nullptr);
|
||||||
|
return roots;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VirtualDir> PatchManager::GetSDMCModificationLoadRoots() const {
|
||||||
|
std::vector<VirtualDir> roots;
|
||||||
|
roots.push_back(fs_controller.GetSDMCModificationLoadRoot(title_id));
|
||||||
|
if (application_id != title_id) {
|
||||||
|
roots.push_back(fs_controller.GetSDMCModificationLoadRoot(application_id));
|
||||||
|
}
|
||||||
|
std::erase(roots, nullptr);
|
||||||
|
return roots;
|
||||||
|
}
|
||||||
|
|
||||||
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||||
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
|
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
|
||||||
|
|
||||||
if (exefs == nullptr)
|
if (exefs == nullptr)
|
||||||
return exefs;
|
return exefs;
|
||||||
|
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto& disabled = Settings::values.disabled_addons[application_id];
|
||||||
|
|
||||||
bool update_disabled = true;
|
bool update_disabled = true;
|
||||||
std::optional<u32> enabled_version;
|
std::optional<u32> enabled_version;
|
||||||
@@ -183,7 +211,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
|||||||
bool checked_manual = false;
|
bool checked_manual = false;
|
||||||
|
|
||||||
const auto* content_union = static_cast<const ContentProviderUnion*>(&content_provider);
|
const auto* content_union = static_cast<const ContentProviderUnion*>(&content_provider);
|
||||||
const auto update_tid = GetUpdateTitleID(title_id);
|
const auto update_tid = GetUpdateTitleIDForContent();
|
||||||
|
|
||||||
if (content_union) {
|
if (content_union) {
|
||||||
// First, check ExternalContentProvider
|
// First, check ExternalContentProvider
|
||||||
@@ -303,17 +331,21 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LayeredExeFS
|
// LayeredExeFS
|
||||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
const auto load_dirs = GetModificationLoadRoots();
|
||||||
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
const auto sdmc_load_dirs = GetSDMCModificationLoadRoots();
|
||||||
|
|
||||||
std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
|
std::vector<VirtualDir> patch_dirs;
|
||||||
if (load_dir != nullptr) {
|
for (const auto& sdmc_load_dir : sdmc_load_dirs) {
|
||||||
|
patch_dirs.push_back(sdmc_load_dir);
|
||||||
|
}
|
||||||
|
for (const auto& load_dir : load_dirs) {
|
||||||
const auto load_patch_dirs = load_dir->GetSubdirectories();
|
const auto load_patch_dirs = load_dir->GetSubdirectories();
|
||||||
patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
|
patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
std::stable_sort(patch_dirs.begin(), patch_dirs.end(), [](const VirtualDir& l, const VirtualDir& r) {
|
||||||
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
return l->GetName() < r->GetName();
|
||||||
|
});
|
||||||
|
|
||||||
std::vector<VirtualDir> layers;
|
std::vector<VirtualDir> layers;
|
||||||
layers.reserve(patch_dirs.size() + 1);
|
layers.reserve(patch_dirs.size() + 1);
|
||||||
@@ -347,7 +379,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
|||||||
|
|
||||||
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
||||||
const std::string& build_id) const {
|
const std::string& build_id) const {
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto& disabled = Settings::values.disabled_addons[application_id];
|
||||||
const auto nso_build_id = fmt::format("{:0<64}", build_id);
|
const auto nso_build_id = fmt::format("{:0<64}", build_id);
|
||||||
|
|
||||||
std::vector<VirtualFile> out;
|
std::vector<VirtualFile> out;
|
||||||
@@ -412,15 +444,20 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
|
|||||||
|
|
||||||
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
|
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
|
||||||
|
|
||||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
const auto load_dirs = GetModificationLoadRoots();
|
||||||
if (load_dir == nullptr) {
|
if (load_dirs.empty()) {
|
||||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||||
return nso;
|
return nso;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto patch_dirs = load_dir->GetSubdirectories();
|
std::vector<VirtualDir> patch_dirs;
|
||||||
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
for (const auto& load_dir : load_dirs) {
|
||||||
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
const auto load_patch_dirs = load_dir->GetSubdirectories();
|
||||||
|
patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
|
||||||
|
}
|
||||||
|
std::stable_sort(patch_dirs.begin(), patch_dirs.end(), [](const VirtualDir& l, const VirtualDir& r) {
|
||||||
|
return l->GetName() < r->GetName();
|
||||||
|
});
|
||||||
const auto patches = CollectPatches(patch_dirs, build_id);
|
const auto patches = CollectPatches(patch_dirs, build_id);
|
||||||
|
|
||||||
auto out = nso;
|
auto out = nso;
|
||||||
@@ -455,29 +492,39 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
|
|||||||
|
|
||||||
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}, name={}", build_id, name);
|
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}, name={}", build_id, name);
|
||||||
|
|
||||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
const auto load_dirs = GetModificationLoadRoots();
|
||||||
if (load_dir == nullptr) {
|
if (load_dirs.empty()) {
|
||||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto patch_dirs = load_dir->GetSubdirectories();
|
std::vector<VirtualDir> patch_dirs;
|
||||||
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
for (const auto& load_dir : load_dirs) {
|
||||||
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
const auto load_patch_dirs = load_dir->GetSubdirectories();
|
||||||
|
patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
|
||||||
|
}
|
||||||
|
std::stable_sort(patch_dirs.begin(), patch_dirs.end(), [](const VirtualDir& l, const VirtualDir& r) {
|
||||||
|
return l->GetName() < r->GetName();
|
||||||
|
});
|
||||||
|
|
||||||
return !CollectPatches(patch_dirs, build_id).empty();
|
return !CollectPatches(patch_dirs, build_id).empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
|
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
|
||||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
const auto load_dirs = GetModificationLoadRoots();
|
||||||
if (load_dir == nullptr) {
|
if (load_dirs.empty()) {
|
||||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto& disabled = Settings::values.disabled_addons[application_id];
|
||||||
auto patch_dirs = load_dir->GetSubdirectories();
|
std::vector<VirtualDir> patch_dirs;
|
||||||
std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
|
for (const auto& load_dir : load_dirs) {
|
||||||
|
const auto load_patch_dirs = load_dir->GetSubdirectories();
|
||||||
|
patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
|
||||||
|
}
|
||||||
|
std::stable_sort(patch_dirs.begin(), patch_dirs.end(),
|
||||||
|
[](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
|
||||||
|
|
||||||
// <mod dir> / <folder> / cheats / <build id>.txt
|
// <mod dir> / <folder> / cheats / <build id>.txt
|
||||||
std::vector<Core::Memory::CheatEntry> out;
|
std::vector<Core::Memory::CheatEntry> out;
|
||||||
@@ -493,39 +540,52 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildI
|
|||||||
}
|
}
|
||||||
// Uncareless user-friendly loading of patches (must start with 'cheat_')
|
// Uncareless user-friendly loading of patches (must start with 'cheat_')
|
||||||
// <mod dir> / <cheat file>.txt
|
// <mod dir> / <cheat file>.txt
|
||||||
for (auto const& f : load_dir->GetFiles()) {
|
for (const auto& load_dir : load_dirs) {
|
||||||
auto const name = f->GetName();
|
for (auto const& f : load_dir->GetFiles()) {
|
||||||
if (name.starts_with("cheat_") && std::find(disabled.cbegin(), disabled.cend(), name) == disabled.cend()) {
|
auto const name = f->GetName();
|
||||||
std::vector<u8> data(f->GetSize());
|
if (name.starts_with("cheat_") && std::find(disabled.cbegin(), disabled.cend(), name) == disabled.cend()) {
|
||||||
if (f->Read(data.data(), data.size()) == data.size()) {
|
std::vector<u8> data(f->GetSize());
|
||||||
const Core::Memory::TextCheatParser parser;
|
if (f->Read(data.data(), data.size()) == data.size()) {
|
||||||
auto const res = parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
|
const Core::Memory::TextCheatParser parser;
|
||||||
std::copy(res.begin(), res.end(), std::back_inserter(out));
|
auto const res = parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
|
||||||
} else {
|
std::copy(res.begin(), res.end(), std::back_inserter(out));
|
||||||
LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}", title_id);
|
} else {
|
||||||
|
LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}", title_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type,
|
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, u64 application_id, ContentRecordType type,
|
||||||
const Service::FileSystem::FileSystemController& fs_controller) {
|
const Service::FileSystem::FileSystemController& fs_controller) {
|
||||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
std::vector<VirtualDir> load_dirs{fs_controller.GetModificationLoadRoot(title_id)};
|
||||||
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
std::vector<VirtualDir> sdmc_load_dirs{fs_controller.GetSDMCModificationLoadRoot(title_id)};
|
||||||
|
if (application_id != title_id) {
|
||||||
|
load_dirs.push_back(fs_controller.GetModificationLoadRoot(application_id));
|
||||||
|
sdmc_load_dirs.push_back(fs_controller.GetSDMCModificationLoadRoot(application_id));
|
||||||
|
}
|
||||||
|
std::erase(load_dirs, nullptr);
|
||||||
|
std::erase(sdmc_load_dirs, nullptr);
|
||||||
if ((type != ContentRecordType::Program && type != ContentRecordType::Data &&
|
if ((type != ContentRecordType::Program && type != ContentRecordType::Data &&
|
||||||
type != ContentRecordType::HtmlDocument) ||
|
type != ContentRecordType::HtmlDocument) ||
|
||||||
(load_dir == nullptr && sdmc_load_dir == nullptr)) {
|
(load_dirs.empty() && sdmc_load_dirs.empty())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto& disabled = Settings::values.disabled_addons[application_id];
|
||||||
std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories();
|
std::vector<VirtualDir> patch_dirs;
|
||||||
if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
|
for (const auto& load_dir : load_dirs) {
|
||||||
patch_dirs.push_back(sdmc_load_dir);
|
const auto load_patch_dirs = load_dir->GetSubdirectories();
|
||||||
|
patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
|
||||||
}
|
}
|
||||||
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
|
||||||
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
patch_dirs.insert(patch_dirs.end(), sdmc_load_dirs.begin(), sdmc_load_dirs.end());
|
||||||
|
}
|
||||||
|
std::stable_sort(patch_dirs.begin(), patch_dirs.end(), [](const VirtualDir& l, const VirtualDir& r) {
|
||||||
|
return l->GetName() < r->GetName();
|
||||||
|
});
|
||||||
|
|
||||||
std::vector<VirtualDir> layers;
|
std::vector<VirtualDir> layers;
|
||||||
std::vector<VirtualDir> layers_ext;
|
std::vector<VirtualDir> layers_ext;
|
||||||
@@ -597,8 +657,8 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
|
|||||||
auto romfs = base_romfs;
|
auto romfs = base_romfs;
|
||||||
|
|
||||||
// Game Updates
|
// Game Updates
|
||||||
const auto update_tid = GetUpdateTitleID(title_id);
|
const auto update_tid = GetUpdateTitleIDForContent();
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto& disabled = Settings::values.disabled_addons[application_id];
|
||||||
|
|
||||||
bool update_disabled = true;
|
bool update_disabled = true;
|
||||||
std::optional<u32> enabled_version;
|
std::optional<u32> enabled_version;
|
||||||
@@ -705,7 +765,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
|
|||||||
|
|
||||||
// LayeredFS
|
// LayeredFS
|
||||||
if (apply_layeredfs) {
|
if (apply_layeredfs) {
|
||||||
ApplyLayeredFS(romfs, title_id, type, fs_controller);
|
ApplyLayeredFS(romfs, title_id, application_id, type, fs_controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
return romfs;
|
return romfs;
|
||||||
@@ -717,10 +777,10 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Patch> out;
|
std::vector<Patch> out;
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto& disabled = Settings::values.disabled_addons[application_id];
|
||||||
|
|
||||||
// Game Updates
|
// Game Updates
|
||||||
const auto update_tid = GetUpdateTitleID(title_id);
|
const auto update_tid = GetUpdateTitleIDForContent();
|
||||||
|
|
||||||
std::vector<Patch> external_update_patches;
|
std::vector<Patch> external_update_patches;
|
||||||
|
|
||||||
@@ -869,7 +929,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
|||||||
.version = "",
|
.version = "",
|
||||||
.type = PatchType::Update,
|
.type = PatchType::Update,
|
||||||
.program_id = title_id,
|
.program_id = title_id,
|
||||||
.title_id = title_id,
|
.title_id = update_tid,
|
||||||
.source = PatchSource::Unknown,
|
.source = PatchSource::Unknown,
|
||||||
.numeric_version = 0};
|
.numeric_version = 0};
|
||||||
|
|
||||||
@@ -895,8 +955,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// General Mods (LayeredFS and IPS)
|
// General Mods (LayeredFS and IPS)
|
||||||
const auto mod_dir = fs_controller.GetModificationLoadRoot(title_id);
|
for (const auto& mod_dir : GetModificationLoadRoots()) {
|
||||||
if (mod_dir != nullptr) {
|
|
||||||
for (auto const& f : mod_dir->GetFiles())
|
for (auto const& f : mod_dir->GetFiles())
|
||||||
if (auto const name = f->GetName(); name.starts_with("cheat_")) {
|
if (auto const name = f->GetName(); name.starts_with("cheat_")) {
|
||||||
auto const mod_disabled = std::find(disabled.begin(), disabled.end(), name) != disabled.end();
|
auto const mod_disabled = std::find(disabled.begin(), disabled.end(), name) != disabled.end();
|
||||||
@@ -963,8 +1022,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SDMC mod directory (RomFS LayeredFS)
|
// SDMC mod directory (RomFS LayeredFS)
|
||||||
const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
for (const auto& sdmc_mod_dir : GetSDMCModificationLoadRoots()) {
|
||||||
if (sdmc_mod_dir != nullptr) {
|
|
||||||
std::string types;
|
std::string types;
|
||||||
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs")))
|
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs")))
|
||||||
AppendCommaIfNotEmpty(types, "LayeredExeFS");
|
AppendCommaIfNotEmpty(types, "LayeredExeFS");
|
||||||
@@ -999,10 +1057,10 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
|||||||
dlc_match.reserve(dlc_entries_with_origin.size());
|
dlc_match.reserve(dlc_entries_with_origin.size());
|
||||||
for (const auto& [slot, entry] : dlc_entries_with_origin) {
|
for (const auto& [slot, entry] : dlc_entries_with_origin) {
|
||||||
const auto base_tid = GetBaseTitleID(entry.title_id);
|
const auto base_tid = GetBaseTitleID(entry.title_id);
|
||||||
const bool matches_base = base_tid == title_id;
|
const bool matches_base = base_tid == application_id;
|
||||||
if (!matches_base) {
|
if (!matches_base) {
|
||||||
LOG_DEBUG(Loader, "DLC {:016X} base {:016X} doesn't match title {:016X}",
|
LOG_DEBUG(Loader, "DLC {:016X} base {:016X} doesn't match title {:016X}",
|
||||||
entry.title_id, base_tid, title_id);
|
entry.title_id, base_tid, application_id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1077,16 +1135,22 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::optional<u32> PatchManager::GetGameVersion() const {
|
std::optional<u32> PatchManager::GetGameVersion() const {
|
||||||
const auto update_tid = GetUpdateTitleID(title_id);
|
const auto update_tid = GetUpdateTitleIDForContent();
|
||||||
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
|
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
|
||||||
return content_provider.GetEntryVersion(update_tid);
|
return content_provider.GetEntryVersion(update_tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
return content_provider.GetEntryVersion(title_id);
|
if (const auto version = content_provider.GetEntryVersion(title_id); version.has_value()) {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
return content_provider.GetEntryVersion(application_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
PatchManager::Metadata PatchManager::GetControlMetadata() const {
|
PatchManager::Metadata PatchManager::GetControlMetadata() const {
|
||||||
const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
|
auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
|
||||||
|
if (base_control_nca == nullptr && application_id != title_id) {
|
||||||
|
base_control_nca = content_provider.GetEntry(application_id, ContentRecordType::Control);
|
||||||
|
}
|
||||||
if (base_control_nca == nullptr) {
|
if (base_control_nca == nullptr) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -1162,8 +1226,14 @@ PatchManager::Metadata PatchManager::ParseControlNCA(const NCA& nca) const {
|
|||||||
auto metadata = pm.GetControlMetadata();
|
auto metadata = pm.GetControlMetadata();
|
||||||
if (metadata.first != nullptr)
|
if (metadata.first != nullptr)
|
||||||
return metadata;
|
return metadata;
|
||||||
const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(application_id), system.GetFileSystemController(), system.GetContentProvider()};
|
const auto update_id = FileSys::GetUpdateTitleID(application_id);
|
||||||
return pm_update.GetControlMetadata();
|
const auto application_update_id = FileSys::GetUpdateTitleID(GetBaseTitleID(application_id));
|
||||||
|
const FileSys::PatchManager pm_update{update_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||||
|
metadata = pm_update.GetControlMetadata();
|
||||||
|
if (metadata.first != nullptr || update_id == application_update_id)
|
||||||
|
return metadata;
|
||||||
|
const FileSys::PatchManager pm_application_update{application_update_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||||
|
return pm_application_update.GetControlMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
#include "core/file_sys/common_funcs.h"
|
||||||
#include "core/file_sys/content_archive.h"
|
#include "core/file_sys/content_archive.h"
|
||||||
#include "core/file_sys/nca_metadata.h"
|
#include "core/file_sys/nca_metadata.h"
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
@@ -104,8 +105,16 @@ std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control,
|
|||||||
|
|
||||||
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
|
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
|
||||||
// current_process_game_card use correct StorageId
|
// current_process_game_card use correct StorageId
|
||||||
launch.base_game_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program));
|
auto base_slot = storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program);
|
||||||
launch.update_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
|
if (!base_slot) {
|
||||||
|
base_slot = storage.GetSlotForEntry(FileSys::GetBaseTitleID(launch.title_id), FileSys::ContentRecordType::Program);
|
||||||
|
}
|
||||||
|
launch.base_game_storage_id = GetStorageIdForFrontendSlot(base_slot);
|
||||||
|
auto update_slot = storage.GetSlotForEntry(FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program);
|
||||||
|
if (!update_slot) {
|
||||||
|
update_slot = storage.GetSlotForEntry(FileSys::GetUpdateTitleID(FileSys::GetBaseTitleID(launch.title_id)), FileSys::ContentRecordType::Program);
|
||||||
|
}
|
||||||
|
launch.update_storage_id = GetStorageIdForFrontendSlot(update_slot);
|
||||||
|
|
||||||
system.GetARPManager().Register(launch.title_id, launch, out_control);
|
system.GetARPManager().Register(launch.title_id, launch, out_control);
|
||||||
return process;
|
return process;
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ Result IApplicationFunctions::EnsureSaveData(Out<u64> out_size, Common::UUID use
|
|||||||
LOG_INFO(Service_AM, "called, uid={}", user_id.FormattedString());
|
LOG_INFO(Service_AM, "called, uid={}", user_id.FormattedString());
|
||||||
|
|
||||||
FileSys::SaveDataAttribute attribute{};
|
FileSys::SaveDataAttribute attribute{};
|
||||||
attribute.program_id = m_applet->program_id;
|
attribute.program_id = FileSys::GetBaseTitleID(m_applet->program_id);
|
||||||
attribute.user_id = user_id.AsU128();
|
attribute.user_id = user_id.AsU128();
|
||||||
attribute.type = FileSys::SaveDataType::Account;
|
attribute.type = FileSys::SaveDataType::Account;
|
||||||
|
|
||||||
@@ -238,7 +238,7 @@ Result IApplicationFunctions::ExtendSaveData(Out<u64> out_required_size, FileSys
|
|||||||
static_cast<u8>(type), user_id.FormattedString(), normal_size, journal_size);
|
static_cast<u8>(type), user_id.FormattedString(), normal_size, journal_size);
|
||||||
|
|
||||||
system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize(
|
system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize(
|
||||||
type, m_applet->program_id, user_id.AsU128(), {normal_size, journal_size});
|
type, FileSys::GetBaseTitleID(m_applet->program_id), user_id.AsU128(), {normal_size, journal_size});
|
||||||
|
|
||||||
// The following value is used to indicate the amount of space remaining on failure
|
// The following value is used to indicate the amount of space remaining on failure
|
||||||
// due to running out of space. Since we always succeed, this should be 0.
|
// due to running out of space. Since we always succeed, this should be 0.
|
||||||
@@ -252,7 +252,7 @@ Result IApplicationFunctions::GetSaveDataSize(Out<u64> out_normal_size, Out<u64>
|
|||||||
LOG_DEBUG(Service_AM, "called with type={} user_id={}", type, user_id.FormattedString());
|
LOG_DEBUG(Service_AM, "called with type={} user_id={}", type, user_id.FormattedString());
|
||||||
|
|
||||||
const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize(
|
const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize(
|
||||||
type, m_applet->program_id, user_id.AsU128());
|
type, FileSys::GetBaseTitleID(m_applet->program_id), user_id.AsU128());
|
||||||
|
|
||||||
*out_normal_size = size.normal;
|
*out_normal_size = size.normal;
|
||||||
*out_journal_size = size.journal;
|
*out_journal_size = size.journal;
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -339,7 +340,7 @@ Result FileSystemController::RegisterProcess(
|
|||||||
registrations.emplace(process_id, Registration{
|
registrations.emplace(process_id, Registration{
|
||||||
.program_id = program_id,
|
.program_id = program_id,
|
||||||
.romfs_factory = std::move(romfs_factory),
|
.romfs_factory = std::move(romfs_factory),
|
||||||
.save_data_factory = CreateSaveDataFactory(program_id),
|
.save_data_factory = CreateSaveDataFactory(FileSys::GetBaseTitleID(program_id)),
|
||||||
});
|
});
|
||||||
|
|
||||||
LOG_DEBUG(Service_FS, "Registered for process {}", process_id);
|
LOG_DEBUG(Service_FS, "Registered for process {}", process_id);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
#include "core/file_sys/common_funcs.h"
|
||||||
#include "core/file_sys/content_archive.h"
|
#include "core/file_sys/content_archive.h"
|
||||||
#include "core/file_sys/errors.h"
|
#include "core/file_sys/errors.h"
|
||||||
#include "core/file_sys/fs_directory.h"
|
#include "core/file_sys/fs_directory.h"
|
||||||
@@ -313,7 +314,7 @@ Result FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId(OutInterface<IFileSyste
|
|||||||
FileSys::ResultInvalidArgument);
|
FileSys::ResultInvalidArgument);
|
||||||
|
|
||||||
if (attribute.program_id == 0) {
|
if (attribute.program_id == 0) {
|
||||||
attribute.program_id = program_id;
|
attribute.program_id = FileSys::GetBaseTitleID(program_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
FileSys::VirtualDir dir{};
|
FileSys::VirtualDir dir{};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
#include "core/hle/kernel/k_process.h"
|
||||||
#include "core/hle/service/nvdrv/core/container.h"
|
#include "core/hle/service/nvdrv/core/container.h"
|
||||||
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
|
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
|
||||||
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
|
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
|
||||||
@@ -71,17 +72,23 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
|
|||||||
|
|
||||||
void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
|
void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
|
||||||
LOG_INFO(Service_NVDRV, "NVDEC video stream started");
|
LOG_INFO(Service_NVDRV, "NVDEC video stream started");
|
||||||
system.SetNVDECActive(true);
|
|
||||||
sessions[fd] = session_id;
|
sessions[fd] = session_id;
|
||||||
|
if (const auto* session = core.GetSession(session_id);
|
||||||
|
session != nullptr && session->process != nullptr) {
|
||||||
|
system.NotifyNVDECChannelOpen(session->process->GetId());
|
||||||
|
}
|
||||||
host1x.StartDevice(fd, Tegra::Host1x::ChannelType::NvDec, channel_syncpoint);
|
host1x.StartDevice(fd, Tegra::Host1x::ChannelType::NvDec, channel_syncpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nvhost_nvdec::OnClose(DeviceFD fd) {
|
void nvhost_nvdec::OnClose(DeviceFD fd) {
|
||||||
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
|
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
|
||||||
host1x.StopDevice(fd, Tegra::Host1x::ChannelType::NvDec);
|
host1x.StopDevice(fd, Tegra::Host1x::ChannelType::NvDec);
|
||||||
system.SetNVDECActive(false);
|
|
||||||
auto it = sessions.find(fd);
|
auto it = sessions.find(fd);
|
||||||
if (it != sessions.end()) {
|
if (it != sessions.end()) {
|
||||||
|
if (const auto* session = core.GetSession(it->second);
|
||||||
|
session != nullptr && session->process != nullptr) {
|
||||||
|
system.NotifyNVDECChannelClose(session->process->GetId());
|
||||||
|
}
|
||||||
sessions.erase(it);
|
sessions.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,16 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <fmt/ranges.h>
|
#include <fmt/ranges.h>
|
||||||
|
#include <string_view>
|
||||||
|
#include <thread>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/ipc.h"
|
#include "core/hle/ipc.h"
|
||||||
|
#include "core/hle/kernel/k_process.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
@@ -33,6 +37,7 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se
|
|||||||
: SessionRequestHandler(system_.Kernel(), service_name_)
|
: SessionRequestHandler(system_.Kernel(), service_name_)
|
||||||
, system{system_}
|
, system{system_}
|
||||||
, service_name{service_name_}
|
, service_name{service_name_}
|
||||||
|
, is_i_storage{std::string_view{service_name_} == "IStorage"}
|
||||||
, handler_invoker{handler_invoker_}
|
, handler_invoker{handler_invoker_}
|
||||||
, max_sessions{max_sessions_}
|
, max_sessions{max_sessions_}
|
||||||
{}
|
{}
|
||||||
@@ -77,13 +82,22 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ServiceFrameworkBase::InvokeRequest(HLERequestContext& ctx) {
|
void ServiceFrameworkBase::InvokeRequest(HLERequestContext& ctx) {
|
||||||
auto it = handlers.find(ctx.GetCommand());
|
const auto command = ctx.GetCommand();
|
||||||
|
auto it = handlers.find(command);
|
||||||
|
const bool is_cmd_read = command == 0;
|
||||||
FunctionInfoBase const* info = it == handlers.end() ? nullptr : &it->second;
|
FunctionInfoBase const* info = it == handlers.end() ? nullptr : &it->second;
|
||||||
if (info == nullptr || info->handler_callback == nullptr)
|
if (info == nullptr || info->handler_callback == nullptr)
|
||||||
return ReportUnimplementedFunction(ctx, info);
|
return ReportUnimplementedFunction(ctx, info);
|
||||||
|
|
||||||
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
|
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
|
||||||
handler_invoker(this, info->handler_callback, ctx);
|
handler_invoker(this, info->handler_callback, ctx);
|
||||||
|
|
||||||
|
if (is_i_storage && is_cmd_read) {
|
||||||
|
const auto* const process = ctx.GetThread().GetOwnerProcess();
|
||||||
|
if (process != nullptr && system.IsNVDECActiveForProcess(process->GetId())) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::microseconds{600});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServiceFrameworkBase::InvokeRequestTipc(HLERequestContext& ctx) {
|
void ServiceFrameworkBase::InvokeRequestTipc(HLERequestContext& ctx) {
|
||||||
|
|||||||
@@ -107,6 +107,8 @@ protected:
|
|||||||
Core::System& system;
|
Core::System& system;
|
||||||
/// Identifier string used to connect to the service.
|
/// Identifier string used to connect to the service.
|
||||||
const char* service_name;
|
const char* service_name;
|
||||||
|
/// Whether this is the IStorage service.
|
||||||
|
const bool is_i_storage;
|
||||||
/// Function used to safely up-cast pointers to the derived class before invoking a handler.
|
/// Function used to safely up-cast pointers to the derived class before invoking a handler.
|
||||||
InvokerFn* handler_invoker;
|
InvokerFn* handler_invoker;
|
||||||
/// Maximum number of concurrent sessions that this service can handle.
|
/// Maximum number of concurrent sessions that this service can handle.
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
|
||||||
@@ -56,13 +56,14 @@ inline bool RemoveDLC(const Service::FileSystem::FileSystemController& fs_contro
|
|||||||
*/
|
*/
|
||||||
inline size_t RemoveAllDLC(Core::System& system, const u64 program_id) {
|
inline size_t RemoveAllDLC(Core::System& system, const u64 program_id) {
|
||||||
size_t count{};
|
size_t count{};
|
||||||
|
const auto application_id = FileSys::GetBaseTitleID(program_id);
|
||||||
const auto& fs_controller = system.GetFileSystemController();
|
const auto& fs_controller = system.GetFileSystemController();
|
||||||
const auto dlc_entries = system.GetContentProvider().ListEntriesFilter(
|
const auto dlc_entries = system.GetContentProvider().ListEntriesFilter(
|
||||||
FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
|
FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
|
||||||
std::vector<u64> program_dlc_entries;
|
std::vector<u64> program_dlc_entries;
|
||||||
|
|
||||||
for (const auto& entry : dlc_entries) {
|
for (const auto& entry : dlc_entries) {
|
||||||
if (FileSys::GetBaseTitleID(entry.title_id) == program_id) {
|
if (FileSys::GetBaseTitleID(entry.title_id) == application_id) {
|
||||||
program_dlc_entries.push_back(entry.title_id);
|
program_dlc_entries.push_back(entry.title_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,9 +84,17 @@ inline size_t RemoveAllDLC(Core::System& system, const u64 program_id) {
|
|||||||
*/
|
*/
|
||||||
inline bool RemoveUpdate(const Service::FileSystem::FileSystemController& fs_controller,
|
inline bool RemoveUpdate(const Service::FileSystem::FileSystemController& fs_controller,
|
||||||
const u64 program_id) {
|
const u64 program_id) {
|
||||||
const auto update_id = program_id | 0x800;
|
const auto remove_update = [&fs_controller](u64 update_id) {
|
||||||
return fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) ||
|
return fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) ||
|
||||||
fs_controller.GetSDMCContents()->RemoveExistingEntry(update_id);
|
fs_controller.GetSDMCContents()->RemoveExistingEntry(update_id);
|
||||||
|
};
|
||||||
|
const auto update_id = FileSys::GetUpdateTitleID(program_id);
|
||||||
|
const auto application_update_id =
|
||||||
|
FileSys::GetUpdateTitleID(FileSys::GetBaseTitleID(program_id));
|
||||||
|
if (update_id != application_update_id && remove_update(update_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return remove_update(application_update_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,15 +120,26 @@ inline bool RemoveBaseContent(const Service::FileSystem::FileSystemController& f
|
|||||||
inline bool RemoveMod(const Service::FileSystem::FileSystemController& fs_controller,
|
inline bool RemoveMod(const Service::FileSystem::FileSystemController& fs_controller,
|
||||||
const u64 program_id, const std::string& mod_name) {
|
const u64 program_id, const std::string& mod_name) {
|
||||||
// Check general Mods (LayeredFS and IPS)
|
// Check general Mods (LayeredFS and IPS)
|
||||||
const auto mod_dir = fs_controller.GetModificationLoadRoot(program_id);
|
const auto remove_from_root = [&mod_name](const auto& root) {
|
||||||
if (mod_dir != nullptr) {
|
return root != nullptr && root->DeleteSubdirectoryRecursive(mod_name);
|
||||||
return mod_dir->DeleteSubdirectoryRecursive(mod_name);
|
};
|
||||||
|
if (remove_from_root(fs_controller.GetModificationLoadRoot(program_id))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (FileSys::GetBaseTitleID(program_id) != program_id &&
|
||||||
|
remove_from_root(
|
||||||
|
fs_controller.GetModificationLoadRoot(FileSys::GetBaseTitleID(program_id)))) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check SDMC mod directory (RomFS LayeredFS)
|
// Check SDMC mod directory (RomFS LayeredFS)
|
||||||
const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(program_id);
|
if (remove_from_root(fs_controller.GetSDMCModificationLoadRoot(program_id))) {
|
||||||
if (sdmc_mod_dir != nullptr) {
|
return true;
|
||||||
return sdmc_mod_dir->DeleteSubdirectoryRecursive(mod_name);
|
}
|
||||||
|
if (FileSys::GetBaseTitleID(program_id) != program_id &&
|
||||||
|
remove_from_root(
|
||||||
|
fs_controller.GetSDMCModificationLoadRoot(FileSys::GetBaseTitleID(program_id)))) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "common/fs/fs.h"
|
#include "common/fs/fs.h"
|
||||||
#include "common/fs/fs_types.h"
|
#include "common/fs/fs_types.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
|
#include "core/file_sys/common_funcs.h"
|
||||||
#include "frontend_common/data_manager.h"
|
#include "frontend_common/data_manager.h"
|
||||||
#include "mod_manager.h"
|
#include "mod_manager.h"
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ std::vector<std::filesystem::path> GetModFolder(const std::string& root) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ModInstallResult InstallMod(const std::filesystem::path& path, const u64 program_id, const bool copy) {
|
ModInstallResult InstallMod(const std::filesystem::path& path, const u64 program_id, const bool copy) {
|
||||||
const auto program_id_string = fmt::format("{:016X}", program_id);
|
const auto program_id_string = fmt::format("{:016X}", FileSys::GetBaseTitleID(program_id));
|
||||||
const auto mod_name = path.filename();
|
const auto mod_name = path.filename();
|
||||||
const auto mod_dir =
|
const auto mod_dir =
|
||||||
DataManager::GetDataDir(DataManager::DataDir::Mods) / program_id_string / mod_name;
|
DataManager::GetDataDir(DataManager::DataDir::Mods) / program_id_string / mod_name;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "common/fs/fs.h"
|
#include "common/fs/fs.h"
|
||||||
#include "common/fs/path_util.h"
|
#include "common/fs/path_util.h"
|
||||||
|
#include "core/file_sys/common_funcs.h"
|
||||||
#include "core/file_sys/savedata_factory.h"
|
#include "core/file_sys/savedata_factory.h"
|
||||||
#include "core/hle/service/am/am_types.h"
|
#include "core/hle/service/am/am_types.h"
|
||||||
#include "frontend_common/content_manager.h"
|
#include "frontend_common/content_manager.h"
|
||||||
@@ -305,6 +306,7 @@ void RemoveAllTransferableShaderCaches(u64 program_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RemoveCustomConfiguration(u64 program_id, const std::string& game_path) {
|
void RemoveCustomConfiguration(u64 program_id, const std::string& game_path) {
|
||||||
|
program_id = FileSys::GetBaseTitleID(program_id);
|
||||||
const auto file_path = std::filesystem::path(Common::FS::ToU8String(game_path));
|
const auto file_path = std::filesystem::path(Common::FS::ToU8String(game_path));
|
||||||
const auto config_file_name =
|
const auto config_file_name =
|
||||||
program_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()).append(".ini")
|
program_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()).append(".ini")
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include "common/settings_input.h"
|
#include "common/settings_input.h"
|
||||||
#include "configuration/shared_widget.h"
|
#include "configuration/shared_widget.h"
|
||||||
#include "core/core.h"
|
#include "core/core.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/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
#include "core/file_sys/xts_archive.h"
|
#include "core/file_sys/xts_archive.h"
|
||||||
@@ -50,7 +51,7 @@
|
|||||||
ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name,
|
ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name,
|
||||||
std::vector<VkDeviceInfo::Record>& vk_device_records,
|
std::vector<VkDeviceInfo::Record>& vk_device_records,
|
||||||
Core::System& system_)
|
Core::System& system_)
|
||||||
: QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{title_id_},
|
: QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{FileSys::GetBaseTitleID(title_id_)},
|
||||||
system{system_},
|
system{system_},
|
||||||
builder{std::make_unique<ConfigurationShared::Builder>(this, !system_.IsPoweredOn())},
|
builder{std::make_unique<ConfigurationShared::Builder>(this, !system_.IsPoweredOn())},
|
||||||
tab_group{std::make_shared<std::vector<ConfigurationShared::Tab*>>()} {
|
tab_group{std::make_shared<std::vector<ConfigurationShared::Tab*>>()} {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include "common/fs/path_util.h"
|
#include "common/fs/path_util.h"
|
||||||
#include "configuration/addon/mod_select_dialog.h"
|
#include "configuration/addon/mod_select_dialog.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
#include "core/file_sys/common_funcs.h"
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
#include "frontend_common/mod_manager.h"
|
#include "frontend_common/mod_manager.h"
|
||||||
@@ -137,7 +138,7 @@ void ConfigurePerGameAddons::LoadFromFile(FileSys::VirtualFile file_) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ConfigurePerGameAddons::SetTitleId(u64 id) {
|
void ConfigurePerGameAddons::SetTitleId(u64 id) {
|
||||||
this->title_id = id;
|
this->title_id = FileSys::GetBaseTitleID(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigurePerGameAddons::InstallMods(const QStringList& mods) {
|
void ConfigurePerGameAddons::InstallMods(const QStringList& mods) {
|
||||||
|
|||||||
@@ -1922,7 +1922,7 @@ void MainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletPa
|
|||||||
std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())};
|
std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())};
|
||||||
const auto config_file_name = title_id == 0
|
const auto config_file_name = title_id == 0
|
||||||
? Common::FS::PathToUTF8String(file_path.filename())
|
? Common::FS::PathToUTF8String(file_path.filename())
|
||||||
: fmt::format("{:016X}", title_id);
|
: fmt::format("{:016X}", FileSys::GetBaseTitleID(title_id));
|
||||||
QtConfig per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
|
QtConfig per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
|
||||||
QtCommon::system->HIDCore().ReloadInputDevices();
|
QtCommon::system->HIDCore().ReloadInputDevices();
|
||||||
QtCommon::system->ApplySettings();
|
QtCommon::system->ApplySettings();
|
||||||
@@ -2544,9 +2544,11 @@ void MainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pat
|
|||||||
}
|
}
|
||||||
|
|
||||||
const FileSys::NCA update_nca{packed_update_raw, nullptr};
|
const FileSys::NCA update_nca{packed_update_raw, nullptr};
|
||||||
|
const auto selected_update_id = FileSys::GetUpdateTitleID(title_id);
|
||||||
|
const auto application_update_id = FileSys::GetUpdateTitleID(FileSys::GetBaseTitleID(title_id));
|
||||||
if (type != FileSys::ContentRecordType::Program ||
|
if (type != FileSys::ContentRecordType::Program ||
|
||||||
update_nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS ||
|
update_nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS ||
|
||||||
update_nca.GetTitleId() != FileSys::GetUpdateTitleID(title_id)) {
|
(update_nca.GetTitleId() != selected_update_id && update_nca.GetTitleId() != application_update_id)) {
|
||||||
packed_update_raw = {};
|
packed_update_raw = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4421,6 +4423,7 @@ void MainWindow::SetFPSSuffix() {
|
|||||||
|
|
||||||
bool MainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id,
|
bool MainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id,
|
||||||
u64* selected_title_id, u8* selected_content_record_type) {
|
u64* selected_title_id, u8* selected_content_record_type) {
|
||||||
|
program_id = FileSys::GetBaseTitleID(program_id);
|
||||||
using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>;
|
using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>;
|
||||||
boost::container::flat_set<ContentInfo> available_title_ids;
|
boost::container::flat_set<ContentInfo> available_title_ids;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user