Compare commits

..

2 Commits

Author SHA1 Message Date
73f2201b18 Android 187 2024-01-09 01:01:13 +00:00
bd9ae51ab8 Merge yuzu-emu#12579 2024-01-09 01:01:12 +00:00
220 changed files with 1569 additions and 5352 deletions

View File

@ -3,35 +3,38 @@
# SPDX-FileCopyrightText: 2019 yuzu Emulator Project # SPDX-FileCopyrightText: 2019 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
shopt -s nullglob globstar if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .ci* dist/*.desktop \
dist/*.svg dist/*.xml; then
if git grep -nrI '\s$' src **/*.yml **/*.txt **/*.md Doxyfile .gitignore .gitmodules .ci* dist/*.desktop dist/*.svg dist/*.xml; then
echo Trailing whitespace found, aborting echo Trailing whitespace found, aborting
exit 1 exit 1
fi fi
# Default clang-format points to default 3.5 version one # Default clang-format points to default 3.5 version one
CLANG_FORMAT="${CLANG_FORMAT:-clang-format-15}" CLANG_FORMAT=${CLANG_FORMAT:-clang-format-15}
"$CLANG_FORMAT" --version $CLANG_FORMAT --version
if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then
# Get list of every file modified in this pull request
files_to_lint="$(git diff --name-only --diff-filter=ACMRTUXB $TRAVIS_COMMIT_RANGE | grep '^src/[^.]*[.]\(cpp\|h\)$' || true)"
else
# Check everything for branch pushes
files_to_lint="$(find src/ -name '*.cpp' -or -name '*.h')"
fi
# Turn off tracing for this because it's too verbose # Turn off tracing for this because it's too verbose
set +x set +x
# Check everything for branch pushes for f in $files_to_lint; do
FILES_TO_LINT="$(find src/ -name '*.cpp' -or -name '*.h')" d=$(diff -u "$f" <($CLANG_FORMAT "$f") || true)
if ! [ -z "$d" ]; then
for f in $FILES_TO_LINT; do echo "!!! $f not compliant to coding style, here is the fix:"
echo "$f" echo "$d"
"$CLANG_FORMAT" -i "$f" fail=1
fi
done done
DIFF=$(git -c core.fileMode=false diff) set -x
if [ ! -z "$DIFF" ]; then if [ "$fail" = 1 ]; then
echo "!!! Not compliant to coding style, here is the fix:"
echo "$DIFF"
exit 1 exit 1
fi fi
cd src/android
./gradlew ktlintCheck

View File

@ -8,7 +8,17 @@ variables:
DisplayVersion: $[counter(variables['DisplayPrefix'], 1)] DisplayVersion: $[counter(variables['DisplayPrefix'], 1)]
stages: stages:
- stage: format
displayName: 'format'
jobs:
- job: format
displayName: 'clang'
pool:
vmImage: ubuntu-latest
steps:
- template: ./templates/format-check.yml
- stage: build - stage: build
dependsOn: format
displayName: 'build' displayName: 'build'
jobs: jobs:
- job: build - job: build
@ -33,6 +43,7 @@ stages:
cache: 'true' cache: 'true'
version: $(DisplayVersion) version: $(DisplayVersion)
- stage: build_win - stage: build_win
dependsOn: format
displayName: 'build-windows' displayName: 'build-windows'
jobs: jobs:
- job: build - job: build

View File

@ -13,15 +13,13 @@ jobs:
format: format:
name: 'verify format' name: 'verify format'
runs-on: ubuntu-latest runs-on: ubuntu-latest
container:
image: yuzuemu/build-environments:linux-clang-format
options: -u 1001
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
submodules: false submodules: false
- name: set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: 'Verify Formatting' - name: 'Verify Formatting'
run: bash -ex ./.ci/scripts/format/script.sh run: bash -ex ./.ci/scripts/format/script.sh
build: build:

View File

@ -1,8 +1,6 @@
| Pull Request | Commit | Title | Author | Merged? | | Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----| |----|----|----|----|----|
| [12579](https://github.com/yuzu-emu/yuzu-android//pull/12579) | [`93ef41f03`](https://github.com/yuzu-emu/yuzu-android//pull/12579/files) | Core: Implement Device Mapping & GPU SMMU | [FernandoS27](https://github.com/FernandoS27/) | Yes | | [12579](https://github.com/yuzu-emu/yuzu-android//pull/12579) | [`66ae60a9e`](https://github.com/yuzu-emu/yuzu-android//pull/12579/files) | Core: Implement Device Mapping & GPU SMMU | [FernandoS27](https://github.com/FernandoS27/) | Yes |
| [12644](https://github.com/yuzu-emu/yuzu-android//pull/12644) | [`2044a289f`](https://github.com/yuzu-emu/yuzu-android//pull/12644/files) | shader_recompiler: fix Offset operand usage for non-OpImage*Gather | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12688](https://github.com/yuzu-emu/yuzu-android//pull/12688) | [`e9eb017aa`](https://github.com/yuzu-emu/yuzu-android//pull/12688/files) | renderer_vulkan: recreate swapchain when frame size changes | [liamwhite](https://github.com/liamwhite/) | Yes |
End of merge log. You can find the original README.md below the break. End of merge log. You can find the original README.md below the break.

View File

@ -188,15 +188,8 @@ tasks.create<Delete>("ktlintReset") {
delete(File(buildDir.path + File.separator + "intermediates/ktLint")) delete(File(buildDir.path + File.separator + "intermediates/ktLint"))
} }
val showFormatHelp = {
logger.lifecycle(
"If this check fails, please try running \"gradlew ktlintFormat\" for automatic " +
"codestyle fixes"
)
}
tasks.getByPath("ktlintKotlinScriptCheck").doFirst { showFormatHelp.invoke() }
tasks.getByPath("ktlintMainSourceSetCheck").doFirst { showFormatHelp.invoke() }
tasks.getByPath("loadKtlintReporters").dependsOn("ktlintReset") tasks.getByPath("loadKtlintReporters").dependsOn("ktlintReset")
tasks.getByPath("preBuild").dependsOn("ktlintCheck")
ktlint { ktlint {
version.set("0.47.1") version.set("0.47.1")
@ -235,33 +228,71 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
} }
fun runGitCommand(command: List<String>): String { fun getGitVersion(): String {
return try { var versionName = "0.0"
ProcessBuilder(command)
try {
versionName = ProcessBuilder("git", "describe", "--always", "--long")
.directory(project.rootDir) .directory(project.rootDir)
.redirectOutput(ProcessBuilder.Redirect.PIPE) .redirectOutput(ProcessBuilder.Redirect.PIPE)
.redirectError(ProcessBuilder.Redirect.PIPE) .redirectError(ProcessBuilder.Redirect.PIPE)
.start().inputStream.bufferedReader().use { it.readText() } .start().inputStream.bufferedReader().use { it.readText() }
.trim() .trim()
} catch (e: Exception) {
logger.error("Cannot find git")
""
}
}
fun getGitVersion(): String {
val versionName = if (System.getenv("GITHUB_ACTIONS") != null) {
val gitTag = System.getenv("GIT_TAG_NAME") ?: ""
gitTag
} else {
runGitCommand(listOf("git", "describe", "--always", "--long"))
.replace(Regex("(-0)?-[^-]+$"), "") .replace(Regex("(-0)?-[^-]+$"), "")
} catch (e: Exception) {
logger.error("Cannot find git, defaulting to dummy version number")
} }
return versionName.ifEmpty { "0.0" }
if (System.getenv("GITHUB_ACTIONS") != null) {
val gitTag = System.getenv("GIT_TAG_NAME")
versionName = gitTag ?: versionName
}
return versionName
} }
fun getGitHash(): String = fun getGitHash(): String {
runGitCommand(listOf("git", "rev-parse", "--short", "HEAD")).ifEmpty { "dummy-hash" } try {
val processBuilder = ProcessBuilder("git", "rev-parse", "--short", "HEAD")
processBuilder.directory(project.rootDir)
val process = processBuilder.start()
val inputStream = process.inputStream
val errorStream = process.errorStream
process.waitFor()
fun getBranch(): String = return if (process.exitValue() == 0) {
runGitCommand(listOf("git", "rev-parse", "--abbrev-ref", "HEAD")).ifEmpty { "dummy-hash" } inputStream.bufferedReader()
.use { it.readText().trim() } // return the value of gitHash
} else {
val errorMessage = errorStream.bufferedReader().use { it.readText().trim() }
logger.error("Error running git command: $errorMessage")
"dummy-hash" // return a dummy hash value in case of an error
}
} catch (e: Exception) {
logger.error("$e: Cannot find git, defaulting to dummy build hash")
return "dummy-hash" // return a dummy hash value in case of an error
}
}
fun getBranch(): String {
try {
val processBuilder = ProcessBuilder("git", "rev-parse", "--abbrev-ref", "HEAD")
processBuilder.directory(project.rootDir)
val process = processBuilder.start()
val inputStream = process.inputStream
val errorStream = process.errorStream
process.waitFor()
return if (process.exitValue() == 0) {
inputStream.bufferedReader()
.use { it.readText().trim() } // return the value of gitHash
} else {
val errorMessage = errorStream.bufferedReader().use { it.readText().trim() }
logger.error("Error running git command: $errorMessage")
"dummy-hash" // return a dummy hash value in case of an error
}
} catch (e: Exception) {
logger.error("$e: Cannot find git, defaulting to dummy build hash")
return "dummy-hash" // return a dummy hash value in case of an error
}
}

View File

@ -1,33 +0,0 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.annotation.SuppressLint
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
import androidx.recyclerview.widget.RecyclerView
/**
* Generic adapter that implements an [AsyncDifferConfig] and covers some of the basic boilerplate
* code used in every [RecyclerView].
* Type assigned to [Model] must inherit from [Object] in order to be compared properly.
*/
abstract class AbstractDiffAdapter<Model : Any, Holder : AbstractViewHolder<Model>> :
ListAdapter<Model, Holder>(AsyncDifferConfig.Builder(DiffCallback<Model>()).build()) {
override fun onBindViewHolder(holder: Holder, position: Int) =
holder.bind(currentList[position])
private class DiffCallback<Model> : DiffUtil.ItemCallback<Model>() {
override fun areItemsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean {
return oldItem === newItem
}
@SuppressLint("DiffUtilEquals")
override fun areContentsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean {
return oldItem == newItem
}
}
}

View File

@ -1,98 +0,0 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.annotation.SuppressLint
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
/**
* Generic list class meant to take care of basic lists
* @param currentList The list to show initially
*/
abstract class AbstractListAdapter<Model : Any, Holder : AbstractViewHolder<Model>>(
open var currentList: List<Model>
) : RecyclerView.Adapter<Holder>() {
override fun onBindViewHolder(holder: Holder, position: Int) =
holder.bind(currentList[position])
override fun getItemCount(): Int = currentList.size
/**
* Adds an item to [currentList] and notifies the underlying adapter of the change. If no parameter
* is passed in for position, [item] is added to the end of the list. Invokes [callback] last.
* @param item The item to add to the list
* @param position Index where [item] will be added
* @param callback Lambda that's called at the end of the list changes and has the added list
* position passed in as a parameter
*/
open fun addItem(item: Model, position: Int = -1, callback: ((position: Int) -> Unit)? = null) {
val newList = currentList.toMutableList()
val positionToUpdate: Int
if (position == -1) {
newList.add(item)
currentList = newList
positionToUpdate = currentList.size - 1
} else {
newList.add(position, item)
currentList = newList
positionToUpdate = position
}
onItemAdded(positionToUpdate, callback)
}
protected fun onItemAdded(position: Int, callback: ((Int) -> Unit)? = null) {
notifyItemInserted(position)
callback?.invoke(position)
}
/**
* Replaces the [item] at [position] in the [currentList] and notifies the underlying adapter
* of the change. Invokes [callback] last.
* @param item New list item
* @param position Index where [item] will replace the existing list item
* @param callback Lambda that's called at the end of the list changes and has the changed list
* position passed in as a parameter
*/
fun changeItem(item: Model, position: Int, callback: ((position: Int) -> Unit)? = null) {
val newList = currentList.toMutableList()
newList[position] = item
currentList = newList
onItemChanged(position, callback)
}
protected fun onItemChanged(position: Int, callback: ((Int) -> Unit)? = null) {
notifyItemChanged(position)
callback?.invoke(position)
}
/**
* Removes the list item at [position] in [currentList] and notifies the underlying adapter
* of the change. Invokes [callback] last.
* @param position Index where the list item will be removed
* @param callback Lambda that's called at the end of the list changes and has the removed list
* position passed in as a parameter
*/
fun removeItem(position: Int, callback: ((position: Int) -> Unit)? = null) {
val newList = currentList.toMutableList()
newList.removeAt(position)
currentList = newList
onItemRemoved(position, callback)
}
protected fun onItemRemoved(position: Int, callback: ((Int) -> Unit)? = null) {
notifyItemRemoved(position)
callback?.invoke(position)
}
/**
* Replaces [currentList] with [newList] and notifies the underlying adapter of the change.
* @param newList The new list to replace [currentList]
*/
@SuppressLint("NotifyDataSetChanged")
open fun replaceList(newList: List<Model>) {
currentList = newList
notifyDataSetChanged()
}
}

View File

@ -1,105 +0,0 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import org.yuzu.yuzu_emu.model.SelectableItem
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
/**
* Generic list class meant to take care of single selection UI updates
* @param currentList The list to show initially
* @param defaultSelection The default selection to use if no list items are selected by
* [SelectableItem.selected] or if the currently selected item is removed from the list
*/
abstract class AbstractSingleSelectionList<
Model : SelectableItem,
Holder : AbstractViewHolder<Model>
>(
final override var currentList: List<Model>,
private val defaultSelection: DefaultSelection = DefaultSelection.Start
) : AbstractListAdapter<Model, Holder>(currentList) {
var selectedItem = getDefaultSelection()
init {
findSelectedItem()
}
/**
* Changes the selection state of the [SelectableItem] that was selected and the previously selected
* item and notifies the underlying adapter of the change for those items. Invokes [callback] last.
* Does nothing if [position] is the same as the currently selected item.
* @param position Index of the item that was selected
* @param callback Lambda that's called at the end of the list changes and has the selected list
* position passed in as a parameter
*/
fun selectItem(position: Int, callback: ((position: Int) -> Unit)? = null) {
if (position == selectedItem) {
return
}
val previouslySelectedItem = selectedItem
selectedItem = position
if (currentList.indices.contains(selectedItem)) {
currentList[selectedItem].onSelectionStateChanged(true)
}
if (currentList.indices.contains(previouslySelectedItem)) {
currentList[previouslySelectedItem].onSelectionStateChanged(false)
}
onItemChanged(previouslySelectedItem)
onItemChanged(selectedItem)
callback?.invoke(position)
}
/**
* Removes a given item from the list and notifies the underlying adapter of the change. If the
* currently selected item was the item that was removed, the item at the position provided
* by [defaultSelection] will be made the new selection. Invokes [callback] last.
* @param position Index of the item that was removed
* @param callback Lambda that's called at the end of the list changes and has the removed and
* selected list positions passed in as parameters
*/
fun removeSelectableItem(
position: Int,
callback: ((removedPosition: Int, selectedPosition: Int) -> Unit)?
) {
removeItem(position)
if (position == selectedItem) {
selectedItem = getDefaultSelection()
currentList[selectedItem].onSelectionStateChanged(true)
onItemChanged(selectedItem)
} else if (position < selectedItem) {
selectedItem--
}
callback?.invoke(position, selectedItem)
}
override fun addItem(item: Model, position: Int, callback: ((Int) -> Unit)?) {
super.addItem(item, position, callback)
if (position <= selectedItem && position != -1) {
selectedItem++
}
}
override fun replaceList(newList: List<Model>) {
super.replaceList(newList)
findSelectedItem()
}
private fun findSelectedItem() {
for (i in currentList.indices) {
if (currentList[i].selected) {
selectedItem = i
break
}
}
}
private fun getDefaultSelection(): Int =
when (defaultSelection) {
DefaultSelection.Start -> currentList.indices.first
DefaultSelection.End -> currentList.indices.last
}
enum class DefaultSelection { Start, End }
}

View File

@ -5,28 +5,48 @@ package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.databinding.ListItemAddonBinding import org.yuzu.yuzu_emu.databinding.ListItemAddonBinding
import org.yuzu.yuzu_emu.model.Addon import org.yuzu.yuzu_emu.model.Addon
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class AddonAdapter : AbstractDiffAdapter<Addon, AddonAdapter.AddonViewHolder>() { class AddonAdapter : ListAdapter<Addon, AddonAdapter.AddonViewHolder>(
AsyncDifferConfig.Builder(DiffCallback()).build()
) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddonViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddonViewHolder {
ListItemAddonBinding.inflate(LayoutInflater.from(parent.context), parent, false) ListItemAddonBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.also { return AddonViewHolder(it) } .also { return AddonViewHolder(it) }
} }
override fun getItemCount(): Int = currentList.size
override fun onBindViewHolder(holder: AddonViewHolder, position: Int) =
holder.bind(currentList[position])
inner class AddonViewHolder(val binding: ListItemAddonBinding) : inner class AddonViewHolder(val binding: ListItemAddonBinding) :
AbstractViewHolder<Addon>(binding) { RecyclerView.ViewHolder(binding.root) {
override fun bind(model: Addon) { fun bind(addon: Addon) {
binding.root.setOnClickListener { binding.root.setOnClickListener {
binding.addonSwitch.isChecked = !binding.addonSwitch.isChecked binding.addonSwitch.isChecked = !binding.addonSwitch.isChecked
} }
binding.title.text = model.title binding.title.text = addon.title
binding.version.text = model.version binding.version.text = addon.version
binding.addonSwitch.setOnCheckedChangeListener { _, checked -> binding.addonSwitch.setOnCheckedChangeListener { _, checked ->
model.enabled = checked addon.enabled = checked
} }
binding.addonSwitch.isChecked = model.enabled binding.addonSwitch.isChecked = addon.enabled
}
}
private class DiffCallback : DiffUtil.ItemCallback<Addon>() {
override fun areItemsTheSame(oldItem: Addon, newItem: Addon): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: Addon, newItem: Addon): Boolean {
return oldItem == newItem
} }
} }
} }

View File

@ -4,11 +4,13 @@
package org.yuzu.yuzu_emu.adapters package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
@ -17,58 +19,72 @@ import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
import org.yuzu.yuzu_emu.model.Applet import org.yuzu.yuzu_emu.model.Applet
import org.yuzu.yuzu_emu.model.AppletInfo import org.yuzu.yuzu_emu.model.AppletInfo
import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class AppletAdapter(val activity: FragmentActivity, applets: List<Applet>) : class AppletAdapter(val activity: FragmentActivity, var applets: List<Applet>) :
AbstractListAdapter<Applet, AppletAdapter.AppletViewHolder>(applets) { RecyclerView.Adapter<AppletAdapter.AppletViewHolder>(),
View.OnClickListener {
override fun onCreateViewHolder( override fun onCreateViewHolder(
parent: ViewGroup, parent: ViewGroup,
viewType: Int viewType: Int
): AppletAdapter.AppletViewHolder { ): AppletAdapter.AppletViewHolder {
CardSimpleOutlinedBinding.inflate(LayoutInflater.from(parent.context), parent, false) CardSimpleOutlinedBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.apply { root.setOnClickListener(this@AppletAdapter) }
.also { return AppletViewHolder(it) } .also { return AppletViewHolder(it) }
} }
override fun onBindViewHolder(holder: AppletViewHolder, position: Int) =
holder.bind(applets[position])
override fun getItemCount(): Int = applets.size
override fun onClick(view: View) {
val applet = (view.tag as AppletViewHolder).applet
val appletPath = NativeLibrary.getAppletLaunchPath(applet.appletInfo.entryId)
if (appletPath.isEmpty()) {
Toast.makeText(
YuzuApplication.appContext,
R.string.applets_error_applet,
Toast.LENGTH_SHORT
).show()
return
}
if (applet.appletInfo == AppletInfo.Cabinet) {
view.findNavController()
.navigate(R.id.action_appletLauncherFragment_to_cabinetLauncherDialogFragment)
return
}
NativeLibrary.setCurrentAppletId(applet.appletInfo.appletId)
val appletGame = Game(
title = YuzuApplication.appContext.getString(applet.titleId),
path = appletPath
)
val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
view.findNavController().navigate(action)
}
inner class AppletViewHolder(val binding: CardSimpleOutlinedBinding) : inner class AppletViewHolder(val binding: CardSimpleOutlinedBinding) :
AbstractViewHolder<Applet>(binding) { RecyclerView.ViewHolder(binding.root) {
override fun bind(model: Applet) { lateinit var applet: Applet
binding.title.setText(model.titleId)
binding.description.setText(model.descriptionId) init {
itemView.tag = this
}
fun bind(applet: Applet) {
this.applet = applet
binding.title.setText(applet.titleId)
binding.description.setText(applet.descriptionId)
binding.icon.setImageDrawable( binding.icon.setImageDrawable(
ResourcesCompat.getDrawable( ResourcesCompat.getDrawable(
binding.icon.context.resources, binding.icon.context.resources,
model.iconId, applet.iconId,
binding.icon.context.theme binding.icon.context.theme
) )
) )
binding.root.setOnClickListener { onClick(model) }
}
fun onClick(applet: Applet) {
val appletPath = NativeLibrary.getAppletLaunchPath(applet.appletInfo.entryId)
if (appletPath.isEmpty()) {
Toast.makeText(
binding.root.context,
R.string.applets_error_applet,
Toast.LENGTH_SHORT
).show()
return
}
if (applet.appletInfo == AppletInfo.Cabinet) {
binding.root.findNavController()
.navigate(R.id.action_appletLauncherFragment_to_cabinetLauncherDialogFragment)
return
}
NativeLibrary.setCurrentAppletId(applet.appletInfo.appletId)
val appletGame = Game(
title = YuzuApplication.appContext.getString(applet.titleId),
path = appletPath
)
val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
binding.root.findNavController().navigate(action)
} }
} }
} }

View File

@ -4,10 +4,12 @@
package org.yuzu.yuzu_emu.adapters package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
@ -17,43 +19,54 @@ import org.yuzu.yuzu_emu.model.CabinetMode
import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter.CabinetModeViewHolder import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter.CabinetModeViewHolder
import org.yuzu.yuzu_emu.model.AppletInfo import org.yuzu.yuzu_emu.model.AppletInfo
import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class CabinetLauncherDialogAdapter(val fragment: Fragment) : class CabinetLauncherDialogAdapter(val fragment: Fragment) :
AbstractListAdapter<CabinetMode, CabinetModeViewHolder>( RecyclerView.Adapter<CabinetModeViewHolder>(),
CabinetMode.values().copyOfRange(1, CabinetMode.entries.size).toList() View.OnClickListener {
) { private val cabinetModes = CabinetMode.values().copyOfRange(1, CabinetMode.values().size)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CabinetModeViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CabinetModeViewHolder {
DialogListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) DialogListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.apply { root.setOnClickListener(this@CabinetLauncherDialogAdapter) }
.also { return CabinetModeViewHolder(it) } .also { return CabinetModeViewHolder(it) }
} }
override fun getItemCount(): Int = cabinetModes.size
override fun onBindViewHolder(holder: CabinetModeViewHolder, position: Int) =
holder.bind(cabinetModes[position])
override fun onClick(view: View) {
val mode = (view.tag as CabinetModeViewHolder).cabinetMode
val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.Cabinet.entryId)
NativeLibrary.setCurrentAppletId(AppletInfo.Cabinet.appletId)
NativeLibrary.setCabinetMode(mode.id)
val appletGame = Game(
title = YuzuApplication.appContext.getString(R.string.cabinet_applet),
path = appletPath
)
val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
fragment.findNavController().navigate(action)
}
inner class CabinetModeViewHolder(val binding: DialogListItemBinding) : inner class CabinetModeViewHolder(val binding: DialogListItemBinding) :
AbstractViewHolder<CabinetMode>(binding) { RecyclerView.ViewHolder(binding.root) {
override fun bind(model: CabinetMode) { lateinit var cabinetMode: CabinetMode
init {
itemView.tag = this
}
fun bind(cabinetMode: CabinetMode) {
this.cabinetMode = cabinetMode
binding.icon.setImageDrawable( binding.icon.setImageDrawable(
ResourcesCompat.getDrawable( ResourcesCompat.getDrawable(
binding.icon.context.resources, binding.icon.context.resources,
model.iconId, cabinetMode.iconId,
binding.icon.context.theme binding.icon.context.theme
) )
) )
binding.title.setText(model.titleId) binding.title.setText(cabinetMode.titleId)
binding.root.setOnClickListener { onClick(model) }
}
private fun onClick(mode: CabinetMode) {
val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.Cabinet.entryId)
NativeLibrary.setCurrentAppletId(AppletInfo.Cabinet.appletId)
NativeLibrary.setCabinetMode(mode.id)
val appletGame = Game(
title = YuzuApplication.appContext.getString(R.string.cabinet_applet),
path = appletPath
)
val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
fragment.findNavController().navigate(action)
} }
} }
} }

