mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-05 12:00:05 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aa0878b636 |
+2
-2
@@ -340,10 +340,10 @@
|
|||||||
"version": "vulkan-sdk-%NUMERIC_VERSION%"
|
"version": "vulkan-sdk-%NUMERIC_VERSION%"
|
||||||
},
|
},
|
||||||
"xbyak": {
|
"xbyak": {
|
||||||
"hash": "e0aa0a603dd3ac1a39d82213df1e73c042831aec2d6b2fe382c899651eaae4ed7e8aeb6be9c41ea0097087c9d091961ab18f313cd6a9e10d621715ffd49bfe36",
|
"hash": "b6475276b2faaeb315734ea8f4f8bd87ededcee768961b39679bee547e7f3e98884d8b7851e176d861dab30a80a76e6ea302f8c111483607dde969b4797ea95a",
|
||||||
"package": "xbyak",
|
"package": "xbyak",
|
||||||
"repo": "herumi/xbyak",
|
"repo": "herumi/xbyak",
|
||||||
"version": "v7.40.1"
|
"version": "v7.35.2"
|
||||||
},
|
},
|
||||||
"zlib": {
|
"zlib": {
|
||||||
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
|
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
|
||||||
|
|||||||
+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 {
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import info.debatty.java.stringsimilarity.Jaccard
|
|||||||
import info.debatty.java.stringsimilarity.JaroWinkler
|
import info.debatty.java.stringsimilarity.JaroWinkler
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
|
import androidx.core.view.doOnNextLayout
|
||||||
|
|
||||||
class GamesFragment : Fragment() {
|
class GamesFragment : Fragment() {
|
||||||
private var _binding: FragmentGamesBinding? = null
|
private var _binding: FragmentGamesBinding? = null
|
||||||
@@ -58,6 +59,7 @@ class GamesFragment : Fragment() {
|
|||||||
private var originalHeaderLeftMargin: Int? = null
|
private var originalHeaderLeftMargin: Int? = null
|
||||||
|
|
||||||
private var lastViewType: Int = GameAdapter.VIEW_TYPE_GRID
|
private var lastViewType: Int = GameAdapter.VIEW_TYPE_GRID
|
||||||
|
private var fallbackBottomInset: Int = 0
|
||||||
private var pendingPostReloadListSettle = false
|
private var pendingPostReloadListSettle = false
|
||||||
private var pendingPostReloadListSettleGeneration = 0
|
private var pendingPostReloadListSettleGeneration = 0
|
||||||
private var gameListSubmitGeneration = 0
|
private var gameListSubmitGeneration = 0
|
||||||
@@ -225,7 +227,12 @@ class GamesFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
else -> throw IllegalArgumentException("Invalid view type: $savedViewType")
|
else -> throw IllegalArgumentException("Invalid view type: $savedViewType")
|
||||||
}
|
}
|
||||||
if (savedViewType != GameAdapter.VIEW_TYPE_CAROUSEL) {
|
if (savedViewType == GameAdapter.VIEW_TYPE_CAROUSEL) {
|
||||||
|
(binding.gridGames as? View)?.let { it -> ViewCompat.requestApplyInsets(it)}
|
||||||
|
doOnNextLayout { //Carousel: important to avoid overlap issues
|
||||||
|
(this as? CarouselRecyclerView)?.notifyLaidOut(fallbackBottomInset)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
(this as? CarouselRecyclerView)?.setupCarousel(false)
|
(this as? CarouselRecyclerView)?.setupCarousel(false)
|
||||||
}
|
}
|
||||||
adapter = gameAdapter
|
adapter = gameAdapter
|
||||||
@@ -583,6 +590,11 @@ class GamesFragment : Fragment() {
|
|||||||
qlaunchButton.layoutParams = mlpQLaunch
|
qlaunchButton.layoutParams = mlpQLaunch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val navInsets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars())
|
||||||
|
val gestureInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures())
|
||||||
|
val bottomInset = maxOf(navInsets.bottom, gestureInsets.bottom, cutoutInsets.bottom)
|
||||||
|
fallbackBottomInset = bottomInset
|
||||||
|
(binding.gridGames as? CarouselRecyclerView)?.notifyInsetsReady(bottomInset)
|
||||||
windowInsets
|
windowInsets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -12,16 +12,13 @@ import androidx.recyclerview.widget.PagerSnapHelper
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
import kotlin.math.pow
|
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.adapters.GameAdapter
|
import org.yuzu.yuzu_emu.adapters.GameAdapter
|
||||||
import androidx.core.view.doOnNextLayout
|
import androidx.core.view.doOnNextLayout
|
||||||
import androidx.core.view.ViewCompat
|
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import org.yuzu.yuzu_emu.utils.FullscreenHelper
|
|
||||||
/**
|
/**
|
||||||
* CarouselRecyclerView encapsulates all carousel content for the games UI.
|
* CarouselRecyclerView encapsulates all carousel content for the games UI.
|
||||||
* It manages overlapping cards, center snapping, custom drawing order,
|
* It manages overlapping cards, center snapping, custom drawing order,
|
||||||
@@ -35,9 +32,7 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
|
|
||||||
private var overlapFactor: Float = 0f
|
private var overlapFactor: Float = 0f
|
||||||
private var overlapPx: Int = 0
|
private var overlapPx: Int = 0
|
||||||
private var bottomInset: Int = 0
|
private var bottomInset: Int = -1
|
||||||
private var latestWindowInsets: WindowInsetsCompat? = null
|
|
||||||
private var cardGeometryInitialized: Boolean = false
|
|
||||||
private var overlapDecoration: OverlappingDecoration? = null
|
private var overlapDecoration: OverlappingDecoration? = null
|
||||||
private var pagerSnapHelper: PagerSnapHelper? = null
|
private var pagerSnapHelper: PagerSnapHelper? = null
|
||||||
private var scalingScrollListener: OnScrollListener? = null
|
private var scalingScrollListener: OnScrollListener? = null
|
||||||
@@ -96,38 +91,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
setChildrenDrawingOrderEnabled(true)
|
setChildrenDrawingOrderEnabled(true)
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(this) { _, insets ->
|
|
||||||
latestWindowInsets = insets
|
|
||||||
updateCardGeometry()
|
|
||||||
applyCarouselPadding()
|
|
||||||
insets
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAttachedToWindow() {
|
|
||||||
super.onAttachedToWindow()
|
|
||||||
ViewCompat.requestApplyInsets(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
|
||||||
super.onSizeChanged(w, h, oldw, oldh)
|
|
||||||
if (w != oldw || h != oldh) {
|
|
||||||
updateCardGeometry()
|
|
||||||
applyCarouselPadding()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
||||||
super.onLayout(changed, left, top, right, bottom)
|
|
||||||
if (isCarouselMode) updateChildScalesAndAlpha()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
|
||||||
super.onWindowFocusChanged(hasFocus)
|
|
||||||
if (hasFocus) {
|
|
||||||
ViewCompat.requestApplyInsets(this)
|
|
||||||
post { updateCardGeometry() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setAdapter(adapter: Adapter<*>?) {
|
override fun setAdapter(adapter: Adapter<*>?) {
|
||||||
@@ -140,8 +103,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
super.setAdapter(adapter)
|
super.setAdapter(adapter)
|
||||||
|
|
||||||
(adapter as? GameAdapter)?.registerAdapterDataObserver(carouselAdapterObserver)
|
(adapter as? GameAdapter)?.registerAdapterDataObserver(carouselAdapterObserver)
|
||||||
updateCardGeometry()
|
|
||||||
applyCarouselPadding()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun calculateCenter(width: Int, paddingStart: Int, paddingEnd: Int): Int {
|
private fun calculateCenter(width: Int, paddingStart: Int, paddingEnd: Int): Int {
|
||||||
@@ -292,71 +253,40 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveBottomInset(windowInsets: WindowInsetsCompat): Int {
|
fun notifyInsetsReady(newBottomInset: Int) {
|
||||||
val navigationBottom = if (FullscreenHelper.isFullscreenEnabled(context)) {
|
if (bottomInset != newBottomInset) {
|
||||||
0
|
bottomInset = newBottomInset
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCarouselMode) {
|
||||||
|
setupCarousel(true)
|
||||||
} else {
|
} else {
|
||||||
windowInsets.getInsetsIgnoringVisibility(WindowInsetsCompat.Type.navigationBars()).bottom
|
setupCarousel(false)
|
||||||
}
|
}
|
||||||
val gestureInsets = windowInsets.getInsetsIgnoringVisibility(
|
|
||||||
WindowInsetsCompat.Type.systemGestures()
|
|
||||||
)
|
|
||||||
val cutoutInsets = windowInsets.getInsetsIgnoringVisibility(
|
|
||||||
WindowInsetsCompat.Type.displayCutout()
|
|
||||||
)
|
|
||||||
return maxOf(navigationBottom, gestureInsets.bottom, cutoutInsets.bottom)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateCardGeometry() {
|
fun notifyLaidOut(fallBackBottomInset: Int) {
|
||||||
if (!isCarouselMode || height <= 0) return
|
if (bottomInset < 0) bottomInset = fallBackBottomInset
|
||||||
|
var gameAdapter = adapter as? GameAdapter ?: return
|
||||||
|
var newCardSize = cardSize(bottomInset)
|
||||||
|
if (gameAdapter.cardSize != newCardSize) {
|
||||||
|
gameAdapter.setCardSize(newCardSize)
|
||||||
|
}
|
||||||
|
|
||||||
val gameAdapter = adapter as? GameAdapter ?: return
|
if (isCarouselMode) {
|
||||||
val windowInsets = latestWindowInsets ?: ViewCompat.getRootWindowInsets(this) ?: return
|
setupCarousel(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cardGeometryInitialized && !hasWindowFocus()) return
|
fun cardSize(bottomInset: Int): Int {
|
||||||
|
|
||||||
val newBottomInset = resolveBottomInset(windowInsets).coerceIn(0, height)
|
|
||||||
val internalFactor = resources.getFraction(R.fraction.carousel_card_size_factor, 1, 1)
|
val internalFactor = resources.getFraction(R.fraction.carousel_card_size_factor, 1, 1)
|
||||||
val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn(
|
val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn(
|
||||||
0f,
|
0f,
|
||||||
1f
|
1f
|
||||||
)
|
)
|
||||||
val screenWidth = resources.displayMetrics.widthPixels.toFloat()
|
val scaledHeight = height * userFactor
|
||||||
val screenHeight = resources.displayMetrics.heightPixels.toFloat()
|
val availableHeight = height - bottomInset
|
||||||
val aspectFactor = ((screenWidth / screenHeight) / (20f / 9f))
|
return minOf(scaledHeight.toInt(), availableHeight.toInt())
|
||||||
.pow(0.75f)
|
|
||||||
.coerceIn(0.5f, 1f)
|
|
||||||
val newCardSize = minOf(
|
|
||||||
(height * userFactor).toInt(),
|
|
||||||
height - newBottomInset,
|
|
||||||
(height * aspectFactor).toInt()
|
|
||||||
)
|
|
||||||
if (newCardSize <= 0) return
|
|
||||||
val insetChanged = bottomInset != newBottomInset
|
|
||||||
val cardSizeChanged = gameAdapter.cardSize != newCardSize
|
|
||||||
|
|
||||||
bottomInset = newBottomInset
|
|
||||||
cardGeometryInitialized = true
|
|
||||||
|
|
||||||
if (cardSizeChanged) gameAdapter.setCardSize(newCardSize)
|
|
||||||
if (insetChanged || cardSizeChanged) setupCarousel(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun applyCarouselPadding() {
|
|
||||||
if (!isCarouselMode) return
|
|
||||||
|
|
||||||
val gameAdapter = adapter as? GameAdapter ?: return
|
|
||||||
val cardSize = gameAdapter.cardSize
|
|
||||||
if (cardSize <= 0 || bottomInset < 0) return
|
|
||||||
|
|
||||||
val topPadding = ((height - bottomInset - cardSize) / 2).coerceAtLeast(0)
|
|
||||||
val sidePadding = (width - cardSize) / 2
|
|
||||||
if (paddingLeft != sidePadding || paddingTop != topPadding ||
|
|
||||||
paddingRight != sidePadding || paddingBottom != 0
|
|
||||||
) {
|
|
||||||
setPadding(sidePadding, topPadding, sidePadding, 0)
|
|
||||||
}
|
|
||||||
clipToPadding = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setupCarousel(enabled: Boolean) {
|
fun setupCarousel(enabled: Boolean) {
|
||||||
@@ -385,6 +315,9 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
internalFlingMultiplier
|
internalFlingMultiplier
|
||||||
).coerceIn(1f, 5f)
|
).coerceIn(1f, 5f)
|
||||||
|
|
||||||
|
// Detach SnapHelper during setup
|
||||||
|
pagerSnapHelper?.attachToRecyclerView(null)
|
||||||
|
|
||||||
// Add overlap decoration if not present
|
// Add overlap decoration if not present
|
||||||
if (overlapDecoration == null) {
|
if (overlapDecoration == null) {
|
||||||
overlapDecoration = OverlappingDecoration(overlapPx)
|
overlapDecoration = OverlappingDecoration(overlapPx)
|
||||||
@@ -402,7 +335,12 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
addOnScrollListener(scalingScrollListener!!)
|
addOnScrollListener(scalingScrollListener!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
applyCarouselPadding()
|
if (cardSize > 0) {
|
||||||
|
val topPadding = ((height - bottomInset - cardSize) / 2).coerceAtLeast(0) // Center vertically
|
||||||
|
val sidePadding = (width - cardSize) / 2 // Center first/last card
|
||||||
|
setPadding(sidePadding, topPadding, sidePadding, 0)
|
||||||
|
clipToPadding = false
|
||||||
|
}
|
||||||
|
|
||||||
if (pagerSnapHelper == null) {
|
if (pagerSnapHelper == null) {
|
||||||
pagerSnapHelper = CenterPagerSnapHelper()
|
pagerSnapHelper = CenterPagerSnapHelper()
|
||||||
@@ -424,7 +362,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
savedItemAnimator = null
|
savedItemAnimator = null
|
||||||
}
|
}
|
||||||
cardGeometryInitialized = false
|
|
||||||
useCustomDrawingOrder = false
|
useCustomDrawingOrder = false
|
||||||
// Reset padding and fling
|
// Reset padding and fling
|
||||||
setPadding(0, 0, 0, 0)
|
setPadding(0, 0, 0, 0)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
|
||||||
AudioCore::AudioCore(Core::System& system) {
|
AudioCore::AudioCore(Core::System& system) {
|
||||||
audio_manager.emplace(system);
|
audio_manager.emplace();
|
||||||
CreateSinks();
|
CreateSinks();
|
||||||
// Must be created after the sinks
|
// Must be created after the sinks
|
||||||
adsp.emplace(system, *output_sink);
|
adsp.emplace(system, *output_sink);
|
||||||
|
|||||||
@@ -15,12 +15,12 @@
|
|||||||
|
|
||||||
namespace AudioCore::AudioIn {
|
namespace AudioCore::AudioIn {
|
||||||
|
|
||||||
Manager::Manager(Core::System& system) {
|
Manager::Manager(Core::System& system_) : system{system_} {
|
||||||
std::iota(session_ids.begin(), session_ids.end(), 0);
|
std::iota(session_ids.begin(), session_ids.end(), 0);
|
||||||
num_free_sessions = MaxInSessions;
|
num_free_sessions = MaxInSessions;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) {
|
Result Manager::AcquireSessionId(size_t& session_id) {
|
||||||
if (num_free_sessions == 0) {
|
if (num_free_sessions == 0) {
|
||||||
LOG_ERROR(Service_Audio, "All 4 AudioIn sessions are in use, cannot create any more");
|
LOG_ERROR(Service_Audio, "All 4 AudioIn sessions are in use, cannot create any more");
|
||||||
return Service::Audio::ResultOutOfSessions;
|
return Service::Audio::ResultOutOfSessions;
|
||||||
@@ -31,7 +31,7 @@ Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) {
|
|||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) {
|
void Manager::ReleaseSessionId(const size_t session_id) {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
LOG_DEBUG(Service_Audio, "Freeing AudioIn session {}", session_id);
|
LOG_DEBUG(Service_Audio, "Freeing AudioIn session {}", session_id);
|
||||||
session_ids[free_session_id] = session_id;
|
session_ids[free_session_id] = session_id;
|
||||||
@@ -41,20 +41,21 @@ void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) {
|
|||||||
applet_resource_user_ids[session_id] = 0;
|
applet_resource_user_ids[session_id] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Manager::LinkToManager(Core::System& system) {
|
Result Manager::LinkToManager() {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
if (!linked_to_manager) {
|
if (!linked_to_manager) {
|
||||||
system.AudioCore().GetAudioManager().SetInManager(&Manager::BufferReleaseAndRegister);
|
system.AudioCore().GetAudioManager().SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||||
linked_to_manager = true;
|
linked_to_manager = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::Start(Core::System& system) {
|
void Manager::Start() {
|
||||||
if (sessions_started) {
|
if (sessions_started) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
for (auto& session : sessions) {
|
for (auto& session : sessions) {
|
||||||
if (session) {
|
if (session) {
|
||||||
@@ -65,19 +66,21 @@ void Manager::Start(Core::System& system) {
|
|||||||
sessions_started = true;
|
sessions_started = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::BufferReleaseAndRegister(void *data, Core::System& system) noexcept {
|
void Manager::BufferReleaseAndRegister() {
|
||||||
Manager* this_ = (Manager*)data;
|
std::scoped_lock l{mutex};
|
||||||
std::scoped_lock l{this_->mutex};
|
for (auto& session : sessions) {
|
||||||
for (auto& session : this_->sessions) {
|
|
||||||
if (session != nullptr) {
|
if (session != nullptr) {
|
||||||
session->ReleaseAndRegisterBuffers();
|
session->ReleaseAndRegisterBuffers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Manager::GetDeviceNames(Core::System& system, std::span<Renderer::AudioDevice::AudioDeviceName> names, [[maybe_unused]] const bool filter) {
|
u32 Manager::GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names,
|
||||||
|
[[maybe_unused]] const bool filter) {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
LinkToManager(system);
|
|
||||||
|
LinkToManager();
|
||||||
|
|
||||||
auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)};
|
auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)};
|
||||||
if (!input_devices.empty() && !names.empty()) {
|
if (!input_devices.empty() && !names.empty()) {
|
||||||
names[0] = Renderer::AudioDevice::AudioDeviceName("Uac");
|
names[0] = Renderer::AudioDevice::AudioDeviceName("Uac");
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -33,29 +30,31 @@ public:
|
|||||||
* @param session_id - Output session_id.
|
* @param session_id - Output session_id.
|
||||||
* @return Result code.
|
* @return Result code.
|
||||||
*/
|
*/
|
||||||
Result AcquireSessionId(Core::System& system, size_t& session_id);
|
Result AcquireSessionId(size_t& session_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Release a session id on close.
|
* Release a session id on close.
|
||||||
*
|
*
|
||||||
* @param session_id - Session id to free.
|
* @param session_id - Session id to free.
|
||||||
*/
|
*/
|
||||||
void ReleaseSessionId(Core::System& system, const size_t session_id);
|
void ReleaseSessionId(size_t session_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Link the audio in manager to the main audio manager.
|
* Link the audio in manager to the main audio manager.
|
||||||
*
|
*
|
||||||
* @return Result code.
|
* @return Result code.
|
||||||
*/
|
*/
|
||||||
Result LinkToManager(Core::System& system);
|
Result LinkToManager();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the audio in manager.
|
* Start the audio in manager.
|
||||||
*/
|
*/
|
||||||
void Start(Core::System& system);
|
void Start();
|
||||||
|
|
||||||
/// @brief Callback function, called by the audio manager when the audio in event is signalled.
|
/**
|
||||||
static void BufferReleaseAndRegister(void *data, Core::System& system) noexcept;
|
* Callback function, called by the audio manager when the audio in event is signalled.
|
||||||
|
*/
|
||||||
|
void BufferReleaseAndRegister();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of audio in device names.
|
* Get a list of audio in device names.
|
||||||
@@ -65,8 +64,10 @@ public:
|
|||||||
*
|
*
|
||||||
* @return Number of names written.
|
* @return Number of names written.
|
||||||
*/
|
*/
|
||||||
u32 GetDeviceNames(Core::System& system, std::span<Renderer::AudioDevice::AudioDeviceName> names, bool filter);
|
u32 GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names, bool filter);
|
||||||
|
|
||||||
|
/// Core system
|
||||||
|
Core::System& system;
|
||||||
/// Array of session ids
|
/// Array of session ids
|
||||||
std::array<size_t, MaxInSessions> session_ids{};
|
std::array<size_t, MaxInSessions> session_ids{};
|
||||||
/// Array of resource user ids
|
/// Array of resource user ids
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
|
||||||
AudioManager::AudioManager(Core::System& system) {
|
AudioManager::AudioManager() {
|
||||||
thread = std::jthread([&](std::stop_token stop_token) {
|
thread = std::jthread([this](std::stop_token stop_token) {
|
||||||
Common::SetCurrentThreadName("AudioManager");
|
Common::SetCurrentThreadName("AudioManager");
|
||||||
std::unique_lock l{events.GetAudioEventLock()};
|
std::unique_lock l{events.GetAudioEventLock()};
|
||||||
events.ClearEvents();
|
events.ClearEvents();
|
||||||
@@ -25,7 +25,7 @@ AudioManager::AudioManager(Core::System& system) {
|
|||||||
const auto event_type = Event::Type(i);
|
const auto event_type = Event::Type(i);
|
||||||
if (events.CheckAudioEventSet(event_type) || timed_out) {
|
if (events.CheckAudioEventSet(event_type) || timed_out) {
|
||||||
if (buffer_events[i]) {
|
if (buffer_events[i]) {
|
||||||
buffer_events[i](this, system);
|
buffer_events[i]();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
events.SetAudioEvent(event_type, false);
|
events.SetAudioEvent(event_type, false);
|
||||||
|
|||||||
@@ -16,10 +16,6 @@
|
|||||||
|
|
||||||
#include "audio_core/audio_event.h"
|
#include "audio_core/audio_event.h"
|
||||||
|
|
||||||
namespace Core {
|
|
||||||
class System;
|
|
||||||
}
|
|
||||||
|
|
||||||
union Result;
|
union Result;
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
@@ -38,9 +34,10 @@ namespace AudioCore {
|
|||||||
* This is only used by audio in and audio out.
|
* This is only used by audio in and audio out.
|
||||||
*/
|
*/
|
||||||
class AudioManager {
|
class AudioManager {
|
||||||
using BufferEventFunc = void (*)(void *data, Core::System& system) noexcept;
|
using BufferEventFunc = std::function<void()>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AudioManager(Core::System& system);
|
explicit AudioManager();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shutdown the audio manager.
|
* Shutdown the audio manager.
|
||||||
|
|||||||
@@ -14,12 +14,12 @@
|
|||||||
|
|
||||||
namespace AudioCore::AudioOut {
|
namespace AudioCore::AudioOut {
|
||||||
|
|
||||||
Manager::Manager(Core::System& system) {
|
Manager::Manager(Core::System& system_) : system{system_} {
|
||||||
std::iota(session_ids.begin(), session_ids.end(), 0);
|
std::iota(session_ids.begin(), session_ids.end(), 0);
|
||||||
num_free_sessions = MaxOutSessions;
|
num_free_sessions = MaxOutSessions;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) {
|
Result Manager::AcquireSessionId(size_t& session_id) {
|
||||||
if (num_free_sessions == 0) {
|
if (num_free_sessions == 0) {
|
||||||
LOG_ERROR(Service_Audio, "All 12 Audio Out sessions are in use, cannot create any more");
|
LOG_ERROR(Service_Audio, "All 12 Audio Out sessions are in use, cannot create any more");
|
||||||
return Service::Audio::ResultOutOfSessions;
|
return Service::Audio::ResultOutOfSessions;
|
||||||
@@ -30,7 +30,7 @@ Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) {
|
|||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) {
|
void Manager::ReleaseSessionId(const size_t session_id) {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
LOG_DEBUG(Service_Audio, "Freeing AudioOut session {}", session_id);
|
LOG_DEBUG(Service_Audio, "Freeing AudioOut session {}", session_id);
|
||||||
session_ids[free_session_id] = session_id;
|
session_ids[free_session_id] = session_id;
|
||||||
@@ -40,17 +40,17 @@ void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) {
|
|||||||
applet_resource_user_ids[session_id] = 0;
|
applet_resource_user_ids[session_id] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Manager::LinkToManager(Core::System& system) {
|
Result Manager::LinkToManager() {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
if (!linked_to_manager) {
|
if (!linked_to_manager) {
|
||||||
system.AudioCore().GetAudioManager().SetOutManager(&Manager::BufferReleaseAndRegister);
|
system.AudioCore().GetAudioManager().SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||||
linked_to_manager = true;
|
linked_to_manager = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::Start(Core::System& system) {
|
void Manager::Start() {
|
||||||
if (sessions_started) {
|
if (sessions_started) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -65,14 +65,19 @@ void Manager::Start(Core::System& system) {
|
|||||||
sessions_started = true;
|
sessions_started = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::BufferReleaseAndRegister(void *data, Core::System& system) noexcept {
|
void Manager::BufferReleaseAndRegister() {
|
||||||
Manager* this_ = (Manager*)data;
|
std::scoped_lock l{mutex};
|
||||||
std::scoped_lock l{this_->mutex};
|
for (auto& session : sessions) {
|
||||||
for (auto& session : this_->sessions) {
|
|
||||||
if (session != nullptr) {
|
if (session != nullptr) {
|
||||||
session->ReleaseAndRegisterBuffers();
|
session->ReleaseAndRegisterBuffers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 Manager::GetAudioOutDeviceNames(
|
||||||
|
std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const {
|
||||||
|
names.emplace_back("DeviceOut");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace AudioCore::AudioOut
|
} // namespace AudioCore::AudioOut
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -32,32 +29,42 @@ public:
|
|||||||
* @param session_id - Output session_id.
|
* @param session_id - Output session_id.
|
||||||
* @return Result code.
|
* @return Result code.
|
||||||
*/
|
*/
|
||||||
Result AcquireSessionId(Core::System& system, size_t& session_id);
|
Result AcquireSessionId(size_t& session_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Release a session id on close.
|
* Release a session id on close.
|
||||||
*
|
*
|
||||||
* @param session_id - Session id to free.
|
* @param session_id - Session id to free.
|
||||||
*/
|
*/
|
||||||
void ReleaseSessionId(Core::System& system, const size_t session_id);
|
void ReleaseSessionId(size_t session_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Link this manager to the main audio manager.
|
* Link this manager to the main audio manager.
|
||||||
*
|
*
|
||||||
* @return Result code.
|
* @return Result code.
|
||||||
*/
|
*/
|
||||||
Result LinkToManager(Core::System& system);
|
Result LinkToManager();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the audio out manager.
|
* Start the audio out manager.
|
||||||
*/
|
*/
|
||||||
void Start(Core::System& system);
|
void Start();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback function, called by the audio manager when the audio out event is signalled.
|
* Callback function, called by the audio manager when the audio out event is signalled.
|
||||||
*/
|
*/
|
||||||
static void BufferReleaseAndRegister(void* data, Core::System& system) noexcept;
|
void BufferReleaseAndRegister();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of audio out device names.
|
||||||
|
*
|
||||||
|
* @param names - Output container to write names to.
|
||||||
|
* @return Number of names written.
|
||||||
|
*/
|
||||||
|
u32 GetAudioOutDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const;
|
||||||
|
|
||||||
|
/// Core system
|
||||||
|
Core::System& system;
|
||||||
/// Array of session ids
|
/// Array of session ids
|
||||||
std::array<size_t, MaxOutSessions> session_ids{};
|
std::array<size_t, MaxOutSessions> session_ids{};
|
||||||
/// Array of resource user ids
|
/// Array of resource user ids
|
||||||
|
|||||||
@@ -1,20 +1,15 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "audio_core/audio_render_manager.h"
|
#include "audio_core/audio_render_manager.h"
|
||||||
#include "audio_core/common/audio_renderer_parameter.h"
|
#include "audio_core/common/audio_renderer_parameter.h"
|
||||||
#include "audio_core/renderer/system_manager.h"
|
|
||||||
#include "audio_core/common/feature_support.h"
|
#include "audio_core/common/feature_support.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
|
||||||
namespace AudioCore::Renderer {
|
namespace AudioCore::Renderer {
|
||||||
|
|
||||||
Manager::Manager(Core::System& system_)
|
Manager::Manager(Core::System& system_)
|
||||||
: system_manager{std::make_unique<SystemManager>(system_)}
|
: system{system_}, system_manager{std::make_unique<SystemManager>(system)} {
|
||||||
{
|
|
||||||
std::iota(session_ids.begin(), session_ids.end(), 0);
|
std::iota(session_ids.begin(), session_ids.end(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,11 +59,11 @@ u32 Manager::GetSessionCount() const {
|
|||||||
return session_count;
|
return session_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Manager::AddSystem(Renderer::System& system_) {
|
bool Manager::AddSystem(System& system_) {
|
||||||
return system_manager->Add(system_);
|
return system_manager->Add(system_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Manager::RemoveSystem(Renderer::System& system_) {
|
bool Manager::RemoveSystem(System& system_) {
|
||||||
return system_manager->Remove(system_);
|
return system_manager->Remove(system_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -74,7 +71,7 @@ public:
|
|||||||
* @param system - The system to add.
|
* @param system - The system to add.
|
||||||
* @return True if the system was successfully added, otherwise false.
|
* @return True if the system was successfully added, otherwise false.
|
||||||
*/
|
*/
|
||||||
bool AddSystem(Renderer::System& system);
|
bool AddSystem(System& system);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a renderer system from the manager.
|
* Remove a renderer system from the manager.
|
||||||
@@ -82,7 +79,7 @@ public:
|
|||||||
* @param system - The system to remove.
|
* @param system - The system to remove.
|
||||||
* @return True if the system was successfully removed, otherwise false.
|
* @return True if the system was successfully removed, otherwise false.
|
||||||
*/
|
*/
|
||||||
bool RemoveSystem(Renderer::System& system);
|
bool RemoveSystem(System& system);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free a session id when the system wants to shut down.
|
* Free a session id when the system wants to shut down.
|
||||||
@@ -92,6 +89,8 @@ public:
|
|||||||
void ReleaseSessionId(s32 session_id);
|
void ReleaseSessionId(s32 session_id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/// Core system
|
||||||
|
Core::System& system;
|
||||||
/// Session ids, -1 when in use
|
/// Session ids, -1 when in use
|
||||||
std::array<s32, MaxRendererSessions> session_ids{};
|
std::array<s32, MaxRendererSessions> session_ids{};
|
||||||
/// Number of active renderers
|
/// Number of active renderers
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -19,9 +16,8 @@ namespace AudioCore {
|
|||||||
*/
|
*/
|
||||||
class WorkbufferAllocator {
|
class WorkbufferAllocator {
|
||||||
public:
|
public:
|
||||||
explicit WorkbufferAllocator(std::span<u8> buffer_)
|
explicit WorkbufferAllocator(std::span<u8> buffer_, u64 size_)
|
||||||
: buffer{buffer_}
|
: buffer{reinterpret_cast<u64>(buffer_.data())}, size{size_} {}
|
||||||
{}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate the given count of T elements, aligned to alignment.
|
* Allocate the given count of T elements, aligned to alignment.
|
||||||
@@ -33,31 +29,36 @@ public:
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
std::span<T> Allocate(u64 count, u64 alignment) {
|
std::span<T> Allocate(u64 count, u64 alignment) {
|
||||||
u64 out{0};
|
u64 out{0};
|
||||||
u64 byte_size = count * sizeof(T);
|
u64 byte_size{count * sizeof(T)};
|
||||||
|
|
||||||
if (byte_size > 0) {
|
if (byte_size > 0) {
|
||||||
auto current{uintptr_t(buffer.data()) + offset};
|
auto current{buffer + offset};
|
||||||
auto aligned_buffer{Common::AlignUp(current, alignment)};
|
auto aligned_buffer{Common::AlignUp(current, alignment)};
|
||||||
if (aligned_buffer + byte_size <= uintptr_t(buffer.data()) + buffer.size()) {
|
if (aligned_buffer + byte_size <= buffer + size) {
|
||||||
out = aligned_buffer;
|
out = aligned_buffer;
|
||||||
offset = byte_size - uintptr_t(buffer.data()) + aligned_buffer;
|
offset = byte_size - buffer + aligned_buffer;
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR(
|
LOG_ERROR(
|
||||||
Service_Audio,
|
Service_Audio,
|
||||||
"Allocated buffer was too small to hold new alloc.\nAllocator size={:08X}, "
|
"Allocated buffer was too small to hold new alloc.\nAllocator size={:08X}, "
|
||||||
"offset={:08X}.\nAttempting to allocate {:08X} with alignment={:02X}",
|
"offset={:08X}.\nAttempting to allocate {:08X} with alignment={:02X}",
|
||||||
buffer.size(), offset, byte_size, alignment);
|
size, offset, byte_size, alignment);
|
||||||
count = 0;
|
count = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::span<T>(reinterpret_cast<T*>(out), count);
|
return std::span<T>(reinterpret_cast<T*>(out), count);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Align the current offset to the given alignment.
|
/**
|
||||||
/// @param alignment - The required starting alignment.
|
* Align the current offset to the given alignment.
|
||||||
|
*
|
||||||
|
* @param alignment - The required starting alignment.
|
||||||
|
*/
|
||||||
void Align(u64 alignment) {
|
void Align(u64 alignment) {
|
||||||
auto current{uintptr_t(buffer.data()) + offset};
|
auto current{buffer + offset};
|
||||||
auto aligned_buffer{Common::AlignUp(current, alignment)};
|
auto aligned_buffer{Common::AlignUp(current, alignment)};
|
||||||
offset = 0 - uintptr_t(buffer.data()) + aligned_buffer;
|
offset = 0 - buffer + aligned_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,7 +76,7 @@ public:
|
|||||||
* @return The size of the current buffer.
|
* @return The size of the current buffer.
|
||||||
*/
|
*/
|
||||||
u64 GetSize() const {
|
u64 GetSize() const {
|
||||||
return buffer.size();
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,11 +85,14 @@ public:
|
|||||||
* @return The remaining size left in the buffer.
|
* @return The remaining size left in the buffer.
|
||||||
*/
|
*/
|
||||||
u64 GetRemainingSize() const {
|
u64 GetRemainingSize() const {
|
||||||
return buffer.size() - offset;
|
return size - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::span<u8> buffer;
|
/// The buffer into which we are allocating.
|
||||||
|
u64 buffer;
|
||||||
|
/// Size of the buffer we're allocating to.
|
||||||
|
u64 size;
|
||||||
/// Current offset into the buffer, an error will be thrown if it exceeds size.
|
/// Current offset into the buffer, an error will be thrown if it exceeds size.
|
||||||
u64 offset{};
|
u64 offset{};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -11,43 +8,42 @@
|
|||||||
namespace AudioCore::AudioIn {
|
namespace AudioCore::AudioIn {
|
||||||
|
|
||||||
In::In(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
|
In::In(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
|
||||||
: manager{manager_}, parent_mutex{manager.mutex}, event{event_}
|
: manager{manager_}, parent_mutex{manager.mutex}, event{event_}, system{system_, event,
|
||||||
, audio_system{system_, event, session_id_}
|
session_id_} {}
|
||||||
{}
|
|
||||||
|
|
||||||
void In::Free(Core::System& system) {
|
void In::Free() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
manager.ReleaseSessionId(system, audio_system.GetSessionId());
|
manager.ReleaseSessionId(system.GetSessionId());
|
||||||
}
|
}
|
||||||
|
|
||||||
System& In::GetSystem() {
|
System& In::GetSystem() {
|
||||||
return audio_system;
|
return system;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioIn::State In::GetState() {
|
AudioIn::State In::GetState() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return audio_system.GetState();
|
return system.GetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result In::StartSystem() {
|
Result In::StartSystem() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return audio_system.Start();
|
return system.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void In::StartSession() {
|
void In::StartSession() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
audio_system.StartSession();
|
system.StartSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result In::StopSystem() {
|
Result In::StopSystem() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return audio_system.Stop();
|
return system.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) {
|
Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
|
|
||||||
if (audio_system.AppendBuffer(buffer, tag)) {
|
if (system.AppendBuffer(buffer, tag)) {
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
return Service::Audio::ResultBufferCountReached;
|
return Service::Audio::ResultBufferCountReached;
|
||||||
@@ -55,20 +51,20 @@ Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) {
|
|||||||
|
|
||||||
void In::ReleaseAndRegisterBuffers() {
|
void In::ReleaseAndRegisterBuffers() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
if (audio_system.GetState() == State::Started) {
|
if (system.GetState() == State::Started) {
|
||||||
audio_system.ReleaseBuffers();
|
system.ReleaseBuffers();
|
||||||
audio_system.RegisterBuffers();
|
system.RegisterBuffers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool In::FlushAudioInBuffers() {
|
bool In::FlushAudioInBuffers() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return audio_system.FlushAudioInBuffers();
|
return system.FlushAudioInBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 In::GetReleasedBuffers(std::span<u64> tags) {
|
u32 In::GetReleasedBuffers(std::span<u64> tags) {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return audio_system.GetReleasedBuffers(tags);
|
return system.GetReleasedBuffers(tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
Kernel::KReadableEvent& In::GetBufferEvent() {
|
Kernel::KReadableEvent& In::GetBufferEvent() {
|
||||||
@@ -78,27 +74,27 @@ Kernel::KReadableEvent& In::GetBufferEvent() {
|
|||||||
|
|
||||||
f32 In::GetVolume() const {
|
f32 In::GetVolume() const {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return audio_system.GetVolume();
|
return system.GetVolume();
|
||||||
}
|
}
|
||||||
|
|
||||||
void In::SetVolume(f32 volume) {
|
void In::SetVolume(f32 volume) {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
audio_system.SetVolume(volume);
|
system.SetVolume(volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool In::ContainsAudioBuffer(u64 tag) const {
|
bool In::ContainsAudioBuffer(u64 tag) const {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return audio_system.ContainsAudioBuffer(tag);
|
return system.ContainsAudioBuffer(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 In::GetBufferCount() const {
|
u32 In::GetBufferCount() const {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return audio_system.GetBufferCount();
|
return system.GetBufferCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 In::GetPlayedSampleCount() const {
|
u64 In::GetPlayedSampleCount() const {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return audio_system.GetPlayedSampleCount();
|
return system.GetPlayedSampleCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace AudioCore::AudioIn
|
} // namespace AudioCore::AudioIn
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -33,7 +30,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Free this audio in from the audio in manager.
|
* Free this audio in from the audio in manager.
|
||||||
*/
|
*/
|
||||||
void Free(Core::System& system);
|
void Free();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get this audio in's system.
|
* Get this audio in's system.
|
||||||
@@ -144,7 +141,7 @@ private:
|
|||||||
/// Buffer event, signalled when buffers are ready to be released
|
/// Buffer event, signalled when buffers are ready to be released
|
||||||
Kernel::KEvent* event;
|
Kernel::KEvent* event;
|
||||||
/// Main audio in system
|
/// Main audio in system
|
||||||
System audio_system;
|
System system;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AudioCore::AudioIn
|
} // namespace AudioCore::AudioIn
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -11,43 +8,42 @@
|
|||||||
namespace AudioCore::AudioOut {
|
namespace AudioCore::AudioOut {
|
||||||
|
|
||||||
Out::Out(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
|
Out::Out(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
|
||||||
: manager{manager_}, parent_mutex{manager.mutex}, event{event_}
|
: manager{manager_}, parent_mutex{manager.mutex}, event{event_}, system{system_, event,
|
||||||
, audio_system{system_, event, session_id_}
|
session_id_} {}
|
||||||
{}
|
|
||||||
|
|
||||||
void Out::Free(Core::System& system) {
|
void Out::Free() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
manager.ReleaseSessionId(system, audio_system.GetSessionId());
|
manager.ReleaseSessionId(system.GetSessionId());
|
||||||
}
|
}
|
||||||
|
|
||||||
System& Out::GetSystem() {
|
System& Out::GetSystem() {
|
||||||
return audio_system;
|
return system;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioOut::State Out::GetState() {
|
AudioOut::State Out::GetState() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return audio_system.GetState();
|
return system.GetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Out::StartSystem() {
|
Result Out::StartSystem() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return audio_system.Start();
|
return system.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Out::StartSession() {
|
void Out::StartSession() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
audio_system.StartSession();
|
system.StartSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Out::StopSystem() {
|
Result Out::StopSystem() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return audio_system.Stop();
|
return system.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) {
|
Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
|
|
||||||
if (audio_system.AppendBuffer(buffer, tag)) {
|
if (system.AppendBuffer(buffer, tag)) {
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
return Service::Audio::ResultBufferCountReached;
|
return Service::Audio::ResultBufferCountReached;
|
||||||
@@ -55,20 +51,20 @@ Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) {
|
|||||||
|
|
||||||
void Out::ReleaseAndRegisterBuffers() {
|
void Out::ReleaseAndRegisterBuffers() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
if (audio_system.GetState() == State::Started) {
|
if (system.GetState() == State::Started) {
|
||||||
audio_system.ReleaseBuffers();
|
system.ReleaseBuffers();
|
||||||
audio_system.RegisterBuffers();
|
system.RegisterBuffers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Out::FlushAudioOutBuffers() {
|
bool Out::FlushAudioOutBuffers() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return audio_system.FlushAudioOutBuffers();
|
return system.FlushAudioOutBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Out::GetReleasedBuffers(std::span<u64> tags) {
|
u32 Out::GetReleasedBuffers(std::span<u64> tags) {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return audio_system.GetReleasedBuffers(tags);
|
return system.GetReleasedBuffers(tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
Kernel::KReadableEvent& Out::GetBufferEvent() {
|
Kernel::KReadableEvent& Out::GetBufferEvent() {
|
||||||
@@ -78,27 +74,27 @@ Kernel::KReadableEvent& Out::GetBufferEvent() {
|
|||||||
|
|
||||||
f32 Out::GetVolume() const {
|
f32 Out::GetVolume() const {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return audio_system.GetVolume();
|
return system.GetVolume();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Out::SetVolume(const f32 volume) {
|
void Out::SetVolume(const f32 volume) {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
audio_system.SetVolume(volume);
|
system.SetVolume(volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Out::ContainsAudioBuffer(const u64 tag) const {
|
bool Out::ContainsAudioBuffer(const u64 tag) const {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return audio_system.ContainsAudioBuffer(tag);
|
return system.ContainsAudioBuffer(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Out::GetBufferCount() const {
|
u32 Out::GetBufferCount() const {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return audio_system.GetBufferCount();
|
return system.GetBufferCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 Out::GetPlayedSampleCount() const {
|
u64 Out::GetPlayedSampleCount() const {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return audio_system.GetPlayedSampleCount();
|
return system.GetPlayedSampleCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace AudioCore::AudioOut
|
} // namespace AudioCore::AudioOut
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -33,7 +30,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Free this audio out from the audio out manager.
|
* Free this audio out from the audio out manager.
|
||||||
*/
|
*/
|
||||||
void Free(Core::System& system);
|
void Free();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get this audio out's system.
|
* Get this audio out's system.
|
||||||
@@ -144,7 +141,7 @@ private:
|
|||||||
/// Buffer event, signalled when buffers are ready to be released
|
/// Buffer event, signalled when buffers are ready to be released
|
||||||
Kernel::KEvent* event;
|
Kernel::KEvent* event;
|
||||||
/// Main audio out system
|
/// Main audio out system
|
||||||
System audio_system;
|
System system;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AudioCore::AudioOut
|
} // namespace AudioCore::AudioOut
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -16,48 +13,56 @@
|
|||||||
namespace AudioCore::Renderer {
|
namespace AudioCore::Renderer {
|
||||||
|
|
||||||
Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* rendered_event)
|
Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* rendered_event)
|
||||||
: system{system_}, manager{manager_}
|
: core{system_}, manager{manager_}, system{system_, rendered_event} {}
|
||||||
, audio_system{system_, rendered_event}
|
|
||||||
{}
|
|
||||||
|
|
||||||
Result Renderer::Initialize(const AudioRendererParameterInternal& params, Kernel::KTransferMemory* transfer_memory, const u64 transfer_memory_size, Kernel::KProcess* process_handle, const u64 applet_resource_user_id, const s32 session_id) {
|
Result Renderer::Initialize(const AudioRendererParameterInternal& params,
|
||||||
|
Kernel::KTransferMemory* transfer_memory,
|
||||||
|
const u64 transfer_memory_size, Kernel::KProcess* process_handle,
|
||||||
|
const u64 applet_resource_user_id, const s32 session_id) {
|
||||||
if (params.execution_mode == ExecutionMode::Auto) {
|
if (params.execution_mode == ExecutionMode::Auto) {
|
||||||
if (!manager.AddSystem(audio_system)) {
|
if (!manager.AddSystem(system)) {
|
||||||
LOG_ERROR(Service_Audio, "Both Audio Render sessions are in use, cannot create any more");
|
LOG_ERROR(Service_Audio,
|
||||||
|
"Both Audio Render sessions are in use, cannot create any more");
|
||||||
return Service::Audio::ResultOutOfSessions;
|
return Service::Audio::ResultOutOfSessions;
|
||||||
}
|
}
|
||||||
system_registered = true;
|
system_registered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
initialized = true;
|
initialized = true;
|
||||||
audio_system.Initialize(params, transfer_memory, transfer_memory_size, process_handle, applet_resource_user_id, session_id);
|
system.Initialize(params, transfer_memory, transfer_memory_size, process_handle,
|
||||||
|
applet_resource_user_id, session_id);
|
||||||
|
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::Finalize() {
|
void Renderer::Finalize() {
|
||||||
auto const session_id{audio_system.GetSessionId()};
|
auto session_id{system.GetSessionId()};
|
||||||
audio_system.Finalize();
|
|
||||||
|
system.Finalize();
|
||||||
|
|
||||||
if (system_registered) {
|
if (system_registered) {
|
||||||
manager.RemoveSystem(audio_system);
|
manager.RemoveSystem(system);
|
||||||
system_registered = false;
|
system_registered = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
manager.ReleaseSessionId(session_id);
|
manager.ReleaseSessionId(session_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
System& Renderer::GetSystem() {
|
System& Renderer::GetSystem() {
|
||||||
return audio_system;
|
return system;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::Start() {
|
void Renderer::Start() {
|
||||||
audio_system.Start();
|
system.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::Stop() {
|
void Renderer::Stop() {
|
||||||
audio_system.Stop();
|
system.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Renderer::RequestUpdate(std::span<const u8> input, std::span<u8> performance, std::span<u8> output) {
|
Result Renderer::RequestUpdate(std::span<const u8> input, std::span<u8> performance,
|
||||||
return audio_system.Update(input, performance, output);
|
std::span<u8> output) {
|
||||||
|
return system.Update(input, performance, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace AudioCore::Renderer
|
} // namespace AudioCore::Renderer
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -87,7 +84,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
/// System core
|
/// System core
|
||||||
Core::System& system;
|
Core::System& core;
|
||||||
/// Manager this renderer is registered with
|
/// Manager this renderer is registered with
|
||||||
Manager& manager;
|
Manager& manager;
|
||||||
/// Is the audio renderer initialized?
|
/// Is the audio renderer initialized?
|
||||||
@@ -95,7 +92,7 @@ private:
|
|||||||
/// Is the system registered with the manager?
|
/// Is the system registered with the manager?
|
||||||
bool system_registered{};
|
bool system_registered{};
|
||||||
/// Audio render system, main driver of audio rendering
|
/// Audio render system, main driver of audio rendering
|
||||||
System audio_system;
|
System system;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Renderer
|
} // namespace Renderer
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ Result System::Initialize(const AudioRendererParameterInternal& params,
|
|||||||
PoolMapper pool_mapper(process_handle, false);
|
PoolMapper pool_mapper(process_handle, false);
|
||||||
pool_mapper.InitializeSystemPool(memory_pool_info, workbuffer.get(), workbuffer_size);
|
pool_mapper.InitializeSystemPool(memory_pool_info, workbuffer.get(), workbuffer_size);
|
||||||
|
|
||||||
WorkbufferAllocator allocator({workbuffer.get(), workbuffer_size});
|
WorkbufferAllocator allocator({workbuffer.get(), workbuffer_size}, workbuffer_size);
|
||||||
|
|
||||||
samples_workbuffer =
|
samples_workbuffer =
|
||||||
allocator.Allocate<s32>((voice_channels + mix_buffer_count) * sample_count, 0x10);
|
allocator.Allocate<s32>((voice_channels + mix_buffer_count) * sample_count, 0x10);
|
||||||
|
|||||||
@@ -11,14 +11,14 @@
|
|||||||
|
|
||||||
namespace Common::Net {
|
namespace Common::Net {
|
||||||
|
|
||||||
struct Asset {
|
typedef struct {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string url;
|
std::string url;
|
||||||
std::string path;
|
std::string path;
|
||||||
std::string filename;
|
std::string filename;
|
||||||
};
|
} Asset;
|
||||||
|
|
||||||
struct Release {
|
typedef struct Release {
|
||||||
std::string title;
|
std::string title;
|
||||||
std::string body;
|
std::string body;
|
||||||
std::string tag;
|
std::string tag;
|
||||||
@@ -39,7 +39,7 @@ struct Release {
|
|||||||
static std::optional<Release> FromJson(const std::string_view& json, const std::string &host, const std::string& repo);
|
static std::optional<Release> FromJson(const std::string_view& json, const std::string &host, const std::string& repo);
|
||||||
static std::vector<Release> ListFromJson(const nlohmann::json &json, const std::string &host, const std::string &repo);
|
static std::vector<Release> ListFromJson(const nlohmann::json &json, const std::string &host, const std::string &repo);
|
||||||
static std::vector<Release> ListFromJson(const std::string_view &json, const std::string &host, const std::string &repo);
|
static std::vector<Release> ListFromJson(const std::string_view &json, const std::string &host, const std::string &repo);
|
||||||
};
|
} Release;
|
||||||
|
|
||||||
// Make a request via httplib, and return the response body if applicable.
|
// Make a request via httplib, and return the response body if applicable.
|
||||||
std::optional<std::string> MakeRequest(const std::string &url, const std::string &path);
|
std::optional<std::string> MakeRequest(const std::string &url, const std::string &path);
|
||||||
|
|||||||
+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;
|
||||||
|
|||||||
@@ -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,6 +540,7 @@ 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 (const auto& load_dir : load_dirs) {
|
||||||
for (auto const& f : load_dir->GetFiles()) {
|
for (auto const& f : load_dir->GetFiles()) {
|
||||||
auto const name = f->GetName();
|
auto const name = f->GetName();
|
||||||
if (name.starts_with("cheat_") && std::find(disabled.cbegin(), disabled.cend(), name) == disabled.cend()) {
|
if (name.starts_with("cheat_") && std::find(disabled.cbegin(), disabled.cend(), name) == disabled.cend()) {
|
||||||
@@ -506,26 +554,38 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1216,7 +1216,7 @@ Result KServerSession::ReceiveRequest(KernelCore& kernel, uintptr_t server_messa
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result KServerSession::SendReply(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
Result KServerSession::SendReply(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
||||||
KPhysicalAddress server_message_paddr, bool is_hle, bool session_closed) {
|
KPhysicalAddress server_message_paddr, bool is_hle) {
|
||||||
// Lock the session.
|
// Lock the session.
|
||||||
KScopedLightLock lk{m_lock};
|
KScopedLightLock lk{m_lock};
|
||||||
|
|
||||||
@@ -1248,7 +1248,7 @@ Result KServerSession::SendReply(KernelCore& kernel, uintptr_t server_message, u
|
|||||||
KEvent* event = request->GetEvent();
|
KEvent* event = request->GetEvent();
|
||||||
|
|
||||||
// Check whether we're closed.
|
// Check whether we're closed.
|
||||||
const bool closed = (client_thread == nullptr || m_parent->IsClientClosed() || session_closed);
|
const bool closed = (client_thread == nullptr || m_parent->IsClientClosed());
|
||||||
|
|
||||||
Result result = ResultSuccess;
|
Result result = ResultSuccess;
|
||||||
if (!closed) {
|
if (!closed) {
|
||||||
|
|||||||
@@ -54,14 +54,14 @@ public:
|
|||||||
|
|
||||||
Result OnRequest(KernelCore& kernel, KSessionRequest* request);
|
Result OnRequest(KernelCore& kernel, KSessionRequest* request);
|
||||||
Result SendReply(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
Result SendReply(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
||||||
KPhysicalAddress server_message_paddr, bool is_hle = false, bool session_closed = false);
|
KPhysicalAddress server_message_paddr, bool is_hle = false);
|
||||||
Result ReceiveRequest(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
Result ReceiveRequest(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
||||||
KPhysicalAddress server_message_paddr,
|
KPhysicalAddress server_message_paddr,
|
||||||
std::shared_ptr<Service::HLERequestContext>* out_context = nullptr,
|
std::shared_ptr<Service::HLERequestContext>* out_context = nullptr,
|
||||||
std::weak_ptr<Service::SessionRequestManager> manager = {});
|
std::weak_ptr<Service::SessionRequestManager> manager = {});
|
||||||
|
|
||||||
Result SendReplyHLE(KernelCore& kernel, bool session_closed = false) {
|
Result SendReplyHLE(KernelCore& kernel) {
|
||||||
R_RETURN(this->SendReply(kernel, 0, 0, 0, true, session_closed));
|
R_RETURN(this->SendReply(kernel, 0, 0, 0, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ReceiveRequestHLE(KernelCore& kernel, std::shared_ptr<Service::HLERequestContext>* out_context,
|
Result ReceiveRequestHLE(KernelCore& kernel, std::shared_ptr<Service::HLERequestContext>* out_context,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ IAudioIn::IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
IAudioIn::~IAudioIn() {
|
IAudioIn::~IAudioIn() {
|
||||||
impl->Free(system);
|
impl->Free();
|
||||||
service_context.CloseEvent(event);
|
service_context.CloseEvent(event);
|
||||||
process->Close(system.Kernel());
|
process->Close(system.Kernel());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -68,7 +65,7 @@ Result IAudioInManager::OpenAudioInAuto(
|
|||||||
Result IAudioInManager::ListAudioInsAutoFiltered(
|
Result IAudioInManager::ListAudioInsAutoFiltered(
|
||||||
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_audio_ins, Out<u32> out_count) {
|
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_audio_ins, Out<u32> out_count) {
|
||||||
LOG_DEBUG(Service_Audio, "called");
|
LOG_DEBUG(Service_Audio, "called");
|
||||||
*out_count = impl->GetDeviceNames(system, out_audio_ins, true);
|
*out_count = impl->GetDeviceNames(out_audio_ins, true);
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,8 +90,8 @@ Result IAudioInManager::OpenAudioInProtocolSpecified(
|
|||||||
|
|
||||||
size_t new_session_id{};
|
size_t new_session_id{};
|
||||||
|
|
||||||
R_TRY(impl->LinkToManager(system));
|
R_TRY(impl->LinkToManager());
|
||||||
R_TRY(impl->AcquireSessionId(system, new_session_id));
|
R_TRY(impl->AcquireSessionId(new_session_id));
|
||||||
|
|
||||||
LOG_DEBUG(Service_Audio, "Opening new AudioIn, session_id={}, free sessions={}", new_session_id,
|
LOG_DEBUG(Service_Audio, "Opening new AudioIn, session_id={}, free sessions={}", new_session_id,
|
||||||
impl->num_free_sessions);
|
impl->num_free_sessions);
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ IAudioOut::IAudioOut(Core::System& system_, Manager& manager, size_t session_id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
IAudioOut::~IAudioOut() {
|
IAudioOut::~IAudioOut() {
|
||||||
impl->Free(system);
|
impl->Free();
|
||||||
service_context.CloseEvent(event);
|
service_context.CloseEvent(event);
|
||||||
process->Close(system.Kernel());
|
process->Close(system.Kernel());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,8 +77,8 @@ Result IAudioOutManager::OpenAudioOutAuto(
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t new_session_id{};
|
size_t new_session_id{};
|
||||||
R_TRY(impl->LinkToManager(system));
|
R_TRY(impl->LinkToManager());
|
||||||
R_TRY(impl->AcquireSessionId(system, new_session_id));
|
R_TRY(impl->AcquireSessionId(new_session_id));
|
||||||
|
|
||||||
const auto device_name = Common::StringFromBuffer(name[0].name);
|
const auto device_name = Common::StringFromBuffer(name[0].name);
|
||||||
LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id,
|
LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id,
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -227,13 +228,12 @@ Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
|
|||||||
std::string src_path(Common::FS::SanitizePath(src_path_));
|
std::string src_path(Common::FS::SanitizePath(src_path_));
|
||||||
std::string dest_path(Common::FS::SanitizePath(dest_path_));
|
std::string dest_path(Common::FS::SanitizePath(dest_path_));
|
||||||
auto src = GetDirectoryRelativeWrapped(backing, src_path);
|
auto src = GetDirectoryRelativeWrapped(backing, src_path);
|
||||||
|
if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
|
||||||
|
// Use more-optimized vfs implementation rename.
|
||||||
if (src == nullptr)
|
if (src == nullptr)
|
||||||
return FileSys::ResultPathNotFound;
|
return FileSys::ResultPathNotFound;
|
||||||
|
if (!src->Rename(Common::FS::GetFilename(dest_path))) {
|
||||||
if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
|
// TODO(DarkLordZach): Find a better error code for this
|
||||||
std::string full_src_path = backing->GetFullPath() + "/" + src_path;
|
|
||||||
std::string full_dest_path = backing->GetFullPath() + "/" + dest_path;
|
|
||||||
if (!Common::FS::RenameDir(full_src_path, full_dest_path)) {
|
|
||||||
return ResultUnknown;
|
return ResultUnknown;
|
||||||
}
|
}
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
@@ -340,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);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGe
|
|||||||
{3, D<&IFileSystem::DeleteDirectory>, "DeleteDirectory"},
|
{3, D<&IFileSystem::DeleteDirectory>, "DeleteDirectory"},
|
||||||
{4, D<&IFileSystem::DeleteDirectoryRecursively>, "DeleteDirectoryRecursively"},
|
{4, D<&IFileSystem::DeleteDirectoryRecursively>, "DeleteDirectoryRecursively"},
|
||||||
{5, D<&IFileSystem::RenameFile>, "RenameFile"},
|
{5, D<&IFileSystem::RenameFile>, "RenameFile"},
|
||||||
{6, D<&IFileSystem::RenameDirectory>, "RenameDirectory"},
|
{6, nullptr, "RenameDirectory"},
|
||||||
{7, D<&IFileSystem::GetEntryType>, "GetEntryType"},
|
{7, D<&IFileSystem::GetEntryType>, "GetEntryType"},
|
||||||
{8, D<&IFileSystem::OpenFile>, "OpenFile"},
|
{8, D<&IFileSystem::OpenFile>, "OpenFile"},
|
||||||
{9, D<&IFileSystem::OpenDirectory>, "OpenDirectory"},
|
{9, D<&IFileSystem::OpenDirectory>, "OpenDirectory"},
|
||||||
@@ -88,14 +88,6 @@ Result IFileSystem::RenameFile(
|
|||||||
R_RETURN(backend->RenameFile(FileSys::Path(old_path->str), FileSys::Path(new_path->str)));
|
R_RETURN(backend->RenameFile(FileSys::Path(old_path->str), FileSys::Path(new_path->str)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IFileSystem::RenameDirectory(
|
|
||||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
|
||||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path) {
|
|
||||||
LOG_DEBUG(Service_FS, "called. directory '{}' to directory '{}'", old_path->str, new_path->str);
|
|
||||||
|
|
||||||
R_RETURN(backend->RenameDirectory(FileSys::Path(old_path->str), FileSys::Path(new_path->str)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result IFileSystem::OpenFile(OutInterface<IFile> out_interface,
|
Result IFileSystem::OpenFile(OutInterface<IFile> out_interface,
|
||||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
|
||||||
u32 mode) {
|
u32 mode) {
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -39,8 +36,6 @@ public:
|
|||||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
||||||
Result RenameFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
Result RenameFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
||||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path);
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path);
|
||||||
Result RenameDirectory(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
|
||||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path);
|
|
||||||
Result OpenFile(OutInterface<IFile> out_interface,
|
Result OpenFile(OutInterface<IFile> out_interface,
|
||||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, u32 mode);
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, u32 mode);
|
||||||
Result OpenDirectory(OutInterface<IDirectory> out_interface,
|
Result OpenDirectory(OutInterface<IDirectory> out_interface,
|
||||||
|
|||||||
@@ -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{};
|
||||||
|
|||||||
@@ -393,7 +393,7 @@ Result ServerManager::CompleteSyncRequest(Session* session) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send the reply.
|
// Send the reply.
|
||||||
res = server_session->SendReplyHLE(m_system.Kernel(), service_res == IPC::ResultSessionClosed);
|
res = server_session->SendReplyHLE(m_system.Kernel());
|
||||||
|
|
||||||
// If the session has been closed, we're done.
|
// If the session has been closed, we're done.
|
||||||
if (res == Kernel::ResultSessionClosed || service_res == IPC::ResultSessionClosed) {
|
if (res == Kernel::ResultSessionClosed || service_res == IPC::ResultSessionClosed) {
|
||||||
|
|||||||
@@ -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());
|
||||||
|
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);
|
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());
|
||||||
|
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);
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ void A32EmitX64::GenTerminalHandlers() {
|
|||||||
calculate_location_descriptor();
|
calculate_location_descriptor();
|
||||||
code.mov(eax, dword[code.ABI_JIT_PTR + offsetof(A32JitState, rsb_ptr)]);
|
code.mov(eax, dword[code.ABI_JIT_PTR + offsetof(A32JitState, rsb_ptr)]);
|
||||||
code.sub(eax, 1);
|
code.sub(eax, 1);
|
||||||
code.and_(eax, u32(A32JitState::RSB_PTR_MASK));
|
code.and_(eax, u32(A32JitState::RSBPtrMask));
|
||||||
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, rsb_ptr)], eax);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, rsb_ptr)], eax);
|
||||||
code.cmp(rbx, qword[code.ABI_JIT_PTR + offsetof(A32JitState, rsb_location_descriptors) + rax * sizeof(u64)]);
|
code.cmp(rbx, qword[code.ABI_JIT_PTR + offsetof(A32JitState, rsb_location_descriptors) + rax * sizeof(u64)]);
|
||||||
if (conf.HasOptimization(OptimizationFlag::FastDispatch)) {
|
if (conf.HasOptimization(OptimizationFlag::FastDispatch)) {
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ using namespace Backend::X64;
|
|||||||
|
|
||||||
static RunCodeCallbacks GenRunCodeCallbacks(A32::UserCallbacks* cb, CodePtr (*LookupBlock)(void* lookup_block_arg), void* arg, const A32::UserConfig& conf) {
|
static RunCodeCallbacks GenRunCodeCallbacks(A32::UserCallbacks* cb, CodePtr (*LookupBlock)(void* lookup_block_arg), void* arg, const A32::UserConfig& conf) {
|
||||||
return RunCodeCallbacks{
|
return RunCodeCallbacks{
|
||||||
ArgCallback(LookupBlock, reinterpret_cast<u64>(arg)),
|
std::make_unique<ArgCallback>(LookupBlock, reinterpret_cast<u64>(arg)),
|
||||||
ArgCallback(Devirtualize<&A32::UserCallbacks::AddTicks>(cb)),
|
std::make_unique<ArgCallback>(Devirtualize<&A32::UserCallbacks::AddTicks>(cb)),
|
||||||
ArgCallback(Devirtualize<&A32::UserCallbacks::GetTicksRemaining>(cb)),
|
std::make_unique<ArgCallback>(Devirtualize<&A32::UserCallbacks::GetTicksRemaining>(cb)),
|
||||||
conf.enable_cycle_counting,
|
conf.enable_cycle_counting,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ struct Jit::Impl {
|
|||||||
jit_interface->is_executing = true;
|
jit_interface->is_executing = true;
|
||||||
const CodePtr current_codeptr = [this] {
|
const CodePtr current_codeptr = [this] {
|
||||||
// RSB optimization
|
// RSB optimization
|
||||||
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A32JitState::RSB_PTR_MASK;
|
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A32JitState::RSBPtrMask;
|
||||||
if (jit_state.GetUniqueHash() == jit_state.rsb_location_descriptors[new_rsb_ptr]) {
|
if (jit_state.GetUniqueHash() == jit_state.rsb_location_descriptors[new_rsb_ptr]) {
|
||||||
jit_state.rsb_ptr = new_rsb_ptr;
|
jit_state.rsb_ptr = new_rsb_ptr;
|
||||||
return reinterpret_cast<CodePtr>(jit_state.rsb_codeptrs[new_rsb_ptr]);
|
return reinterpret_cast<CodePtr>(jit_state.rsb_codeptrs[new_rsb_ptr]);
|
||||||
|
|||||||
@@ -27,9 +27,6 @@ struct A32JitState {
|
|||||||
|
|
||||||
A32JitState() { ResetRSB(); }
|
A32JitState() { ResetRSB(); }
|
||||||
|
|
||||||
static constexpr std::size_t RSB_SIZE = 8; // MUST be a power of 2.
|
|
||||||
static constexpr std::size_t RSB_PTR_MASK = RSB_SIZE - 1;
|
|
||||||
|
|
||||||
std::array<u32, 16> Reg{}; // Current register file.
|
std::array<u32, 16> Reg{}; // Current register file.
|
||||||
// TODO: Mode-specific register sets unimplemented.
|
// TODO: Mode-specific register sets unimplemented.
|
||||||
|
|
||||||
@@ -39,9 +36,8 @@ struct A32JitState {
|
|||||||
u32 cpsr_q = 0;
|
u32 cpsr_q = 0;
|
||||||
u32 cpsr_nzcv = 0;
|
u32 cpsr_nzcv = 0;
|
||||||
u32 cpsr_jaifm = 0;
|
u32 cpsr_jaifm = 0;
|
||||||
u32 fpsr_exc = 0;
|
u32 Cpsr() const;
|
||||||
u32 fpsr_qc = 0;
|
void SetCpsr(u32 cpsr);
|
||||||
u32 fpsr_nzcv = 0;
|
|
||||||
|
|
||||||
alignas(16) std::array<u32, 64> ExtReg{}; // Extension registers.
|
alignas(16) std::array<u32, 64> ExtReg{}; // Extension registers.
|
||||||
|
|
||||||
@@ -53,19 +49,21 @@ struct A32JitState {
|
|||||||
// Exclusive state
|
// Exclusive state
|
||||||
u32 exclusive_state = 0;
|
u32 exclusive_state = 0;
|
||||||
|
|
||||||
|
static constexpr std::size_t RSBSize = 8; // MUST be a power of 2.
|
||||||
|
static constexpr std::size_t RSBPtrMask = RSBSize - 1;
|
||||||
u32 rsb_ptr = 0;
|
u32 rsb_ptr = 0;
|
||||||
std::array<u64, RSB_SIZE> rsb_location_descriptors;
|
std::array<u64, RSBSize> rsb_location_descriptors;
|
||||||
std::array<u64, RSB_SIZE> rsb_codeptrs;
|
std::array<u64, RSBSize> rsb_codeptrs;
|
||||||
|
|
||||||
u32 Cpsr() const;
|
|
||||||
void SetCpsr(u32 cpsr);
|
|
||||||
|
|
||||||
void ResetRSB();
|
void ResetRSB();
|
||||||
|
|
||||||
|
u32 fpsr_exc = 0;
|
||||||
|
u32 fpsr_qc = 0;
|
||||||
|
u32 fpsr_nzcv = 0;
|
||||||
u32 Fpscr() const;
|
u32 Fpscr() const;
|
||||||
void SetFpscr(u32 FPSCR);
|
void SetFpscr(u32 FPSCR);
|
||||||
|
|
||||||
u64 GetUniqueHash() const noexcept {
|
u64 GetUniqueHash() const noexcept {
|
||||||
return (u64(upper_location_descriptor) << 32) | (u64(Reg[15]));
|
return (static_cast<u64>(upper_location_descriptor) << 32) | (static_cast<u64>(Reg[15]));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransferJitState(const A32JitState& src, bool reset_rsb) {
|
void TransferJitState(const A32JitState& src, bool reset_rsb) {
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ void A64EmitX64::GenTerminalHandlers() {
|
|||||||
calculate_location_descriptor();
|
calculate_location_descriptor();
|
||||||
code.mov(eax, dword[code.ABI_JIT_PTR + offsetof(A64JitState, rsb_ptr)]);
|
code.mov(eax, dword[code.ABI_JIT_PTR + offsetof(A64JitState, rsb_ptr)]);
|
||||||
code.sub(eax, 1);
|
code.sub(eax, 1);
|
||||||
code.and_(eax, u32(A64JitState::RSB_PTR_MASK));
|
code.and_(eax, u32(A64JitState::RSBPtrMask));
|
||||||
code.mov(dword[code.ABI_JIT_PTR + offsetof(A64JitState, rsb_ptr)], eax);
|
code.mov(dword[code.ABI_JIT_PTR + offsetof(A64JitState, rsb_ptr)], eax);
|
||||||
code.cmp(rbx, qword[code.ABI_JIT_PTR + offsetof(A64JitState, rsb_location_descriptors) + rax * sizeof(u64)]);
|
code.cmp(rbx, qword[code.ABI_JIT_PTR + offsetof(A64JitState, rsb_location_descriptors) + rax * sizeof(u64)]);
|
||||||
if (conf.HasOptimization(OptimizationFlag::FastDispatch)) {
|
if (conf.HasOptimization(OptimizationFlag::FastDispatch)) {
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ using namespace Backend::X64;
|
|||||||
|
|
||||||
static RunCodeCallbacks GenRunCodeCallbacks(A64::UserCallbacks* cb, CodePtr (*LookupBlock)(void* lookup_block_arg), void* arg, const A64::UserConfig& conf) {
|
static RunCodeCallbacks GenRunCodeCallbacks(A64::UserCallbacks* cb, CodePtr (*LookupBlock)(void* lookup_block_arg), void* arg, const A64::UserConfig& conf) {
|
||||||
return RunCodeCallbacks{
|
return RunCodeCallbacks{
|
||||||
ArgCallback(LookupBlock, reinterpret_cast<u64>(arg)),
|
std::make_unique<ArgCallback>(LookupBlock, reinterpret_cast<u64>(arg)),
|
||||||
ArgCallback(Devirtualize<&A64::UserCallbacks::AddTicks>(cb)),
|
std::make_unique<ArgCallback>(Devirtualize<&A64::UserCallbacks::AddTicks>(cb)),
|
||||||
ArgCallback(Devirtualize<&A64::UserCallbacks::GetTicksRemaining>(cb)),
|
std::make_unique<ArgCallback>(Devirtualize<&A64::UserCallbacks::GetTicksRemaining>(cb)),
|
||||||
conf.enable_cycle_counting,
|
conf.enable_cycle_counting,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,7 @@ public:
|
|||||||
// TODO: Check code alignment
|
// TODO: Check code alignment
|
||||||
const CodePtr current_code_ptr = [this] {
|
const CodePtr current_code_ptr = [this] {
|
||||||
// RSB optimization
|
// RSB optimization
|
||||||
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A64JitState::RSB_PTR_MASK;
|
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A64JitState::RSBPtrMask;
|
||||||
if (jit_state.GetUniqueHash() == jit_state.rsb_location_descriptors[new_rsb_ptr]) {
|
if (jit_state.GetUniqueHash() == jit_state.rsb_location_descriptors[new_rsb_ptr]) {
|
||||||
jit_state.rsb_ptr = new_rsb_ptr;
|
jit_state.rsb_ptr = new_rsb_ptr;
|
||||||
return CodePtr(jit_state.rsb_codeptrs[new_rsb_ptr]);
|
return CodePtr(jit_state.rsb_codeptrs[new_rsb_ptr]);
|
||||||
|
|||||||
@@ -29,19 +29,18 @@ struct A64JitState {
|
|||||||
|
|
||||||
A64JitState() { ResetRSB(); }
|
A64JitState() { ResetRSB(); }
|
||||||
|
|
||||||
// Exclusive state stuff
|
|
||||||
static constexpr u64 RESERVATION_GRANULE_MASK = 0xFFFF'FFFF'FFFF'FFF0ull;
|
|
||||||
// Return stack buffer
|
|
||||||
static constexpr size_t RSB_SIZE = 8; // MUST be a power of 2.
|
|
||||||
static constexpr size_t RSB_PTR_MASK = RSB_SIZE - 1;
|
|
||||||
|
|
||||||
std::array<u64, 31> reg{};
|
std::array<u64, 31> reg{};
|
||||||
u64 sp = 0;
|
u64 sp = 0;
|
||||||
u64 pc = 0;
|
u64 pc = 0;
|
||||||
|
|
||||||
u32 cpsr_nzcv = 0;
|
u32 cpsr_nzcv = 0;
|
||||||
u32 fpsr_exc = 0;
|
|
||||||
u32 fpsr_qc = 0;
|
u32 GetPstate() const {
|
||||||
u32 fpcr = 0;
|
return NZCV::FromX64(cpsr_nzcv);
|
||||||
|
}
|
||||||
|
void SetPstate(u32 new_pstate) {
|
||||||
|
cpsr_nzcv = NZCV::ToX64(new_pstate);
|
||||||
|
}
|
||||||
|
|
||||||
alignas(16) std::array<u64, 64> vec{}; // Extension registers.
|
alignas(16) std::array<u64, 64> vec{}; // Extension registers.
|
||||||
|
|
||||||
@@ -51,31 +50,29 @@ struct A64JitState {
|
|||||||
volatile u32 halt_reason = 0;
|
volatile u32 halt_reason = 0;
|
||||||
|
|
||||||
// Exclusive state
|
// Exclusive state
|
||||||
|
static constexpr u64 RESERVATION_GRANULE_MASK = 0xFFFF'FFFF'FFFF'FFF0ull;
|
||||||
u8 exclusive_state = 0;
|
u8 exclusive_state = 0;
|
||||||
|
|
||||||
|
static constexpr size_t RSBSize = 8; // MUST be a power of 2.
|
||||||
|
static constexpr size_t RSBPtrMask = RSBSize - 1;
|
||||||
u32 rsb_ptr = 0;
|
u32 rsb_ptr = 0;
|
||||||
std::array<u64, RSB_SIZE> rsb_location_descriptors;
|
std::array<u64, RSBSize> rsb_location_descriptors;
|
||||||
std::array<u64, RSB_SIZE> rsb_codeptrs;
|
std::array<u64, RSBSize> rsb_codeptrs;
|
||||||
|
|
||||||
u32 GetPstate() const {
|
|
||||||
return NZCV::FromX64(cpsr_nzcv);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetPstate(u32 new_pstate) {
|
|
||||||
cpsr_nzcv = NZCV::ToX64(new_pstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResetRSB() {
|
void ResetRSB() {
|
||||||
rsb_location_descriptors.fill(0xFFFFFFFFFFFFFFFFull);
|
rsb_location_descriptors.fill(0xFFFFFFFFFFFFFFFFull);
|
||||||
rsb_codeptrs.fill(0);
|
rsb_codeptrs.fill(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 fpsr_exc = 0;
|
||||||
|
u32 fpsr_qc = 0;
|
||||||
|
u32 fpcr = 0;
|
||||||
u32 GetFpcr() const;
|
u32 GetFpcr() const;
|
||||||
u32 GetFpsr() const;
|
u32 GetFpsr() const;
|
||||||
void SetFpcr(u32 value);
|
void SetFpcr(u32 value);
|
||||||
void SetFpsr(u32 value);
|
void SetFpsr(u32 value);
|
||||||
|
|
||||||
u64 GetUniqueHash() const noexcept {
|
u64 GetUniqueHash() const noexcept {
|
||||||
const u64 fpcr_u64 = u64(fpcr & A64::LocationDescriptor::fpcr_mask) << A64::LocationDescriptor::fpcr_shift;
|
const u64 fpcr_u64 = static_cast<u64>(fpcr & A64::LocationDescriptor::fpcr_mask) << A64::LocationDescriptor::fpcr_shift;
|
||||||
const u64 pc_u64 = pc & A64::LocationDescriptor::pc_mask;
|
const u64 pc_u64 = pc & A64::LocationDescriptor::pc_mask;
|
||||||
return pc_u64 | fpcr_u64;
|
return pc_u64 | fpcr_u64;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,73 @@ namespace {
|
|||||||
constexpr size_t CONSTANT_POOL_SIZE = 2 * 1024 * 1024;
|
constexpr size_t CONSTANT_POOL_SIZE = 2 * 1024 * 1024;
|
||||||
constexpr size_t PRELUDE_COMMIT_SIZE = 16 * 1024 * 1024;
|
constexpr size_t PRELUDE_COMMIT_SIZE = 16 * 1024 * 1024;
|
||||||
|
|
||||||
|
class CustomXbyakAllocator : public Xbyak::Allocator {
|
||||||
|
public:
|
||||||
|
#ifdef _WIN32
|
||||||
|
uint8_t* alloc(size_t size) override {
|
||||||
|
void* p = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
||||||
|
if (p == nullptr) {
|
||||||
|
using Xbyak::Error;
|
||||||
|
XBYAK_THROW(Xbyak::ERR_CANT_ALLOC);
|
||||||
|
}
|
||||||
|
return static_cast<uint8_t*>(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(uint8_t* p) override {
|
||||||
|
VirtualFree(static_cast<void*>(p), 0, MEM_RELEASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool useProtect() const override { return false; }
|
||||||
|
#else
|
||||||
|
static constexpr size_t DYNARMIC_PAGE_SIZE = 4096;
|
||||||
|
|
||||||
|
// Can't subclass Xbyak::MmapAllocator because it is not a pure interface
|
||||||
|
// and doesn't expose its construtor
|
||||||
|
uint8_t* alloc(size_t size) override {
|
||||||
|
// Waste a page to store the size
|
||||||
|
size += DYNARMIC_PAGE_SIZE;
|
||||||
|
|
||||||
|
int mode = MAP_PRIVATE;
|
||||||
|
#if defined(MAP_ANONYMOUS)
|
||||||
|
mode |= MAP_ANONYMOUS;
|
||||||
|
#elif defined(MAP_ANON)
|
||||||
|
mode |= MAP_ANON;
|
||||||
|
#else
|
||||||
|
# error "not supported"
|
||||||
|
#endif
|
||||||
|
#ifdef MAP_JIT
|
||||||
|
mode |= MAP_JIT;
|
||||||
|
#endif
|
||||||
|
int prot = PROT_READ | PROT_WRITE;
|
||||||
|
#ifdef PROT_MPROTECT
|
||||||
|
// https://man.netbsd.org/mprotect.2 specifies that an mprotect() that is LESS
|
||||||
|
// restrictive than the original mapping MUST fail
|
||||||
|
prot |= PROT_MPROTECT(PROT_READ) | PROT_MPROTECT(PROT_WRITE) | PROT_MPROTECT(PROT_EXEC);
|
||||||
|
#endif
|
||||||
|
void* p = mmap(nullptr, size, prot, mode, -1, 0);
|
||||||
|
if (p == MAP_FAILED) {
|
||||||
|
using Xbyak::Error;
|
||||||
|
XBYAK_THROW(Xbyak::ERR_CANT_ALLOC);
|
||||||
|
}
|
||||||
|
std::memcpy(p, &size, sizeof(size_t));
|
||||||
|
return static_cast<uint8_t*>(p) + DYNARMIC_PAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(uint8_t* p) override {
|
||||||
|
size_t size;
|
||||||
|
std::memcpy(&size, p - DYNARMIC_PAGE_SIZE, sizeof(size_t));
|
||||||
|
munmap(p - DYNARMIC_PAGE_SIZE, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
# ifdef DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT
|
||||||
|
bool useProtect() const override { return false; }
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is threadsafe as Xbyak::Allocator does not contain any state; it is a pure interface.
|
||||||
|
CustomXbyakAllocator s_allocator;
|
||||||
|
|
||||||
#ifdef DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT
|
#ifdef DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT
|
||||||
void ProtectMemory(const void* base, size_t size, bool is_executable) {
|
void ProtectMemory(const void* base, size_t size, bool is_executable) {
|
||||||
# ifdef _WIN32
|
# ifdef _WIN32
|
||||||
@@ -78,9 +145,11 @@ void ProtectMemory(const void* base, size_t size, bool is_executable) {
|
|||||||
|
|
||||||
HostFeature GetHostFeatures() {
|
HostFeature GetHostFeatures() {
|
||||||
HostFeature features = {};
|
HostFeature features = {};
|
||||||
|
|
||||||
#ifdef DYNARMIC_ENABLE_CPU_FEATURE_DETECTION
|
#ifdef DYNARMIC_ENABLE_CPU_FEATURE_DETECTION
|
||||||
using Cpu = Xbyak::util::Cpu;
|
using Cpu = Xbyak::util::Cpu;
|
||||||
Xbyak::util::Cpu cpu_info{};
|
Xbyak::util::Cpu cpu_info;
|
||||||
|
|
||||||
if (cpu_info.has(Cpu::tSSSE3))
|
if (cpu_info.has(Cpu::tSSSE3))
|
||||||
features |= HostFeature::SSSE3;
|
features |= HostFeature::SSSE3;
|
||||||
if (cpu_info.has(Cpu::tSSE41))
|
if (cpu_info.has(Cpu::tSSE41))
|
||||||
@@ -127,6 +196,7 @@ HostFeature GetHostFeatures() {
|
|||||||
features |= HostFeature::GFNI;
|
features |= HostFeature::GFNI;
|
||||||
if (cpu_info.has(Cpu::tWAITPKG))
|
if (cpu_info.has(Cpu::tWAITPKG))
|
||||||
features |= HostFeature::WAITPKG;
|
features |= HostFeature::WAITPKG;
|
||||||
|
|
||||||
if (cpu_info.has(Cpu::tBMI2)) {
|
if (cpu_info.has(Cpu::tBMI2)) {
|
||||||
// BMI2 instructions such as pdep and pext have been very slow up until Zen 3.
|
// BMI2 instructions such as pdep and pext have been very slow up until Zen 3.
|
||||||
// Check for Zen 3 or newer by its family (0x19).
|
// Check for Zen 3 or newer by its family (0x19).
|
||||||
@@ -144,6 +214,7 @@ HostFeature GetHostFeatures() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return features;
|
return features;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,27 +233,23 @@ bool IsUnderRosetta() {
|
|||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
BlockOfCode::BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi, size_t total_code_size, std::function<void(BlockOfCode&)> rcp)
|
|
||||||
: Xbyak::CodeGenerator(total_code_size
|
|
||||||
#ifdef DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT
|
#ifdef DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT
|
||||||
, Xbyak::DontSetProtectRWE
|
static const auto default_cg_mode = Xbyak::DontSetProtectRWE;
|
||||||
#else
|
#else
|
||||||
, nullptr //Allow RWE
|
static const auto default_cg_mode = nullptr; //Allow RWE
|
||||||
#endif
|
#endif
|
||||||
, nullptr)
|
|
||||||
, constant_pool(*this, CONSTANT_POOL_SIZE)
|
BlockOfCode::BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi, size_t total_code_size, std::function<void(BlockOfCode&)> rcp)
|
||||||
, jsi(jsi)
|
: Xbyak::CodeGenerator(total_code_size, default_cg_mode, &s_allocator)
|
||||||
, cb(std::move(cb))
|
, cb(std::move(cb))
|
||||||
{
|
, jsi(jsi)
|
||||||
|
, constant_pool(*this, CONSTANT_POOL_SIZE)
|
||||||
|
, host_features(GetHostFeatures()) {
|
||||||
EnableWriting();
|
EnableWriting();
|
||||||
EnsureMemoryCommitted(PRELUDE_COMMIT_SIZE);
|
EnsureMemoryCommitted(PRELUDE_COMMIT_SIZE);
|
||||||
GenRunCode(rcp);
|
GenRunCode(rcp);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BlockOfCode::HasHostFeature(HostFeature feature) const noexcept {
|
|
||||||
return (GetHostFeatures() & feature) == feature;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BlockOfCode::PreludeComplete() {
|
void BlockOfCode::PreludeComplete() {
|
||||||
prelude_complete = true;
|
prelude_complete = true;
|
||||||
code_begin = getCurr();
|
code_begin = getCurr();
|
||||||
@@ -274,7 +341,7 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
|
|||||||
mov(rbx, ABI_PARAM2); // save temporarily in non-volatile register
|
mov(rbx, ABI_PARAM2); // save temporarily in non-volatile register
|
||||||
|
|
||||||
if (cb.enable_cycle_counting) {
|
if (cb.enable_cycle_counting) {
|
||||||
cb.GetTicksRemaining.EmitCall(*this);
|
cb.GetTicksRemaining->EmitCall(*this);
|
||||||
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_to_run)], ABI_RETURN);
|
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_to_run)], ABI_RETURN);
|
||||||
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], ABI_RETURN);
|
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], ABI_RETURN);
|
||||||
}
|
}
|
||||||
@@ -321,7 +388,7 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
|
|||||||
cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0);
|
cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0);
|
||||||
jng(return_to_caller);
|
jng(return_to_caller);
|
||||||
}
|
}
|
||||||
cb.LookupBlock.EmitCall(*this);
|
cb.LookupBlock->EmitCall(*this);
|
||||||
jmp(ABI_RETURN);
|
jmp(ABI_RETURN);
|
||||||
|
|
||||||
align();
|
align();
|
||||||
@@ -334,7 +401,7 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
|
|||||||
jng(return_to_caller_mxcsr_already_exited);
|
jng(return_to_caller_mxcsr_already_exited);
|
||||||
}
|
}
|
||||||
SwitchMxcsrOnEntry();
|
SwitchMxcsrOnEntry();
|
||||||
cb.LookupBlock.EmitCall(*this);
|
cb.LookupBlock->EmitCall(*this);
|
||||||
jmp(ABI_RETURN);
|
jmp(ABI_RETURN);
|
||||||
|
|
||||||
align();
|
align();
|
||||||
@@ -348,7 +415,7 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
|
|||||||
L(return_to_caller_mxcsr_already_exited);
|
L(return_to_caller_mxcsr_already_exited);
|
||||||
|
|
||||||
if (cb.enable_cycle_counting) {
|
if (cb.enable_cycle_counting) {
|
||||||
cb.AddTicks.EmitCall(*this, [this](RegList param) {
|
cb.AddTicks->EmitCall(*this, [this](RegList param) {
|
||||||
mov(param[0], qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_to_run)]);
|
mov(param[0], qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_to_run)]);
|
||||||
sub(param[0], qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)]);
|
sub(param[0], qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)]);
|
||||||
});
|
});
|
||||||
@@ -388,18 +455,18 @@ void BlockOfCode::UpdateTicks() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cb.AddTicks.EmitCall(*this, [this](RegList param) {
|
cb.AddTicks->EmitCall(*this, [this](RegList param) {
|
||||||
mov(param[0], qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_to_run)]);
|
mov(param[0], qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_to_run)]);
|
||||||
sub(param[0], qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)]);
|
sub(param[0], qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)]);
|
||||||
});
|
});
|
||||||
|
|
||||||
cb.GetTicksRemaining.EmitCall(*this);
|
cb.GetTicksRemaining->EmitCall(*this);
|
||||||
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_to_run)], ABI_RETURN);
|
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_to_run)], ABI_RETURN);
|
||||||
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], ABI_RETURN);
|
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], ABI_RETURN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockOfCode::LookupBlock() {
|
void BlockOfCode::LookupBlock() {
|
||||||
cb.LookupBlock.EmitCall(*this);
|
cb.LookupBlock->EmitCall(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockOfCode::LoadRequiredFlagsForCondFromRax(IR::Cond cond) {
|
void BlockOfCode::LoadRequiredFlagsForCondFromRax(IR::Cond cond) {
|
||||||
@@ -453,7 +520,7 @@ void BlockOfCode::LoadRequiredFlagsForCondFromRax(IR::Cond cond) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Xbyak::Address BlockOfCode::Const(const Xbyak::AddressFrame& frame, u64 lower, u64 upper) {
|
Xbyak::Address BlockOfCode::Const(const Xbyak::AddressFrame& frame, u64 lower, u64 upper) {
|
||||||
return constant_pool.GetConstant(*this, frame, lower, upper);
|
return constant_pool.GetConstant(frame, lower, upper);
|
||||||
}
|
}
|
||||||
|
|
||||||
CodePtr BlockOfCode::GetCodeBegin() const {
|
CodePtr BlockOfCode::GetCodeBegin() const {
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ namespace Dynarmic::Backend::X64 {
|
|||||||
using CodePtr = const void*;
|
using CodePtr = const void*;
|
||||||
|
|
||||||
struct RunCodeCallbacks {
|
struct RunCodeCallbacks {
|
||||||
ArgCallback LookupBlock;
|
std::unique_ptr<Callback> LookupBlock;
|
||||||
ArgCallback AddTicks;
|
std::unique_ptr<Callback> AddTicks;
|
||||||
ArgCallback GetTicksRemaining;
|
std::unique_ptr<Callback> GetTicksRemaining;
|
||||||
bool enable_cycle_counting;
|
bool enable_cycle_counting;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -166,24 +166,27 @@ public:
|
|||||||
|
|
||||||
JitStateInfo GetJitStateInfo() const { return jsi; }
|
JitStateInfo GetJitStateInfo() const { return jsi; }
|
||||||
|
|
||||||
bool HasHostFeature(HostFeature feature) const noexcept;
|
bool HasHostFeature(HostFeature feature) const {
|
||||||
|
return (host_features & feature) == feature;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using RunCodeFuncType = HaltReason (*)(void*, CodePtr);
|
using RunCodeFuncType = HaltReason (*)(void*, CodePtr);
|
||||||
static constexpr size_t MXCSR_ALREADY_EXITED = 1 << 0;
|
static constexpr size_t MXCSR_ALREADY_EXITED = 1 << 0;
|
||||||
static constexpr size_t FORCE_RETURN = 1 << 1;
|
static constexpr size_t FORCE_RETURN = 1 << 1;
|
||||||
|
|
||||||
ConstantPool constant_pool;
|
|
||||||
JitStateInfo jsi;
|
|
||||||
std::array<const void*, 4> return_from_run_code;
|
|
||||||
RunCodeFuncType run_code = nullptr;
|
|
||||||
RunCodeFuncType step_code = nullptr;
|
|
||||||
RunCodeCallbacks cb;
|
RunCodeCallbacks cb;
|
||||||
|
JitStateInfo jsi;
|
||||||
CodePtr code_begin = nullptr;
|
CodePtr code_begin = nullptr;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
size_t committed_size = 0;
|
size_t committed_size = 0;
|
||||||
#endif
|
#endif
|
||||||
|
ConstantPool constant_pool;
|
||||||
|
RunCodeFuncType run_code = nullptr;
|
||||||
|
RunCodeFuncType step_code = nullptr;
|
||||||
|
std::array<const void*, 4> return_from_run_code;
|
||||||
bool prelude_complete = false;
|
bool prelude_complete = false;
|
||||||
|
const HostFeature host_features;
|
||||||
|
|
||||||
void GenRunCode(std::function<void(BlockOfCode&)> rcp);
|
void GenRunCode(std::function<void(BlockOfCode&)> rcp);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
namespace Dynarmic::Backend::X64 {
|
namespace Dynarmic::Backend::X64 {
|
||||||
|
|
||||||
ConstantPool::ConstantPool(BlockOfCode& code, size_t size)
|
ConstantPool::ConstantPool(BlockOfCode& code, size_t size)
|
||||||
: insertion_point(0)
|
: code(code)
|
||||||
|
, insertion_point(0)
|
||||||
{
|
{
|
||||||
code.EnsureMemoryCommitted(align_size + size);
|
code.EnsureMemoryCommitted(align_size + size);
|
||||||
code.int3();
|
code.int3();
|
||||||
@@ -24,17 +25,17 @@ ConstantPool::ConstantPool(BlockOfCode& code, size_t size)
|
|||||||
pool = std::span<ConstantT>(reinterpret_cast<ConstantT*>(code.AllocateFromCodeSpace(size)), size / align_size);
|
pool = std::span<ConstantT>(reinterpret_cast<ConstantT*>(code.AllocateFromCodeSpace(size)), size / align_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Xbyak::Address ConstantPool::GetConstant(BlockOfCode& code, const Xbyak::AddressFrame& frame, u64 lower, u64 upper) {
|
Xbyak::Address ConstantPool::GetConstant(const Xbyak::AddressFrame& frame, u64 lower, u64 upper) {
|
||||||
const auto constant = ConstantT(lower, upper);
|
const auto constant = ConstantT(lower, upper);
|
||||||
auto it = constant_info.find(constant);
|
auto iter = constant_info.find(constant);
|
||||||
if (it == constant_info.end()) {
|
if (iter == constant_info.end()) {
|
||||||
ASSERT(insertion_point < pool.size());
|
ASSERT(insertion_point < pool.size());
|
||||||
ConstantT& target_constant = pool[insertion_point];
|
ConstantT& target_constant = pool[insertion_point];
|
||||||
target_constant = constant;
|
target_constant = constant;
|
||||||
it = constant_info.insert({constant, &target_constant}).first;
|
iter = constant_info.insert({constant, &target_constant}).first;
|
||||||
++insertion_point;
|
++insertion_point;
|
||||||
}
|
}
|
||||||
return frame[code.rip + it->second];
|
return frame[code.rip + iter->second];
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::Backend::X64
|
} // namespace Dynarmic::Backend::X64
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class ConstantPool final {
|
|||||||
public:
|
public:
|
||||||
ConstantPool(BlockOfCode& code, size_t size);
|
ConstantPool(BlockOfCode& code, size_t size);
|
||||||
|
|
||||||
Xbyak::Address GetConstant(BlockOfCode& code, const Xbyak::AddressFrame& frame, u64 lower, u64 upper = 0);
|
Xbyak::Address GetConstant(const Xbyak::AddressFrame& frame, u64 lower, u64 upper = 0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr size_t align_size = 16; // bytes
|
static constexpr size_t align_size = 16; // bytes
|
||||||
@@ -45,6 +45,7 @@ private:
|
|||||||
|
|
||||||
ankerl::unordered_dense::map<ConstantT, void*, ConstantHash> constant_info;
|
ankerl::unordered_dense::map<ConstantT, void*, ConstantHash> constant_info;
|
||||||
std::span<ConstantT> pool;
|
std::span<ConstantT> pool;
|
||||||
|
BlockOfCode& code;
|
||||||
std::size_t insertion_point;
|
std::size_t insertion_point;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
namespace Dynarmic::Backend::X64 {
|
namespace Dynarmic::Backend::X64 {
|
||||||
|
|
||||||
enum class HostFeature : u32 {
|
enum class HostFeature : u64 {
|
||||||
SSSE3 = 1ULL << 0,
|
SSSE3 = 1ULL << 0,
|
||||||
SSE41 = 1ULL << 1,
|
SSE41 = 1ULL << 1,
|
||||||
SSE42 = 1ULL << 2,
|
SSE42 = 1ULL << 2,
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
/* This file is part of the dynarmic project.
|
/* This file is part of the dynarmic project.
|
||||||
* Copyright (c) 2016 MerryMage
|
* Copyright (c) 2016 MerryMage
|
||||||
* SPDX-License-Identifier: 0BSD
|
* SPDX-License-Identifier: 0BSD
|
||||||
@@ -18,7 +15,7 @@ struct JitStateInfo {
|
|||||||
: offsetof_guest_MXCSR(offsetof(JitStateType, guest_MXCSR))
|
: offsetof_guest_MXCSR(offsetof(JitStateType, guest_MXCSR))
|
||||||
, offsetof_asimd_MXCSR(offsetof(JitStateType, asimd_MXCSR))
|
, offsetof_asimd_MXCSR(offsetof(JitStateType, asimd_MXCSR))
|
||||||
, offsetof_rsb_ptr(offsetof(JitStateType, rsb_ptr))
|
, offsetof_rsb_ptr(offsetof(JitStateType, rsb_ptr))
|
||||||
, rsb_ptr_mask(JitStateType::RSB_PTR_MASK)
|
, rsb_ptr_mask(JitStateType::RSBPtrMask)
|
||||||
, offsetof_rsb_location_descriptors(offsetof(JitStateType, rsb_location_descriptors))
|
, offsetof_rsb_location_descriptors(offsetof(JitStateType, rsb_location_descriptors))
|
||||||
, offsetof_rsb_codeptrs(offsetof(JitStateType, rsb_codeptrs))
|
, offsetof_rsb_codeptrs(offsetof(JitStateType, rsb_codeptrs))
|
||||||
, offsetof_cpsr_nzcv(offsetof(JitStateType, cpsr_nzcv))
|
, offsetof_cpsr_nzcv(offsetof(JitStateType, cpsr_nzcv))
|
||||||
|
|||||||
@@ -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