View File

@ -7,39 +7,65 @@ import android.text.TextUtils
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.model.Driver
import org.yuzu.yuzu_emu.model.DriverViewModel import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
class DriverAdapter(private val driverViewModel: DriverViewModel) : class DriverAdapter(private val driverViewModel: DriverViewModel) :
AbstractSingleSelectionList<Driver, DriverAdapter.DriverViewHolder>( ListAdapter<Pair<String, GpuDriverMetadata>, DriverAdapter.DriverViewHolder>(
driverViewModel.driverList.value AsyncDifferConfig.Builder(DiffCallback()).build()
) { ) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverViewHolder {
CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) val binding =
.also { return DriverViewHolder(it) } CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return DriverViewHolder(binding)
}
override fun getItemCount(): Int = currentList.size
override fun onBindViewHolder(holder: DriverViewHolder, position: Int) =
holder.bind(currentList[position])
private fun onSelectDriver(position: Int) {
driverViewModel.setSelectedDriverIndex(position)
notifyItemChanged(driverViewModel.previouslySelectedDriver)
notifyItemChanged(driverViewModel.selectedDriver)
}
private fun onDeleteDriver(driverData: Pair<String, GpuDriverMetadata>, position: Int) {
if (driverViewModel.selectedDriver > position) {
driverViewModel.setSelectedDriverIndex(driverViewModel.selectedDriver - 1)
}
if (GpuDriverHelper.customDriverSettingData == driverData.second) {
driverViewModel.setSelectedDriverIndex(0)
}
driverViewModel.driversToDelete.add(driverData.first)
driverViewModel.removeDriver(driverData)
notifyItemRemoved(position)
notifyItemChanged(driverViewModel.selectedDriver)
} }
inner class DriverViewHolder(val binding: CardDriverOptionBinding) : inner class DriverViewHolder(val binding: CardDriverOptionBinding) :
AbstractViewHolder<Driver>(binding) { RecyclerView.ViewHolder(binding.root) {
override fun bind(model: Driver) { private lateinit var driverData: Pair<String, GpuDriverMetadata>
fun bind(driverData: Pair<String, GpuDriverMetadata>) {
this.driverData = driverData
val driver = driverData.second
binding.apply { binding.apply {
radioButton.isChecked = model.selected radioButton.isChecked = driverViewModel.selectedDriver == bindingAdapterPosition
root.setOnClickListener { root.setOnClickListener {
selectItem(bindingAdapterPosition) { onSelectDriver(bindingAdapterPosition)
driverViewModel.onDriverSelected(it)
driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
}
} }
buttonDelete.setOnClickListener { buttonDelete.setOnClickListener {
removeSelectableItem( onDeleteDriver(driverData, bindingAdapterPosition)
bindingAdapterPosition
) { removedPosition: Int, selectedPosition: Int ->
driverViewModel.onDriverRemoved(removedPosition, selectedPosition)
driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
}
} }
// Delay marquee by 3s // Delay marquee by 3s
@ -54,19 +80,38 @@ class DriverAdapter(private val driverViewModel: DriverViewModel) :
}, },
3000 3000
) )
title.text = model.title if (driver.name == null) {
version.text = model.version title.setText(R.string.system_gpu_driver)
description.text = model.description description.text = ""
if (model.description.isNotEmpty()) { version.text = ""
version.visibility = View.VISIBLE
description.visibility = View.VISIBLE
buttonDelete.visibility = View.VISIBLE
} else {
version.visibility = View.GONE version.visibility = View.GONE
description.visibility = View.GONE description.visibility = View.GONE
buttonDelete.visibility = View.GONE buttonDelete.visibility = View.GONE
} else {
title.text = driver.name
version.text = driver.version
description.text = driver.description
version.visibility = View.VISIBLE
description.visibility = View.VISIBLE
buttonDelete.visibility = View.VISIBLE
} }
} }
} }
} }
private class DiffCallback : DiffUtil.ItemCallback<Pair<String, GpuDriverMetadata>>() {
override fun areItemsTheSame(
oldItem: Pair<String, GpuDriverMetadata>,
newItem: Pair<String, GpuDriverMetadata>
): Boolean {
return oldItem.first == newItem.first
}
override fun areContentsTheSame(
oldItem: Pair<String, GpuDriverMetadata>,
newItem: Pair<String, GpuDriverMetadata>
): Boolean {
return oldItem.second == newItem.second
}
}
} }

View File

@ -8,14 +8,19 @@ import android.text.TextUtils
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.databinding.CardFolderBinding import org.yuzu.yuzu_emu.databinding.CardFolderBinding
import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment
import org.yuzu.yuzu_emu.model.GameDir import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) : class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) :
AbstractDiffAdapter<GameDir, FolderAdapter.FolderViewHolder>() { ListAdapter<GameDir, FolderAdapter.FolderViewHolder>(
AsyncDifferConfig.Builder(DiffCallback()).build()
) {
override fun onCreateViewHolder( override fun onCreateViewHolder(
parent: ViewGroup, parent: ViewGroup,
viewType: Int viewType: Int
@ -24,11 +29,18 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
.also { return FolderViewHolder(it) } .also { return FolderViewHolder(it) }
} }
override fun onBindViewHolder(holder: FolderAdapter.FolderViewHolder, position: Int) =
holder.bind(currentList[position])
inner class FolderViewHolder(val binding: CardFolderBinding) : inner class FolderViewHolder(val binding: CardFolderBinding) :
AbstractViewHolder<GameDir>(binding) { RecyclerView.ViewHolder(binding.root) {
override fun bind(model: GameDir) { private lateinit var gameDir: GameDir
fun bind(gameDir: GameDir) {
this.gameDir = gameDir
binding.apply { binding.apply {
path.text = Uri.parse(model.uriString).path path.text = Uri.parse(gameDir.uriString).path
path.postDelayed( path.postDelayed(
{ {
path.isSelected = true path.isSelected = true
@ -38,7 +50,7 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
) )
buttonEdit.setOnClickListener { buttonEdit.setOnClickListener {
GameFolderPropertiesDialogFragment.newInstance(model) GameFolderPropertiesDialogFragment.newInstance(this@FolderViewHolder.gameDir)
.show( .show(
activity.supportFragmentManager, activity.supportFragmentManager,
GameFolderPropertiesDialogFragment.TAG GameFolderPropertiesDialogFragment.TAG
@ -46,9 +58,19 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
} }
buttonDelete.setOnClickListener { buttonDelete.setOnClickListener {
gamesViewModel.removeFolder(model) gamesViewModel.removeFolder(this@FolderViewHolder.gameDir)
} }
} }
} }
} }
private class DiffCallback : DiffUtil.ItemCallback<GameDir>() {
override fun areItemsTheSame(oldItem: GameDir, newItem: GameDir): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: GameDir, newItem: GameDir): Boolean {
return oldItem == newItem
}
}
} }

View File

@ -9,6 +9,7 @@ import android.graphics.drawable.LayerDrawable
import android.net.Uri import android.net.Uri
import android.text.TextUtils import android.text.TextUtils
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.Toast import android.widget.Toast
@ -24,6 +25,10 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -31,26 +36,122 @@ import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.activities.EmulationActivity import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder
import org.yuzu.yuzu_emu.databinding.CardGameBinding import org.yuzu.yuzu_emu.databinding.CardGameBinding
import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.utils.GameIconUtils import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class GameAdapter(private val activity: AppCompatActivity) : class GameAdapter(private val activity: AppCompatActivity) :
AbstractDiffAdapter<Game, GameAdapter.GameViewHolder>() { ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()),
View.OnClickListener,
View.OnLongClickListener {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder {
CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false) // Create a new view.
.also { return GameViewHolder(it) } val binding = CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
binding.cardGame.setOnClickListener(this)
binding.cardGame.setOnLongClickListener(this)
// Use that view to create a ViewHolder.
return GameViewHolder(binding)
}
override fun onBindViewHolder(holder: GameViewHolder, position: Int) =
holder.bind(currentList[position])
override fun getItemCount(): Int = currentList.size
/**
* Launches the game that was clicked on.
*
* @param view The card representing the game the user wants to play.
*/
override fun onClick(view: View) {
val holder = view.tag as GameViewHolder
val gameExists = DocumentFile.fromSingleUri(
YuzuApplication.appContext,
Uri.parse(holder.game.path)
)?.exists() == true
if (!gameExists) {
Toast.makeText(
YuzuApplication.appContext,
R.string.loader_error_file_not_found,
Toast.LENGTH_LONG
).show()
ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
return
}
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
preferences.edit()
.putLong(
holder.game.keyLastPlayedTime,
System.currentTimeMillis()
)
.apply()
val openIntent = Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply {
action = Intent.ACTION_VIEW
data = Uri.parse(holder.game.path)
}
activity.lifecycleScope.launch {
withContext(Dispatchers.IO) {
val layerDrawable = ResourcesCompat.getDrawable(
YuzuApplication.appContext.resources,
R.drawable.shortcut,
null
) as LayerDrawable
layerDrawable.setDrawableByLayerId(
R.id.shortcut_foreground,
GameIconUtils.getGameIcon(activity, holder.game)
.toDrawable(YuzuApplication.appContext.resources)
)
val inset = YuzuApplication.appContext.resources
.getDimensionPixelSize(R.dimen.icon_inset)
layerDrawable.setLayerInset(1, inset, inset, inset, inset)
val shortcut =
ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
.setShortLabel(holder.game.title)
.setIcon(
IconCompat.createWithAdaptiveBitmap(
layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
)
)
.setIntent(openIntent)
.build()
ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
}
}
val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game, true)
view.findNavController().navigate(action)
}
override fun onLongClick(view: View): Boolean {
val holder = view.tag as GameViewHolder
val action = HomeNavigationDirections.actionGlobalPerGamePropertiesFragment(holder.game)
view.findNavController().navigate(action)
return true
} }
inner class GameViewHolder(val binding: CardGameBinding) : inner class GameViewHolder(val binding: CardGameBinding) :
AbstractViewHolder<Game>(binding) { RecyclerView.ViewHolder(binding.root) {
override fun bind(model: Game) { lateinit var game: Game
binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP
GameIconUtils.loadGameIcon(model, binding.imageGameScreen)
binding.textGameTitle.text = model.title.replace("[\\t\\n\\r]+".toRegex(), " ") init {
binding.cardGame.tag = this
}
fun bind(game: Game) {
this.game = game
binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP
GameIconUtils.loadGameIcon(game, binding.imageGameScreen)
binding.textGameTitle.text = game.title.replace("[\\t\\n\\r]+".toRegex(), " ")
binding.textGameTitle.postDelayed( binding.textGameTitle.postDelayed(
{ {
@ -59,79 +160,16 @@ class GameAdapter(private val activity: AppCompatActivity) :
}, },
3000 3000
) )
}
}
binding.cardGame.setOnClickListener { onClick(model) } private class DiffCallback : DiffUtil.ItemCallback<Game>() {
binding.cardGame.setOnLongClickListener { onLongClick(model) } override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean {
return oldItem == newItem
} }
fun onClick(game: Game) { override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean {
val gameExists = DocumentFile.fromSingleUri( return oldItem == newItem
YuzuApplication.appContext,
Uri.parse(game.path)
)?.exists() == true
if (!gameExists) {
Toast.makeText(
YuzuApplication.appContext,
R.string.loader_error_file_not_found,
Toast.LENGTH_LONG
).show()
ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
return
}
val preferences =
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
preferences.edit()
.putLong(
game.keyLastPlayedTime,
System.currentTimeMillis()
)
.apply()
val openIntent =
Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply {
action = Intent.ACTION_VIEW
data = Uri.parse(game.path)
}
activity.lifecycleScope.launch {
withContext(Dispatchers.IO) {
val layerDrawable = ResourcesCompat.getDrawable(
YuzuApplication.appContext.resources,
R.drawable.shortcut,
null
) as LayerDrawable
layerDrawable.setDrawableByLayerId(
R.id.shortcut_foreground,
GameIconUtils.getGameIcon(activity, game)
.toDrawable(YuzuApplication.appContext.resources)
)
val inset = YuzuApplication.appContext.resources
.getDimensionPixelSize(R.dimen.icon_inset)
layerDrawable.setLayerInset(1, inset, inset, inset, inset)
val shortcut =
ShortcutInfoCompat.Builder(YuzuApplication.appContext, game.path)
.setShortLabel(game.title)
.setIcon(
IconCompat.createWithAdaptiveBitmap(
layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
)
)
.setIntent(openIntent)
.build()
ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
}
}
val action = HomeNavigationDirections.actionGlobalEmulationActivity(game, true)
binding.root.findNavController().navigate(action)
}
fun onLongClick(game: Game): Boolean {
val action = HomeNavigationDirections.actionGlobalPerGamePropertiesFragment(game)
binding.root.findNavController().navigate(action)
return true
} }
} }
} }

View File

@ -12,22 +12,23 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding
import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
import org.yuzu.yuzu_emu.model.GameProperty import org.yuzu.yuzu_emu.model.GameProperty
import org.yuzu.yuzu_emu.model.InstallableProperty import org.yuzu.yuzu_emu.model.InstallableProperty
import org.yuzu.yuzu_emu.model.SubmenuProperty import org.yuzu.yuzu_emu.model.SubmenuProperty
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class GamePropertiesAdapter( class GamePropertiesAdapter(
private val viewLifecycle: LifecycleOwner, private val viewLifecycle: LifecycleOwner,
private var properties: List<GameProperty> private var properties: List<GameProperty>
) : AbstractListAdapter<GameProperty, AbstractViewHolder<GameProperty>>(properties) { ) :
RecyclerView.Adapter<GamePropertiesAdapter.GamePropertyViewHolder>() {
override fun onCreateViewHolder( override fun onCreateViewHolder(
parent: ViewGroup, parent: ViewGroup,
viewType: Int viewType: Int
): AbstractViewHolder<GameProperty> { ): GamePropertyViewHolder {
val inflater = LayoutInflater.from(parent.context) val inflater = LayoutInflater.from(parent.context)
return when (viewType) { return when (viewType) {
PropertyType.Submenu.ordinal -> { PropertyType.Submenu.ordinal -> {
@ -50,6 +51,11 @@ class GamePropertiesAdapter(
} }
} }
override fun getItemCount(): Int = properties.size
override fun onBindViewHolder(holder: GamePropertyViewHolder, position: Int) =
holder.bind(properties[position])
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
return when (properties[position]) { return when (properties[position]) {
is SubmenuProperty -> PropertyType.Submenu.ordinal is SubmenuProperty -> PropertyType.Submenu.ordinal
@ -57,10 +63,14 @@ class GamePropertiesAdapter(
} }
} }
sealed class GamePropertyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
abstract fun bind(property: GameProperty)
}
inner class SubmenuPropertyViewHolder(val binding: CardSimpleOutlinedBinding) : inner class SubmenuPropertyViewHolder(val binding: CardSimpleOutlinedBinding) :
AbstractViewHolder<GameProperty>(binding) { GamePropertyViewHolder(binding.root) {
override fun bind(model: GameProperty) { override fun bind(property: GameProperty) {
val submenuProperty = model as SubmenuProperty val submenuProperty = property as SubmenuProperty
binding.root.setOnClickListener { binding.root.setOnClickListener {
submenuProperty.action.invoke() submenuProperty.action.invoke()
@ -98,9 +108,9 @@ class GamePropertiesAdapter(
} }
inner class InstallablePropertyViewHolder(val binding: CardInstallableIconBinding) : inner class InstallablePropertyViewHolder(val binding: CardInstallableIconBinding) :
AbstractViewHolder<GameProperty>(binding) { GamePropertyViewHolder(binding.root) {
override fun bind(model: GameProperty) { override fun bind(property: GameProperty) {
val installableProperty = model as InstallableProperty val installableProperty = property as InstallableProperty
binding.title.setText(installableProperty.titleId) binding.title.setText(installableProperty.titleId)
binding.description.setText(installableProperty.descriptionId) binding.description.setText(installableProperty.descriptionId)

View File

@ -14,37 +14,69 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.model.HomeSetting import org.yuzu.yuzu_emu.model.HomeSetting
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class HomeSettingAdapter( class HomeSettingAdapter(
private val activity: AppCompatActivity, private val activity: AppCompatActivity,
private val viewLifecycle: LifecycleOwner, private val viewLifecycle: LifecycleOwner,
options: List<HomeSetting> var options: List<HomeSetting>
) : AbstractListAdapter<HomeSetting, HomeSettingAdapter.HomeOptionViewHolder>(options) { ) :
RecyclerView.Adapter<HomeSettingAdapter.HomeOptionViewHolder>(),
View.OnClickListener {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeOptionViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeOptionViewHolder {
CardHomeOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) val binding =
.also { return HomeOptionViewHolder(it) } CardHomeOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
binding.root.setOnClickListener(this)
return HomeOptionViewHolder(binding)
}
override fun getItemCount(): Int {
return options.size
}
override fun onBindViewHolder(holder: HomeOptionViewHolder, position: Int) {
holder.bind(options[position])
}
override fun onClick(view: View) {
val holder = view.tag as HomeOptionViewHolder
if (holder.option.isEnabled.invoke()) {
holder.option.onClick.invoke()
} else {
MessageDialogFragment.newInstance(
activity,
titleId = holder.option.disabledTitleId,
descriptionId = holder.option.disabledMessageId
).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
}
} }
inner class HomeOptionViewHolder(val binding: CardHomeOptionBinding) : inner class HomeOptionViewHolder(val binding: CardHomeOptionBinding) :
AbstractViewHolder<HomeSetting>(binding) { RecyclerView.ViewHolder(binding.root) {
override fun bind(model: HomeSetting) { lateinit var option: HomeSetting
binding.optionTitle.text = activity.resources.getString(model.titleId)
binding.optionDescription.text = activity.resources.getString(model.descriptionId) init {
itemView.tag = this
}
fun bind(option: HomeSetting) {
this.option = option
binding.optionTitle.text = activity.resources.getString(option.titleId)
binding.optionDescription.text = activity.resources.getString(option.descriptionId)
binding.optionIcon.setImageDrawable( binding.optionIcon.setImageDrawable(
ResourcesCompat.getDrawable( ResourcesCompat.getDrawable(
activity.resources, activity.resources,
model.iconId, option.iconId,
activity.theme activity.theme
) )
) )
when (model.titleId) { when (option.titleId) {
R.string.get_early_access -> R.string.get_early_access ->
binding.optionLayout.background = binding.optionLayout.background =
ContextCompat.getDrawable( ContextCompat.getDrawable(
@ -53,7 +85,7 @@ class HomeSettingAdapter(
) )
} }
if (!model.isEnabled.invoke()) { if (!option.isEnabled.invoke()) {
binding.optionTitle.alpha = 0.5f binding.optionTitle.alpha = 0.5f
binding.optionDescription.alpha = 0.5f binding.optionDescription.alpha = 0.5f
binding.optionIcon.alpha = 0.5f binding.optionIcon.alpha = 0.5f
@ -61,7 +93,7 @@ class HomeSettingAdapter(
viewLifecycle.lifecycleScope.launch { viewLifecycle.lifecycleScope.launch {
viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) { viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) {
model.details.collect { updateOptionDetails(it) } option.details.collect { updateOptionDetails(it) }
} }
} }
binding.optionDetail.postDelayed( binding.optionDetail.postDelayed(
@ -71,20 +103,6 @@ class HomeSettingAdapter(
}, },
3000 3000
) )
binding.root.setOnClickListener { onClick(model) }
}
private fun onClick(model: HomeSetting) {
if (model.isEnabled.invoke()) {
model.onClick.invoke()
} else {
MessageDialogFragment.newInstance(
activity,
titleId = model.disabledTitleId,
descriptionId = model.disabledMessageId
).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
}
} }
private fun updateOptionDetails(detailString: String) { private fun updateOptionDetails(detailString: String) {

View File

@ -6,33 +6,43 @@ package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.databinding.CardInstallableBinding import org.yuzu.yuzu_emu.databinding.CardInstallableBinding
import org.yuzu.yuzu_emu.model.Installable import org.yuzu.yuzu_emu.model.Installable
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class InstallableAdapter(installables: List<Installable>) : class InstallableAdapter(private val installables: List<Installable>) :
AbstractListAdapter<Installable, InstallableAdapter.InstallableViewHolder>(installables) { RecyclerView.Adapter<InstallableAdapter.InstallableViewHolder>() {
override fun onCreateViewHolder( override fun onCreateViewHolder(
parent: ViewGroup, parent: ViewGroup,
viewType: Int viewType: Int
): InstallableAdapter.InstallableViewHolder { ): InstallableAdapter.InstallableViewHolder {
CardInstallableBinding.inflate(LayoutInflater.from(parent.context), parent, false) val binding =
.also { return InstallableViewHolder(it) } CardInstallableBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return InstallableViewHolder(binding)
} }
inner class InstallableViewHolder(val binding: CardInstallableBinding) : override fun getItemCount(): Int = installables.size
AbstractViewHolder<Installable>(binding) {
override fun bind(model: Installable) {
binding.title.setText(model.titleId)
binding.description.setText(model.descriptionId)
if (model.install != null) { override fun onBindViewHolder(holder: InstallableAdapter.InstallableViewHolder, position: Int) =
holder.bind(installables[position])
inner class InstallableViewHolder(val binding: CardInstallableBinding) :
RecyclerView.ViewHolder(binding.root) {
lateinit var installable: Installable
fun bind(installable: Installable) {
this.installable = installable
binding.title.setText(installable.titleId)
binding.description.setText(installable.descriptionId)
if (installable.install != null) {
binding.buttonInstall.visibility = View.VISIBLE binding.buttonInstall.visibility = View.VISIBLE
binding.buttonInstall.setOnClickListener { model.install.invoke() } binding.buttonInstall.setOnClickListener { installable.install.invoke() }
} }
if (model.export != null) { if (installable.export != null) {
binding.buttonExport.visibility = View.VISIBLE binding.buttonExport.visibility = View.VISIBLE
binding.buttonExport.setOnClickListener { model.export.invoke() } binding.buttonExport.setOnClickListener { installable.export.invoke() }
} }
} }
} }

View File

@ -7,33 +7,49 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.fragments.LicenseBottomSheetDialogFragment import org.yuzu.yuzu_emu.fragments.LicenseBottomSheetDialogFragment
import org.yuzu.yuzu_emu.model.License import org.yuzu.yuzu_emu.model.License
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class LicenseAdapter(private val activity: AppCompatActivity, licenses: List<License>) : class LicenseAdapter(private val activity: AppCompatActivity, var licenses: List<License>) :
AbstractListAdapter<License, LicenseAdapter.LicenseViewHolder>(licenses) { RecyclerView.Adapter<LicenseAdapter.LicenseViewHolder>(),
View.OnClickListener {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LicenseViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LicenseViewHolder {
ListItemSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false) val binding =
.also { return LicenseViewHolder(it) } ListItemSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false)
binding.root.setOnClickListener(this)
return LicenseViewHolder(binding)
} }
inner class LicenseViewHolder(val binding: ListItemSettingBinding) : override fun getItemCount(): Int = licenses.size
AbstractViewHolder<License>(binding) {
override fun bind(model: License) {
binding.apply {
textSettingName.text = root.context.getString(model.titleId)
textSettingDescription.text = root.context.getString(model.descriptionId)
textSettingValue.visibility = View.GONE
root.setOnClickListener { onClick(model) } override fun onBindViewHolder(holder: LicenseViewHolder, position: Int) {
} holder.bind(licenses[position])
}
override fun onClick(view: View) {
val license = (view.tag as LicenseViewHolder).license
LicenseBottomSheetDialogFragment.newInstance(license)
.show(activity.supportFragmentManager, LicenseBottomSheetDialogFragment.TAG)
}
inner class LicenseViewHolder(val binding: ListItemSettingBinding) : ViewHolder(binding.root) {
lateinit var license: License
init {
itemView.tag = this
} }
private fun onClick(license: License) { fun bind(license: License) {
LicenseBottomSheetDialogFragment.newInstance(license) this.license = license
.show(activity.supportFragmentManager, LicenseBottomSheetDialogFragment.TAG)
val context = YuzuApplication.appContext
binding.textSettingName.text = context.getString(license.titleId)
binding.textSettingDescription.text = context.getString(license.descriptionId)
binding.textSettingValue.visibility = View.GONE
} }
} }
} }

View File

@ -10,6 +10,7 @@ import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import org.yuzu.yuzu_emu.databinding.PageSetupBinding import org.yuzu.yuzu_emu.databinding.PageSetupBinding
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
@ -17,19 +18,31 @@ import org.yuzu.yuzu_emu.model.SetupCallback
import org.yuzu.yuzu_emu.model.SetupPage import org.yuzu.yuzu_emu.model.SetupPage
import org.yuzu.yuzu_emu.model.StepState import org.yuzu.yuzu_emu.model.StepState
import org.yuzu.yuzu_emu.utils.ViewUtils import org.yuzu.yuzu_emu.utils.ViewUtils
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) : class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) :
AbstractListAdapter<SetupPage, SetupAdapter.SetupPageViewHolder>(pages) { RecyclerView.Adapter<SetupAdapter.SetupPageViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SetupPageViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SetupPageViewHolder {
PageSetupBinding.inflate(LayoutInflater.from(parent.context), parent, false) val binding = PageSetupBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.also { return SetupPageViewHolder(it) } return SetupPageViewHolder(binding)
} }
override fun getItemCount(): Int = pages.size
override fun onBindViewHolder(holder: SetupPageViewHolder, position: Int) =
holder.bind(pages[position])
inner class SetupPageViewHolder(val binding: PageSetupBinding) : inner class SetupPageViewHolder(val binding: PageSetupBinding) :
AbstractViewHolder<SetupPage>(binding), SetupCallback { RecyclerView.ViewHolder(binding.root), SetupCallback {
override fun bind(model: SetupPage) { lateinit var page: SetupPage
if (model.stepCompleted.invoke() == StepState.COMPLETE) {
init {
itemView.tag = this
}
fun bind(page: SetupPage) {
this.page = page
if (page.stepCompleted.invoke() == StepState.COMPLETE) {
binding.buttonAction.visibility = View.INVISIBLE binding.buttonAction.visibility = View.INVISIBLE
binding.textConfirmation.visibility = View.VISIBLE binding.textConfirmation.visibility = View.VISIBLE
} }
@ -37,31 +50,31 @@ class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
binding.icon.setImageDrawable( binding.icon.setImageDrawable(
ResourcesCompat.getDrawable( ResourcesCompat.getDrawable(
activity.resources, activity.resources,
model.iconId, page.iconId,
activity.theme activity.theme
) )
) )
binding.textTitle.text = activity.resources.getString(model.titleId) binding.textTitle.text = activity.resources.getString(page.titleId)
binding.textDescription.text = binding.textDescription.text =
Html.fromHtml(activity.resources.getString(model.descriptionId), 0) Html.fromHtml(activity.resources.getString(page.descriptionId), 0)
binding.buttonAction.apply { binding.buttonAction.apply {
text = activity.resources.getString(model.buttonTextId) text = activity.resources.getString(page.buttonTextId)
if (model.buttonIconId != 0) { if (page.buttonIconId != 0) {
icon = ResourcesCompat.getDrawable( icon = ResourcesCompat.getDrawable(
activity.resources, activity.resources,
model.buttonIconId, page.buttonIconId,
activity.theme activity.theme
) )
} }
iconGravity = iconGravity =
if (model.leftAlignedIcon) { if (page.leftAlignedIcon) {
MaterialButton.ICON_GRAVITY_START MaterialButton.ICON_GRAVITY_START
} else { } else {
MaterialButton.ICON_GRAVITY_END MaterialButton.ICON_GRAVITY_END
} }
setOnClickListener { setOnClickListener {
model.buttonAction.invoke(this@SetupPageViewHolder) page.buttonAction.invoke(this@SetupPageViewHolder)
} }
} }
} }

View File

@ -76,8 +76,8 @@ class AboutFragment : Fragment() {
binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment) binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment)
} }
binding.textVersionName.text = BuildConfig.VERSION_NAME binding.textBuildHash.text = BuildConfig.GIT_HASH
binding.textVersionName.setOnClickListener { binding.buttonBuildHash.setOnClickListener {
val clipBoard = val clipBoard =
requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText(getString(R.string.build), BuildConfig.GIT_HASH) val clip = ClipData.newPlainText(getString(R.string.build), BuildConfig.GIT_HASH)

View File

@ -3,7 +3,6 @@
package org.yuzu.yuzu_emu.fragments package org.yuzu.yuzu_emu.fragments
import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -14,26 +13,20 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.DriverAdapter import org.yuzu.yuzu_emu.adapters.DriverAdapter
import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver
import org.yuzu.yuzu_emu.model.DriverViewModel import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.NativeConfig
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
@ -62,43 +55,12 @@ class DriverManagerFragment : Fragment() {
return binding.root return binding.root
} }
// This is using the correct scope, lint is just acting up
@SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
homeViewModel.setNavigationVisibility(visible = false, animated = true) homeViewModel.setNavigationVisibility(visible = false, animated = true)
homeViewModel.setStatusBarShadeVisibility(visible = false) homeViewModel.setStatusBarShadeVisibility(visible = false)
driverViewModel.onOpenDriverManager(args.game) driverViewModel.onOpenDriverManager(args.game)
if (NativeConfig.isPerGameConfigLoaded()) {
binding.toolbarDrivers.inflateMenu(R.menu.menu_driver_manager)
driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
binding.toolbarDrivers.setOnMenuItemClickListener {
when (it.itemId) {
R.id.menu_driver_clear -> {
StringSetting.DRIVER_PATH.global = true
driverViewModel.updateDriverList()
(binding.listDrivers.adapter as DriverAdapter)
.replaceList(driverViewModel.driverList.value)
driverViewModel.showClearButton(false)
true
}
else -> false
}
}
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
driverViewModel.showClearButton.collect {
binding.toolbarDrivers.menu
.findItem(R.id.menu_driver_clear).isVisible = it
}
}
}
}
}
if (!driverViewModel.isInteractionAllowed.value) { if (!driverViewModel.isInteractionAllowed.value) {
DriversLoadingDialogFragment().show( DriversLoadingDialogFragment().show(
@ -123,6 +85,25 @@ class DriverManagerFragment : Fragment() {
adapter = DriverAdapter(driverViewModel) adapter = DriverAdapter(driverViewModel)
} }
viewLifecycleOwner.lifecycleScope.apply {
launch {
driverViewModel.driverList.collectLatest {
(binding.listDrivers.adapter as DriverAdapter).submitList(it)
}
}
launch {
driverViewModel.newDriverInstalled.collect {
if (_binding != null && it) {
(binding.listDrivers.adapter as DriverAdapter).apply {
notifyItemChanged(driverViewModel.previouslySelectedDriver)
notifyItemChanged(driverViewModel.selectedDriver)
driverViewModel.setNewDriverInstalled(false)
}
}
}
}
}
setInsets() setInsets()
} }
@ -179,7 +160,7 @@ class DriverManagerFragment : Fragment() {
false false
) { ) {
val driverPath = val driverPath =
"${GpuDriverHelper.driverStoragePath}${FileUtil.getFilename(result)}" "${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}"
val driverFile = File(driverPath) val driverFile = File(driverPath)
// Ignore file exceptions when a user selects an invalid zip // Ignore file exceptions when a user selects an invalid zip
@ -196,21 +177,12 @@ class DriverManagerFragment : Fragment() {
val driverData = GpuDriverHelper.getMetadataFromZip(driverFile) val driverData = GpuDriverHelper.getMetadataFromZip(driverFile)
val driverInList = val driverInList =
driverViewModel.driverData.firstOrNull { it.second == driverData } driverViewModel.driverList.value.firstOrNull { it.second == driverData }
if (driverInList != null) { if (driverInList != null) {
return@newInstance getString(R.string.driver_already_installed) return@newInstance getString(R.string.driver_already_installed)
} else { } else {
driverViewModel.onDriverAdded(Pair(driverPath, driverData)) driverViewModel.addDriver(Pair(driverPath, driverData))
withContext(Dispatchers.Main) { driverViewModel.setNewDriverInstalled(true)
if (_binding != null) {
val adapter = binding.listDrivers.adapter as DriverAdapter
adapter.addItem(driverData.toDriver())
adapter.selectItem(adapter.currentList.indices.last)
driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
binding.listDrivers
.smoothScrollToPosition(adapter.currentList.indices.last)
}
}
} }
return@newInstance Any() return@newInstance Any()
}.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG) }.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG)

View File

@ -1,27 +0,0 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.model
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
data class Driver(
override var selected: Boolean,
val title: String,
val version: String = "",
val description: String = ""
) : SelectableItem {
override fun onSelectionStateChanged(selected: Boolean) {
this.selected = selected
}
companion object {
fun GpuDriverMetadata.toDriver(selected: Boolean = false): Driver =
Driver(
selected,
this.name ?: "",
this.version ?: "",
this.description ?: ""
)
}
}

View File

@ -9,7 +9,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -18,10 +17,11 @@ import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.StringSetting import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.NativeConfig
import java.io.BufferedOutputStream
import java.io.File import java.io.File
class DriverViewModel : ViewModel() { class DriverViewModel : ViewModel() {
@ -38,81 +38,97 @@ class DriverViewModel : ViewModel() {
!loading && ready && !deleting !loading && ready && !deleting
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), initialValue = false) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), initialValue = false)
var driverData = GpuDriverHelper.getDrivers() private val _driverList = MutableStateFlow(GpuDriverHelper.getDrivers())
val driverList: StateFlow<MutableList<Pair<String, GpuDriverMetadata>>> get() = _driverList
private val _driverList = MutableStateFlow(emptyList<Driver>()) var previouslySelectedDriver = 0
val driverList: StateFlow<List<Driver>> get() = _driverList var selectedDriver = -1
// Used for showing which driver is currently installed within the driver manager card // Used for showing which driver is currently installed within the driver manager card
private val _selectedDriverTitle = MutableStateFlow("") private val _selectedDriverTitle = MutableStateFlow("")
val selectedDriverTitle: StateFlow<String> get() = _selectedDriverTitle val selectedDriverTitle: StateFlow<String> get() = _selectedDriverTitle
private val _showClearButton = MutableStateFlow(false) private val _newDriverInstalled = MutableStateFlow(false)
val showClearButton = _showClearButton.asStateFlow() val newDriverInstalled: StateFlow<Boolean> get() = _newDriverInstalled
private val driversToDelete = mutableListOf<String>() val driversToDelete = mutableListOf<String>()
init { init {
updateDriverList() val currentDriverMetadata = GpuDriverHelper.installedCustomDriverData
findSelectedDriver(currentDriverMetadata)
// If a user had installed a driver before the manager was implemented, this zips
// the installed driver to UserData/gpu_drivers/CustomDriver.zip so that it can
// be indexed and exported as expected.
if (selectedDriver == -1) {
val driverToSave =
File(GpuDriverHelper.driverStoragePath, "CustomDriver.zip")
driverToSave.createNewFile()
FileUtil.zipFromInternalStorage(
File(GpuDriverHelper.driverInstallationPath!!),
GpuDriverHelper.driverInstallationPath!!,
BufferedOutputStream(driverToSave.outputStream())
)
_driverList.value.add(Pair(driverToSave.path, currentDriverMetadata))
setSelectedDriverIndex(_driverList.value.size - 1)
}
// If a user had installed a driver before the config was reworked to be multiplatform,
// we have save the path of the previously selected driver to the new setting.
if (StringSetting.DRIVER_PATH.getString(true).isEmpty() && selectedDriver > 0 &&
StringSetting.DRIVER_PATH.global
) {
StringSetting.DRIVER_PATH.setString(_driverList.value[selectedDriver].first)
NativeConfig.saveGlobalConfig()
} else {
findSelectedDriver(GpuDriverHelper.customDriverSettingData)
}
updateDriverNameForGame(null) updateDriverNameForGame(null)
} }
fun reloadDriverData() { fun setSelectedDriverIndex(value: Int) {
_areDriversLoading.value = true if (selectedDriver != -1) {
driverData = GpuDriverHelper.getDrivers() previouslySelectedDriver = selectedDriver
updateDriverList() }
_areDriversLoading.value = false selectedDriver = value
} }
fun updateDriverList() { fun setNewDriverInstalled(value: Boolean) {
val selectedDriver = GpuDriverHelper.customDriverSettingData _newDriverInstalled.value = value
val newDriverList = mutableListOf( }
Driver(
selectedDriver == GpuDriverMetadata(), fun addDriver(driverData: Pair<String, GpuDriverMetadata>) {
YuzuApplication.appContext.getString(R.string.system_gpu_driver) val driverIndex = _driverList.value.indexOfFirst { it == driverData }
) if (driverIndex == -1) {
) _driverList.value.add(driverData)
driverData.forEach { setSelectedDriverIndex(_driverList.value.size - 1)
newDriverList.add(it.second.toDriver(it.second == selectedDriver)) _selectedDriverTitle.value = driverData.second.name
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
} else {
setSelectedDriverIndex(driverIndex)
} }
_driverList.value = newDriverList }
fun removeDriver(driverData: Pair<String, GpuDriverMetadata>) {
_driverList.value.remove(driverData)
} }
fun onOpenDriverManager(game: Game?) { fun onOpenDriverManager(game: Game?) {
if (game != null) { if (game != null) {
SettingsFile.loadCustomConfig(game) SettingsFile.loadCustomConfig(game)
} }
updateDriverList()
}
fun showClearButton(value: Boolean) { val driverPath = StringSetting.DRIVER_PATH.getString()
_showClearButton.value = value if (driverPath.isEmpty()) {
} setSelectedDriverIndex(0)
fun onDriverSelected(position: Int) {
if (position == 0) {
StringSetting.DRIVER_PATH.setString("")
} else { } else {
StringSetting.DRIVER_PATH.setString(driverData[position - 1].first) findSelectedDriver(GpuDriverHelper.getMetadataFromZip(File(driverPath)))
} }
} }
fun onDriverRemoved(removedPosition: Int, selectedPosition: Int) {
driversToDelete.add(driverData[removedPosition - 1].first)
driverData.removeAt(removedPosition - 1)
onDriverSelected(selectedPosition)
}
fun onDriverAdded(driver: Pair<String, GpuDriverMetadata>) {
if (driversToDelete.contains(driver.first)) {
driversToDelete.remove(driver.first)
}
driverData.add(driver)
onDriverSelected(driverData.size)
}
fun onCloseDriverManager(game: Game?) { fun onCloseDriverManager(game: Game?) {
_isDeletingDrivers.value = true _isDeletingDrivers.value = true
StringSetting.DRIVER_PATH.setString(driverList.value[selectedDriver].first)
updateDriverNameForGame(game) updateDriverNameForGame(game)
if (game == null) { if (game == null) {
NativeConfig.saveGlobalConfig() NativeConfig.saveGlobalConfig()
@ -165,6 +181,20 @@ class DriverViewModel : ViewModel() {
} }
} }
private fun findSelectedDriver(currentDriverMetadata: GpuDriverMetadata) {
if (driverList.value.size == 1) {
setSelectedDriverIndex(0)
return
}
driverList.value.forEachIndexed { i: Int, driver: Pair<String, GpuDriverMetadata> ->
if (driver.second == currentDriverMetadata) {
setSelectedDriverIndex(i)
return
}
}
}
fun updateDriverNameForGame(game: Game?) { fun updateDriverNameForGame(game: Game?) {
if (!GpuDriverHelper.supportsCustomDriverLoading()) { if (!GpuDriverHelper.supportsCustomDriverLoading()) {
return return
@ -187,6 +217,7 @@ class DriverViewModel : ViewModel() {
private fun setDriverReady() { private fun setDriverReady() {
_isDriverReady.value = true _isDriverReady.value = true
updateName() _selectedDriverTitle.value = GpuDriverHelper.customDriverSettingData.name
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
} }
} }

View File

@ -1,9 +0,0 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.model
interface SelectableItem {
var selected: Boolean
fun onSelectionStateChanged(selected: Boolean)
}

View File

@ -41,7 +41,6 @@ import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.model.AddonViewModel import org.yuzu.yuzu_emu.model.AddonViewModel
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.TaskState import org.yuzu.yuzu_emu.model.TaskState
@ -59,7 +58,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
private val gamesViewModel: GamesViewModel by viewModels() private val gamesViewModel: GamesViewModel by viewModels()
private val taskViewModel: TaskViewModel by viewModels() private val taskViewModel: TaskViewModel by viewModels()
private val addonViewModel: AddonViewModel by viewModels() private val addonViewModel: AddonViewModel by viewModels()
private val driverViewModel: DriverViewModel by viewModels()
override var themeId: Int = 0 override var themeId: Int = 0
@ -691,7 +689,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
NativeLibrary.initializeSystem(true) NativeLibrary.initializeSystem(true)
NativeConfig.initializeGlobalConfig() NativeConfig.initializeGlobalConfig()
gamesViewModel.reloadGames(false) gamesViewModel.reloadGames(false)
driverViewModel.reloadDriverData()
return@newInstance getString(R.string.user_data_import_success) return@newInstance getString(R.string.user_data_import_success)
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)

View File

@ -104,7 +104,7 @@ object FileUtil {
/** /**
* Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow * Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow
* This function will be faster than DocumentFile.listFiles * This function will be faster than DoucmentFile.listFiles
* @param uri Directory uri. * @param uri Directory uri.
* @return CheapDocument lists. * @return CheapDocument lists.
*/ */

View File

@ -62,6 +62,9 @@ object GpuDriverHelper {
?.sortedByDescending { it: Pair<String, GpuDriverMetadata> -> it.second.name } ?.sortedByDescending { it: Pair<String, GpuDriverMetadata> -> it.second.name }
?.distinct() ?.distinct()
?.toMutableList() ?: mutableListOf() ?.toMutableList() ?: mutableListOf()
// TODO: Get system driver information
drivers.add(0, Pair("", GpuDriverMetadata()))
return drivers return drivers
} }

View File

@ -1,18 +0,0 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.viewholder
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import org.yuzu.yuzu_emu.adapters.AbstractDiffAdapter
import org.yuzu.yuzu_emu.adapters.AbstractListAdapter
/**
* [RecyclerView.ViewHolder] meant to work together with a [AbstractDiffAdapter] or a
* [AbstractListAdapter] so we can run [bind] on each list item without needing a manual hookup.
*/
abstract class AbstractViewHolder<Model>(binding: ViewBinding) :
RecyclerView.ViewHolder(binding.root) {
abstract fun bind(model: Model)
}

View File

@ -21,7 +21,7 @@ void AndroidConfig::ReloadAllValues() {
} }
void AndroidConfig::SaveAllValues() { void AndroidConfig::SaveAllValues() {
SaveValues(); Save();
SaveAndroidValues(); SaveAndroidValues();
} }

View File

@ -410,8 +410,8 @@ void EmulationSession::OnGamepadConnectEvent([[maybe_unused]] int index) {
jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey); handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey); controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
handheld->Disconnect(); handheld->Disconnect();
} }
} }
@ -770,8 +770,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv*
ASSERT(user_id); ASSERT(user_id);
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
{}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, 1, EmulationSession::GetInstance().System(), vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser,
user_id->AsU128(), 0); FileSys::SaveDataType::SaveData, 1, user_id->AsU128(), 0);
const auto full_path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path); const auto full_path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path);
if (!Common::FS::CreateParentDirs(full_path)) { if (!Common::FS::CreateParentDirs(full_path)) {
@ -878,7 +878,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j
FileSys::Mode::Read); FileSys::Mode::Read);
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
{}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, system, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
program_id, user_id->AsU128(), 0); program_id, user_id->AsU128(), 0);
return ToJString(env, user_save_data_path); return ToJString(env, user_save_data_path);
} }

View File

@ -205,7 +205,7 @@ jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsRuntimeModifiable(JNIEn
jstring jkey) { jstring jkey) {
auto setting = getSetting<std::string>(env, jkey); auto setting = getSetting<std::string>(env, jkey);
if (setting != nullptr) { if (setting != nullptr) {
return setting->RuntimeModifiable(); return setting->RuntimeModfiable();
} }
return true; return true;
} }

View File

@ -147,7 +147,7 @@
android:layout_marginHorizontal="20dp" /> android:layout_marginHorizontal="20dp" />
<LinearLayout <LinearLayout
android:id="@+id/button_version_name" android:id="@+id/button_build_hash"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
@ -164,7 +164,7 @@
android:textAlignment="viewStart" /> android:textAlignment="viewStart" />
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/text_version_name" android:id="@+id/text_build_hash"
style="@style/TextAppearance.Material3.BodyMedium" style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -148,7 +148,7 @@
android:layout_marginHorizontal="20dp" /> android:layout_marginHorizontal="20dp" />
<LinearLayout <LinearLayout
android:id="@+id/button_version_name" android:id="@+id/button_build_hash"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingVertical="16dp" android:paddingVertical="16dp"
@ -165,7 +165,7 @@
android:text="@string/build" /> android:text="@string/build" />
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/text_version_name" android:id="@+id/text_build_hash"
style="@style/TextAppearance.Material3.BodyMedium" style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_driver_clear"
android:icon="@drawable/ic_clear"
android:title="@string/clear"
app:showAsAction="always" />
</menu>

View File

@ -29,7 +29,7 @@
<item>@string/language_dutch</item> <item>@string/language_dutch</item>
<item>@string/language_english</item> <item>@string/language_english</item>
<item>@string/language_french</item> <item>@string/language_french</item>
<item>@string/language_german</item> <item>@string/langauge_german</item>
<item>@string/language_italian</item> <item>@string/language_italian</item>
<item>@string/language_japanese</item> <item>@string/language_japanese</item>
<item>@string/language_korean</item> <item>@string/language_korean</item>
@ -228,10 +228,10 @@
<item>R</item> <item>R</item>
<item>ZL</item> <item>ZL</item>
<item>ZR</item> <item>ZR</item>
<item>L3</item>
<item>R3</item>
<item>@string/gamepad_left_stick</item> <item>@string/gamepad_left_stick</item>
<item>@string/gamepad_right_stick</item> <item>@string/gamepad_right_stick</item>
<item>L3</item>
<item>R3</item>
<item>@string/gamepad_d_pad</item> <item>@string/gamepad_d_pad</item>
</string-array> </string-array>

View File

@ -410,7 +410,7 @@
<string name="language_japanese" translatable="false">日本語</string> <string name="language_japanese" translatable="false">日本語</string>
<string name="language_english" translatable="false">English</string> <string name="language_english" translatable="false">English</string>
<string name="language_french" translatable="false">Français</string> <string name="language_french" translatable="false">Français</string>
<string name="language_german" translatable="false">Deutsch</string> <string name="langauge_german" translatable="false">Deutsch</string>
<string name="language_italian" translatable="false">Italiano</string> <string name="language_italian" translatable="false">Italiano</string>
<string name="language_spanish" translatable="false">Español</string> <string name="language_spanish" translatable="false">Español</string>
<string name="language_chinese" translatable="false">简体中文</string> <string name="language_chinese" translatable="false">简体中文</string>

View File

@ -11,7 +11,7 @@ ADSP::ADSP(Core::System& system, Sink::Sink& sink) {
opus_decoder = std::make_unique<OpusDecoder::OpusDecoder>(system); opus_decoder = std::make_unique<OpusDecoder::OpusDecoder>(system);
opus_decoder->Send(Direction::DSP, OpusDecoder::Message::Start); opus_decoder->Send(Direction::DSP, OpusDecoder::Message::Start);
if (opus_decoder->Receive(Direction::Host) != OpusDecoder::Message::StartOK) { if (opus_decoder->Receive(Direction::Host) != OpusDecoder::Message::StartOK) {
LOG_ERROR(Service_Audio, "OpusDecoder failed to initialize."); LOG_ERROR(Service_Audio, "OpusDeocder failed to initialize.");
return; return;
} }
} }

View File

@ -11,8 +11,6 @@
#include "core/guest_memory.h" #include "core/guest_memory.h"
#include "core/memory.h" #include "core/memory.h"
#include "core/hle/kernel/k_process.h"
namespace AudioCore { namespace AudioCore {
using namespace std::literals; using namespace std::literals;
@ -28,7 +26,7 @@ DeviceSession::~DeviceSession() {
} }
Result DeviceSession::Initialize(std::string_view name_, SampleFormat sample_format_, Result DeviceSession::Initialize(std::string_view name_, SampleFormat sample_format_,
u16 channel_count_, size_t session_id_, Kernel::KProcess* handle_, u16 channel_count_, size_t session_id_, u32 handle_,
u64 applet_resource_user_id_, Sink::StreamType type_) { u64 applet_resource_user_id_, Sink::StreamType type_) {
if (stream) { if (stream) {
Finalize(); Finalize();
@ -39,7 +37,6 @@ Result DeviceSession::Initialize(std::string_view name_, SampleFormat sample_for
channel_count = channel_count_; channel_count = channel_count_;
session_id = session_id_; session_id = session_id_;
handle = handle_; handle = handle_;
handle->Open();
applet_resource_user_id = applet_resource_user_id_; applet_resource_user_id = applet_resource_user_id_;
if (type == Sink::StreamType::In) { if (type == Sink::StreamType::In) {
@ -58,11 +55,6 @@ void DeviceSession::Finalize() {
sink->CloseStream(stream); sink->CloseStream(stream);
stream = nullptr; stream = nullptr;
} }
if (handle) {
handle->Close();
handle = nullptr;
}
} }
void DeviceSession::Start() { void DeviceSession::Start() {
@ -100,7 +92,7 @@ void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) {
stream->AppendBuffer(new_buffer, tmp_samples); stream->AppendBuffer(new_buffer, tmp_samples);
} else { } else {
Core::Memory::CpuGuestMemory<s16, Core::Memory::GuestMemoryFlags::UnsafeRead> samples( Core::Memory::CpuGuestMemory<s16, Core::Memory::GuestMemoryFlags::UnsafeRead> samples(
handle->GetMemory(), buffer.samples, buffer.size / sizeof(s16)); system.ApplicationMemory(), buffer.samples, buffer.size / sizeof(s16));
stream->AppendBuffer(new_buffer, samples); stream->AppendBuffer(new_buffer, samples);
} }
} }
@ -109,7 +101,7 @@ void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) {
void DeviceSession::ReleaseBuffer(const AudioBuffer& buffer) const { void DeviceSession::ReleaseBuffer(const AudioBuffer& buffer) const {
if (type == Sink::StreamType::In) { if (type == Sink::StreamType::In) {
auto samples{stream->ReleaseBuffer(buffer.size / sizeof(s16))}; auto samples{stream->ReleaseBuffer(buffer.size / sizeof(s16))};
handle->GetMemory().WriteBlockUnsafe(buffer.samples, samples.data(), buffer.size); system.ApplicationMemory().WriteBlockUnsafe(buffer.samples, samples.data(), buffer.size);
} }
} }

View File

@ -20,10 +20,6 @@ struct EventType;
} // namespace Timing } // namespace Timing
} // namespace Core } // namespace Core
namespace Kernel {
class KProcess;
} // namespace Kernel
namespace AudioCore { namespace AudioCore {
namespace Sink { namespace Sink {
@ -48,13 +44,13 @@ public:
* @param sample_format - Sample format for this device's output. * @param sample_format - Sample format for this device's output.
* @param channel_count - Number of channels for this device (2 or 6). * @param channel_count - Number of channels for this device (2 or 6).
* @param session_id - This session's id. * @param session_id - This session's id.
* @param handle - Process handle for this device session. * @param handle - Handle for this device session (unused).
* @param applet_resource_user_id - Applet resource user id for this device session (unused). * @param applet_resource_user_id - Applet resource user id for this device session (unused).
* @param type - Type of this stream (Render, In, Out). * @param type - Type of this stream (Render, In, Out).
* @return Result code for this call. * @return Result code for this call.
*/ */
Result Initialize(std::string_view name, SampleFormat sample_format, u16 channel_count, Result Initialize(std::string_view name, SampleFormat sample_format, u16 channel_count,
size_t session_id, Kernel::KProcess* handle, u64 applet_resource_user_id, size_t session_id, u32 handle, u64 applet_resource_user_id,
Sink::StreamType type); Sink::StreamType type);
/** /**
@ -141,8 +137,8 @@ private:
u16 channel_count{}; u16 channel_count{};
/// Session id of this device session /// Session id of this device session
size_t session_id{}; size_t session_id{};
/// Process handle of device memory owner /// Handle of this device session
Kernel::KProcess* handle{}; u32 handle{};
/// Applet resource user id of this device session /// Applet resource user id of this device session
u64 applet_resource_user_id{}; u64 applet_resource_user_id{};
/// Total number of samples played by this device session /// Total number of samples played by this device session

View File

@ -57,7 +57,7 @@ Result System::IsConfigValid(const std::string_view device_name,
} }
Result System::Initialize(std::string device_name, const AudioInParameter& in_params, Result System::Initialize(std::string device_name, const AudioInParameter& in_params,
Kernel::KProcess* handle_, const u64 applet_resource_user_id_) { const u32 handle_, const u64 applet_resource_user_id_) {
auto result{IsConfigValid(device_name, in_params)}; auto result{IsConfigValid(device_name, in_params)};
if (result.IsError()) { if (result.IsError()) {
return result; return result;

View File

@ -19,8 +19,7 @@ class System;
namespace Kernel { namespace Kernel {
class KEvent; class KEvent;
class KProcess; }
} // namespace Kernel
namespace AudioCore::AudioIn { namespace AudioCore::AudioIn {
@ -94,12 +93,12 @@ public:
* *
* @param device_name - The name of the requested input device. * @param device_name - The name of the requested input device.
* @param in_params - Input parameters, see AudioInParameter. * @param in_params - Input parameters, see AudioInParameter.
* @param handle - Process handle. * @param handle - Unused.
* @param applet_resource_user_id - Unused. * @param applet_resource_user_id - Unused.
* @return Result code. * @return Result code.
*/ */
Result Initialize(std::string device_name, const AudioInParameter& in_params, Result Initialize(std::string device_name, const AudioInParameter& in_params, u32 handle,
Kernel::KProcess* handle, u64 applet_resource_user_id); u64 applet_resource_user_id);
/** /**
* Start this system. * Start this system.
@ -245,8 +244,8 @@ public:
private: private:
/// Core system /// Core system
Core::System& system; Core::System& system;
/// Process handle /// (Unused)
Kernel::KProcess* handle{}; u32 handle{};
/// (Unused) /// (Unused)
u64 applet_resource_user_id{}; u64 applet_resource_user_id{};
/// Buffer event, signalled when a buffer is ready /// Buffer event, signalled when a buffer is ready

View File

@ -48,8 +48,8 @@ Result System::IsConfigValid(std::string_view device_name,
return Service::Audio::ResultInvalidChannelCount; return Service::Audio::ResultInvalidChannelCount;
} }
Result System::Initialize(std::string device_name, const AudioOutParameter& in_params, Result System::Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle_,
Kernel::KProcess* handle_, u64 applet_resource_user_id_) { u64 applet_resource_user_id_) {
auto result = IsConfigValid(device_name, in_params); auto result = IsConfigValid(device_name, in_params);
if (result.IsError()) { if (result.IsError()) {
return result; return result;

View File

@ -19,8 +19,7 @@ class System;
namespace Kernel { namespace Kernel {
class KEvent; class KEvent;
class KProcess; }
} // namespace Kernel
namespace AudioCore::AudioOut { namespace AudioCore::AudioOut {
@ -85,12 +84,12 @@ public:
* *
* @param device_name - The name of the requested output device. * @param device_name - The name of the requested output device.
* @param in_params - Input parameters, see AudioOutParameter. * @param in_params - Input parameters, see AudioOutParameter.
* @param handle - Process handle. * @param handle - Unused.
* @param applet_resource_user_id - Unused. * @param applet_resource_user_id - Unused.
* @return Result code. * @return Result code.
*/ */
Result Initialize(std::string device_name, const AudioOutParameter& in_params, Result Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle,
Kernel::KProcess* handle, u64 applet_resource_user_id); u64 applet_resource_user_id);
/** /**
* Start this system. * Start this system.
@ -229,8 +228,8 @@ public:
private: private:
/// Core system /// Core system
Core::System& system; Core::System& system;
/// Process handle /// (Unused)
Kernel::KProcess* handle{}; u32 handle{};
/// (Unused) /// (Unused)
u64 applet_resource_user_id{}; u64 applet_resource_user_id{};
/// Buffer event, signalled when a buffer is ready /// Buffer event, signalled when a buffer is ready

View File

@ -41,7 +41,7 @@ void CommandGenerator::GenerateDataSourceCommand(VoiceInfo& voice_info,
const VoiceState& voice_state, const s8 channel) { const VoiceState& voice_state, const s8 channel) {
if (voice_info.mix_id == UnusedMixId) { if (voice_info.mix_id == UnusedMixId) {
if (voice_info.splitter_id != UnusedSplitterId) { if (voice_info.splitter_id != UnusedSplitterId) {
auto destination{splitter_context.GetDestinationData(voice_info.splitter_id, 0)}; auto destination{splitter_context.GetDesintationData(voice_info.splitter_id, 0)};
u32 dest_id{0}; u32 dest_id{0};
while (destination != nullptr) { while (destination != nullptr) {
if (destination->IsConfigured()) { if (destination->IsConfigured()) {
@ -55,7 +55,7 @@ void CommandGenerator::GenerateDataSourceCommand(VoiceInfo& voice_info,
} }
} }
dest_id++; dest_id++;
destination = splitter_context.GetDestinationData(voice_info.splitter_id, dest_id); destination = splitter_context.GetDesintationData(voice_info.splitter_id, dest_id);
} }
} }
} else { } else {
@ -234,7 +234,7 @@ void CommandGenerator::GenerateVoiceCommand(VoiceInfo& voice_info) {
if (voice_info.mix_id == UnusedMixId) { if (voice_info.mix_id == UnusedMixId) {
if (voice_info.splitter_id != UnusedSplitterId) { if (voice_info.splitter_id != UnusedSplitterId) {
auto i{channel}; auto i{channel};
auto destination{splitter_context.GetDestinationData(voice_info.splitter_id, i)}; auto destination{splitter_context.GetDesintationData(voice_info.splitter_id, i)};
while (destination != nullptr) { while (destination != nullptr) {
if (destination->IsConfigured()) { if (destination->IsConfigured()) {
const auto mix_id{destination->GetMixId()}; const auto mix_id{destination->GetMixId()};
@ -249,7 +249,7 @@ void CommandGenerator::GenerateVoiceCommand(VoiceInfo& voice_info) {
} }
} }
i += voice_info.channel_count; i += voice_info.channel_count;
destination = splitter_context.GetDestinationData(voice_info.splitter_id, i); destination = splitter_context.GetDesintationData(voice_info.splitter_id, i);
} }
} }
} else { } else {
@ -591,7 +591,7 @@ void CommandGenerator::GenerateMixCommands(MixInfo& mix_info) {
if (mix_info.dst_splitter_id != UnusedSplitterId) { if (mix_info.dst_splitter_id != UnusedSplitterId) {
s16 dest_id{0}; s16 dest_id{0};
auto destination{ auto destination{
splitter_context.GetDestinationData(mix_info.dst_splitter_id, dest_id)}; splitter_context.GetDesintationData(mix_info.dst_splitter_id, dest_id)};
while (destination != nullptr) { while (destination != nullptr) {
if (destination->IsConfigured()) { if (destination->IsConfigured()) {
auto splitter_mix_id{destination->GetMixId()}; auto splitter_mix_id{destination->GetMixId()};
@ -612,7 +612,7 @@ void CommandGenerator::GenerateMixCommands(MixInfo& mix_info) {
} }
dest_id++; dest_id++;
destination = destination =
splitter_context.GetDestinationData(mix_info.dst_splitter_id, dest_id); splitter_context.GetDesintationData(mix_info.dst_splitter_id, dest_id);
} }
} }
} else { } else {

View File

@ -93,7 +93,7 @@ bool MixInfo::UpdateConnection(EdgeMatrix& edge_matrix, const InParameter& in_pa
for (u32 i = 0; i < destination_count; i++) { for (u32 i = 0; i < destination_count; i++) {
auto destination{ auto destination{
splitter_context.GetDestinationData(in_params.dest_splitter_id, i)}; splitter_context.GetDesintationData(in_params.dest_splitter_id, i)};
if (destination) { if (destination) {
const auto destination_id{destination->GetMixId()}; const auto destination_id{destination->GetMixId()};

View File

@ -9,7 +9,7 @@
namespace AudioCore::Renderer { namespace AudioCore::Renderer {
SplitterDestinationData* SplitterContext::GetDestinationData(const s32 splitter_id, SplitterDestinationData* SplitterContext::GetDesintationData(const s32 splitter_id,
const s32 destination_id) { const s32 destination_id) {
return splitter_infos[splitter_id].GetData(destination_id); return splitter_infos[splitter_id].GetData(destination_id);
} }

View File

@ -42,7 +42,7 @@ public:
* @param destination_id - Destination index within the splitter. * @param destination_id - Destination index within the splitter.
* @return Pointer to the found destination. May be nullptr. * @return Pointer to the found destination. May be nullptr.
*/ */
SplitterDestinationData* GetDestinationData(s32 splitter_id, s32 destination_id); SplitterDestinationData* GetDesintationData(s32 splitter_id, s32 destination_id);
/** /**
* Get a splitter from the given index. * Get a splitter from the given index.

View File

@ -2,7 +2,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "common/page_table.h" #include "common/page_table.h"
#include "common/scope_exit.h"
namespace Common { namespace Common {
@ -12,10 +11,29 @@ PageTable::~PageTable() noexcept = default;
bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context, bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
Common::ProcessAddress address) const { Common::ProcessAddress address) const {
out_context->next_offset = GetInteger(address); // Setup invalid defaults.
out_context->next_page = address / page_size; out_entry->phys_addr = 0;
out_entry->block_size = page_size;
out_context->next_page = 0;
return this->ContinueTraversal(out_entry, out_context); // Validate that we can read the actual entry.
const auto page = address / page_size;
if (page >= backing_addr.size()) {
return false;
}
// Validate that the entry is mapped.
const auto phys_addr = backing_addr[page];
if (phys_addr == 0) {
return false;
}
// Populate the results.
out_entry->phys_addr = phys_addr + GetInteger(address);
out_context->next_page = page + 1;
out_context->next_offset = GetInteger(address) + page_size;
return true;
} }
bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const { bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const {
@ -23,12 +41,6 @@ bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* c
out_entry->phys_addr = 0; out_entry->phys_addr = 0;
out_entry->block_size = page_size; out_entry->block_size = page_size;
// Regardless of whether the page was mapped, advance on exit.
SCOPE_EXIT({
context->next_page += 1;
context->next_offset += page_size;
});
// Validate that we can read the actual entry. // Validate that we can read the actual entry.
const auto page = context->next_page; const auto page = context->next_page;
if (page >= backing_addr.size()) { if (page >= backing_addr.size()) {
@ -43,6 +55,8 @@ bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* c
// Populate the results. // Populate the results.
out_entry->phys_addr = phys_addr + context->next_offset; out_entry->phys_addr = phys_addr + context->next_offset;
context->next_page = page + 1;
context->next_offset += page_size;
return true; return true;
} }

View File

@ -35,7 +35,7 @@ bool BasicSetting::Save() const {
return save; return save;
} }
bool BasicSetting::RuntimeModifiable() const { bool BasicSetting::RuntimeModfiable() const {
return runtime_modifiable; return runtime_modifiable;
} }

View File

@ -186,7 +186,7 @@ public:
/** /**
* @returns true if the current setting can be changed while the guest is running. * @returns true if the current setting can be changed while the guest is running.
*/ */
[[nodiscard]] bool RuntimeModifiable() const; [[nodiscard]] bool RuntimeModfiable() const;
/** /**
* @returns A unique number corresponding to the setting. * @returns A unique number corresponding to the setting.

View File

@ -492,10 +492,6 @@ add_library(core STATIC
hle/service/filesystem/fsp_pr.h hle/service/filesystem/fsp_pr.h
hle/service/filesystem/fsp_srv.cpp hle/service/filesystem/fsp_srv.cpp
hle/service/filesystem/fsp_srv.h hle/service/filesystem/fsp_srv.h
hle/service/filesystem/romfs_controller.cpp
hle/service/filesystem/romfs_controller.h
hle/service/filesystem/save_data_controller.cpp
hle/service/filesystem/save_data_controller.h
hle/service/fgm/fgm.cpp hle/service/fgm/fgm.cpp
hle/service/fgm/fgm.h hle/service/fgm/fgm.h
hle/service/friend/friend.cpp hle/service/friend/friend.cpp

View File

@ -22,10 +22,14 @@ using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
constexpr size_t MaxRelativeBranch = 128_MiB; constexpr size_t MaxRelativeBranch = 128_MiB;
constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32); constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32);
Patcher::Patcher() : c(m_patch_instructions) { Patcher::Patcher() : c(m_patch_instructions) {}
// The first word of the patch section is always a branch to the first instruction of the
// module. Patcher::~Patcher() = default;
c.dw(0);
void Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
const Kernel::CodeSet::Segment& code) {
// Branch to the first instruction of the module.
this->BranchToModule(0);
// Write save context helper function. // Write save context helper function.
c.l(m_save_context); c.l(m_save_context);
@ -34,25 +38,6 @@ Patcher::Patcher() : c(m_patch_instructions) {
// Write load context helper function. // Write load context helper function.
c.l(m_load_context); c.l(m_load_context);
WriteLoadContext(); WriteLoadContext();
}
Patcher::~Patcher() = default;
bool Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
const Kernel::CodeSet::Segment& code) {
// If we have patched modules but cannot reach the new module, then it needs its own patcher.
const size_t image_size = program_image.size();
if (total_program_size + image_size > MaxRelativeBranch && total_program_size > 0) {
return false;
}
// Add a new module patch to our list
modules.emplace_back();
curr_patch = &modules.back();
// The first word of the patch section is always a branch to the first instruction of the
// module.
curr_patch->m_branch_to_module_relocations.push_back({0, 0});
// Retrieve text segment data. // Retrieve text segment data.
const auto text = std::span{program_image}.subspan(code.offset, code.size); const auto text = std::span{program_image}.subspan(code.offset, code.size);
@ -109,17 +94,16 @@ bool Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
} }
if (auto exclusive = Exclusive{inst}; exclusive.Verify()) { if (auto exclusive = Exclusive{inst}; exclusive.Verify()) {
curr_patch->m_exclusives.push_back(i); m_exclusives.push_back(i);
} }
} }
// Determine patching mode for the final relocation step // Determine patching mode for the final relocation step
total_program_size += image_size; const size_t image_size = program_image.size();
this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData; this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData;
return true;
} }
bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base, void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
const Kernel::CodeSet::Segment& code, const Kernel::CodeSet::Segment& code,
Kernel::PhysicalMemory& program_image, Kernel::PhysicalMemory& program_image,
EntryTrampolines* out_trampolines) { EntryTrampolines* out_trampolines) {
@ -136,7 +120,7 @@ bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
if (mode == PatchMode::PreText) { if (mode == PatchMode::PreText) {
rc.B(rel.patch_offset - patch_size - rel.module_offset); rc.B(rel.patch_offset - patch_size - rel.module_offset);
} else { } else {
rc.B(total_program_size - rel.module_offset + rel.patch_offset); rc.B(image_size - rel.module_offset + rel.patch_offset);
} }
}; };
@ -145,7 +129,7 @@ bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
if (mode == PatchMode::PreText) { if (mode == PatchMode::PreText) {
rc.B(patch_size - rel.patch_offset + rel.module_offset); rc.B(patch_size - rel.patch_offset + rel.module_offset);
} else { } else {
rc.B(rel.module_offset - total_program_size - rel.patch_offset); rc.B(rel.module_offset - image_size - rel.patch_offset);
} }
}; };
@ -153,7 +137,7 @@ bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
if (mode == PatchMode::PreText) { if (mode == PatchMode::PreText) {
return GetInteger(load_base) + patch_offset; return GetInteger(load_base) + patch_offset;
} else { } else {
return GetInteger(load_base) + total_program_size + patch_offset; return GetInteger(load_base) + image_size + patch_offset;
} }
}; };
@ -166,50 +150,39 @@ bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
}; };
// We are now ready to relocate! // We are now ready to relocate!
auto& patch = modules[m_relocate_module_index++]; for (const Relocation& rel : m_branch_to_patch_relocations) {
for (const Relocation& rel : patch.m_branch_to_patch_relocations) {
ApplyBranchToPatchRelocation(text_words.data() + rel.module_offset / sizeof(u32), rel); ApplyBranchToPatchRelocation(text_words.data() + rel.module_offset / sizeof(u32), rel);
} }
for (const Relocation& rel : patch.m_branch_to_module_relocations) { for (const Relocation& rel : m_branch_to_module_relocations) {
ApplyBranchToModuleRelocation(m_patch_instructions.data() + rel.patch_offset / sizeof(u32), ApplyBranchToModuleRelocation(m_patch_instructions.data() + rel.patch_offset / sizeof(u32),
rel); rel);
} }
// Rewrite PC constants and record post trampolines // Rewrite PC constants and record post trampolines
for (const Relocation& rel : patch.m_write_module_pc_relocations) { for (const Relocation& rel : m_write_module_pc_relocations) {
oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)}; oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)};
rc.dx(RebasePc(rel.module_offset)); rc.dx(RebasePc(rel.module_offset));
} }
for (const Trampoline& rel : patch.m_trampolines) { for (const Trampoline& rel : m_trampolines) {
out_trampolines->insert({RebasePc(rel.module_offset), RebasePatch(rel.patch_offset)}); out_trampolines->insert({RebasePc(rel.module_offset), RebasePatch(rel.patch_offset)});
} }
// Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not. // Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not.
// Convert to ordered to preserve this assumption. // Convert to ordered to preserve this assumption.
for (const ModuleTextAddress i : patch.m_exclusives) { for (const ModuleTextAddress i : m_exclusives) {
auto exclusive = Exclusive{text_words[i]}; auto exclusive = Exclusive{text_words[i]};
text_words[i] = exclusive.AsOrdered(); text_words[i] = exclusive.AsOrdered();
} }
// Remove the patched module size from the total. This is done so total_program_size // Copy to program image
// always represents the distance from the currently patched module to the patch section. if (this->mode == PatchMode::PreText) {
total_program_size -= image_size; std::memcpy(program_image.data(), m_patch_instructions.data(),
m_patch_instructions.size() * sizeof(u32));
// Only copy to the program image of the last module } else {
if (m_relocate_module_index == modules.size()) { program_image.resize(image_size + patch_size);
if (this->mode == PatchMode::PreText) { std::memcpy(program_image.data() + image_size, m_patch_instructions.data(),
ASSERT(image_size == total_program_size); m_patch_instructions.size() * sizeof(u32));
std::memcpy(program_image.data(), m_patch_instructions.data(),
m_patch_instructions.size() * sizeof(u32));
} else {
program_image.resize(image_size + patch_size);
std::memcpy(program_image.data() + image_size, m_patch_instructions.data(),
m_patch_instructions.size() * sizeof(u32));
}
return true;
} }
return false;
} }
size_t Patcher::GetSectionSize() const noexcept { size_t Patcher::GetSectionSize() const noexcept {
@ -349,7 +322,7 @@ void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id) {
// Write the post-SVC trampoline address, which will jump back to the guest after restoring its // Write the post-SVC trampoline address, which will jump back to the guest after restoring its
// state. // state.
curr_patch->m_trampolines.push_back({c.offset(), module_dest}); m_trampolines.push_back({c.offset(), module_dest});
// Host called this location. Save the return address so we can // Host called this location. Save the return address so we can
// unwind the stack properly when jumping back. // unwind the stack properly when jumping back.

View File

@ -31,9 +31,9 @@ public:
explicit Patcher(); explicit Patcher();
~Patcher(); ~Patcher();
bool PatchText(const Kernel::PhysicalMemory& program_image, void PatchText(const Kernel::PhysicalMemory& program_image,
const Kernel::CodeSet::Segment& code); const Kernel::CodeSet::Segment& code);
bool RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, void RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code,
Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines); Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines);
size_t GetSectionSize() const noexcept; size_t GetSectionSize() const noexcept;
@ -61,16 +61,16 @@ private:
private: private:
void BranchToPatch(uintptr_t module_dest) { void BranchToPatch(uintptr_t module_dest) {
curr_patch->m_branch_to_patch_relocations.push_back({c.offset(), module_dest}); m_branch_to_patch_relocations.push_back({c.offset(), module_dest});
} }
void BranchToModule(uintptr_t module_dest) { void BranchToModule(uintptr_t module_dest) {
curr_patch->m_branch_to_module_relocations.push_back({c.offset(), module_dest}); m_branch_to_module_relocations.push_back({c.offset(), module_dest});
c.dw(0); c.dw(0);
} }
void WriteModulePc(uintptr_t module_dest) { void WriteModulePc(uintptr_t module_dest) {
curr_patch->m_write_module_pc_relocations.push_back({c.offset(), module_dest}); m_write_module_pc_relocations.push_back({c.offset(), module_dest});
c.dx(0); c.dx(0);
} }
@ -84,22 +84,15 @@ private:
uintptr_t module_offset; ///< Offset in bytes from the start of the text section. uintptr_t module_offset; ///< Offset in bytes from the start of the text section.
}; };
struct ModulePatch {
std::vector<Trampoline> m_trampolines;
std::vector<Relocation> m_branch_to_patch_relocations{};
std::vector<Relocation> m_branch_to_module_relocations{};
std::vector<Relocation> m_write_module_pc_relocations{};
std::vector<ModuleTextAddress> m_exclusives{};
};
oaknut::VectorCodeGenerator c; oaknut::VectorCodeGenerator c;
std::vector<Trampoline> m_trampolines;
std::vector<Relocation> m_branch_to_patch_relocations{};
std::vector<Relocation> m_branch_to_module_relocations{};
std::vector<Relocation> m_write_module_pc_relocations{};
std::vector<ModuleTextAddress> m_exclusives{};
oaknut::Label m_save_context{}; oaknut::Label m_save_context{};
oaknut::Label m_load_context{}; oaknut::Label m_load_context{};
PatchMode mode{PatchMode::None}; PatchMode mode{PatchMode::None};
size_t total_program_size{};
size_t m_relocate_module_index{};
std::vector<ModulePatch> modules;
ModulePatch* curr_patch;
}; };
} // namespace Core::NCE } // namespace Core::NCE

View File

@ -414,7 +414,6 @@ struct System::Impl {
kernel.ShutdownCores(); kernel.ShutdownCores();
services.reset(); services.reset();
service_manager.reset(); service_manager.reset();
fs_controller.Reset();
cheat_engine.reset(); cheat_engine.reset();
telemetry_session.reset(); telemetry_session.reset();
time_manager.Shutdown(); time_manager.Shutdown();

View File

@ -114,7 +114,7 @@ public:
} }
Kernel::KThread* GetActiveThread() override { Kernel::KThread* GetActiveThread() override {
return state->active_thread.GetPointerUnsafe(); return state->active_thread;
} }
private: private:
@ -147,14 +147,11 @@ private:
std::scoped_lock lk{connection_lock}; std::scoped_lock lk{connection_lock};
// Find the process we are going to debug.
SetDebugProcess();
// Ensure everything is stopped. // Ensure everything is stopped.
PauseEmulation(); PauseEmulation();
// Set up the new frontend. // Set up the new frontend.
frontend = std::make_unique<GDBStub>(*this, system, debug_process.GetPointerUnsafe()); frontend = std::make_unique<GDBStub>(*this, system);
// Set the new state. This will tear down any existing state. // Set the new state. This will tear down any existing state.
state = ConnectionState{ state = ConnectionState{
@ -197,20 +194,15 @@ private:
UpdateActiveThread(); UpdateActiveThread();
if (state->info.type == SignalType::Watchpoint) { if (state->info.type == SignalType::Watchpoint) {
frontend->Watchpoint(std::addressof(*state->active_thread), frontend->Watchpoint(state->active_thread, *state->info.watchpoint);
*state->info.watchpoint);
} else { } else {
frontend->Stopped(std::addressof(*state->active_thread)); frontend->Stopped(state->active_thread);
} }
break; break;
case SignalType::ShuttingDown: case SignalType::ShuttingDown:
frontend->ShuttingDown(); frontend->ShuttingDown();
// Release members.
state->active_thread.Reset(nullptr);
debug_process.Reset(nullptr);
// Wait for emulation to shut down gracefully now. // Wait for emulation to shut down gracefully now.
state->signal_pipe.close(); state->signal_pipe.close();
state->client_socket.shutdown(boost::asio::socket_base::shutdown_both); state->client_socket.shutdown(boost::asio::socket_base::shutdown_both);
@ -230,7 +222,7 @@ private:
stopped = true; stopped = true;
PauseEmulation(); PauseEmulation();
UpdateActiveThread(); UpdateActiveThread();
frontend->Stopped(state->active_thread.GetPointerUnsafe()); frontend->Stopped(state->active_thread);
break; break;
} }
case DebuggerAction::Continue: case DebuggerAction::Continue:
@ -240,7 +232,7 @@ private:
MarkResumed([&] { MarkResumed([&] {
state->active_thread->SetStepState(Kernel::StepState::StepPending); state->active_thread->SetStepState(Kernel::StepState::StepPending);
state->active_thread->Resume(Kernel::SuspendType::Debug); state->active_thread->Resume(Kernel::SuspendType::Debug);
ResumeEmulation(state->active_thread.GetPointerUnsafe()); ResumeEmulation(state->active_thread);
}); });
break; break;
case DebuggerAction::StepThreadLocked: { case DebuggerAction::StepThreadLocked: {
@ -263,7 +255,6 @@ private:
} }
void PauseEmulation() { void PauseEmulation() {
Kernel::KScopedLightLock ll{debug_process->GetListLock()};
Kernel::KScopedSchedulerLock sl{system.Kernel()}; Kernel::KScopedSchedulerLock sl{system.Kernel()};
// Put all threads to sleep on next scheduler round. // Put all threads to sleep on next scheduler round.
@ -273,9 +264,6 @@ private:
} }
void ResumeEmulation(Kernel::KThread* except = nullptr) { void ResumeEmulation(Kernel::KThread* except = nullptr) {
Kernel::KScopedLightLock ll{debug_process->GetListLock()};
Kernel::KScopedSchedulerLock sl{system.Kernel()};
// Wake up all threads. // Wake up all threads.
for (auto& thread : ThreadList()) { for (auto& thread : ThreadList()) {
if (std::addressof(thread) == except) { if (std::addressof(thread) == except) {
@ -289,16 +277,15 @@ private:
template <typename Callback> template <typename Callback>
void MarkResumed(Callback&& cb) { void MarkResumed(Callback&& cb) {
Kernel::KScopedSchedulerLock sl{system.Kernel()};
stopped = false; stopped = false;
cb(); cb();
} }
void UpdateActiveThread() { void UpdateActiveThread() {
Kernel::KScopedLightLock ll{debug_process->GetListLock()};
auto& threads{ThreadList()}; auto& threads{ThreadList()};
for (auto& thread : threads) { for (auto& thread : threads) {
if (std::addressof(thread) == state->active_thread.GetPointerUnsafe()) { if (std::addressof(thread) == state->active_thread) {
// Thread is still alive, no need to update. // Thread is still alive, no need to update.
return; return;
} }
@ -306,18 +293,12 @@ private:
state->active_thread = std::addressof(threads.front()); state->active_thread = std::addressof(threads.front());
} }
private:
void SetDebugProcess() {
debug_process = std::move(system.Kernel().GetProcessList().back());
}
Kernel::KProcess::ThreadList& ThreadList() { Kernel::KProcess::ThreadList& ThreadList() {
return debug_process->GetThreadList(); return system.ApplicationProcess()->GetThreadList();
} }
private: private:
System& system; System& system;
Kernel::KScopedAutoObject<Kernel::KProcess> debug_process;
std::unique_ptr<DebuggerFrontend> frontend; std::unique_ptr<DebuggerFrontend> frontend;
boost::asio::io_context io_context; boost::asio::io_context io_context;
@ -329,7 +310,7 @@ private:
boost::process::async_pipe signal_pipe; boost::process::async_pipe signal_pipe;
SignalInfo info; SignalInfo info;
Kernel::KScopedAutoObject<Kernel::KThread> active_thread; Kernel::KThread* active_thread;
std::array<u8, 4096> client_data; std::array<u8, 4096> client_data;
bool pipe_data; bool pipe_data;
}; };

View File

@ -108,9 +108,9 @@ static std::string EscapeXML(std::string_view data) {
return escaped; return escaped;
} }
GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_, Kernel::KProcess* debug_process_) GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_)
: DebuggerFrontend(backend_), system{system_}, debug_process{debug_process_} { : DebuggerFrontend(backend_), system{system_} {
if (GetProcess()->Is64Bit()) { if (system.ApplicationProcess()->Is64Bit()) {
arch = std::make_unique<GDBStubA64>(); arch = std::make_unique<GDBStubA64>();
} else { } else {
arch = std::make_unique<GDBStubA32>(); arch = std::make_unique<GDBStubA32>();
@ -276,7 +276,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))}; const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))};
std::vector<u8> mem(size); std::vector<u8> mem(size);
if (GetMemory().ReadBlock(addr, mem.data(), size)) { if (system.ApplicationMemory().ReadBlock(addr, mem.data(), size)) {
// Restore any bytes belonging to replaced instructions. // Restore any bytes belonging to replaced instructions.
auto it = replaced_instructions.lower_bound(addr); auto it = replaced_instructions.lower_bound(addr);
for (; it != replaced_instructions.end() && it->first < addr + size; it++) { for (; it != replaced_instructions.end() && it->first < addr + size; it++) {
@ -310,8 +310,8 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
const auto mem_substr{std::string_view(command).substr(mem_sep)}; const auto mem_substr{std::string_view(command).substr(mem_sep)};
const auto mem{Common::HexStringToVector(mem_substr, false)}; const auto mem{Common::HexStringToVector(mem_substr, false)};
if (GetMemory().WriteBlock(addr, mem.data(), size)) { if (system.ApplicationMemory().WriteBlock(addr, mem.data(), size)) {
Core::InvalidateInstructionCacheRange(GetProcess(), addr, size); Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, size);
SendReply(GDB_STUB_REPLY_OK); SendReply(GDB_STUB_REPLY_OK);
} else { } else {
SendReply(GDB_STUB_REPLY_ERR); SendReply(GDB_STUB_REPLY_ERR);
@ -353,7 +353,7 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))}; const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
if (!GetMemory().IsValidVirtualAddressRange(addr, size)) { if (!system.ApplicationMemory().IsValidVirtualAddressRange(addr, size)) {
SendReply(GDB_STUB_REPLY_ERR); SendReply(GDB_STUB_REPLY_ERR);
return; return;
} }
@ -362,20 +362,22 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
switch (type) { switch (type) {
case BreakpointType::Software: case BreakpointType::Software:
replaced_instructions[addr] = GetMemory().Read32(addr); replaced_instructions[addr] = system.ApplicationMemory().Read32(addr);
GetMemory().Write32(addr, arch->BreakpointInstruction()); system.ApplicationMemory().Write32(addr, arch->BreakpointInstruction());
Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32)); Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, sizeof(u32));
success = true; success = true;
break; break;
case BreakpointType::WriteWatch: case BreakpointType::WriteWatch:
success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Write); success = system.ApplicationProcess()->InsertWatchpoint(addr, size,
Kernel::DebugWatchpointType::Write);
break; break;
case BreakpointType::ReadWatch: case BreakpointType::ReadWatch:
success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Read); success = system.ApplicationProcess()->InsertWatchpoint(addr, size,
Kernel::DebugWatchpointType::Read);
break; break;
case BreakpointType::AccessWatch: case BreakpointType::AccessWatch:
success = success = system.ApplicationProcess()->InsertWatchpoint(
GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite); addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
break; break;
case BreakpointType::Hardware: case BreakpointType::Hardware:
default: default:
@ -398,7 +400,7 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) {
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))}; const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
if (!GetMemory().IsValidVirtualAddressRange(addr, size)) { if (!system.ApplicationMemory().IsValidVirtualAddressRange(addr, size)) {
SendReply(GDB_STUB_REPLY_ERR); SendReply(GDB_STUB_REPLY_ERR);
return; return;
} }
@ -409,22 +411,24 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) {
case BreakpointType::Software: { case BreakpointType::Software: {
const auto orig_insn{replaced_instructions.find(addr)}; const auto orig_insn{replaced_instructions.find(addr)};
if (orig_insn != replaced_instructions.end()) { if (orig_insn != replaced_instructions.end()) {
GetMemory().Write32(addr, orig_insn->second); system.ApplicationMemory().Write32(addr, orig_insn->second);
Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32)); Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, sizeof(u32));
replaced_instructions.erase(addr); replaced_instructions.erase(addr);
success = true; success = true;
} }
break; break;
} }
case BreakpointType::WriteWatch: case BreakpointType::WriteWatch:
success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Write); success = system.ApplicationProcess()->RemoveWatchpoint(addr, size,
Kernel::DebugWatchpointType::Write);
break; break;
case BreakpointType::ReadWatch: case BreakpointType::ReadWatch:
success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Read); success = system.ApplicationProcess()->RemoveWatchpoint(addr, size,
Kernel::DebugWatchpointType::Read);
break; break;
case BreakpointType::AccessWatch: case BreakpointType::AccessWatch:
success = success = system.ApplicationProcess()->RemoveWatchpoint(
GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite); addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
break; break;
case BreakpointType::Hardware: case BreakpointType::Hardware:
default: default:
@ -462,10 +466,10 @@ void GDBStub::HandleQuery(std::string_view command) {
const auto target_xml{arch->GetTargetXML()}; const auto target_xml{arch->GetTargetXML()};
SendReply(PaginateBuffer(target_xml, command.substr(30))); SendReply(PaginateBuffer(target_xml, command.substr(30)));
} else if (command.starts_with("Offsets")) { } else if (command.starts_with("Offsets")) {
const auto main_offset = Core::FindMainModuleEntrypoint(GetProcess()); const auto main_offset = Core::FindMainModuleEntrypoint(system.ApplicationProcess());
SendReply(fmt::format("TextSeg={:x}", GetInteger(main_offset))); SendReply(fmt::format("TextSeg={:x}", GetInteger(main_offset)));
} else if (command.starts_with("Xfer:libraries:read::")) { } else if (command.starts_with("Xfer:libraries:read::")) {
auto modules = Core::FindModules(GetProcess()); auto modules = Core::FindModules(system.ApplicationProcess());
std::string buffer; std::string buffer;
buffer += R"(<?xml version="1.0"?>)"; buffer += R"(<?xml version="1.0"?>)";
@ -479,7 +483,7 @@ void GDBStub::HandleQuery(std::string_view command) {
SendReply(PaginateBuffer(buffer, command.substr(21))); SendReply(PaginateBuffer(buffer, command.substr(21)));
} else if (command.starts_with("fThreadInfo")) { } else if (command.starts_with("fThreadInfo")) {
// beginning of list // beginning of list
const auto& threads = GetProcess()->GetThreadList(); const auto& threads = system.ApplicationProcess()->GetThreadList();
std::vector<std::string> thread_ids; std::vector<std::string> thread_ids;
for (const auto& thread : threads) { for (const auto& thread : threads) {
thread_ids.push_back(fmt::format("{:x}", thread.GetThreadId())); thread_ids.push_back(fmt::format("{:x}", thread.GetThreadId()));
@ -493,7 +497,7 @@ void GDBStub::HandleQuery(std::string_view command) {
buffer += R"(<?xml version="1.0"?>)"; buffer += R"(<?xml version="1.0"?>)";
buffer += "<threads>"; buffer += "<threads>";
const auto& threads = GetProcess()->GetThreadList(); const auto& threads = system.ApplicationProcess()->GetThreadList();
for (const auto& thread : threads) { for (const auto& thread : threads) {
auto thread_name{Core::GetThreadName(&thread)}; auto thread_name{Core::GetThreadName(&thread)};
if (!thread_name) { if (!thread_name) {
@ -609,7 +613,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()}; std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
std::string reply; std::string reply;
auto* process = GetProcess(); auto* process = system.ApplicationProcess();
auto& page_table = process->GetPageTable(); auto& page_table = process->GetPageTable();
const char* commands = "Commands:\n" const char* commands = "Commands:\n"
@ -710,7 +714,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
} }
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
auto& threads{GetProcess()->GetThreadList()}; auto& threads{system.ApplicationProcess()->GetThreadList()};
for (auto& thread : threads) { for (auto& thread : threads) {
if (thread.GetThreadId() == thread_id) { if (thread.GetThreadId() == thread_id) {
return std::addressof(thread); return std::addressof(thread);
@ -779,12 +783,4 @@ void GDBStub::SendStatus(char status) {
backend.WriteToClient(buf); backend.WriteToClient(buf);
} }
Kernel::KProcess* GDBStub::GetProcess() {
return debug_process;
}
Core::Memory::Memory& GDBStub::GetMemory() {
return GetProcess()->GetMemory();
}
} // namespace Core } // namespace Core

View File

@ -12,22 +12,13 @@
#include "core/debugger/debugger_interface.h" #include "core/debugger/debugger_interface.h"
#include "core/debugger/gdbstub_arch.h" #include "core/debugger/gdbstub_arch.h"
namespace Kernel {
class KProcess;
}
namespace Core::Memory {
class Memory;
}
namespace Core { namespace Core {
class System; class System;
class GDBStub : public DebuggerFrontend { class GDBStub : public DebuggerFrontend {
public: public:
explicit GDBStub(DebuggerBackend& backend, Core::System& system, explicit GDBStub(DebuggerBackend& backend, Core::System& system);
Kernel::KProcess* debug_process);
~GDBStub() override; ~GDBStub() override;
void Connected() override; void Connected() override;
@ -51,12 +42,8 @@ private:
void SendReply(std::string_view data); void SendReply(std::string_view data);
void SendStatus(char status); void SendStatus(char status);
Kernel::KProcess* GetProcess();
Core::Memory::Memory& GetMemory();
private: private:
Core::System& system; Core::System& system;
Kernel::KProcess* debug_process;
std::unique_ptr<GDBStubArch> arch; std::unique_ptr<GDBStubArch> arch;
std::vector<char> current_command; std::vector<char> current_command;
std::map<VAddr, u32> replaced_instructions; std::map<VAddr, u32> replaced_instructions;

View File

@ -28,10 +28,6 @@ class Memory;
template <typename DTraits> template <typename DTraits>
struct DeviceMemoryManagerAllocator; struct DeviceMemoryManagerAllocator;
struct Asid {
size_t id;
};
template <typename Traits> template <typename Traits>
class DeviceMemoryManager { class DeviceMemoryManager {
using DeviceInterface = typename Traits::DeviceInterface; using DeviceInterface = typename Traits::DeviceInterface;
@ -47,14 +43,15 @@ public:
void AllocateFixed(DAddr start, size_t size); void AllocateFixed(DAddr start, size_t size);
void Free(DAddr start, size_t size); void Free(DAddr start, size_t size);
void Map(DAddr address, VAddr virtual_address, size_t size, Asid asid, bool track = false); void Map(DAddr address, VAddr virtual_address, size_t size, size_t process_id,
bool track = false);
void Unmap(DAddr address, size_t size); void Unmap(DAddr address, size_t size);
void TrackContinuityImpl(DAddr address, VAddr virtual_address, size_t size, Asid asid); void TrackContinuityImpl(DAddr address, VAddr virtual_address, size_t size, size_t process_id);
void TrackContinuity(DAddr address, VAddr virtual_address, size_t size, Asid asid) { void TrackContinuity(DAddr address, VAddr virtual_address, size_t size, size_t process_id) {
std::scoped_lock lk(mapping_guard); std::scoped_lock lk(mapping_guard);
TrackContinuityImpl(address, virtual_address, size, asid); TrackContinuityImpl(address, virtual_address, size, process_id);
} }
// Write / Read // Write / Read
@ -108,8 +105,8 @@ public:
void WriteBlock(DAddr address, const void* src_pointer, size_t size); void WriteBlock(DAddr address, const void* src_pointer, size_t size);
void WriteBlockUnsafe(DAddr address, const void* src_pointer, size_t size); void WriteBlockUnsafe(DAddr address, const void* src_pointer, size_t size);
Asid RegisterProcess(Memory::Memory* memory); size_t RegisterProcess(Memory::Memory* memory);
void UnregisterProcess(Asid id); void UnregisterProcess(size_t id);
void UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta); void UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta);
@ -166,17 +163,17 @@ private:
static constexpr size_t guest_max_as_bits = 39; static constexpr size_t guest_max_as_bits = 39;
static constexpr size_t guest_as_size = 1ULL << guest_max_as_bits; static constexpr size_t guest_as_size = 1ULL << guest_max_as_bits;
static constexpr size_t guest_mask = guest_as_size - 1ULL; static constexpr size_t guest_mask = guest_as_size - 1ULL;
static constexpr size_t asid_start_bit = guest_max_as_bits; static constexpr size_t process_id_start_bit = guest_max_as_bits;
std::pair<Asid, VAddr> ExtractCPUBacking(size_t page_index) { std::pair<size_t, VAddr> ExtractCPUBacking(size_t page_index) {
auto content = cpu_backing_address[page_index]; auto content = cpu_backing_address[page_index];
const VAddr address = content & guest_mask; const VAddr address = content & guest_mask;
const Asid asid{static_cast<size_t>(content >> asid_start_bit)}; const size_t process_id = static_cast<size_t>(content >> process_id_start_bit);
return std::make_pair(asid, address); return std::make_pair(process_id, address);
} }
void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) { void InsertCPUBacking(size_t page_index, VAddr address, size_t process_id) {
cpu_backing_address[page_index] = address | (asid.id << asid_start_bit); cpu_backing_address[page_index] = address | (process_id << process_id_start_bit);
} }
Common::VirtualBuffer<VAddr> cpu_backing_address; Common::VirtualBuffer<VAddr> cpu_backing_address;

View File

@ -215,8 +215,8 @@ void DeviceMemoryManager<Traits>::Free(DAddr start, size_t size) {
template <typename Traits> template <typename Traits>
void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size_t size, void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size_t size,
Asid asid, bool track) { size_t process_id, bool track) {
Core::Memory::Memory* process_memory = registered_processes[asid.id]; Core::Memory::Memory* process_memory = registered_processes[process_id];
size_t start_page_d = address >> Memory::YUZU_PAGEBITS; size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS; size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
std::scoped_lock lk(mapping_guard); std::scoped_lock lk(mapping_guard);
@ -229,7 +229,7 @@ void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size
} }
auto phys_addr = static_cast<u32>(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U; auto phys_addr = static_cast<u32>(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U;
compressed_physical_ptr[start_page_d + i] = phys_addr; compressed_physical_ptr[start_page_d + i] = phys_addr;
InsertCPUBacking(start_page_d + i, new_vaddress, asid); InsertCPUBacking(start_page_d + i, new_vaddress, process_id);
const u32 base_dev = compressed_device_addr[phys_addr - 1U]; const u32 base_dev = compressed_device_addr[phys_addr - 1U];
const u32 new_dev = static_cast<u32>(start_page_d + i); const u32 new_dev = static_cast<u32>(start_page_d + i);
if (base_dev == 0) [[likely]] { if (base_dev == 0) [[likely]] {
@ -244,7 +244,7 @@ void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size
impl->multi_dev_address.Register(new_dev, start_id); impl->multi_dev_address.Register(new_dev, start_id);
} }
if (track) { if (track) {
TrackContinuityImpl(address, virtual_address, size, asid); TrackContinuityImpl(address, virtual_address, size, process_id);
} }
} }
@ -277,8 +277,8 @@ void DeviceMemoryManager<Traits>::Unmap(DAddr address, size_t size) {
} }
template <typename Traits> template <typename Traits>
void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtual_address, void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtual_address,
size_t size, Asid asid) { size_t size, size_t process_id) {
Core::Memory::Memory* process_memory = registered_processes[asid.id]; Core::Memory::Memory* process_memory = registered_processes[process_id];
size_t start_page_d = address >> Memory::YUZU_PAGEBITS; size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS; size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
uintptr_t last_ptr = 0; uintptr_t last_ptr = 0;
@ -488,8 +488,8 @@ void DeviceMemoryManager<Traits>::WriteBlockUnsafe(DAddr address, const void* sr
} }
template <typename Traits> template <typename Traits>
Asid DeviceMemoryManager<Traits>::RegisterProcess(Memory::Memory* memory_device_inter) { size_t DeviceMemoryManager<Traits>::RegisterProcess(Memory::Memory* memory_device_inter) {
size_t new_id{}; size_t new_id;
if (!id_pool.empty()) { if (!id_pool.empty()) {
new_id = id_pool.front(); new_id = id_pool.front();
id_pool.pop_front(); id_pool.pop_front();
@ -498,23 +498,29 @@ Asid DeviceMemoryManager<Traits>::RegisterProcess(Memory::Memory* memory_device_
registered_processes.emplace_back(memory_device_inter); registered_processes.emplace_back(memory_device_inter);
new_id = registered_processes.size() - 1U; new_id = registered_processes.size() - 1U;
} }
return Asid{new_id}; return new_id;
} }
template <typename Traits> template <typename Traits>
void DeviceMemoryManager<Traits>::UnregisterProcess(Asid asid) { void DeviceMemoryManager<Traits>::UnregisterProcess(size_t id) {
registered_processes[asid.id] = nullptr; registered_processes[id] = nullptr;
id_pool.push_front(asid.id); id_pool.push_front(id);
} }
template <typename Traits> template <typename Traits>
void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta) { void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta) {
std::unique_lock<std::mutex> lk(counter_guard, std::defer_lock); bool locked = false;
const auto Lock = [&] { auto lock = [&] {
if (!lk) { if (!locked) {
lk.lock(); counter_guard.lock();
locked = true;
} }
}; };
SCOPE_EXIT({
if (locked) {
counter_guard.unlock();
}
});
u64 uncache_begin = 0; u64 uncache_begin = 0;
u64 cache_begin = 0; u64 cache_begin = 0;
u64 uncache_bytes = 0; u64 uncache_bytes = 0;
@ -524,9 +530,9 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
std::atomic_thread_fence(std::memory_order_acquire); std::atomic_thread_fence(std::memory_order_acquire);
const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE); const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE);
size_t page = addr >> Memory::YUZU_PAGEBITS; size_t page = addr >> Memory::YUZU_PAGEBITS;
auto [asid, base_vaddress] = ExtractCPUBacking(page); auto [process_id, base_vaddress] = ExtractCPUBacking(page);
size_t vpage = base_vaddress >> Memory::YUZU_PAGEBITS; size_t vpage = base_vaddress >> Memory::YUZU_PAGEBITS;
auto* memory_device_inter = registered_processes[asid.id]; auto* memory_device_inter = registered_processes[process_id];
for (; page != page_end; ++page) { for (; page != page_end; ++page) {
std::atomic_uint8_t& count = cached_pages->at(page >> 3).Count(page); std::atomic_uint8_t& count = cached_pages->at(page >> 3).Count(page);
@ -549,7 +555,7 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
} }
uncache_bytes += Memory::YUZU_PAGESIZE; uncache_bytes += Memory::YUZU_PAGESIZE;
} else if (uncache_bytes > 0) { } else if (uncache_bytes > 0) {
Lock(); lock();
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS,
uncache_bytes, false); uncache_bytes, false);
uncache_bytes = 0; uncache_bytes = 0;
@ -560,7 +566,7 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
} }
cache_bytes += Memory::YUZU_PAGESIZE; cache_bytes += Memory::YUZU_PAGESIZE;
} else if (cache_bytes > 0) { } else if (cache_bytes > 0) {
Lock(); lock();
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes, MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes,
true); true);
cache_bytes = 0; cache_bytes = 0;
@ -568,12 +574,12 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
vpage++; vpage++;
} }
if (uncache_bytes > 0) { if (uncache_bytes > 0) {
Lock(); lock();
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, uncache_bytes, MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, uncache_bytes,
false); false);
} }
if (cache_bytes > 0) { if (cache_bytes > 0) {
Lock(); lock();
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes, MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes,
true); true);
} }

View File

@ -97,9 +97,8 @@ std::string SaveDataAttribute::DebugInfo() const {
static_cast<u8>(rank), index); static_cast<u8>(rank), index);
} }
SaveDataFactory::SaveDataFactory(Core::System& system_, ProgramId program_id_, SaveDataFactory::SaveDataFactory(Core::System& system_, VirtualDir save_directory_)
VirtualDir save_directory_) : dir{std::move(save_directory_)}, system{system_} {
: system{system_}, program_id{program_id_}, dir{std::move(save_directory_)} {
// Delete all temporary storages // Delete all temporary storages
// On hardware, it is expected that temporary storage be empty at first use. // On hardware, it is expected that temporary storage be empty at first use.
dir->DeleteSubdirectoryRecursive("temp"); dir->DeleteSubdirectoryRecursive("temp");
@ -111,7 +110,7 @@ VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribut
PrintSaveDataAttributeWarnings(meta); PrintSaveDataAttributeWarnings(meta);
const auto save_directory = const auto save_directory =
GetFullPath(program_id, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
return dir->CreateDirectoryRelative(save_directory); return dir->CreateDirectoryRelative(save_directory);
} }
@ -119,7 +118,7 @@ VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribut
VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const { VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
const auto save_directory = const auto save_directory =
GetFullPath(program_id, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
auto out = dir->GetDirectoryRelative(save_directory); auto out = dir->GetDirectoryRelative(save_directory);
@ -148,14 +147,14 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
} }
} }
std::string SaveDataFactory::GetFullPath(ProgramId program_id, VirtualDir dir, std::string SaveDataFactory::GetFullPath(Core::System& system, VirtualDir dir,
SaveDataSpaceId space, SaveDataType type, u64 title_id, SaveDataSpaceId space, SaveDataType type, u64 title_id,
u128 user_id, u64 save_id) { u128 user_id, u64 save_id) {
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
// be interpreted as the title id of the current process. // be interpreted as the title id of the current process.
if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) { if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) {
if (title_id == 0) { if (title_id == 0) {
title_id = program_id; title_id = system.GetApplicationProcessProgramID();
} }
} }
@ -202,7 +201,7 @@ std::string SaveDataFactory::GetUserGameSaveDataRoot(u128 user_id, bool future)
SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
u128 user_id) const { u128 user_id) const {
const auto path = const auto path =
GetFullPath(program_id, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName()); const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName());
@ -221,7 +220,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
SaveDataSize new_value) const { SaveDataSize new_value) const {
const auto path = const auto path =
GetFullPath(program_id, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName()); const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName());

View File

@ -87,13 +87,10 @@ constexpr const char* GetSaveDataSizeFileName() {
return ".yuzu_save_size"; return ".yuzu_save_size";
} }
using ProgramId = u64;
/// File system interface to the SaveData archive /// File system interface to the SaveData archive
class SaveDataFactory { class SaveDataFactory {
public: public:
explicit SaveDataFactory(Core::System& system_, ProgramId program_id_, explicit SaveDataFactory(Core::System& system_, VirtualDir save_directory_);
VirtualDir save_directory_);
~SaveDataFactory(); ~SaveDataFactory();
VirtualDir Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const; VirtualDir Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const;
@ -102,7 +99,7 @@ public:
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space); static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
static std::string GetFullPath(ProgramId program_id, VirtualDir dir, SaveDataSpaceId space, static std::string GetFullPath(Core::System& system, VirtualDir dir, SaveDataSpaceId space,
SaveDataType type, u64 title_id, u128 user_id, u64 save_id); SaveDataType type, u64 title_id, u128 user_id, u64 save_id);
static std::string GetUserGameSaveDataRoot(u128 user_id, bool future); static std::string GetUserGameSaveDataRoot(u128 user_id, bool future);
@ -113,9 +110,8 @@ public:
void SetAutoCreate(bool state); void SetAutoCreate(bool state);
private: private:
Core::System& system;
ProgramId program_id;
VirtualDir dir; VirtualDir dir;
Core::System& system;
bool auto_create{true}; bool auto_create{true};
}; };

View File

@ -47,7 +47,7 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
// Connect controllers based on the following priority list from highest to lowest priority: // Connect controllers based on the following priority list from highest to lowest priority:
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
if (parameters.allow_pro_controller) { if (parameters.allow_pro_controller) {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey); controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
controller->Connect(true); controller->Connect(true);
} else if (parameters.allow_dual_joycons) { } else if (parameters.allow_dual_joycons) {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual); controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);

View File

@ -28,14 +28,14 @@ Result KMemoryBlockManager::Initialize(KProcessAddress st, KProcessAddress nd,
} }
void KMemoryBlockManager::Finalize(KMemoryBlockSlabManager* slab_manager, void KMemoryBlockManager::Finalize(KMemoryBlockSlabManager* slab_manager,
BlockCallback&& block_callback) { HostUnmapCallback&& host_unmap_callback) {
// Erase every block until we have none left. // Erase every block until we have none left.
auto it = m_memory_block_tree.begin(); auto it = m_memory_block_tree.begin();
while (it != m_memory_block_tree.end()) { while (it != m_memory_block_tree.end()) {
KMemoryBlock* block = std::addressof(*it); KMemoryBlock* block = std::addressof(*it);
it = m_memory_block_tree.erase(it); it = m_memory_block_tree.erase(it);
block_callback(block->GetAddress(), block->GetSize());
slab_manager->Free(block); slab_manager->Free(block);
host_unmap_callback(block->GetAddress(), block->GetSize());
} }
ASSERT(m_memory_block_tree.empty()); ASSERT(m_memory_block_tree.empty());

View File

@ -85,11 +85,11 @@ public:
public: public:
KMemoryBlockManager(); KMemoryBlockManager();
using BlockCallback = std::function<void(Common::ProcessAddress, u64)>; using HostUnmapCallback = std::function<void(Common::ProcessAddress, u64)>;
Result Initialize(KProcessAddress st, KProcessAddress nd, Result Initialize(KProcessAddress st, KProcessAddress nd,
KMemoryBlockSlabManager* slab_manager); KMemoryBlockSlabManager* slab_manager);
void Finalize(KMemoryBlockSlabManager* slab_manager, BlockCallback&& block_callback); void Finalize(KMemoryBlockSlabManager* slab_manager, HostUnmapCallback&& host_unmap_callback);
iterator end() { iterator end() {
return m_memory_block_tree.end(); return m_memory_block_tree.end();

View File

@ -431,43 +431,15 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
m_memory_block_slab_manager)); m_memory_block_slab_manager));
} }
Result KPageTableBase::FinalizeProcess() {
// Only process tables should be finalized.
ASSERT(!this->IsKernel());
// NOTE: Here Nintendo calls an unknown OnFinalize function.
// this->OnFinalize();
// NOTE: Here Nintendo calls a second unknown OnFinalize function.
// this->OnFinalize2();
// NOTE: Here Nintendo does a page table walk to discover heap pages to free.
// We will use the block manager finalization below to free them.
R_SUCCEED();
}
void KPageTableBase::Finalize() { void KPageTableBase::Finalize() {
this->FinalizeProcess(); auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) {
if (Settings::IsFastmemEnabled()) {
auto BlockCallback = [&](KProcessAddress addr, u64 size) {
if (m_impl->fastmem_arena) {
m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false); m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false);
} }
// Get physical pages.
KPageGroup pg(m_kernel, m_block_info_manager);
this->MakePageGroup(pg, addr, size / PageSize);
// Free the pages.
pg.CloseAndReset();
}; };
// Finalize memory blocks. // Finalize memory blocks.
{ m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(HostUnmapCallback));
KScopedLightLock lk(m_general_lock);
m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(BlockCallback));
}
// Free any unsafe mapped memory. // Free any unsafe mapped memory.
if (m_mapped_unsafe_physical_memory) { if (m_mapped_unsafe_physical_memory) {

View File

@ -241,7 +241,6 @@ public:
KResourceLimit* resource_limit, Core::Memory::Memory& memory, KResourceLimit* resource_limit, Core::Memory::Memory& memory,
KProcessAddress aslr_space_start); KProcessAddress aslr_space_start);
Result FinalizeProcess();
void Finalize(); void Finalize();
bool IsKernel() const { bool IsKernel() const {

View File

@ -172,12 +172,6 @@ void KProcess::Finalize() {
m_resource_limit->Close(); m_resource_limit->Close();
} }
// Clear expensive resources, as the destructor is not called for guest objects.
for (auto& interface : m_arm_interfaces) {
interface.reset();
}
m_exclusive_monitor.reset();
// Perform inherited finalization. // Perform inherited finalization.
KSynchronizationObject::Finalize(); KSynchronizationObject::Finalize();
} }
@ -1239,10 +1233,10 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) {
ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite);
#ifdef HAS_NCE #ifdef HAS_NCE
const auto& patch = code_set.PatchSegment(); if (this->IsApplication() && Settings::IsNceEnabled()) {
if (this->IsApplication() && Settings::IsNceEnabled() && patch.size != 0) {
auto& buffer = m_kernel.System().DeviceMemory().buffer; auto& buffer = m_kernel.System().DeviceMemory().buffer;
const auto& code = code_set.CodeSegment(); const auto& code = code_set.CodeSegment();
const auto& patch = code_set.PatchSegment();
buffer.Protect(GetInteger(base_addr + code.addr), code.size, buffer.Protect(GetInteger(base_addr + code.addr), code.size,
Common::MemoryPermission::Read | Common::MemoryPermission::Execute); Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, buffer.Protect(GetInteger(base_addr + patch.addr), patch.size,

View File

@ -112,14 +112,7 @@ struct KernelCore::Impl {
old_process->Close(); old_process->Close();
} }
{ process_list.clear();
std::scoped_lock lk{process_list_lock};
for (auto* const process : process_list) {
process->Terminate();
process->Close();
}
process_list.clear();
}
next_object_id = 0; next_object_id = 0;
next_kernel_process_id = KProcess::InitialProcessIdMin; next_kernel_process_id = KProcess::InitialProcessIdMin;
@ -777,7 +770,6 @@ struct KernelCore::Impl {
std::atomic<u64> next_thread_id{1}; std::atomic<u64> next_thread_id{1};
// Lists all processes that exist in the current session. // Lists all processes that exist in the current session.
std::mutex process_list_lock;
std::vector<KProcess*> process_list; std::vector<KProcess*> process_list;
std::atomic<KProcess*> application_process{}; std::atomic<KProcess*> application_process{};
std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
@ -877,19 +869,9 @@ KResourceLimit* KernelCore::GetSystemResourceLimit() {
} }
void KernelCore::AppendNewProcess(KProcess* process) { void KernelCore::AppendNewProcess(KProcess* process) {
process->Open();
std::scoped_lock lk{impl->process_list_lock};
impl->process_list.push_back(process); impl->process_list.push_back(process);
} }
void KernelCore::RemoveProcess(KProcess* process) {
std::scoped_lock lk{impl->process_list_lock};
if (std::erase(impl->process_list, process)) {
process->Close();
}
}
void KernelCore::MakeApplicationProcess(KProcess* process) { void KernelCore::MakeApplicationProcess(KProcess* process) {
impl->MakeApplicationProcess(process); impl->MakeApplicationProcess(process);
} }
@ -902,15 +884,8 @@ const KProcess* KernelCore::ApplicationProcess() const {
return impl->application_process; return impl->application_process;
} }
std::list<KScopedAutoObject<KProcess>> KernelCore::GetProcessList() { const std::vector<KProcess*>& KernelCore::GetProcessList() const {
std::list<KScopedAutoObject<KProcess>> processes; return impl->process_list;
std::scoped_lock lk{impl->process_list_lock};
for (auto* const process : impl->process_list) {
processes.emplace_back(process);
}
return processes;
} }
Kernel::GlobalSchedulerContext& KernelCore::GlobalSchedulerContext() { Kernel::GlobalSchedulerContext& KernelCore::GlobalSchedulerContext() {

View File

@ -5,7 +5,6 @@
#include <array> #include <array>
#include <functional> #include <functional>
#include <list>
#include <memory> #include <memory>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
@ -117,9 +116,8 @@ public:
/// Retrieves a shared pointer to the system resource limit instance. /// Retrieves a shared pointer to the system resource limit instance.
KResourceLimit* GetSystemResourceLimit(); KResourceLimit* GetSystemResourceLimit();
/// Adds/removes the given pointer to an internal list of active processes. /// Adds the given shared pointer to an internal list of active processes.
void AppendNewProcess(KProcess* process); void AppendNewProcess(KProcess* process);
void RemoveProcess(KProcess* process);
/// Makes the given process the new application process. /// Makes the given process the new application process.
void MakeApplicationProcess(KProcess* process); void MakeApplicationProcess(KProcess* process);
@ -131,7 +129,7 @@ public:
const KProcess* ApplicationProcess() const; const KProcess* ApplicationProcess() const;
/// Retrieves the list of processes. /// Retrieves the list of processes.
std::list<KScopedAutoObject<KProcess>> GetProcessList(); const std::vector<KProcess*>& GetProcessList() const;
/// Gets the sole instance of the global scheduler /// Gets the sole instance of the global scheduler
Kernel::GlobalSchedulerContext& GlobalSchedulerContext(); Kernel::GlobalSchedulerContext& GlobalSchedulerContext();

View File

@ -74,15 +74,13 @@ Result GetProcessList(Core::System& system, s32* out_num_processes, u64 out_proc
} }
auto& memory = GetCurrentMemory(kernel); auto& memory = GetCurrentMemory(kernel);
auto process_list = kernel.GetProcessList(); const auto& process_list = kernel.GetProcessList();
auto it = process_list.begin();
const auto num_processes = process_list.size(); const auto num_processes = process_list.size();
const auto copy_amount = const auto copy_amount =
std::min(static_cast<std::size_t>(out_process_ids_size), num_processes); std::min(static_cast<std::size_t>(out_process_ids_size), num_processes);
for (std::size_t i = 0; i < copy_amount && it != process_list.end(); ++i, ++it) { for (std::size_t i = 0; i < copy_amount; ++i) {
memory.Write64(out_process_ids, (*it)->GetProcessId()); memory.Write64(out_process_ids, process_list[i]->GetProcessId());
out_process_ids += sizeof(u64); out_process_ids += sizeof(u64);
} }

View File

@ -61,7 +61,9 @@ ProfileManager::ProfileManager() {
OpenUser(*GetUser(current)); OpenUser(*GetUser(current));
} }
ProfileManager::~ProfileManager() = default; ProfileManager::~ProfileManager() {
WriteUserSaveFile();
}
/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the /// After a users creation it needs to be "registered" to the system. AddToProfiles handles the
/// internal management of the users profiles /// internal management of the users profiles
@ -111,8 +113,6 @@ Result ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& username)
return ERROR_USER_ALREADY_EXISTS; return ERROR_USER_ALREADY_EXISTS;
} }
is_save_needed = true;
return AddUser({ return AddUser({
.user_uuid = uuid, .user_uuid = uuid,
.username = username, .username = username,
@ -326,9 +326,6 @@ bool ProfileManager::RemoveUser(UUID uuid) {
profiles[*index] = ProfileInfo{}; profiles[*index] = ProfileInfo{};
std::stable_partition(profiles.begin(), profiles.end(), std::stable_partition(profiles.begin(), profiles.end(),
[](const ProfileInfo& profile) { return profile.user_uuid.IsValid(); }); [](const ProfileInfo& profile) { return profile.user_uuid.IsValid(); });
is_save_needed = true;
return true; return true;
} }
@ -343,8 +340,6 @@ bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
profile.username = profile_new.username; profile.username = profile_new.username;
profile.creation_time = profile_new.timestamp; profile.creation_time = profile_new.timestamp;
is_save_needed = true;
return true; return true;
} }
@ -353,7 +348,6 @@ bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase&
const auto index = GetUserIndex(uuid); const auto index = GetUserIndex(uuid);
if (index.has_value() && SetProfileBase(uuid, profile_new)) { if (index.has_value() && SetProfileBase(uuid, profile_new)) {
profiles[*index].data = data_new; profiles[*index].data = data_new;
is_save_needed = true;
return true; return true;
} }
@ -397,10 +391,6 @@ void ProfileManager::ParseUserSaveFile() {
} }
void ProfileManager::WriteUserSaveFile() { void ProfileManager::WriteUserSaveFile() {
if (!is_save_needed) {
return;
}
ProfileDataRaw raw{}; ProfileDataRaw raw{};
for (std::size_t i = 0; i < MAX_USERS; ++i) { for (std::size_t i = 0; i < MAX_USERS; ++i) {
@ -433,10 +423,7 @@ void ProfileManager::WriteUserSaveFile() {
if (!save.IsOpen() || !save.SetSize(sizeof(ProfileDataRaw)) || !save.WriteObject(raw)) { if (!save.IsOpen() || !save.SetSize(sizeof(ProfileDataRaw)) || !save.WriteObject(raw)) {
LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data " LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data "
"made in current session will be saved."); "made in current session will be saved.");
return;
} }
is_save_needed = false;
} }
}; // namespace Service::Account }; // namespace Service::Account

View File

@ -103,7 +103,6 @@ private:
std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
bool RemoveProfileAtIndex(std::size_t index); bool RemoveProfileAtIndex(std::size_t index);
bool is_save_needed{};
std::array<ProfileInfo, MAX_USERS> profiles{}; std::array<ProfileInfo, MAX_USERS> profiles{};
std::array<ProfileInfo, MAX_USERS> stored_opened_profiles{}; std::array<ProfileInfo, MAX_USERS> stored_opened_profiles{};
std::size_t user_count{}; std::size_t user_count{};

View File

@ -36,7 +36,6 @@
#include "core/hle/service/caps/caps_su.h" #include "core/hle/service/caps/caps_su.h"
#include "core/hle/service/caps/caps_types.h" #include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/save_data_controller.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ns/ns.h" #include "core/hle/service/ns/ns.h"
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
@ -2179,7 +2178,7 @@ void IApplicationFunctions::EnsureSaveData(HLERequestContext& ctx) {
attribute.type = FileSys::SaveDataType::SaveData; attribute.type = FileSys::SaveDataType::SaveData;
FileSys::VirtualDir save_data{}; FileSys::VirtualDir save_data{};
const auto res = system.GetFileSystemController().OpenSaveDataController()->CreateSaveData( const auto res = system.GetFileSystemController().CreateSaveData(
&save_data, FileSys::SaveDataSpaceId::NandUser, attribute); &save_data, FileSys::SaveDataSpaceId::NandUser, attribute);
IPC::ResponseBuilder rb{ctx, 4}; IPC::ResponseBuilder rb{ctx, 4};
@ -2354,7 +2353,7 @@ void IApplicationFunctions::ExtendSaveData(HLERequestContext& ctx) {
"new_journal={:016X}", "new_journal={:016X}",
static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size); static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize( system.GetFileSystemController().WriteSaveDataSize(
type, system.GetApplicationProcessProgramID(), user_id, type, system.GetApplicationProcessProgramID(), user_id,
{new_normal_size, new_journal_size}); {new_normal_size, new_journal_size});
@ -2379,7 +2378,7 @@ void IApplicationFunctions::GetSaveDataSize(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", type, user_id[1], LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", type, user_id[1],
user_id[0]); user_id[0]);
const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize( const auto size = system.GetFileSystemController().ReadSaveDataSize(
type, system.GetApplicationProcessProgramID(), user_id); type, system.GetApplicationProcessProgramID(), user_id);
IPC::ResponseBuilder rb{ctx, 6}; IPC::ResponseBuilder rb{ctx, 6};

View File

@ -18,11 +18,11 @@ using namespace AudioCore::AudioIn;
class IAudioIn final : public ServiceFramework<IAudioIn> { class IAudioIn final : public ServiceFramework<IAudioIn> {
public: public:
explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id, explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
const std::string& device_name, const AudioInParameter& in_params, const std::string& device_name, const AudioInParameter& in_params, u32 handle,
Kernel::KProcess* handle, u64 applet_resource_user_id) u64 applet_resource_user_id)
: ServiceFramework{system_, "IAudioIn"}, : ServiceFramework{system_, "IAudioIn"},
service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")}, service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")},
process{handle}, impl{std::make_shared<In>(system_, manager, event, session_id)} { impl{std::make_shared<In>(system_, manager, event, session_id)} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &IAudioIn::GetAudioInState, "GetAudioInState"}, {0, &IAudioIn::GetAudioInState, "GetAudioInState"},
@ -45,8 +45,6 @@ public:
RegisterHandlers(functions); RegisterHandlers(functions);
process->Open();
if (impl->GetSystem() if (impl->GetSystem()
.Initialize(device_name, in_params, handle, applet_resource_user_id) .Initialize(device_name, in_params, handle, applet_resource_user_id)
.IsError()) { .IsError()) {
@ -57,7 +55,6 @@ public:
~IAudioIn() override { ~IAudioIn() override {
impl->Free(); impl->Free();
service_context.CloseEvent(event); service_context.CloseEvent(event);
process->Close();
} }
[[nodiscard]] std::shared_ptr<In> GetImpl() { [[nodiscard]] std::shared_ptr<In> GetImpl() {
@ -199,7 +196,6 @@ private:
KernelHelpers::ServiceContext service_context; KernelHelpers::ServiceContext service_context;
Kernel::KEvent* event; Kernel::KEvent* event;
Kernel::KProcess* process;
std::shared_ptr<AudioCore::AudioIn::In> impl; std::shared_ptr<AudioCore::AudioIn::In> impl;
Common::ScratchBuffer<u64> released_buffer; Common::ScratchBuffer<u64> released_buffer;
}; };
@ -271,14 +267,6 @@ void AudInU::OpenAudioIn(HLERequestContext& ctx) {
auto device_name = Common::StringFromBuffer(device_name_data); auto device_name = Common::StringFromBuffer(device_name_data);
auto handle{ctx.GetCopyHandle(0)}; auto handle{ctx.GetCopyHandle(0)};
auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(handle)};
if (process.IsNull()) {
LOG_ERROR(Service_Audio, "Failed to get process handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
std::scoped_lock l{impl->mutex}; std::scoped_lock l{impl->mutex};
auto link{impl->LinkToManager()}; auto link{impl->LinkToManager()};
if (link.IsError()) { if (link.IsError()) {
@ -299,9 +287,8 @@ void AudInU::OpenAudioIn(HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id, LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
impl->num_free_sessions); impl->num_free_sessions);
auto audio_in = auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name,
std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, in_params, in_params, handle, applet_resource_user_id);
process.GetPointerUnsafe(), applet_resource_user_id);
impl->sessions[new_session_id] = audio_in->GetImpl(); impl->sessions[new_session_id] = audio_in->GetImpl();
impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
@ -331,14 +318,6 @@ void AudInU::OpenAudioInProtocolSpecified(HLERequestContext& ctx) {
auto device_name = Common::StringFromBuffer(device_name_data); auto device_name = Common::StringFromBuffer(device_name_data);
auto handle{ctx.GetCopyHandle(0)}; auto handle{ctx.GetCopyHandle(0)};
auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(handle)};
if (process.IsNull()) {
LOG_ERROR(Service_Audio, "Failed to get process handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
std::scoped_lock l{impl->mutex}; std::scoped_lock l{impl->mutex};
auto link{impl->LinkToManager()}; auto link{impl->LinkToManager()};
if (link.IsError()) { if (link.IsError()) {
@ -359,9 +338,8 @@ void AudInU::OpenAudioInProtocolSpecified(HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id, LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
impl->num_free_sessions); impl->num_free_sessions);
auto audio_in = auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name,
std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, in_params, in_params, handle, applet_resource_user_id);
process.GetPointerUnsafe(), applet_resource_user_id);
impl->sessions[new_session_id] = audio_in->GetImpl(); impl->sessions[new_session_id] = audio_in->GetImpl();
impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;

View File

@ -26,10 +26,9 @@ class IAudioOut final : public ServiceFramework<IAudioOut> {
public: public:
explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager,
size_t session_id, const std::string& device_name, size_t session_id, const std::string& device_name,
const AudioOutParameter& in_params, Kernel::KProcess* handle, const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id)
u64 applet_resource_user_id)
: ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"}, : ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"},
event{service_context.CreateEvent("AudioOutEvent")}, process{handle}, event{service_context.CreateEvent("AudioOutEvent")},
impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} { impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} {
// clang-format off // clang-format off
@ -51,14 +50,11 @@ public:
}; };
// clang-format on // clang-format on
RegisterHandlers(functions); RegisterHandlers(functions);
process->Open();
} }
~IAudioOut() override { ~IAudioOut() override {
impl->Free(); impl->Free();
service_context.CloseEvent(event); service_context.CloseEvent(event);
process->Close();
} }
[[nodiscard]] std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() { [[nodiscard]] std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() {
@ -210,7 +206,6 @@ private:
KernelHelpers::ServiceContext service_context; KernelHelpers::ServiceContext service_context;
Kernel::KEvent* event; Kernel::KEvent* event;
Kernel::KProcess* process;
std::shared_ptr<AudioCore::AudioOut::Out> impl; std::shared_ptr<AudioCore::AudioOut::Out> impl;
Common::ScratchBuffer<u64> released_buffer; Common::ScratchBuffer<u64> released_buffer;
}; };
@ -262,14 +257,6 @@ void AudOutU::OpenAudioOut(HLERequestContext& ctx) {
auto device_name = Common::StringFromBuffer(device_name_data); auto device_name = Common::StringFromBuffer(device_name_data);
auto handle{ctx.GetCopyHandle(0)}; auto handle{ctx.GetCopyHandle(0)};
auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(handle)};
if (process.IsNull()) {
LOG_ERROR(Service_Audio, "Failed to get process handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
auto link{impl->LinkToManager()}; auto link{impl->LinkToManager()};
if (link.IsError()) { if (link.IsError()) {
LOG_ERROR(Service_Audio, "Failed to link Audio Out to Audio Manager"); LOG_ERROR(Service_Audio, "Failed to link Audio Out to Audio Manager");
@ -289,11 +276,10 @@ void AudOutU::OpenAudioOut(HLERequestContext& ctx) {
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,
impl->num_free_sessions); impl->num_free_sessions);
auto audio_out = auto audio_out = std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name,
std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name, in_params, in_params, handle, applet_resource_user_id);
process.GetPointerUnsafe(), applet_resource_user_id); result = audio_out->GetImpl()->GetSystem().Initialize(device_name, in_params, handle,
result = audio_out->GetImpl()->GetSystem().Initialize( applet_resource_user_id);
device_name, in_params, process.GetPointerUnsafe(), applet_resource_user_id);
if (result.IsError()) { if (result.IsError()) {
LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!"); LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!");
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};

View File

@ -24,13 +24,15 @@
#include "core/hle/service/filesystem/fsp_ldr.h" #include "core/hle/service/filesystem/fsp_ldr.h"
#include "core/hle/service/filesystem/fsp_pr.h" #include "core/hle/service/filesystem/fsp_pr.h"
#include "core/hle/service/filesystem/fsp_srv.h" #include "core/hle/service/filesystem/fsp_srv.h"
#include "core/hle/service/filesystem/romfs_controller.h"
#include "core/hle/service/filesystem/save_data_controller.h"
#include "core/hle/service/server_manager.h" #include "core/hle/service/server_manager.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
namespace Service::FileSystem { namespace Service::FileSystem {
// A default size for normal/journal save data size if application control metadata cannot be found.
// This should be large enough to satisfy even the most extreme requirements (~4.2GB)
constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
std::string_view dir_name_) { std::string_view dir_name_) {
std::string dir_name(Common::FS::SanitizePath(dir_name_)); std::string dir_name(Common::FS::SanitizePath(dir_name_));
@ -295,65 +297,145 @@ FileSystemController::FileSystemController(Core::System& system_) : system{syste
FileSystemController::~FileSystemController() = default; FileSystemController::~FileSystemController() = default;
Result FileSystemController::RegisterProcess( Result FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) {
ProcessId process_id, ProgramId program_id, romfs_factory = std::move(factory);
std::shared_ptr<FileSys::RomFSFactory>&& romfs_factory) { LOG_DEBUG(Service_FS, "Registered RomFS");
std::scoped_lock lk{registration_lock};
registrations.emplace(process_id, Registration{
.program_id = program_id,
.romfs_factory = std::move(romfs_factory),
.save_data_factory = CreateSaveDataFactory(program_id),
});
LOG_DEBUG(Service_FS, "Registered for process {}", process_id);
return ResultSuccess; return ResultSuccess;
} }
Result FileSystemController::OpenProcess( Result FileSystemController::RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
ProgramId* out_program_id, std::shared_ptr<SaveDataController>* out_save_data_controller, ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data");
std::shared_ptr<RomFsController>* out_romfs_controller, ProcessId process_id) { save_data_factory = std::move(factory);
std::scoped_lock lk{registration_lock}; LOG_DEBUG(Service_FS, "Registered save data");
return ResultSuccess;
}
const auto it = registrations.find(process_id); Result FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
if (it == registrations.end()) { ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC");
sdmc_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered SDMC");
return ResultSuccess;
}
Result FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS");
bis_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered BIS");
return ResultSuccess;
}
void FileSystemController::SetPackedUpdate(FileSys::VirtualFile update_raw) {
LOG_TRACE(Service_FS, "Setting packed update for romfs");
if (romfs_factory == nullptr)
return;
romfs_factory->SetPackedUpdate(std::move(update_raw));
}
FileSys::VirtualFile FileSystemController::OpenRomFSCurrentProcess() const {
LOG_TRACE(Service_FS, "Opening RomFS for current process");
if (romfs_factory == nullptr) {
return nullptr;
}
return romfs_factory->OpenCurrentProcess(system.GetApplicationProcessProgramID());
}
FileSys::VirtualFile FileSystemController::OpenPatchedRomFS(u64 title_id,
FileSys::ContentRecordType type) const {
LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}", title_id);
if (romfs_factory == nullptr) {
return nullptr;
}
return romfs_factory->OpenPatchedRomFS(title_id, type);
}
FileSys::VirtualFile FileSystemController::OpenPatchedRomFSWithProgramIndex(
u64 title_id, u8 program_index, FileSys::ContentRecordType type) const {
LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}, program_index={}", title_id,
program_index);
if (romfs_factory == nullptr) {
return nullptr;
}
return romfs_factory->OpenPatchedRomFSWithProgramIndex(title_id, program_index, type);
}
FileSys::VirtualFile FileSystemController::OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
FileSys::ContentRecordType type) const {
LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}",
title_id, storage_id, type);
if (romfs_factory == nullptr) {
return nullptr;
}
return romfs_factory->Open(title_id, storage_id, type);
}
std::shared_ptr<FileSys::NCA> FileSystemController::OpenBaseNca(
u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const {
return romfs_factory->GetEntry(title_id, storage_id, type);
}
Result FileSystemController::CreateSaveData(FileSys::VirtualDir* out_save_data,
FileSys::SaveDataSpaceId space,
const FileSys::SaveDataAttribute& save_struct) const {
LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space,
save_struct.DebugInfo());
if (save_data_factory == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND; return FileSys::ERROR_ENTITY_NOT_FOUND;
} }
*out_program_id = it->second.program_id; auto save_data = save_data_factory->Create(space, save_struct);
*out_save_data_controller = if (save_data == nullptr) {
std::make_shared<SaveDataController>(system, it->second.save_data_factory); return FileSys::ERROR_ENTITY_NOT_FOUND;
*out_romfs_controller = }
std::make_shared<RomFsController>(it->second.romfs_factory, it->second.program_id);
*out_save_data = save_data;
return ResultSuccess; return ResultSuccess;
} }
void FileSystemController::SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw) { Result FileSystemController::OpenSaveData(FileSys::VirtualDir* out_save_data,
LOG_TRACE(Service_FS, "Setting packed update for romfs"); FileSys::SaveDataSpaceId space,
const FileSys::SaveDataAttribute& attribute) const {
LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", space,
attribute.DebugInfo());
std::scoped_lock lk{registration_lock}; if (save_data_factory == nullptr) {
const auto it = registrations.find(process_id); return FileSys::ERROR_ENTITY_NOT_FOUND;
if (it == registrations.end()) {
return;
} }
it->second.romfs_factory->SetPackedUpdate(std::move(update_raw)); auto save_data = save_data_factory->Open(space, attribute);
if (save_data == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
*out_save_data = save_data;
return ResultSuccess;
} }
std::shared_ptr<SaveDataController> FileSystemController::OpenSaveDataController() { Result FileSystemController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space,
return std::make_shared<SaveDataController>(system, CreateSaveDataFactory(ProgramId{})); FileSys::SaveDataSpaceId space) const {
} LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", space);
std::shared_ptr<FileSys::SaveDataFactory> FileSystemController::CreateSaveDataFactory( if (save_data_factory == nullptr) {
ProgramId program_id) { return FileSys::ERROR_ENTITY_NOT_FOUND;
using YuzuPath = Common::FS::YuzuPath; }
const auto rw_mode = FileSys::Mode::ReadWrite;
auto vfs = system.GetFilesystem(); auto save_data_space = save_data_factory->GetSaveDataSpaceDirectory(space);
const auto nand_directory = if (save_data_space == nullptr) {
vfs->OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode); return FileSys::ERROR_ENTITY_NOT_FOUND;
return std::make_shared<FileSys::SaveDataFactory>(system, program_id, }
std::move(nand_directory));
*out_save_data_space = save_data_space;
return ResultSuccess;
} }
Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const { Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const {
@ -458,6 +540,48 @@ u64 FileSystemController::GetTotalSpaceSize(FileSys::StorageId id) const {
return 0; return 0;
} }
FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataType type,
u64 title_id, u128 user_id) const {
if (save_data_factory == nullptr) {
return {0, 0};
}
const auto value = save_data_factory->ReadSaveDataSize(type, title_id, user_id);
if (value.normal == 0 && value.journal == 0) {
FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE};
FileSys::NACP nacp;
const auto res = system.GetAppLoader().ReadControlData(nacp);
if (res != Loader::ResultStatus::Success) {
const FileSys::PatchManager pm{system.GetApplicationProcessProgramID(),
system.GetFileSystemController(),
system.GetContentProvider()};
const auto metadata = pm.GetControlMetadata();
const auto& nacp_unique = metadata.first;
if (nacp_unique != nullptr) {
new_size = {nacp_unique->GetDefaultNormalSaveSize(),
nacp_unique->GetDefaultJournalSaveSize()};
}
} else {
new_size = {nacp.GetDefaultNormalSaveSize(), nacp.GetDefaultJournalSaveSize()};
}
WriteSaveDataSize(type, title_id, user_id, new_size);
return new_size;
}
return value;
}
void FileSystemController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
FileSys::SaveDataSize new_value) const {
if (save_data_factory != nullptr)
save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
}
void FileSystemController::SetGameCard(FileSys::VirtualFile file) { void FileSystemController::SetGameCard(FileSys::VirtualFile file) {
gamecard = std::make_unique<FileSys::XCI>(file); gamecard = std::make_unique<FileSys::XCI>(file);
const auto dir = gamecard->ConcatenatedPseudoDirectory(); const auto dir = gamecard->ConcatenatedPseudoDirectory();
@ -677,9 +801,14 @@ FileSys::VirtualDir FileSystemController::GetBCATDirectory(u64 title_id) const {
return bis_factory->GetBCATDirectory(title_id); return bis_factory->GetBCATDirectory(title_id);
} }
void FileSystemController::SetAutoSaveDataCreation(bool enable) {
save_data_factory->SetAutoCreate(enable);
}
void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
if (overwrite) { if (overwrite) {
bis_factory = nullptr; bis_factory = nullptr;
save_data_factory = nullptr;
sdmc_factory = nullptr; sdmc_factory = nullptr;
} }
@ -707,6 +836,11 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
bis_factory->GetUserNANDContents()); bis_factory->GetUserNANDContents());
} }
if (save_data_factory == nullptr) {
save_data_factory =
std::make_unique<FileSys::SaveDataFactory>(system, std::move(nand_directory));
}
if (sdmc_factory == nullptr) { if (sdmc_factory == nullptr) {
sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory), sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory),
std::move(sd_load_directory)); std::move(sd_load_directory));
@ -715,19 +849,12 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
} }
} }
void FileSystemController::Reset() {
std::scoped_lock lk{registration_lock};
registrations.clear();
}
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
const auto FileSystemProxyFactory = [&] { return std::make_shared<FSP_SRV>(system); };
server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system)); server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system));
server_manager->RegisterNamedService("fsp:pr", std::make_shared<FSP_PR>(system)); server_manager->RegisterNamedService("fsp:pr", std::make_shared<FSP_PR>(system));
server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory)); server_manager->RegisterNamedService("fsp-srv", std::make_shared<FSP_SRV>(system));
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }

View File

@ -43,9 +43,6 @@ class ServiceManager;
namespace FileSystem { namespace FileSystem {
class RomFsController;
class SaveDataController;
enum class ContentStorageId : u32 { enum class ContentStorageId : u32 {
System, System,
User, User,
@ -64,24 +61,32 @@ enum class OpenDirectoryMode : u64 {
}; };
DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode); DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode);
using ProcessId = u64;
using ProgramId = u64;
class FileSystemController { class FileSystemController {
public: public:
explicit FileSystemController(Core::System& system_); explicit FileSystemController(Core::System& system_);
~FileSystemController(); ~FileSystemController();
Result RegisterProcess(ProcessId process_id, ProgramId program_id, Result RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
std::shared_ptr<FileSys::RomFSFactory>&& factory); Result RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory);
Result OpenProcess(ProgramId* out_program_id, Result RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
std::shared_ptr<SaveDataController>* out_save_data_controller, Result RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
std::shared_ptr<RomFsController>* out_romfs_controller,
ProcessId process_id);
void SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw);
std::shared_ptr<SaveDataController> OpenSaveDataController(); void SetPackedUpdate(FileSys::VirtualFile update_raw);
FileSys::VirtualFile OpenRomFSCurrentProcess() const;
FileSys::VirtualFile OpenPatchedRomFS(u64 title_id, FileSys::ContentRecordType type) const;
FileSys::VirtualFile OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index,
FileSys::ContentRecordType type) const;
FileSys::VirtualFile OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
FileSys::ContentRecordType type) const;
std::shared_ptr<FileSys::NCA> OpenBaseNca(u64 title_id, FileSys::StorageId storage_id,
FileSys::ContentRecordType type) const;
Result CreateSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
const FileSys::SaveDataAttribute& save_struct) const;
Result OpenSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
const FileSys::SaveDataAttribute& save_struct) const;
Result OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space,
FileSys::SaveDataSpaceId space) const;
Result OpenSDMC(FileSys::VirtualDir* out_sdmc) const; Result OpenSDMC(FileSys::VirtualDir* out_sdmc) const;
Result OpenBISPartition(FileSys::VirtualDir* out_bis_partition, Result OpenBISPartition(FileSys::VirtualDir* out_bis_partition,
FileSys::BisPartitionId id) const; FileSys::BisPartitionId id) const;
@ -91,6 +96,11 @@ public:
u64 GetFreeSpaceSize(FileSys::StorageId id) const; u64 GetFreeSpaceSize(FileSys::StorageId id) const;
u64 GetTotalSpaceSize(FileSys::StorageId id) const; u64 GetTotalSpaceSize(FileSys::StorageId id) const;
FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
u128 user_id) const;
void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
FileSys::SaveDataSize new_value) const;
void SetGameCard(FileSys::VirtualFile file); void SetGameCard(FileSys::VirtualFile file);
FileSys::XCI* GetGameCard() const; FileSys::XCI* GetGameCard() const;
@ -123,24 +133,15 @@ public:
FileSys::VirtualDir GetBCATDirectory(u64 title_id) const; FileSys::VirtualDir GetBCATDirectory(u64 title_id) const;
void SetAutoSaveDataCreation(bool enable);
// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
// above is called. // above is called.
void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
void Reset();
private: private:
std::shared_ptr<FileSys::SaveDataFactory> CreateSaveDataFactory(ProgramId program_id); std::unique_ptr<FileSys::RomFSFactory> romfs_factory;
std::unique_ptr<FileSys::SaveDataFactory> save_data_factory;
struct Registration {
ProgramId program_id;
std::shared_ptr<FileSys::RomFSFactory> romfs_factory;
std::shared_ptr<FileSys::SaveDataFactory> save_data_factory;
};
std::mutex registration_lock;
std::map<ProcessId, Registration> registrations;
std::unique_ptr<FileSys::SDMCFactory> sdmc_factory; std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
std::unique_ptr<FileSys::BISFactory> bis_factory; std::unique_ptr<FileSys::BISFactory> bis_factory;

View File

@ -27,8 +27,6 @@
#include "core/hle/result.h" #include "core/hle/result.h"
#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp_srv.h" #include "core/hle/service/filesystem/fsp_srv.h"
#include "core/hle/service/filesystem/romfs_controller.h"
#include "core/hle/service/filesystem/save_data_controller.h"
#include "core/hle/service/hle_ipc.h" #include "core/hle/service/hle_ipc.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/reporter.h" #include "core/reporter.h"
@ -579,11 +577,9 @@ private:
class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
public: public:
explicit ISaveDataInfoReader(Core::System& system_, explicit ISaveDataInfoReader(Core::System& system_, FileSys::SaveDataSpaceId space,
std::shared_ptr<SaveDataController> save_data_controller_, FileSystemController& fsc_)
FileSys::SaveDataSpaceId space) : ServiceFramework{system_, "ISaveDataInfoReader"}, fsc{fsc_} {
: ServiceFramework{system_, "ISaveDataInfoReader"}, save_data_controller{
save_data_controller_} {
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"}, {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"},
}; };
@ -630,7 +626,7 @@ private:
void FindAllSaves(FileSys::SaveDataSpaceId space) { void FindAllSaves(FileSys::SaveDataSpaceId space) {
FileSys::VirtualDir save_root{}; FileSys::VirtualDir save_root{};
const auto result = save_data_controller->OpenSaveDataSpace(&save_root, space); const auto result = fsc.OpenSaveDataSpace(&save_root, space);
if (result != ResultSuccess || save_root == nullptr) { if (result != ResultSuccess || save_root == nullptr) {
LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!", space); LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!", space);
@ -727,8 +723,7 @@ private:
}; };
static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
ProcessId process_id = 0; FileSystemController& fsc;
std::shared_ptr<SaveDataController> save_data_controller;
std::vector<SaveDataInfo> info; std::vector<SaveDataInfo> info;
u64 next_entry_index = 0; u64 next_entry_index = 0;
}; };
@ -868,20 +863,21 @@ FSP_SRV::FSP_SRV(Core::System& system_)
if (Settings::values.enable_fs_access_log) { if (Settings::values.enable_fs_access_log) {
access_log_mode = AccessLogMode::SdCard; access_log_mode = AccessLogMode::SdCard;
} }
// This should be true on creation
fsc.SetAutoSaveDataCreation(true);
} }
FSP_SRV::~FSP_SRV() = default; FSP_SRV::~FSP_SRV() = default;
void FSP_SRV::SetCurrentProcess(HLERequestContext& ctx) { void FSP_SRV::SetCurrentProcess(HLERequestContext& ctx) {
current_process_id = ctx.GetPID(); IPC::RequestParser rp{ctx};
current_process_id = rp.Pop<u64>();
LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id); LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id);
const auto res =
fsc.OpenProcess(&program_id, &save_data_controller, &romfs_controller, current_process_id);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res); rb.Push(ResultSuccess);
} }
void FSP_SRV::OpenFileSystemWithPatch(HLERequestContext& ctx) { void FSP_SRV::OpenFileSystemWithPatch(HLERequestContext& ctx) {
@ -920,8 +916,7 @@ void FSP_SRV::CreateSaveDataFileSystem(HLERequestContext& ctx) {
uid[1], uid[0]); uid[1], uid[0]);
FileSys::VirtualDir save_data_dir{}; FileSys::VirtualDir save_data_dir{};
save_data_controller->CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandUser, fsc.CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandUser, save_struct);
save_struct);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -936,8 +931,7 @@ void FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx)
LOG_DEBUG(Service_FS, "called save_struct = {}", save_struct.DebugInfo()); LOG_DEBUG(Service_FS, "called save_struct = {}", save_struct.DebugInfo());
FileSys::VirtualDir save_data_dir{}; FileSys::VirtualDir save_data_dir{};
save_data_controller->CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandSystem, fsc.CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandSystem, save_struct);
save_struct);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
@ -956,8 +950,7 @@ void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) {
LOG_INFO(Service_FS, "called."); LOG_INFO(Service_FS, "called.");
FileSys::VirtualDir dir{}; FileSys::VirtualDir dir{};
auto result = auto result = fsc.OpenSaveData(&dir, parameters.space_id, parameters.attribute);
save_data_controller->OpenSaveData(&dir, parameters.space_id, parameters.attribute);
if (result != ResultSuccess) { if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2, 0, 0}; IPC::ResponseBuilder rb{ctx, 2, 0, 0};
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
@ -1008,7 +1001,7 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<ISaveDataInfoReader>( rb.PushIpcInterface<ISaveDataInfoReader>(
std::make_shared<ISaveDataInfoReader>(system, save_data_controller, space)); std::make_shared<ISaveDataInfoReader>(system, space, fsc));
} }
void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) { void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) {
@ -1016,8 +1009,8 @@ void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<ISaveDataInfoReader>(system, save_data_controller, rb.PushIpcInterface<ISaveDataInfoReader>(system, FileSys::SaveDataSpaceId::TemporaryStorage,
FileSys::SaveDataSpaceId::TemporaryStorage); fsc);
} }
void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx) { void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx) {
@ -1057,7 +1050,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called"); LOG_DEBUG(Service_FS, "called");
if (!romfs) { if (!romfs) {
auto current_romfs = romfs_controller->OpenRomFSCurrentProcess(); auto current_romfs = fsc.OpenRomFSCurrentProcess();
if (!current_romfs) { if (!current_romfs) {
// TODO (bunnei): Find the right error code to use here // TODO (bunnei): Find the right error code to use here
LOG_CRITICAL(Service_FS, "no file system interface available!"); LOG_CRITICAL(Service_FS, "no file system interface available!");
@ -1085,7 +1078,7 @@ void FSP_SRV::OpenDataStorageByDataId(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}", LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}",
storage_id, unknown, title_id); storage_id, unknown, title_id);
auto data = romfs_controller->OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
if (!data) { if (!data) {
const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
@ -1108,8 +1101,7 @@ void FSP_SRV::OpenDataStorageByDataId(HLERequestContext& ctx) {
const FileSys::PatchManager pm{title_id, fsc, content_provider}; const FileSys::PatchManager pm{title_id, fsc, content_provider};
auto base = auto base = fsc.OpenBaseNca(title_id, storage_id, FileSys::ContentRecordType::Data);
romfs_controller->OpenBaseNca(title_id, storage_id, FileSys::ContentRecordType::Data);
auto storage = std::make_shared<IStorage>( auto storage = std::make_shared<IStorage>(
system, pm.PatchRomFS(base.get(), std::move(data), FileSys::ContentRecordType::Data)); system, pm.PatchRomFS(base.get(), std::move(data), FileSys::ContentRecordType::Data));
@ -1137,8 +1129,9 @@ void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called, program_index={}", program_index); LOG_DEBUG(Service_FS, "called, program_index={}", program_index);
auto patched_romfs = romfs_controller->OpenPatchedRomFSWithProgramIndex( auto patched_romfs =
program_id, program_index, FileSys::ContentRecordType::Program); fsc.OpenPatchedRomFSWithProgramIndex(system.GetApplicationProcessProgramID(), program_index,
FileSys::ContentRecordType::Program);
if (!patched_romfs) { if (!patched_romfs) {
// TODO: Find the right error code to use here // TODO: Find the right error code to use here
@ -1159,7 +1152,7 @@ void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) {
void FSP_SRV::DisableAutoSaveDataCreation(HLERequestContext& ctx) { void FSP_SRV::DisableAutoSaveDataCreation(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called"); LOG_DEBUG(Service_FS, "called");
save_data_controller->SetAutoCreate(false); fsc.SetAutoSaveDataCreation(false);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);

View File

@ -17,9 +17,6 @@ class FileSystemBackend;
namespace Service::FileSystem { namespace Service::FileSystem {
class RomFsController;
class SaveDataController;
enum class AccessLogVersion : u32 { enum class AccessLogVersion : u32 {
V7_0_0 = 2, V7_0_0 = 2,
@ -70,9 +67,6 @@ private:
u64 current_process_id = 0; u64 current_process_id = 0;
u32 access_log_program_index = 0; u32 access_log_program_index = 0;
AccessLogMode access_log_mode = AccessLogMode::None; AccessLogMode access_log_mode = AccessLogMode::None;
u64 program_id = 0;
std::shared_ptr<SaveDataController> save_data_controller;
std::shared_ptr<RomFsController> romfs_controller;
}; };
} // namespace Service::FileSystem } // namespace Service::FileSystem

View File

@ -1,37 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/filesystem/romfs_controller.h"
namespace Service::FileSystem {
RomFsController::RomFsController(std::shared_ptr<FileSys::RomFSFactory> factory_, u64 program_id_)
: factory{std::move(factory_)}, program_id{program_id_} {}
RomFsController::~RomFsController() = default;
FileSys::VirtualFile RomFsController::OpenRomFSCurrentProcess() {
return factory->OpenCurrentProcess(program_id);
}
FileSys::VirtualFile RomFsController::OpenPatchedRomFS(u64 title_id,
FileSys::ContentRecordType type) {
return factory->OpenPatchedRomFS(title_id, type);
}
FileSys::VirtualFile RomFsController::OpenPatchedRomFSWithProgramIndex(
u64 title_id, u8 program_index, FileSys::ContentRecordType type) {
return factory->OpenPatchedRomFSWithProgramIndex(title_id, program_index, type);
}
FileSys::VirtualFile RomFsController::OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
FileSys::ContentRecordType type) {
return factory->Open(title_id, storage_id, type);
}
std::shared_ptr<FileSys::NCA> RomFsController::OpenBaseNca(u64 title_id,
FileSys::StorageId storage_id,
FileSys::ContentRecordType type) {
return factory->GetEntry(title_id, storage_id, type);
}
} // namespace Service::FileSystem

View File

@ -1,31 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/vfs_types.h"
namespace Service::FileSystem {
class RomFsController {
public:
explicit RomFsController(std::shared_ptr<FileSys::RomFSFactory> factory_, u64 program_id_);
~RomFsController();
FileSys::VirtualFile OpenRomFSCurrentProcess();
FileSys::VirtualFile OpenPatchedRomFS(u64 title_id, FileSys::ContentRecordType type);
FileSys::VirtualFile OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index,
FileSys::ContentRecordType type);
FileSys::VirtualFile OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
FileSys::ContentRecordType type);
std::shared_ptr<FileSys::NCA> OpenBaseNca(u64 title_id, FileSys::StorageId storage_id,
FileSys::ContentRecordType type);
private:
const std::shared_ptr<FileSys::RomFSFactory> factory;
const u64 program_id;
};
} // namespace Service::FileSystem

View File

@ -1,99 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/patch_manager.h"
#include "core/hle/service/filesystem/save_data_controller.h"
#include "core/loader/loader.h"
namespace Service::FileSystem {
namespace {
// A default size for normal/journal save data size if application control metadata cannot be found.
// This should be large enough to satisfy even the most extreme requirements (~4.2GB)
constexpr u64 SufficientSaveDataSize = 0xF0000000;
FileSys::SaveDataSize GetDefaultSaveDataSize(Core::System& system, u64 program_id) {
const FileSys::PatchManager pm{program_id, system.GetFileSystemController(),
system.GetContentProvider()};
const auto metadata = pm.GetControlMetadata();
const auto& nacp = metadata.first;
if (nacp != nullptr) {
return {nacp->GetDefaultNormalSaveSize(), nacp->GetDefaultJournalSaveSize()};
}
return {SufficientSaveDataSize, SufficientSaveDataSize};
}
} // namespace
SaveDataController::SaveDataController(Core::System& system_,
std::shared_ptr<FileSys::SaveDataFactory> factory_)
: system{system_}, factory{std::move(factory_)} {}
SaveDataController::~SaveDataController() = default;
Result SaveDataController::CreateSaveData(FileSys::VirtualDir* out_save_data,
FileSys::SaveDataSpaceId space,
const FileSys::SaveDataAttribute& attribute) {
LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space,
attribute.DebugInfo());
auto save_data = factory->Create(space, attribute);
if (save_data == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
*out_save_data = save_data;
return ResultSuccess;
}
Result SaveDataController::OpenSaveData(FileSys::VirtualDir* out_save_data,
FileSys::SaveDataSpaceId space,
const FileSys::SaveDataAttribute& attribute) {
auto save_data = factory->Open(space, attribute);
if (save_data == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
*out_save_data = save_data;
return ResultSuccess;
}
Result SaveDataController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space,
FileSys::SaveDataSpaceId space) {
auto save_data_space = factory->GetSaveDataSpaceDirectory(space);
if (save_data_space == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
*out_save_data_space = save_data_space;
return ResultSuccess;
}
FileSys::SaveDataSize SaveDataController::ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
u128 user_id) {
const auto value = factory->ReadSaveDataSize(type, title_id, user_id);
if (value.normal == 0 && value.journal == 0) {
const auto size = GetDefaultSaveDataSize(system, title_id);
factory->WriteSaveDataSize(type, title_id, user_id, size);
return size;
}
return value;
}
void SaveDataController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
FileSys::SaveDataSize new_value) {
factory->WriteSaveDataSize(type, title_id, user_id, new_value);
}
void SaveDataController::SetAutoCreate(bool state) {
factory->SetAutoCreate(state);
}
} // namespace Service::FileSystem

View File

@ -1,35 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs_types.h"
namespace Service::FileSystem {
class SaveDataController {
public:
explicit SaveDataController(Core::System& system,
std::shared_ptr<FileSys::SaveDataFactory> factory_);
~SaveDataController();
Result CreateSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
const FileSys::SaveDataAttribute& attribute);
Result OpenSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
const FileSys::SaveDataAttribute& attribute);
Result OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space,
FileSys::SaveDataSpaceId space);
FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id);
void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
FileSys::SaveDataSize new_value);
void SetAutoCreate(bool state);
private:
Core::System& system;
const std::shared_ptr<FileSys::SaveDataFactory> factory;
};
} // namespace Service::FileSystem

View File

@ -15,10 +15,9 @@
namespace Service::Glue { namespace Service::Glue {
namespace { namespace {
std::optional<u64> GetTitleIDForProcessID(Core::System& system, u64 process_id) { std::optional<u64> GetTitleIDForProcessID(const Core::System& system, u64 process_id) {
auto list = system.Kernel().GetProcessList(); const auto& list = system.Kernel().GetProcessList();
const auto iter = std::find_if(list.begin(), list.end(), [&process_id](const auto& process) {
const auto iter = std::find_if(list.begin(), list.end(), [&process_id](auto& process) {
return process->GetProcessId() == process_id; return process->GetProcessId() == process_id;
}); });

View File

@ -22,10 +22,12 @@ void LoopProcess(Core::System& system) {
std::shared_ptr<HidFirmwareSettings> firmware_settings = std::shared_ptr<HidFirmwareSettings> firmware_settings =
std::make_shared<HidFirmwareSettings>(); std::make_shared<HidFirmwareSettings>();
// TODO: Remove this hack when am is emulated properly. // TODO: Remove this hack until this service is emulated properly.
resource_manager->Initialize(); const auto process_list = system.Kernel().GetProcessList();
resource_manager->RegisterAppletResourceUserId(system.ApplicationProcess()->GetProcessId(), if (!process_list.empty()) {
true); resource_manager->Initialize();
resource_manager->RegisterAppletResourceUserId(process_list[0]->GetId(), true);
}
server_manager->RegisterNamedService( server_manager->RegisterNamedService(
"hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings)); "hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings));

View File

@ -1498,7 +1498,7 @@ void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) {
bool check_device_index = false; bool check_device_index = false;
switch (vibration_device_handle.npad_type) { switch (vibration_device_handle.npad_type) {
case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::ProController:
case Core::HID::NpadStyleIndex::Handheld: case Core::HID::NpadStyleIndex::Handheld:
case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::JoyconLeft: case Core::HID::NpadStyleIndex::JoyconLeft:

View File

@ -16,8 +16,9 @@
namespace Service::Nvidia::NvCore { namespace Service::Nvidia::NvCore {
Session::Session(SessionId id_, Kernel::KProcess* process_, Core::Asid asid_) Session::Session(size_t id_, Kernel::KProcess* process_, size_t smmu_id_)
: id{id_}, process{process_}, asid{asid_}, has_preallocated_area{}, mapper{}, is_active{} {} : id{id_}, process{process_}, smmu_id{smmu_id_},
has_preallocated_area{}, mapper{}, is_active{} {}
Session::~Session() = default; Session::~Session() = default;
@ -40,9 +41,7 @@ Container::Container(Tegra::Host1x::Host1x& host1x_) {
Container::~Container() = default; Container::~Container() = default;
SessionId Container::OpenSession(Kernel::KProcess* process) { size_t Container::OpenSession(Kernel::KProcess* process) {
using namespace Common::Literals;
std::scoped_lock lk(impl->session_guard); std::scoped_lock lk(impl->session_guard);
for (auto& session : impl->sessions) { for (auto& session : impl->sessions) {
if (!session.is_active) { if (!session.is_active) {
@ -55,14 +54,14 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
size_t new_id{}; size_t new_id{};
auto* memory_interface = &process->GetMemory(); auto* memory_interface = &process->GetMemory();
auto& smmu = impl->host1x.MemoryManager(); auto& smmu = impl->host1x.MemoryManager();
auto asid = smmu.RegisterProcess(memory_interface); auto smmu_id = smmu.RegisterProcess(memory_interface);
if (!impl->id_pool.empty()) { if (!impl->id_pool.empty()) {
new_id = impl->id_pool.front(); new_id = impl->id_pool.front();
impl->id_pool.pop_front(); impl->id_pool.pop_front();
impl->sessions[new_id] = Session{SessionId{new_id}, process, asid}; impl->sessions[new_id] = Session{new_id, process, smmu_id};
} else { } else {
new_id = impl->new_ids++; new_id = impl->new_ids++;
impl->sessions.emplace_back(SessionId{new_id}, process, asid); impl->sessions.emplace_back(new_id, process, smmu_id);
} }
auto& session = impl->sessions[new_id]; auto& session = impl->sessions[new_id];
session.is_active = true; session.is_active = true;
@ -81,7 +80,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
cur_addr)); cur_addr));
auto svc_mem_info = mem_info.GetSvcMemoryInfo(); auto svc_mem_info = mem_info.GetSvcMemoryInfo();
// Check if this memory block is heap. // check if this memory block is heap
if (svc_mem_info.state == Kernel::Svc::MemoryState::Normal) { if (svc_mem_info.state == Kernel::Svc::MemoryState::Normal) {
if (svc_mem_info.size > region_size) { if (svc_mem_info.size > region_size) {
region_size = svc_mem_info.size; region_size = svc_mem_info.size;
@ -98,21 +97,21 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
cur_addr = next_address; cur_addr = next_address;
} }
session.has_preallocated_area = false; session.has_preallocated_area = false;
auto start_region = region_size >= 32_MiB ? smmu.Allocate(region_size) : 0; auto start_region = (region_size >> 15) >= 1024 ? smmu.Allocate(region_size) : 0;
if (start_region != 0) { if (start_region != 0) {
session.mapper = std::make_unique<HeapMapper>(region_start, start_region, region_size, session.mapper = std::make_unique<HeapMapper>(region_start, start_region, region_size,
asid, impl->host1x); smmu_id, impl->host1x);
smmu.TrackContinuity(start_region, region_start, region_size, asid); smmu.TrackContinuity(start_region, region_start, region_size, smmu_id);
session.has_preallocated_area = true; session.has_preallocated_area = true;
LOG_DEBUG(Debug, "Preallocation created!"); LOG_CRITICAL(Debug, "Preallocation created!");
} }
} }
return SessionId{new_id}; return new_id;
} }
void Container::CloseSession(SessionId session_id) { void Container::CloseSession(size_t id) {
std::scoped_lock lk(impl->session_guard); std::scoped_lock lk(impl->session_guard);
auto& session = impl->sessions[session_id.id]; auto& session = impl->sessions[id];
auto& smmu = impl->host1x.MemoryManager(); auto& smmu = impl->host1x.MemoryManager();
if (session.has_preallocated_area) { if (session.has_preallocated_area) {
const DAddr region_start = session.mapper->GetRegionStart(); const DAddr region_start = session.mapper->GetRegionStart();
@ -122,13 +121,13 @@ void Container::CloseSession(SessionId session_id) {
session.has_preallocated_area = false; session.has_preallocated_area = false;
} }
session.is_active = false; session.is_active = false;
smmu.UnregisterProcess(impl->sessions[session_id.id].asid); smmu.UnregisterProcess(impl->sessions[id].smmu_id);
impl->id_pool.emplace_front(session_id.id); impl->id_pool.emplace_front(id);
} }
Session* Container::GetSession(SessionId session_id) { Session* Container::GetSession(size_t id) {
std::atomic_thread_fence(std::memory_order_acquire); std::atomic_thread_fence(std::memory_order_acquire);
return &impl->sessions[session_id.id]; return &impl->sessions[id];
} }
NvMap& Container::GetNvMapFile() { NvMap& Container::GetNvMapFile() {

View File

@ -8,7 +8,6 @@
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
#include "core/device_memory_manager.h"
#include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdata.h"
namespace Kernel { namespace Kernel {
@ -27,12 +26,8 @@ class SyncpointManager;
struct ContainerImpl; struct ContainerImpl;
struct SessionId {
size_t id;
};
struct Session { struct Session {
Session(SessionId id_, Kernel::KProcess* process_, Core::Asid asid_); Session(size_t id_, Kernel::KProcess* process_, size_t smmu_id_);
~Session(); ~Session();
Session(const Session&) = delete; Session(const Session&) = delete;
@ -40,9 +35,9 @@ struct Session {
Session(Session&&) = default; Session(Session&&) = default;
Session& operator=(Session&&) = default; Session& operator=(Session&&) = default;
SessionId id; size_t id;
Kernel::KProcess* process; Kernel::KProcess* process;
Core::Asid asid; size_t smmu_id;
bool has_preallocated_area{}; bool has_preallocated_area{};
std::unique_ptr<HeapMapper> mapper{}; std::unique_ptr<HeapMapper> mapper{};
bool is_active{}; bool is_active{};
@ -53,10 +48,10 @@ public:
explicit Container(Tegra::Host1x::Host1x& host1x); explicit Container(Tegra::Host1x::Host1x& host1x);
~Container(); ~Container();
SessionId OpenSession(Kernel::KProcess* process); size_t OpenSession(Kernel::KProcess* process);
void CloseSession(SessionId id); void CloseSession(size_t id);
Session* GetSession(SessionId id); Session* GetSession(size_t id);
NvMap& GetNvMapFile(); NvMap& GetNvMapFile();

View File

@ -109,9 +109,9 @@ struct HeapMapper::HeapMapperInternal {
std::mutex guard; std::mutex guard;
}; };
HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid, HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, size_t smmu_id,
Tegra::Host1x::Host1x& host1x) Tegra::Host1x::Host1x& host1x)
: m_vaddress{start_vaddress}, m_daddress{start_daddress}, m_size{size}, m_asid{asid} { : m_vaddress{start_vaddress}, m_daddress{start_daddress}, m_size{size}, m_smmu_id{smmu_id} {
m_internal = std::make_unique<HeapMapperInternal>(host1x); m_internal = std::make_unique<HeapMapperInternal>(host1x);
} }
@ -138,7 +138,7 @@ DAddr HeapMapper::Map(VAddr start, size_t size) {
const size_t offset = inter_addr - m_vaddress; const size_t offset = inter_addr - m_vaddress;
const size_t sub_size = inter_addr_end - inter_addr; const size_t sub_size = inter_addr_end - inter_addr;
m_internal->device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size, m_internal->device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size,
m_asid); m_smmu_id);
} }
} }
m_internal->mapping_overlaps += std::make_pair(interval, 1); m_internal->mapping_overlaps += std::make_pair(interval, 1);

View File

@ -6,7 +6,6 @@
#include <memory> #include <memory>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/device_memory_manager.h"
namespace Tegra::Host1x { namespace Tegra::Host1x {
class Host1x; class Host1x;
@ -16,7 +15,7 @@ namespace Service::Nvidia::NvCore {
class HeapMapper { class HeapMapper {
public: public:
HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid, HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, size_t smmu_id,
Tegra::Host1x::Host1x& host1x); Tegra::Host1x::Host1x& host1x);
~HeapMapper(); ~HeapMapper();
@ -42,7 +41,7 @@ private:
VAddr m_vaddress; VAddr m_vaddress;
DAddr m_daddress; DAddr m_daddress;
size_t m_size; size_t m_size;
Core::Asid m_asid; size_t m_smmu_id;
std::unique_ptr<HeapMapperInternal> m_internal; std::unique_ptr<HeapMapperInternal> m_internal;
}; };

View File

@ -22,8 +22,7 @@ NvMap::Handle::Handle(u64 size_, Id id_)
flags.raw = 0; flags.raw = 0;
} }
NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress, NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress, size_t pSessionId) {
NvCore::SessionId pSessionId) {
std::scoped_lock lock(mutex); std::scoped_lock lock(mutex);
// Handles cannot be allocated twice // Handles cannot be allocated twice
if (allocated) { if (allocated) {
@ -224,7 +223,7 @@ DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) {
} }
handle_description->d_address = address; handle_description->d_address = address;
smmu.Map(address, vaddress, map_size, session->asid, true); smmu.Map(address, vaddress, map_size, session->smmu_id, true);
handle_description->in_heap = false; handle_description->in_heap = false;
} }
} }

View File

@ -14,7 +14,6 @@
#include "common/bit_field.h" #include "common/bit_field.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdata.h"
namespace Tegra { namespace Tegra {
@ -72,7 +71,7 @@ public:
u8 kind{}; //!< Used for memory compression u8 kind{}; //!< Used for memory compression
bool allocated{}; //!< If the handle has been allocated with `Alloc` bool allocated{}; //!< If the handle has been allocated with `Alloc`
bool in_heap{}; bool in_heap{};
NvCore::SessionId session_id{}; size_t session_id{};
DAddr d_address{}; //!< The memory location in the device's AS that this handle corresponds DAddr d_address{}; //!< The memory location in the device's AS that this handle corresponds
//!< to, this can also be in the nvdrv tmem //!< to, this can also be in the nvdrv tmem
@ -84,7 +83,7 @@ public:
* if a 0 address is passed * if a 0 address is passed
*/ */
[[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress, [[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress,
NvCore::SessionId pSessionId); size_t pSessionId);
/** /**
* @brief Increases the dupe counter of the handle for the given session * @brief Increases the dupe counter of the handle for the given session

View File

@ -7,7 +7,6 @@
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdata.h"
namespace Core { namespace Core {
@ -63,7 +62,7 @@ public:
* Called once a device is opened * Called once a device is opened
* @param fd The device fd * @param fd The device fd
*/ */
virtual void OnOpen(NvCore::SessionId session_id, DeviceFD fd) = 0; virtual void OnOpen(size_t session_id, DeviceFD fd) = 0;
/** /**
* Called once a device is closed * Called once a device is closed

View File

@ -35,7 +35,7 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
return NvResult::NotImplemented; return NvResult::NotImplemented;
} }
void nvdisp_disp0::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} void nvdisp_disp0::OnOpen(size_t session_id, DeviceFD fd) {}
void nvdisp_disp0::OnClose(DeviceFD fd) {} void nvdisp_disp0::OnClose(DeviceFD fd) {}
void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width,

View File

@ -32,7 +32,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override; std::span<u8> inline_output) override;
void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnOpen(size_t session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override; void OnClose(DeviceFD fd) override;
/// Performs a screen flip, drawing the buffer pointed to by the handle. /// Performs a screen flip, drawing the buffer pointed to by the handle.

View File

@ -86,7 +86,7 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i
return NvResult::NotImplemented; return NvResult::NotImplemented;
} }
void nvhost_as_gpu::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} void nvhost_as_gpu::OnOpen(size_t session_id, DeviceFD fd) {}
void nvhost_as_gpu::OnClose(DeviceFD fd) {} void nvhost_as_gpu::OnClose(DeviceFD fd) {}
NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) { NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {

View File

@ -55,7 +55,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override; std::span<u8> inline_output) override;
void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnOpen(size_t session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override; void OnClose(DeviceFD fd) override;
Kernel::KEvent* QueryEvent(u32 event_id) override; Kernel::KEvent* QueryEvent(u32 event_id) override;

View File

@ -76,7 +76,7 @@ NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inp
return NvResult::NotImplemented; return NvResult::NotImplemented;
} }
void nvhost_ctrl::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} void nvhost_ctrl::OnOpen(size_t session_id, DeviceFD fd) {}
void nvhost_ctrl::OnClose(DeviceFD fd) {} void nvhost_ctrl::OnClose(DeviceFD fd) {}

View File

@ -32,7 +32,7 @@ public:
NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
std::span<u8> inline_output) override; std::span<u8> inline_output) override;
void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnOpen(size_t session_id, DeviceFD fd) override;
void OnClose(DeviceFD fd) override; void OnClose(DeviceFD fd) override;
Kernel::KEvent* QueryEvent(u32 event_id) override; Kernel::KEvent* QueryEvent(u32 event_id) override;

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