Compare commits
77 Commits
android-18
...
android-19
Author | SHA1 | Date | |
---|---|---|---|
e4f0886adb | |||
f5cb7c9f3c | |||
e78f6e5a87 | |||
3e44d8cdfe | |||
da329b15e8 | |||
931686dbc3 | |||
b4a8e1ef8a | |||
5ea8f05ec6 | |||
10535e0016 | |||
a8c552e261 | |||
932bd98824 | |||
9f376cd901 | |||
a560b9f5a2 | |||
4f04bd3697 | |||
97c8b49444 | |||
3092855d5a | |||
72f803c366 | |||
c87b96435d | |||
6536d29c61 | |||
116f76e4b6 | |||
dff0a7c52a | |||
915efa4236 | |||
4548e5ae1d | |||
46c2435235 | |||
0b0e9ef18d | |||
7f5adf8982 | |||
89d6856090 | |||
2c29c2b8dd | |||
16abda59be | |||
90ab89a0b0 | |||
6531ad56a6 | |||
e8671ed04e | |||
2044ae6b3a | |||
c661b95864 | |||
c683ec2bcb | |||
2e4e33156e | |||
04f4eeaca2 | |||
2e4b32204c | |||
34db13486a | |||
c6c6bb4041 | |||
a2ffb419c9 | |||
0127cec371 | |||
db3a6075f5 | |||
8876a15227 | |||
954eb40237 | |||
d4acdac168 | |||
817c7c445d | |||
da714a362b | |||
7b3941e5d4 | |||
15d8a40529 | |||
cdeaca73c4 | |||
bee22540a1 | |||
76880b84f9 | |||
2f0b57ca13 | |||
f90a022d3a | |||
f2fed21c11 | |||
d940974789 | |||
f7a3c135e2 | |||
fcb0dff67c | |||
b5dac5f525 | |||
a4d90a9a64 | |||
84787a2ada | |||
2a0d707ce1 | |||
aae9eea532 | |||
2044a289f8 | |||
d3ba6b334b | |||
dac8c4ce4d | |||
9e974d4c7e | |||
6bfc3c530c | |||
93239f191a | |||
b17db2b462 | |||
9130366a58 | |||
ad0066a6b6 | |||
78c323c4eb | |||
51ad2d10de | |||
63b835f822 | |||
200b371d13 |
@ -25,10 +25,13 @@ for f in $FILES_TO_LINT; do
|
|||||||
"$CLANG_FORMAT" -i "$f"
|
"$CLANG_FORMAT" -i "$f"
|
||||||
done
|
done
|
||||||
|
|
||||||
DIFF=$(git diff)
|
DIFF=$(git -c core.fileMode=false diff)
|
||||||
|
|
||||||
if [ ! -z "$DIFF" ]; then
|
if [ ! -z "$DIFF" ]; then
|
||||||
echo "!!! Not compliant to coding style, here is the fix:"
|
echo "!!! Not compliant to coding style, here is the fix:"
|
||||||
echo "$DIFF"
|
echo "$DIFF"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
cd src/android
|
||||||
|
./gradlew ktlintCheck
|
||||||
|
@ -8,17 +8,7 @@ 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
|
||||||
@ -43,7 +33,6 @@ 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
|
||||||
|
8
.github/workflows/verify.yml
vendored
8
.github/workflows/verify.yml
vendored
@ -13,13 +13,15 @@ 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:
|
||||||
|
10
README.md
10
README.md
@ -1,10 +1,10 @@
|
|||||||
| Pull Request | Commit | Title | Author | Merged? |
|
| Pull Request | Commit | Title | Author | Merged? |
|
||||||
|----|----|----|----|----|
|
|----|----|----|----|----|
|
||||||
| [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 |
|
| [12579](https://github.com/yuzu-emu/yuzu-android//pull/12579) | [`748465f5a`](https://github.com/yuzu-emu/yuzu-android//pull/12579/files) | Core: Implement Device Mapping & GPU SMMU | [FernandoS27](https://github.com/FernandoS27/) | Yes |
|
||||||
| [12610](https://github.com/yuzu-emu/yuzu-android//pull/12610) | [`200b371d1`](https://github.com/yuzu-emu/yuzu-android//pull/12610/files) | server_manager: respond to session close correctly | [liamwhite](https://github.com/liamwhite/) | Yes |
|
| [12660](https://github.com/yuzu-emu/yuzu-android//pull/12660) | [`2cacb9d48`](https://github.com/yuzu-emu/yuzu-android//pull/12660/files) | service: hid: Fully implement abstract vibration | [german77](https://github.com/german77/) | Yes |
|
||||||
| [12611](https://github.com/yuzu-emu/yuzu-android//pull/12611) | [`642a29923`](https://github.com/yuzu-emu/yuzu-android//pull/12611/files) | kernel: fix resource management issues | [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 |
|
||||||
| [12612](https://github.com/yuzu-emu/yuzu-android//pull/12612) | [`417df3b6e`](https://github.com/yuzu-emu/yuzu-android//pull/12612/files) | fsp-srv: use program registry for SetCurrentProcess | [liamwhite](https://github.com/liamwhite/) | Yes |
|
| [12701](https://github.com/yuzu-emu/yuzu-android//pull/12701) | [`e4bbb24dc`](https://github.com/yuzu-emu/yuzu-android//pull/12701/files) | vi: check layer state before opening or closing | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||||
| [12642](https://github.com/yuzu-emu/yuzu-android//pull/12642) | [`4037afea1`](https://github.com/yuzu-emu/yuzu-android//pull/12642/files) | android: Refactor list adapters | [t895](https://github.com/t895/) | Yes |
|
| [12715](https://github.com/yuzu-emu/yuzu-android//pull/12715) | [`a363fa78e`](https://github.com/yuzu-emu/yuzu-android//pull/12715/files) | android: Add uninstall addon button | [t895](https://github.com/t895/) | 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.
|
||||||
|
@ -188,8 +188,15 @@ 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")
|
||||||
@ -228,71 +235,33 @@ dependencies {
|
|||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getGitVersion(): String {
|
fun runGitCommand(command: List<String>): String {
|
||||||
var versionName = "0.0"
|
return try {
|
||||||
|
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 =
|
||||||
try {
|
runGitCommand(listOf("git", "rev-parse", "--short", "HEAD")).ifEmpty { "dummy-hash" }
|
||||||
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()
|
|
||||||
|
|
||||||
return if (process.exitValue() == 0) {
|
fun getBranch(): String =
|
||||||
inputStream.bufferedReader()
|
runGitCommand(listOf("git", "rev-parse", "--abbrev-ref", "HEAD")).ifEmpty { "dummy-hash" }
|
||||||
.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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -21,6 +21,8 @@ import org.yuzu.yuzu_emu.utils.DocumentsTree
|
|||||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||||
import org.yuzu.yuzu_emu.utils.Log
|
import org.yuzu.yuzu_emu.utils.Log
|
||||||
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
|
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
|
||||||
|
import org.yuzu.yuzu_emu.model.InstallResult
|
||||||
|
import org.yuzu.yuzu_emu.model.Patch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class which contains methods that interact
|
* Class which contains methods that interact
|
||||||
@ -235,9 +237,12 @@ object NativeLibrary {
|
|||||||
/**
|
/**
|
||||||
* Installs a nsp or xci file to nand
|
* Installs a nsp or xci file to nand
|
||||||
* @param filename String representation of file uri
|
* @param filename String representation of file uri
|
||||||
* @param extension Lowercase string representation of file extension without "."
|
* @return int representation of [InstallResult]
|
||||||
*/
|
*/
|
||||||
external fun installFileToNand(filename: String, extension: String): Int
|
external fun installFileToNand(
|
||||||
|
filename: String,
|
||||||
|
callback: (max: Long, progress: Long) -> Boolean
|
||||||
|
): Int
|
||||||
|
|
||||||
external fun doesUpdateMatchProgram(programId: String, updatePath: String): Boolean
|
external fun doesUpdateMatchProgram(programId: String, updatePath: String): Boolean
|
||||||
|
|
||||||
@ -535,9 +540,29 @@ object NativeLibrary {
|
|||||||
*
|
*
|
||||||
* @param path Path to game file. Can be a [Uri].
|
* @param path Path to game file. Can be a [Uri].
|
||||||
* @param programId String representation of a game's program ID
|
* @param programId String representation of a game's program ID
|
||||||
* @return Array of pairs where the first value is the name of an addon and the second is the version
|
* @return Array of available patches
|
||||||
*/
|
*/
|
||||||
external fun getAddonsForFile(path: String, programId: String): Array<Pair<String, String>>?
|
external fun getPatchesForFile(path: String, programId: String): Array<Patch>?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an update for a given [programId]
|
||||||
|
* @param programId String representation of a game's program ID
|
||||||
|
*/
|
||||||
|
external fun removeUpdate(programId: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all DLC for a [programId]
|
||||||
|
* @param programId String representation of a game's program ID
|
||||||
|
*/
|
||||||
|
external fun removeDLC(programId: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a mod installed for a given [programId]
|
||||||
|
* @param programId String representation of a game's program ID
|
||||||
|
* @param name The name of a mod as given by [getPatchesForFile]. This corresponds with the name
|
||||||
|
* of the mod's directory in a game's load folder.
|
||||||
|
*/
|
||||||
|
external fun removeMod(programId: String, name: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the save location for a specific game
|
* Gets the save location for a specific game
|
||||||
@ -609,15 +634,4 @@ object NativeLibrary {
|
|||||||
const val RELEASED = 0
|
const val RELEASED = 0
|
||||||
const val PRESSED = 1
|
const val PRESSED = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Result from installFileToNand
|
|
||||||
*/
|
|
||||||
object InstallFileToNandResult {
|
|
||||||
const val Success = 0
|
|
||||||
const val SuccessFileOverwritten = 1
|
|
||||||
const val Error = 2
|
|
||||||
const val ErrorBaseGame = 3
|
|
||||||
const val ErrorFilenameExtension = 4
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,6 @@ import org.yuzu.yuzu_emu.utils.ForegroundService
|
|||||||
import org.yuzu.yuzu_emu.utils.InputHandler
|
import org.yuzu.yuzu_emu.utils.InputHandler
|
||||||
import org.yuzu.yuzu_emu.utils.Log
|
import org.yuzu.yuzu_emu.utils.Log
|
||||||
import org.yuzu.yuzu_emu.utils.MemoryUtil
|
import org.yuzu.yuzu_emu.utils.MemoryUtil
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
|
||||||
import org.yuzu.yuzu_emu.utils.NfcReader
|
import org.yuzu.yuzu_emu.utils.NfcReader
|
||||||
import org.yuzu.yuzu_emu.utils.ThemeHelper
|
import org.yuzu.yuzu_emu.utils.ThemeHelper
|
||||||
import java.text.NumberFormat
|
import java.text.NumberFormat
|
||||||
@ -171,11 +170,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
|||||||
stopMotionSensorListener()
|
stopMotionSensorListener()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
|
||||||
super.onStop()
|
|
||||||
NativeConfig.saveGlobalConfig()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUserLeaveHint() {
|
override fun onUserLeaveHint() {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
||||||
if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) {
|
if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) {
|
||||||
|
@ -6,27 +6,32 @@ package org.yuzu.yuzu_emu.adapters
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
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.Patch
|
||||||
|
import org.yuzu.yuzu_emu.model.AddonViewModel
|
||||||
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
||||||
|
|
||||||
class AddonAdapter : AbstractDiffAdapter<Addon, AddonAdapter.AddonViewHolder>() {
|
class AddonAdapter(val addonViewModel: AddonViewModel) :
|
||||||
|
AbstractDiffAdapter<Patch, AddonAdapter.AddonViewHolder>() {
|
||||||
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) }
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class AddonViewHolder(val binding: ListItemAddonBinding) :
|
inner class AddonViewHolder(val binding: ListItemAddonBinding) :
|
||||||
AbstractViewHolder<Addon>(binding) {
|
AbstractViewHolder<Patch>(binding) {
|
||||||
override fun bind(model: Addon) {
|
override fun bind(model: Patch) {
|
||||||
binding.root.setOnClickListener {
|
binding.root.setOnClickListener {
|
||||||
binding.addonSwitch.isChecked = !binding.addonSwitch.isChecked
|
binding.addonCheckbox.isChecked = !binding.addonCheckbox.isChecked
|
||||||
}
|
}
|
||||||
binding.title.text = model.title
|
binding.title.text = model.name
|
||||||
binding.version.text = model.version
|
binding.version.text = model.version
|
||||||
binding.addonSwitch.setOnCheckedChangeListener { _, checked ->
|
binding.addonCheckbox.setOnCheckedChangeListener { _, checked ->
|
||||||
model.enabled = checked
|
model.enabled = checked
|
||||||
}
|
}
|
||||||
binding.addonSwitch.isChecked = model.enabled
|
binding.addonCheckbox.isChecked = model.enabled
|
||||||
|
binding.buttonDelete.setOnClickListener {
|
||||||
|
addonViewModel.setAddonToDelete(model)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.textBuildHash.text = BuildConfig.GIT_HASH
|
binding.textVersionName.text = BuildConfig.VERSION_NAME
|
||||||
binding.buttonBuildHash.setOnClickListener {
|
binding.textVersionName.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)
|
||||||
|
@ -74,7 +74,7 @@ class AddonsFragment : Fragment() {
|
|||||||
|
|
||||||
binding.listAddons.apply {
|
binding.listAddons.apply {
|
||||||
layoutManager = LinearLayoutManager(requireContext())
|
layoutManager = LinearLayoutManager(requireContext())
|
||||||
adapter = AddonAdapter()
|
adapter = AddonAdapter(addonViewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.apply {
|
viewLifecycleOwner.lifecycleScope.apply {
|
||||||
@ -110,6 +110,21 @@ class AddonsFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
addonViewModel.addonToDelete.collect {
|
||||||
|
if (it != null) {
|
||||||
|
MessageDialogFragment.newInstance(
|
||||||
|
requireActivity(),
|
||||||
|
titleId = R.string.confirm_uninstall,
|
||||||
|
descriptionId = R.string.confirm_uninstall_description,
|
||||||
|
positiveAction = { addonViewModel.onDeleteAddon(it) }
|
||||||
|
).show(parentFragmentManager, MessageDialogFragment.TAG)
|
||||||
|
addonViewModel.setAddonToDelete(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.buttonInstall.setOnClickListener {
|
binding.buttonInstall.setOnClickListener {
|
||||||
@ -156,22 +171,22 @@ class AddonsFragment : Fragment() {
|
|||||||
descriptionId = R.string.invalid_directory_description
|
descriptionId = R.string.invalid_directory_description
|
||||||
)
|
)
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
ProgressDialogFragment.newInstance(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
R.string.installing_game_content,
|
R.string.installing_game_content,
|
||||||
false
|
false
|
||||||
) {
|
) { progressCallback, _ ->
|
||||||
val parentDirectoryName = externalAddonDirectory.name
|
val parentDirectoryName = externalAddonDirectory.name
|
||||||
val internalAddonDirectory =
|
val internalAddonDirectory =
|
||||||
File(args.game.addonDir + parentDirectoryName)
|
File(args.game.addonDir + parentDirectoryName)
|
||||||
try {
|
try {
|
||||||
externalAddonDirectory.copyFilesTo(internalAddonDirectory)
|
externalAddonDirectory.copyFilesTo(internalAddonDirectory, progressCallback)
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
return@newInstance errorMessage
|
return@newInstance errorMessage
|
||||||
}
|
}
|
||||||
addonViewModel.refreshAddons()
|
addonViewModel.refreshAddons()
|
||||||
return@newInstance getString(R.string.addon_installed_successfully)
|
return@newInstance getString(R.string.addon_installed_successfully)
|
||||||
}.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||||
} else {
|
} else {
|
||||||
errorMessage.show(parentFragmentManager, MessageDialogFragment.TAG)
|
errorMessage.show(parentFragmentManager, MessageDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
|
@ -173,11 +173,11 @@ class DriverManagerFragment : Fragment() {
|
|||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
ProgressDialogFragment.newInstance(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
R.string.installing_driver,
|
R.string.installing_driver,
|
||||||
false
|
false
|
||||||
) {
|
) { _, _ ->
|
||||||
val driverPath =
|
val driverPath =
|
||||||
"${GpuDriverHelper.driverStoragePath}${FileUtil.getFilename(result)}"
|
"${GpuDriverHelper.driverStoragePath}${FileUtil.getFilename(result)}"
|
||||||
val driverFile = File(driverPath)
|
val driverFile = File(driverPath)
|
||||||
@ -213,6 +213,6 @@ class DriverManagerFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return@newInstance Any()
|
return@newInstance Any()
|
||||||
}.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(childFragmentManager, ProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -554,6 +554,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
findItem(R.id.menu_touchscreen).isChecked = BooleanSetting.TOUCHSCREEN.getBoolean()
|
findItem(R.id.menu_touchscreen).isChecked = BooleanSetting.TOUCHSCREEN.getBoolean()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
popup.setOnDismissListener { NativeConfig.saveGlobalConfig() }
|
||||||
popup.setOnMenuItemClickListener {
|
popup.setOnMenuItemClickListener {
|
||||||
when (it.itemId) {
|
when (it.itemId) {
|
||||||
R.id.menu_toggle_fps -> {
|
R.id.menu_toggle_fps -> {
|
||||||
@ -720,7 +721,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
MaterialAlertDialogBuilder(requireContext())
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
.setTitle(R.string.emulation_control_adjust)
|
.setTitle(R.string.emulation_control_adjust)
|
||||||
.setView(adjustBinding.root)
|
.setView(adjustBinding.root)
|
||||||
.setPositiveButton(android.R.string.ok, null)
|
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
|
||||||
|
NativeConfig.saveGlobalConfig()
|
||||||
|
}
|
||||||
.setNeutralButton(R.string.slider_default) { _: DialogInterface?, _: Int ->
|
.setNeutralButton(R.string.slider_default) { _: DialogInterface?, _: Int ->
|
||||||
setControlScale(50)
|
setControlScale(50)
|
||||||
setControlOpacity(100)
|
setControlOpacity(100)
|
||||||
|
@ -44,7 +44,6 @@ import org.yuzu.yuzu_emu.utils.FileUtil
|
|||||||
import org.yuzu.yuzu_emu.utils.GameIconUtils
|
import org.yuzu.yuzu_emu.utils.GameIconUtils
|
||||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||||
import org.yuzu.yuzu_emu.utils.MemoryUtil
|
import org.yuzu.yuzu_emu.utils.MemoryUtil
|
||||||
import java.io.BufferedInputStream
|
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@ -357,27 +356,17 @@ class GamePropertiesFragment : Fragment() {
|
|||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
val inputZip = requireContext().contentResolver.openInputStream(result)
|
|
||||||
val savesFolder = File(args.game.saveDir)
|
val savesFolder = File(args.game.saveDir)
|
||||||
val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
|
val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
|
||||||
cacheSaveDir.mkdir()
|
cacheSaveDir.mkdir()
|
||||||
|
|
||||||
if (inputZip == null) {
|
ProgressDialogFragment.newInstance(
|
||||||
Toast.makeText(
|
|
||||||
YuzuApplication.appContext,
|
|
||||||
getString(R.string.fatal_error),
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
return@registerForActivityResult
|
|
||||||
}
|
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
R.string.save_files_importing,
|
R.string.save_files_importing,
|
||||||
false
|
false
|
||||||
) {
|
) { _, _ ->
|
||||||
try {
|
try {
|
||||||
FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheSaveDir)
|
FileUtil.unzipToInternalStorage(result.toString(), cacheSaveDir)
|
||||||
val files = cacheSaveDir.listFiles()
|
val files = cacheSaveDir.listFiles()
|
||||||
var savesFolderFile: File? = null
|
var savesFolderFile: File? = null
|
||||||
if (files != null) {
|
if (files != null) {
|
||||||
@ -422,7 +411,7 @@ class GamePropertiesFragment : Fragment() {
|
|||||||
Toast.LENGTH_LONG
|
Toast.LENGTH_LONG
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
}.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -436,11 +425,11 @@ class GamePropertiesFragment : Fragment() {
|
|||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
ProgressDialogFragment.newInstance(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
R.string.save_files_exporting,
|
R.string.save_files_exporting,
|
||||||
false
|
false
|
||||||
) {
|
) { _, _ ->
|
||||||
val saveLocation = args.game.saveDir
|
val saveLocation = args.game.saveDir
|
||||||
val zipResult = FileUtil.zipFromInternalStorage(
|
val zipResult = FileUtil.zipFromInternalStorage(
|
||||||
File(saveLocation),
|
File(saveLocation),
|
||||||
@ -452,6 +441,6 @@ class GamePropertiesFragment : Fragment() {
|
|||||||
TaskState.Completed -> getString(R.string.export_success)
|
TaskState.Completed -> getString(R.string.export_success)
|
||||||
TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
|
TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
|
||||||
}
|
}
|
||||||
}.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,6 @@ import org.yuzu.yuzu_emu.model.TaskState
|
|||||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
||||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||||
import java.io.BufferedInputStream
|
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
@ -195,26 +194,20 @@ class InstallableFragment : Fragment() {
|
|||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
val inputZip = requireContext().contentResolver.openInputStream(result)
|
|
||||||
val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
|
val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
|
||||||
cacheSaveDir.mkdir()
|
cacheSaveDir.mkdir()
|
||||||
|
|
||||||
if (inputZip == null) {
|
ProgressDialogFragment.newInstance(
|
||||||
Toast.makeText(
|
|
||||||
YuzuApplication.appContext,
|
|
||||||
getString(R.string.fatal_error),
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
return@registerForActivityResult
|
|
||||||
}
|
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
R.string.save_files_importing,
|
R.string.save_files_importing,
|
||||||
false
|
false
|
||||||
) {
|
) { progressCallback, _ ->
|
||||||
try {
|
try {
|
||||||
FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheSaveDir)
|
FileUtil.unzipToInternalStorage(
|
||||||
|
result.toString(),
|
||||||
|
cacheSaveDir,
|
||||||
|
progressCallback
|
||||||
|
)
|
||||||
val files = cacheSaveDir.listFiles()
|
val files = cacheSaveDir.listFiles()
|
||||||
var successfulImports = 0
|
var successfulImports = 0
|
||||||
var failedImports = 0
|
var failedImports = 0
|
||||||
@ -287,7 +280,7 @@ class InstallableFragment : Fragment() {
|
|||||||
Toast.LENGTH_LONG
|
Toast.LENGTH_LONG
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
}.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val exportSaves = registerForActivityResult(
|
private val exportSaves = registerForActivityResult(
|
||||||
@ -297,11 +290,11 @@ class InstallableFragment : Fragment() {
|
|||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
ProgressDialogFragment.newInstance(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
R.string.save_files_exporting,
|
R.string.save_files_exporting,
|
||||||
false
|
false
|
||||||
) {
|
) { _, _ ->
|
||||||
val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
|
val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
|
||||||
cacheSaveDir.mkdir()
|
cacheSaveDir.mkdir()
|
||||||
|
|
||||||
@ -338,6 +331,6 @@ class InstallableFragment : Fragment() {
|
|||||||
TaskState.Completed -> getString(R.string.export_success)
|
TaskState.Completed -> getString(R.string.export_success)
|
||||||
TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
|
TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
|
||||||
}
|
}
|
||||||
}.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,11 +23,13 @@ import org.yuzu.yuzu_emu.R
|
|||||||
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
|
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
|
||||||
import org.yuzu.yuzu_emu.model.TaskViewModel
|
import org.yuzu.yuzu_emu.model.TaskViewModel
|
||||||
|
|
||||||
class IndeterminateProgressDialogFragment : DialogFragment() {
|
class ProgressDialogFragment : DialogFragment() {
|
||||||
private val taskViewModel: TaskViewModel by activityViewModels()
|
private val taskViewModel: TaskViewModel by activityViewModels()
|
||||||
|
|
||||||
private lateinit var binding: DialogProgressBarBinding
|
private lateinit var binding: DialogProgressBarBinding
|
||||||
|
|
||||||
|
private val PROGRESS_BAR_RESOLUTION = 1000
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val titleId = requireArguments().getInt(TITLE)
|
val titleId = requireArguments().getInt(TITLE)
|
||||||
val cancellable = requireArguments().getBoolean(CANCELLABLE)
|
val cancellable = requireArguments().getBoolean(CANCELLABLE)
|
||||||
@ -61,6 +63,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
|||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
binding.message.isSelected = true
|
||||||
viewLifecycleOwner.lifecycleScope.apply {
|
viewLifecycleOwner.lifecycleScope.apply {
|
||||||
launch {
|
launch {
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||||
@ -97,6 +100,35 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||||
|
taskViewModel.progress.collect {
|
||||||
|
if (it != 0.0) {
|
||||||
|
binding.progressBar.apply {
|
||||||
|
isIndeterminate = false
|
||||||
|
progress = (
|
||||||
|
(it / taskViewModel.maxProgress.value) *
|
||||||
|
PROGRESS_BAR_RESOLUTION
|
||||||
|
).toInt()
|
||||||
|
min = 0
|
||||||
|
max = PROGRESS_BAR_RESOLUTION
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||||
|
taskViewModel.message.collect {
|
||||||
|
if (it.isEmpty()) {
|
||||||
|
binding.message.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
binding.message.visibility = View.VISIBLE
|
||||||
|
binding.message.text = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +140,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
|||||||
val negativeButton = alertDialog.getButton(Dialog.BUTTON_NEGATIVE)
|
val negativeButton = alertDialog.getButton(Dialog.BUTTON_NEGATIVE)
|
||||||
negativeButton.setOnClickListener {
|
negativeButton.setOnClickListener {
|
||||||
alertDialog.setTitle(getString(R.string.cancelling))
|
alertDialog.setTitle(getString(R.string.cancelling))
|
||||||
|
binding.progressBar.isIndeterminate = true
|
||||||
taskViewModel.setCancelled(true)
|
taskViewModel.setCancelled(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,9 +155,12 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
|||||||
activity: FragmentActivity,
|
activity: FragmentActivity,
|
||||||
titleId: Int,
|
titleId: Int,
|
||||||
cancellable: Boolean = false,
|
cancellable: Boolean = false,
|
||||||
task: suspend () -> Any
|
task: suspend (
|
||||||
): IndeterminateProgressDialogFragment {
|
progressCallback: (max: Long, progress: Long) -> Boolean,
|
||||||
val dialog = IndeterminateProgressDialogFragment()
|
messageCallback: (message: String) -> Unit
|
||||||
|
) -> Any
|
||||||
|
): ProgressDialogFragment {
|
||||||
|
val dialog = ProgressDialogFragment()
|
||||||
val args = Bundle()
|
val args = Bundle()
|
||||||
ViewModelProvider(activity)[TaskViewModel::class.java].task = task
|
ViewModelProvider(activity)[TaskViewModel::class.java].task = task
|
||||||
args.putInt(TITLE, titleId)
|
args.putInt(TITLE, titleId)
|
@ -1,10 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.model
|
|
||||||
|
|
||||||
data class Addon(
|
|
||||||
var enabled: Boolean,
|
|
||||||
val title: String,
|
|
||||||
val version: String
|
|
||||||
)
|
|
@ -15,8 +15,8 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
|
|||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
class AddonViewModel : ViewModel() {
|
class AddonViewModel : ViewModel() {
|
||||||
private val _addonList = MutableStateFlow(mutableListOf<Addon>())
|
private val _patchList = MutableStateFlow(mutableListOf<Patch>())
|
||||||
val addonList get() = _addonList.asStateFlow()
|
val addonList get() = _patchList.asStateFlow()
|
||||||
|
|
||||||
private val _showModInstallPicker = MutableStateFlow(false)
|
private val _showModInstallPicker = MutableStateFlow(false)
|
||||||
val showModInstallPicker get() = _showModInstallPicker.asStateFlow()
|
val showModInstallPicker get() = _showModInstallPicker.asStateFlow()
|
||||||
@ -24,6 +24,9 @@ class AddonViewModel : ViewModel() {
|
|||||||
private val _showModNoticeDialog = MutableStateFlow(false)
|
private val _showModNoticeDialog = MutableStateFlow(false)
|
||||||
val showModNoticeDialog get() = _showModNoticeDialog.asStateFlow()
|
val showModNoticeDialog get() = _showModNoticeDialog.asStateFlow()
|
||||||
|
|
||||||
|
private val _addonToDelete = MutableStateFlow<Patch?>(null)
|
||||||
|
val addonToDelete = _addonToDelete.asStateFlow()
|
||||||
|
|
||||||
var game: Game? = null
|
var game: Game? = null
|
||||||
|
|
||||||
private val isRefreshing = AtomicBoolean(false)
|
private val isRefreshing = AtomicBoolean(false)
|
||||||
@ -40,36 +43,47 @@ class AddonViewModel : ViewModel() {
|
|||||||
isRefreshing.set(true)
|
isRefreshing.set(true)
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val addonList = mutableListOf<Addon>()
|
val patchList = (
|
||||||
val disabledAddons = NativeConfig.getDisabledAddons(game!!.programId)
|
NativeLibrary.getPatchesForFile(game!!.path, game!!.programId)
|
||||||
NativeLibrary.getAddonsForFile(game!!.path, game!!.programId)?.forEach {
|
?: emptyArray()
|
||||||
val name = it.first.replace("[D] ", "")
|
).toMutableList()
|
||||||
addonList.add(Addon(!disabledAddons.contains(name), name, it.second))
|
patchList.sortBy { it.name }
|
||||||
}
|
_patchList.value = patchList
|
||||||
addonList.sortBy { it.title }
|
|
||||||
_addonList.value = addonList
|
|
||||||
isRefreshing.set(false)
|
isRefreshing.set(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setAddonToDelete(patch: Patch?) {
|
||||||
|
_addonToDelete.value = patch
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDeleteAddon(patch: Patch) {
|
||||||
|
when (PatchType.from(patch.type)) {
|
||||||
|
PatchType.Update -> NativeLibrary.removeUpdate(patch.programId)
|
||||||
|
PatchType.DLC -> NativeLibrary.removeDLC(patch.programId)
|
||||||
|
PatchType.Mod -> NativeLibrary.removeMod(patch.programId, patch.name)
|
||||||
|
}
|
||||||
|
refreshAddons()
|
||||||
|
}
|
||||||
|
|
||||||
fun onCloseAddons() {
|
fun onCloseAddons() {
|
||||||
if (_addonList.value.isEmpty()) {
|
if (_patchList.value.isEmpty()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeConfig.setDisabledAddons(
|
NativeConfig.setDisabledAddons(
|
||||||
game!!.programId,
|
game!!.programId,
|
||||||
_addonList.value.mapNotNull {
|
_patchList.value.mapNotNull {
|
||||||
if (it.enabled) {
|
if (it.enabled) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
it.title
|
it.name
|
||||||
}
|
}
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
)
|
)
|
||||||
NativeConfig.saveGlobalConfig()
|
NativeConfig.saveGlobalConfig()
|
||||||
_addonList.value.clear()
|
_patchList.value.clear()
|
||||||
game = null
|
game = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.model
|
||||||
|
|
||||||
|
enum class InstallResult(val int: Int) {
|
||||||
|
Success(0),
|
||||||
|
Overwrite(1),
|
||||||
|
Failure(2),
|
||||||
|
BaseInstallAttempted(3);
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun from(int: Int): InstallResult = entries.firstOrNull { it.int == int } ?: Success
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.model
|
||||||
|
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
data class Patch(
|
||||||
|
var enabled: Boolean,
|
||||||
|
val name: String,
|
||||||
|
val version: String,
|
||||||
|
val type: Int,
|
||||||
|
val programId: String,
|
||||||
|
val titleId: String
|
||||||
|
)
|
@ -0,0 +1,14 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.model
|
||||||
|
|
||||||
|
enum class PatchType(val int: Int) {
|
||||||
|
Update(0),
|
||||||
|
DLC(1),
|
||||||
|
Mod(2);
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun from(int: Int): PatchType = entries.firstOrNull { it.int == int } ?: Update
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ import androidx.lifecycle.viewModelScope
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class TaskViewModel : ViewModel() {
|
class TaskViewModel : ViewModel() {
|
||||||
@ -23,13 +24,28 @@ class TaskViewModel : ViewModel() {
|
|||||||
val cancelled: StateFlow<Boolean> get() = _cancelled
|
val cancelled: StateFlow<Boolean> get() = _cancelled
|
||||||
private val _cancelled = MutableStateFlow(false)
|
private val _cancelled = MutableStateFlow(false)
|
||||||
|
|
||||||
lateinit var task: suspend () -> Any
|
private val _progress = MutableStateFlow(0.0)
|
||||||
|
val progress = _progress.asStateFlow()
|
||||||
|
|
||||||
|
private val _maxProgress = MutableStateFlow(0.0)
|
||||||
|
val maxProgress = _maxProgress.asStateFlow()
|
||||||
|
|
||||||
|
private val _message = MutableStateFlow("")
|
||||||
|
val message = _message.asStateFlow()
|
||||||
|
|
||||||
|
lateinit var task: suspend (
|
||||||
|
progressCallback: (max: Long, progress: Long) -> Boolean,
|
||||||
|
messageCallback: (message: String) -> Unit
|
||||||
|
) -> Any
|
||||||
|
|
||||||
fun clear() {
|
fun clear() {
|
||||||
_result.value = Any()
|
_result.value = Any()
|
||||||
_isComplete.value = false
|
_isComplete.value = false
|
||||||
_isRunning.value = false
|
_isRunning.value = false
|
||||||
_cancelled.value = false
|
_cancelled.value = false
|
||||||
|
_progress.value = 0.0
|
||||||
|
_maxProgress.value = 0.0
|
||||||
|
_message.value = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setCancelled(value: Boolean) {
|
fun setCancelled(value: Boolean) {
|
||||||
@ -43,7 +59,16 @@ class TaskViewModel : ViewModel() {
|
|||||||
_isRunning.value = true
|
_isRunning.value = true
|
||||||
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val res = task()
|
val res = task(
|
||||||
|
{ max, progress ->
|
||||||
|
_maxProgress.value = max.toDouble()
|
||||||
|
_progress.value = progress.toDouble()
|
||||||
|
return@task cancelled.value
|
||||||
|
},
|
||||||
|
{ message ->
|
||||||
|
_message.value = message
|
||||||
|
}
|
||||||
|
)
|
||||||
_result.value = res
|
_result.value = res
|
||||||
_isComplete.value = true
|
_isComplete.value = true
|
||||||
_isRunning.value = false
|
_isRunning.value = false
|
||||||
|
@ -38,12 +38,13 @@ import org.yuzu.yuzu_emu.activities.EmulationActivity
|
|||||||
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
|
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||||
import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment
|
import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment
|
||||||
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
|
import org.yuzu.yuzu_emu.fragments.ProgressDialogFragment
|
||||||
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.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.InstallResult
|
||||||
import org.yuzu.yuzu_emu.model.TaskState
|
import org.yuzu.yuzu_emu.model.TaskState
|
||||||
import org.yuzu.yuzu_emu.model.TaskViewModel
|
import org.yuzu.yuzu_emu.model.TaskViewModel
|
||||||
import org.yuzu.yuzu_emu.utils.*
|
import org.yuzu.yuzu_emu.utils.*
|
||||||
@ -369,26 +370,23 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
val inputZip = contentResolver.openInputStream(result)
|
|
||||||
if (inputZip == null) {
|
|
||||||
Toast.makeText(
|
|
||||||
applicationContext,
|
|
||||||
getString(R.string.fatal_error),
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
return@registerForActivityResult
|
|
||||||
}
|
|
||||||
|
|
||||||
val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") }
|
val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") }
|
||||||
|
|
||||||
val firmwarePath =
|
val firmwarePath =
|
||||||
File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/")
|
File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/")
|
||||||
val cacheFirmwareDir = File("${cacheDir.path}/registered/")
|
val cacheFirmwareDir = File("${cacheDir.path}/registered/")
|
||||||
|
|
||||||
val task: () -> Any = {
|
ProgressDialogFragment.newInstance(
|
||||||
|
this,
|
||||||
|
R.string.firmware_installing
|
||||||
|
) { progressCallback, _ ->
|
||||||
var messageToShow: Any
|
var messageToShow: Any
|
||||||
try {
|
try {
|
||||||
FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheFirmwareDir)
|
FileUtil.unzipToInternalStorage(
|
||||||
|
result.toString(),
|
||||||
|
cacheFirmwareDir,
|
||||||
|
progressCallback
|
||||||
|
)
|
||||||
val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1
|
val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1
|
||||||
val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2
|
val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2
|
||||||
messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) {
|
messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) {
|
||||||
@ -404,18 +402,13 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
getString(R.string.save_file_imported_success)
|
getString(R.string.save_file_imported_success)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
Log.error("[MainActivity] Firmware install failed - ${e.message}")
|
||||||
messageToShow = getString(R.string.fatal_error)
|
messageToShow = getString(R.string.fatal_error)
|
||||||
} finally {
|
} finally {
|
||||||
cacheFirmwareDir.deleteRecursively()
|
cacheFirmwareDir.deleteRecursively()
|
||||||
}
|
}
|
||||||
messageToShow
|
messageToShow
|
||||||
}
|
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
|
||||||
this,
|
|
||||||
R.string.firmware_installing,
|
|
||||||
task = task
|
|
||||||
).show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val getAmiiboKey =
|
val getAmiiboKey =
|
||||||
@ -474,11 +467,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
ProgressDialogFragment.newInstance(
|
||||||
this@MainActivity,
|
this@MainActivity,
|
||||||
R.string.verifying_content,
|
R.string.verifying_content,
|
||||||
false
|
false
|
||||||
) {
|
) { _, _ ->
|
||||||
var updatesMatchProgram = true
|
var updatesMatchProgram = true
|
||||||
for (document in documents) {
|
for (document in documents) {
|
||||||
val valid = NativeLibrary.doesUpdateMatchProgram(
|
val valid = NativeLibrary.doesUpdateMatchProgram(
|
||||||
@ -501,44 +494,42 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
positiveAction = { homeViewModel.setContentToInstall(documents) }
|
positiveAction = { homeViewModel.setContentToInstall(documents) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun installContent(documents: List<Uri>) {
|
private fun installContent(documents: List<Uri>) {
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
ProgressDialogFragment.newInstance(
|
||||||
this@MainActivity,
|
this@MainActivity,
|
||||||
R.string.installing_game_content
|
R.string.installing_game_content
|
||||||
) {
|
) { progressCallback, messageCallback ->
|
||||||
var installSuccess = 0
|
var installSuccess = 0
|
||||||
var installOverwrite = 0
|
var installOverwrite = 0
|
||||||
var errorBaseGame = 0
|
var errorBaseGame = 0
|
||||||
var errorExtension = 0
|
var error = 0
|
||||||
var errorOther = 0
|
|
||||||
documents.forEach {
|
documents.forEach {
|
||||||
|
messageCallback.invoke(FileUtil.getFilename(it))
|
||||||
when (
|
when (
|
||||||
NativeLibrary.installFileToNand(
|
InstallResult.from(
|
||||||
it.toString(),
|
NativeLibrary.installFileToNand(
|
||||||
FileUtil.getExtension(it)
|
it.toString(),
|
||||||
|
progressCallback
|
||||||
|
)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
NativeLibrary.InstallFileToNandResult.Success -> {
|
InstallResult.Success -> {
|
||||||
installSuccess += 1
|
installSuccess += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> {
|
InstallResult.Overwrite -> {
|
||||||
installOverwrite += 1
|
installOverwrite += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> {
|
InstallResult.BaseInstallAttempted -> {
|
||||||
errorBaseGame += 1
|
errorBaseGame += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> {
|
InstallResult.Failure -> {
|
||||||
errorExtension += 1
|
error += 1
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
errorOther += 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -565,7 +556,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
)
|
)
|
||||||
installResult.append(separator)
|
installResult.append(separator)
|
||||||
}
|
}
|
||||||
val errorTotal: Int = errorBaseGame + errorExtension + errorOther
|
val errorTotal: Int = errorBaseGame + error
|
||||||
if (errorTotal > 0) {
|
if (errorTotal > 0) {
|
||||||
installResult.append(separator)
|
installResult.append(separator)
|
||||||
installResult.append(
|
installResult.append(
|
||||||
@ -582,14 +573,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
)
|
)
|
||||||
installResult.append(separator)
|
installResult.append(separator)
|
||||||
}
|
}
|
||||||
if (errorExtension > 0) {
|
if (error > 0) {
|
||||||
installResult.append(separator)
|
|
||||||
installResult.append(
|
|
||||||
getString(R.string.install_game_content_failure_file_extension)
|
|
||||||
)
|
|
||||||
installResult.append(separator)
|
|
||||||
}
|
|
||||||
if (errorOther > 0) {
|
|
||||||
installResult.append(
|
installResult.append(
|
||||||
getString(R.string.install_game_content_failure_description)
|
getString(R.string.install_game_content_failure_description)
|
||||||
)
|
)
|
||||||
@ -608,7 +592,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
descriptionString = installResult.toString().trim()
|
descriptionString = installResult.toString().trim()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
|
|
||||||
val exportUserData = registerForActivityResult(
|
val exportUserData = registerForActivityResult(
|
||||||
@ -618,16 +602,16 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
ProgressDialogFragment.newInstance(
|
||||||
this,
|
this,
|
||||||
R.string.exporting_user_data,
|
R.string.exporting_user_data,
|
||||||
true
|
true
|
||||||
) {
|
) { progressCallback, _ ->
|
||||||
val zipResult = FileUtil.zipFromInternalStorage(
|
val zipResult = FileUtil.zipFromInternalStorage(
|
||||||
File(DirectoryInitialization.userDirectory!!),
|
File(DirectoryInitialization.userDirectory!!),
|
||||||
DirectoryInitialization.userDirectory!!,
|
DirectoryInitialization.userDirectory!!,
|
||||||
BufferedOutputStream(contentResolver.openOutputStream(result)),
|
BufferedOutputStream(contentResolver.openOutputStream(result)),
|
||||||
taskViewModel.cancelled,
|
progressCallback,
|
||||||
compression = false
|
compression = false
|
||||||
)
|
)
|
||||||
return@newInstance when (zipResult) {
|
return@newInstance when (zipResult) {
|
||||||
@ -635,7 +619,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
TaskState.Failed -> R.string.export_failed
|
TaskState.Failed -> R.string.export_failed
|
||||||
TaskState.Cancelled -> R.string.user_data_export_cancelled
|
TaskState.Cancelled -> R.string.user_data_export_cancelled
|
||||||
}
|
}
|
||||||
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
|
|
||||||
val importUserData =
|
val importUserData =
|
||||||
@ -644,10 +628,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
ProgressDialogFragment.newInstance(
|
||||||
this,
|
this,
|
||||||
R.string.importing_user_data
|
R.string.importing_user_data
|
||||||
) {
|
) { progressCallback, _ ->
|
||||||
val checkStream =
|
val checkStream =
|
||||||
ZipInputStream(BufferedInputStream(contentResolver.openInputStream(result)))
|
ZipInputStream(BufferedInputStream(contentResolver.openInputStream(result)))
|
||||||
var isYuzuBackup = false
|
var isYuzuBackup = false
|
||||||
@ -676,8 +660,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
// Copy archive to internal storage
|
// Copy archive to internal storage
|
||||||
try {
|
try {
|
||||||
FileUtil.unzipToInternalStorage(
|
FileUtil.unzipToInternalStorage(
|
||||||
BufferedInputStream(contentResolver.openInputStream(result)),
|
result.toString(),
|
||||||
File(DirectoryInitialization.userDirectory!!)
|
File(DirectoryInitialization.userDirectory!!),
|
||||||
|
progressCallback
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
return@newInstance MessageDialogFragment.newInstance(
|
return@newInstance MessageDialogFragment.newInstance(
|
||||||
@ -694,6 +679,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
driverViewModel.reloadDriverData()
|
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, ProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import android.database.Cursor
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@ -19,6 +18,7 @@ import org.yuzu.yuzu_emu.YuzuApplication
|
|||||||
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
||||||
import org.yuzu.yuzu_emu.model.TaskState
|
import org.yuzu.yuzu_emu.model.TaskState
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
|
import java.io.OutputStream
|
||||||
import java.lang.NullPointerException
|
import java.lang.NullPointerException
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.util.zip.Deflater
|
import java.util.zip.Deflater
|
||||||
@ -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 DoucmentFile.listFiles
|
* This function will be faster than DocumentFile.listFiles
|
||||||
* @param uri Directory uri.
|
* @param uri Directory uri.
|
||||||
* @return CheapDocument lists.
|
* @return CheapDocument lists.
|
||||||
*/
|
*/
|
||||||
@ -283,12 +283,34 @@ object FileUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts the given zip file into the given directory.
|
* Extracts the given zip file into the given directory.
|
||||||
|
* @param path String representation of a [Uri] or a typical path delimited by '/'
|
||||||
|
* @param destDir Location to unzip the contents of [path] into
|
||||||
|
* @param progressCallback Lambda that is called with the total number of files and the current
|
||||||
|
* progress through the process. Stops execution as soon as possible if this returns true.
|
||||||
*/
|
*/
|
||||||
@Throws(SecurityException::class)
|
@Throws(SecurityException::class)
|
||||||
fun unzipToInternalStorage(zipStream: BufferedInputStream, destDir: File) {
|
fun unzipToInternalStorage(
|
||||||
ZipInputStream(zipStream).use { zis ->
|
path: String,
|
||||||
|
destDir: File,
|
||||||
|
progressCallback: (max: Long, progress: Long) -> Boolean = { _, _ -> false }
|
||||||
|
) {
|
||||||
|
var totalEntries = 0L
|
||||||
|
ZipInputStream(getInputStream(path)).use { zis ->
|
||||||
|
var tempEntry = zis.nextEntry
|
||||||
|
while (tempEntry != null) {
|
||||||
|
tempEntry = zis.nextEntry
|
||||||
|
totalEntries++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var progress = 0L
|
||||||
|
ZipInputStream(getInputStream(path)).use { zis ->
|
||||||
var entry: ZipEntry? = zis.nextEntry
|
var entry: ZipEntry? = zis.nextEntry
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
|
if (progressCallback.invoke(totalEntries, progress)) {
|
||||||
|
return@use
|
||||||
|
}
|
||||||
|
|
||||||
val newFile = File(destDir, entry.name)
|
val newFile = File(destDir, entry.name)
|
||||||
val destinationDirectory = if (entry.isDirectory) newFile else newFile.parentFile
|
val destinationDirectory = if (entry.isDirectory) newFile else newFile.parentFile
|
||||||
|
|
||||||
@ -304,6 +326,7 @@ object FileUtil {
|
|||||||
newFile.outputStream().use { fos -> zis.copyTo(fos) }
|
newFile.outputStream().use { fos -> zis.copyTo(fos) }
|
||||||
}
|
}
|
||||||
entry = zis.nextEntry
|
entry = zis.nextEntry
|
||||||
|
progress++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -313,14 +336,15 @@ object FileUtil {
|
|||||||
* @param inputFile File representation of the item that will be zipped
|
* @param inputFile File representation of the item that will be zipped
|
||||||
* @param rootDir Directory containing the inputFile
|
* @param rootDir Directory containing the inputFile
|
||||||
* @param outputStream Stream where the zip file will be output
|
* @param outputStream Stream where the zip file will be output
|
||||||
* @param cancelled [StateFlow] that reports whether this process has been cancelled
|
* @param progressCallback Lambda that is called with the total number of files and the current
|
||||||
|
* progress through the process. Stops execution as soon as possible if this returns true.
|
||||||
* @param compression Disables compression if true
|
* @param compression Disables compression if true
|
||||||
*/
|
*/
|
||||||
fun zipFromInternalStorage(
|
fun zipFromInternalStorage(
|
||||||
inputFile: File,
|
inputFile: File,
|
||||||
rootDir: String,
|
rootDir: String,
|
||||||
outputStream: BufferedOutputStream,
|
outputStream: BufferedOutputStream,
|
||||||
cancelled: StateFlow<Boolean>? = null,
|
progressCallback: (max: Long, progress: Long) -> Boolean = { _, _ -> false },
|
||||||
compression: Boolean = true
|
compression: Boolean = true
|
||||||
): TaskState {
|
): TaskState {
|
||||||
try {
|
try {
|
||||||
@ -330,8 +354,10 @@ object FileUtil {
|
|||||||
zos.setLevel(Deflater.NO_COMPRESSION)
|
zos.setLevel(Deflater.NO_COMPRESSION)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var count = 0L
|
||||||
|
val totalFiles = inputFile.walkTopDown().count().toLong()
|
||||||
inputFile.walkTopDown().forEach { file ->
|
inputFile.walkTopDown().forEach { file ->
|
||||||
if (cancelled?.value == true) {
|
if (progressCallback.invoke(totalFiles, count)) {
|
||||||
return TaskState.Cancelled
|
return TaskState.Cancelled
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,6 +369,7 @@ object FileUtil {
|
|||||||
if (file.isFile) {
|
if (file.isFile) {
|
||||||
file.inputStream().use { fis -> fis.copyTo(zos) }
|
file.inputStream().use { fis -> fis.copyTo(zos) }
|
||||||
}
|
}
|
||||||
|
count++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -356,9 +383,14 @@ object FileUtil {
|
|||||||
/**
|
/**
|
||||||
* Helper function that copies the contents of a DocumentFile folder into a [File]
|
* Helper function that copies the contents of a DocumentFile folder into a [File]
|
||||||
* @param file [File] representation of the folder to copy into
|
* @param file [File] representation of the folder to copy into
|
||||||
|
* @param progressCallback Lambda that is called with the total number of files and the current
|
||||||
|
* progress through the process. Stops execution as soon as possible if this returns true.
|
||||||
* @throws IllegalStateException Fails when trying to copy a folder into a file and vice versa
|
* @throws IllegalStateException Fails when trying to copy a folder into a file and vice versa
|
||||||
*/
|
*/
|
||||||
fun DocumentFile.copyFilesTo(file: File) {
|
fun DocumentFile.copyFilesTo(
|
||||||
|
file: File,
|
||||||
|
progressCallback: (max: Long, progress: Long) -> Boolean = { _, _ -> false }
|
||||||
|
) {
|
||||||
file.mkdirs()
|
file.mkdirs()
|
||||||
if (!this.isDirectory || !file.isDirectory) {
|
if (!this.isDirectory || !file.isDirectory) {
|
||||||
throw IllegalStateException(
|
throw IllegalStateException(
|
||||||
@ -366,7 +398,13 @@ object FileUtil {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var count = 0L
|
||||||
|
val totalFiles = this.listFiles().size.toLong()
|
||||||
this.listFiles().forEach {
|
this.listFiles().forEach {
|
||||||
|
if (progressCallback.invoke(totalFiles, count)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val newFile = File(file, it.name!!)
|
val newFile = File(file, it.name!!)
|
||||||
if (it.isDirectory) {
|
if (it.isDirectory) {
|
||||||
newFile.mkdirs()
|
newFile.mkdirs()
|
||||||
@ -381,6 +419,7 @@ object FileUtil {
|
|||||||
newFile.outputStream().use { os -> bos.copyTo(os) }
|
newFile.outputStream().use { os -> bos.copyTo(os) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
count++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -427,6 +466,18 @@ object FileUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getInputStream(path: String) = if (path.contains("content://")) {
|
||||||
|
Uri.parse(path).inputStream()
|
||||||
|
} else {
|
||||||
|
File(path).inputStream()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getOutputStream(path: String) = if (path.contains("content://")) {
|
||||||
|
Uri.parse(path).outputStream()
|
||||||
|
} else {
|
||||||
|
File(path).outputStream()
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun getStringFromFile(file: File): String =
|
fun getStringFromFile(file: File): String =
|
||||||
String(file.readBytes(), StandardCharsets.UTF_8)
|
String(file.readBytes(), StandardCharsets.UTF_8)
|
||||||
@ -434,4 +485,19 @@ object FileUtil {
|
|||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun getStringFromInputStream(stream: InputStream): String =
|
fun getStringFromInputStream(stream: InputStream): String =
|
||||||
String(stream.readBytes(), StandardCharsets.UTF_8)
|
String(stream.readBytes(), StandardCharsets.UTF_8)
|
||||||
|
|
||||||
|
fun DocumentFile.inputStream(): InputStream =
|
||||||
|
YuzuApplication.appContext.contentResolver.openInputStream(uri)!!
|
||||||
|
|
||||||
|
fun DocumentFile.outputStream(): OutputStream =
|
||||||
|
YuzuApplication.appContext.contentResolver.openOutputStream(uri)!!
|
||||||
|
|
||||||
|
fun Uri.inputStream(): InputStream =
|
||||||
|
YuzuApplication.appContext.contentResolver.openInputStream(this)!!
|
||||||
|
|
||||||
|
fun Uri.outputStream(): OutputStream =
|
||||||
|
YuzuApplication.appContext.contentResolver.openOutputStream(this)!!
|
||||||
|
|
||||||
|
fun Uri.asDocumentFile(): DocumentFile? =
|
||||||
|
DocumentFile.fromSingleUri(YuzuApplication.appContext, this)
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.utils
|
|||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import java.io.BufferedInputStream
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
@ -123,7 +122,7 @@ object GpuDriverHelper {
|
|||||||
// Unzip the driver.
|
// Unzip the driver.
|
||||||
try {
|
try {
|
||||||
FileUtil.unzipToInternalStorage(
|
FileUtil.unzipToInternalStorage(
|
||||||
BufferedInputStream(copiedFile.inputStream()),
|
copiedFile.path,
|
||||||
File(driverInstallationPath!!)
|
File(driverInstallationPath!!)
|
||||||
)
|
)
|
||||||
} catch (e: SecurityException) {
|
} catch (e: SecurityException) {
|
||||||
@ -156,7 +155,7 @@ object GpuDriverHelper {
|
|||||||
// Unzip the driver to the private installation directory
|
// Unzip the driver to the private installation directory
|
||||||
try {
|
try {
|
||||||
FileUtil.unzipToInternalStorage(
|
FileUtil.unzipToInternalStorage(
|
||||||
BufferedInputStream(driver.inputStream()),
|
driver.path,
|
||||||
File(driverInstallationPath!!)
|
File(driverInstallationPath!!)
|
||||||
)
|
)
|
||||||
} catch (e: SecurityException) {
|
} catch (e: SecurityException) {
|
||||||
|
@ -42,3 +42,19 @@ double GetJDouble(JNIEnv* env, jobject jdouble) {
|
|||||||
jobject ToJDouble(JNIEnv* env, double value) {
|
jobject ToJDouble(JNIEnv* env, double value) {
|
||||||
return env->NewObject(IDCache::GetDoubleClass(), IDCache::GetDoubleConstructor(), value);
|
return env->NewObject(IDCache::GetDoubleClass(), IDCache::GetDoubleConstructor(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s32 GetJInteger(JNIEnv* env, jobject jinteger) {
|
||||||
|
return env->GetIntField(jinteger, IDCache::GetIntegerValueField());
|
||||||
|
}
|
||||||
|
|
||||||
|
jobject ToJInteger(JNIEnv* env, s32 value) {
|
||||||
|
return env->NewObject(IDCache::GetIntegerClass(), IDCache::GetIntegerConstructor(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetJBoolean(JNIEnv* env, jobject jboolean) {
|
||||||
|
return env->GetBooleanField(jboolean, IDCache::GetBooleanValueField());
|
||||||
|
}
|
||||||
|
|
||||||
|
jobject ToJBoolean(JNIEnv* env, bool value) {
|
||||||
|
return env->NewObject(IDCache::GetBooleanClass(), IDCache::GetBooleanConstructor(), value);
|
||||||
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
std::string GetJString(JNIEnv* env, jstring jstr);
|
std::string GetJString(JNIEnv* env, jstring jstr);
|
||||||
jstring ToJString(JNIEnv* env, std::string_view str);
|
jstring ToJString(JNIEnv* env, std::string_view str);
|
||||||
@ -13,3 +14,9 @@ jstring ToJString(JNIEnv* env, std::u16string_view str);
|
|||||||
|
|
||||||
double GetJDouble(JNIEnv* env, jobject jdouble);
|
double GetJDouble(JNIEnv* env, jobject jdouble);
|
||||||
jobject ToJDouble(JNIEnv* env, double value);
|
jobject ToJDouble(JNIEnv* env, double value);
|
||||||
|
|
||||||
|
s32 GetJInteger(JNIEnv* env, jobject jinteger);
|
||||||
|
jobject ToJInteger(JNIEnv* env, s32 value);
|
||||||
|
|
||||||
|
bool GetJBoolean(JNIEnv* env, jobject jboolean);
|
||||||
|
jobject ToJBoolean(JNIEnv* env, bool value);
|
||||||
|
@ -21,7 +21,7 @@ void AndroidConfig::ReloadAllValues() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AndroidConfig::SaveAllValues() {
|
void AndroidConfig::SaveAllValues() {
|
||||||
Save();
|
SaveValues();
|
||||||
SaveAndroidValues();
|
SaveAndroidValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,10 +43,27 @@ static jfieldID s_overlay_control_data_landscape_position_field;
|
|||||||
static jfieldID s_overlay_control_data_portrait_position_field;
|
static jfieldID s_overlay_control_data_portrait_position_field;
|
||||||
static jfieldID s_overlay_control_data_foldable_position_field;
|
static jfieldID s_overlay_control_data_foldable_position_field;
|
||||||
|
|
||||||
|
static jclass s_patch_class;
|
||||||
|
static jmethodID s_patch_constructor;
|
||||||
|
static jfieldID s_patch_enabled_field;
|
||||||
|
static jfieldID s_patch_name_field;
|
||||||
|
static jfieldID s_patch_version_field;
|
||||||
|
static jfieldID s_patch_type_field;
|
||||||
|
static jfieldID s_patch_program_id_field;
|
||||||
|
static jfieldID s_patch_title_id_field;
|
||||||
|
|
||||||
static jclass s_double_class;
|
static jclass s_double_class;
|
||||||
static jmethodID s_double_constructor;
|
static jmethodID s_double_constructor;
|
||||||
static jfieldID s_double_value_field;
|
static jfieldID s_double_value_field;
|
||||||
|
|
||||||
|
static jclass s_integer_class;
|
||||||
|
static jmethodID s_integer_constructor;
|
||||||
|
static jfieldID s_integer_value_field;
|
||||||
|
|
||||||
|
static jclass s_boolean_class;
|
||||||
|
static jmethodID s_boolean_constructor;
|
||||||
|
static jfieldID s_boolean_value_field;
|
||||||
|
|
||||||
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
|
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
|
||||||
|
|
||||||
namespace IDCache {
|
namespace IDCache {
|
||||||
@ -186,6 +203,38 @@ jfieldID GetOverlayControlDataFoldablePositionField() {
|
|||||||
return s_overlay_control_data_foldable_position_field;
|
return s_overlay_control_data_foldable_position_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jclass GetPatchClass() {
|
||||||
|
return s_patch_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
jmethodID GetPatchConstructor() {
|
||||||
|
return s_patch_constructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
jfieldID GetPatchEnabledField() {
|
||||||
|
return s_patch_enabled_field;
|
||||||
|
}
|
||||||
|
|
||||||
|
jfieldID GetPatchNameField() {
|
||||||
|
return s_patch_name_field;
|
||||||
|
}
|
||||||
|
|
||||||
|
jfieldID GetPatchVersionField() {
|
||||||
|
return s_patch_version_field;
|
||||||
|
}
|
||||||
|
|
||||||
|
jfieldID GetPatchTypeField() {
|
||||||
|
return s_patch_type_field;
|
||||||
|
}
|
||||||
|
|
||||||
|
jfieldID GetPatchProgramIdField() {
|
||||||
|
return s_patch_program_id_field;
|
||||||
|
}
|
||||||
|
|
||||||
|
jfieldID GetPatchTitleIdField() {
|
||||||
|
return s_patch_title_id_field;
|
||||||
|
}
|
||||||
|
|
||||||
jclass GetDoubleClass() {
|
jclass GetDoubleClass() {
|
||||||
return s_double_class;
|
return s_double_class;
|
||||||
}
|
}
|
||||||
@ -198,6 +247,30 @@ jfieldID GetDoubleValueField() {
|
|||||||
return s_double_value_field;
|
return s_double_value_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jclass GetIntegerClass() {
|
||||||
|
return s_integer_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
jmethodID GetIntegerConstructor() {
|
||||||
|
return s_integer_constructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
jfieldID GetIntegerValueField() {
|
||||||
|
return s_integer_value_field;
|
||||||
|
}
|
||||||
|
|
||||||
|
jclass GetBooleanClass() {
|
||||||
|
return s_boolean_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
jmethodID GetBooleanConstructor() {
|
||||||
|
return s_boolean_constructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
jfieldID GetBooleanValueField() {
|
||||||
|
return s_boolean_value_field;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace IDCache
|
} // namespace IDCache
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@ -278,12 +351,37 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|||||||
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
|
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
|
||||||
env->DeleteLocalRef(overlay_control_data_class);
|
env->DeleteLocalRef(overlay_control_data_class);
|
||||||
|
|
||||||
|
const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");
|
||||||
|
s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
|
||||||
|
s_patch_constructor = env->GetMethodID(
|
||||||
|
patch_class, "<init>",
|
||||||
|
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V");
|
||||||
|
s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
|
||||||
|
s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
|
||||||
|
s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
|
||||||
|
s_patch_type_field = env->GetFieldID(patch_class, "type", "I");
|
||||||
|
s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
|
||||||
|
s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
|
||||||
|
env->DeleteLocalRef(patch_class);
|
||||||
|
|
||||||
const jclass double_class = env->FindClass("java/lang/Double");
|
const jclass double_class = env->FindClass("java/lang/Double");
|
||||||
s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
|
s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
|
||||||
s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
|
s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
|
||||||
s_double_value_field = env->GetFieldID(double_class, "value", "D");
|
s_double_value_field = env->GetFieldID(double_class, "value", "D");
|
||||||
env->DeleteLocalRef(double_class);
|
env->DeleteLocalRef(double_class);
|
||||||
|
|
||||||
|
const jclass int_class = env->FindClass("java/lang/Integer");
|
||||||
|
s_integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
|
||||||
|
s_integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
|
||||||
|
s_integer_value_field = env->GetFieldID(int_class, "value", "I");
|
||||||
|
env->DeleteLocalRef(int_class);
|
||||||
|
|
||||||
|
const jclass boolean_class = env->FindClass("java/lang/Boolean");
|
||||||
|
s_boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
|
||||||
|
s_boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
|
||||||
|
s_boolean_value_field = env->GetFieldID(boolean_class, "value", "Z");
|
||||||
|
env->DeleteLocalRef(boolean_class);
|
||||||
|
|
||||||
// Initialize Android Storage
|
// Initialize Android Storage
|
||||||
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
|
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
|
||||||
|
|
||||||
@ -309,7 +407,10 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
|
|||||||
env->DeleteGlobalRef(s_string_class);
|
env->DeleteGlobalRef(s_string_class);
|
||||||
env->DeleteGlobalRef(s_pair_class);
|
env->DeleteGlobalRef(s_pair_class);
|
||||||
env->DeleteGlobalRef(s_overlay_control_data_class);
|
env->DeleteGlobalRef(s_overlay_control_data_class);
|
||||||
|
env->DeleteGlobalRef(s_patch_class);
|
||||||
env->DeleteGlobalRef(s_double_class);
|
env->DeleteGlobalRef(s_double_class);
|
||||||
|
env->DeleteGlobalRef(s_integer_class);
|
||||||
|
env->DeleteGlobalRef(s_boolean_class);
|
||||||
|
|
||||||
// UnInitialize applets
|
// UnInitialize applets
|
||||||
SoftwareKeyboard::CleanupJNI(env);
|
SoftwareKeyboard::CleanupJNI(env);
|
||||||
|
@ -43,8 +43,25 @@ jfieldID GetOverlayControlDataLandscapePositionField();
|
|||||||
jfieldID GetOverlayControlDataPortraitPositionField();
|
jfieldID GetOverlayControlDataPortraitPositionField();
|
||||||
jfieldID GetOverlayControlDataFoldablePositionField();
|
jfieldID GetOverlayControlDataFoldablePositionField();
|
||||||
|
|
||||||
|
jclass GetPatchClass();
|
||||||
|
jmethodID GetPatchConstructor();
|
||||||
|
jfieldID GetPatchEnabledField();
|
||||||
|
jfieldID GetPatchNameField();
|
||||||
|
jfieldID GetPatchVersionField();
|
||||||
|
jfieldID GetPatchTypeField();
|
||||||
|
jfieldID GetPatchProgramIdField();
|
||||||
|
jfieldID GetPatchTitleIdField();
|
||||||
|
|
||||||
jclass GetDoubleClass();
|
jclass GetDoubleClass();
|
||||||
jmethodID GetDoubleConstructor();
|
jmethodID GetDoubleConstructor();
|
||||||
jfieldID GetDoubleValueField();
|
jfieldID GetDoubleValueField();
|
||||||
|
|
||||||
|
jclass GetIntegerClass();
|
||||||
|
jmethodID GetIntegerConstructor();
|
||||||
|
jfieldID GetIntegerValueField();
|
||||||
|
|
||||||
|
jclass GetBooleanClass();
|
||||||
|
jmethodID GetBooleanConstructor();
|
||||||
|
jfieldID GetBooleanValueField();
|
||||||
|
|
||||||
} // namespace IDCache
|
} // namespace IDCache
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <core/file_sys/patch_manager.h>
|
#include <core/file_sys/patch_manager.h>
|
||||||
#include <core/file_sys/savedata_factory.h>
|
#include <core/file_sys/savedata_factory.h>
|
||||||
#include <core/loader/nro.h>
|
#include <core/loader/nro.h>
|
||||||
|
#include <frontend_common/content_manager.h>
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
#include "common/detached_tasks.h"
|
#include "common/detached_tasks.h"
|
||||||
@ -100,67 +101,6 @@ void EmulationSession::SetNativeWindow(ANativeWindow* native_window) {
|
|||||||
m_native_window = native_window;
|
m_native_window = native_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
int EmulationSession::InstallFileToNand(std::string filename, std::string file_extension) {
|
|
||||||
jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
|
|
||||||
std::size_t block_size) {
|
|
||||||
if (src == nullptr || dest == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!dest->Resize(src->GetSize())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
using namespace Common::Literals;
|
|
||||||
[[maybe_unused]] std::vector<u8> buffer(1_MiB);
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
|
|
||||||
jconst read = src->Read(buffer.data(), buffer.size(), i);
|
|
||||||
dest->Write(buffer.data(), read, i);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum InstallResult {
|
|
||||||
Success = 0,
|
|
||||||
SuccessFileOverwritten = 1,
|
|
||||||
InstallError = 2,
|
|
||||||
ErrorBaseGame = 3,
|
|
||||||
ErrorFilenameExtension = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
[[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp;
|
|
||||||
if (file_extension == "nsp") {
|
|
||||||
nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
|
|
||||||
if (nsp->IsExtractedType()) {
|
|
||||||
return InstallError;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return ErrorFilenameExtension;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nsp) {
|
|
||||||
return InstallError;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nsp->GetStatus() != Loader::ResultStatus::Success) {
|
|
||||||
return InstallError;
|
|
||||||
}
|
|
||||||
|
|
||||||
jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true,
|
|
||||||
copy_func);
|
|
||||||
|
|
||||||
switch (res) {
|
|
||||||
case FileSys::InstallResult::Success:
|
|
||||||
return Success;
|
|
||||||
case FileSys::InstallResult::OverwriteExisting:
|
|
||||||
return SuccessFileOverwritten;
|
|
||||||
case FileSys::InstallResult::ErrorBaseInstall:
|
|
||||||
return ErrorBaseGame;
|
|
||||||
default:
|
|
||||||
return InstallError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmulationSession::InitializeGpuDriver(const std::string& hook_lib_dir,
|
void EmulationSession::InitializeGpuDriver(const std::string& hook_lib_dir,
|
||||||
const std::string& custom_driver_dir,
|
const std::string& custom_driver_dir,
|
||||||
const std::string& custom_driver_name,
|
const std::string& custom_driver_name,
|
||||||
@ -410,8 +350,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::ProController);
|
handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
|
||||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
|
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
|
||||||
handheld->Disconnect();
|
handheld->Disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -512,10 +452,20 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject
|
|||||||
}
|
}
|
||||||
|
|
||||||
int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance,
|
int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance,
|
||||||
jstring j_file,
|
jstring j_file, jobject jcallback) {
|
||||||
jstring j_file_extension) {
|
auto jlambdaClass = env->GetObjectClass(jcallback);
|
||||||
return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file),
|
auto jlambdaInvokeMethod = env->GetMethodID(
|
||||||
GetJString(env, j_file_extension));
|
jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
||||||
|
const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) {
|
||||||
|
auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod,
|
||||||
|
ToJDouble(env, max), ToJDouble(env, progress));
|
||||||
|
return GetJBoolean(env, jwasCancelled);
|
||||||
|
};
|
||||||
|
|
||||||
|
return static_cast<int>(
|
||||||
|
ContentManager::InstallNSP(&EmulationSession::GetInstance().System(),
|
||||||
|
EmulationSession::GetInstance().System().GetFilesystem().get(),
|
||||||
|
GetJString(env, j_file), callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
|
||||||
@ -824,9 +774,9 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isFirmwareAvailable(JNIEnv* env,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getAddonsForFile(JNIEnv* env, jobject jobj,
|
jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env, jobject jobj,
|
||||||
jstring jpath,
|
jstring jpath,
|
||||||
jstring jprogramId) {
|
jstring jprogramId) {
|
||||||
const auto path = GetJString(env, jpath);
|
const auto path = GetJString(env, jpath);
|
||||||
const auto vFile =
|
const auto vFile =
|
||||||
Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path);
|
Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path);
|
||||||
@ -843,20 +793,40 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getAddonsForFile(JNIEnv* env,
|
|||||||
FileSys::VirtualFile update_raw;
|
FileSys::VirtualFile update_raw;
|
||||||
loader->ReadUpdateRaw(update_raw);
|
loader->ReadUpdateRaw(update_raw);
|
||||||
|
|
||||||
auto addons = pm.GetPatchVersionNames(update_raw);
|
auto patches = pm.GetPatches(update_raw);
|
||||||
auto jemptyString = ToJString(env, "");
|
jobjectArray jpatchArray =
|
||||||
auto jemptyStringPair = env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
|
env->NewObjectArray(patches.size(), IDCache::GetPatchClass(), nullptr);
|
||||||
jemptyString, jemptyString);
|
|
||||||
jobjectArray jaddonsArray =
|
|
||||||
env->NewObjectArray(addons.size(), IDCache::GetPairClass(), jemptyStringPair);
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (const auto& addon : addons) {
|
for (const auto& patch : patches) {
|
||||||
jobject jaddon = env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
|
jobject jpatch = env->NewObject(
|
||||||
ToJString(env, addon.first), ToJString(env, addon.second));
|
IDCache::GetPatchClass(), IDCache::GetPatchConstructor(), patch.enabled,
|
||||||
env->SetObjectArrayElement(jaddonsArray, i, jaddon);
|
ToJString(env, patch.name), ToJString(env, patch.version),
|
||||||
|
static_cast<jint>(patch.type), ToJString(env, std::to_string(patch.program_id)),
|
||||||
|
ToJString(env, std::to_string(patch.title_id)));
|
||||||
|
env->SetObjectArrayElement(jpatchArray, i, jpatch);
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
return jaddonsArray;
|
return jpatchArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeUpdate(JNIEnv* env, jobject jobj,
|
||||||
|
jstring jprogramId) {
|
||||||
|
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||||
|
ContentManager::RemoveUpdate(EmulationSession::GetInstance().System().GetFileSystemController(),
|
||||||
|
program_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj,
|
||||||
|
jstring jprogramId) {
|
||||||
|
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||||
|
ContentManager::RemoveAllDLC(&EmulationSession::GetInstance().System(), program_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId,
|
||||||
|
jstring jname) {
|
||||||
|
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||||
|
ContentManager::RemoveMod(EmulationSession::GetInstance().System().GetFileSystemController(),
|
||||||
|
program_id, GetJString(env, jname));
|
||||||
}
|
}
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
|
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "core/file_sys/registered_cache.h"
|
#include "core/file_sys/registered_cache.h"
|
||||||
#include "core/hle/service/acc/profile_manager.h"
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
#include "core/perf_stats.h"
|
#include "core/perf_stats.h"
|
||||||
|
#include "frontend_common/content_manager.h"
|
||||||
#include "jni/applets/software_keyboard.h"
|
#include "jni/applets/software_keyboard.h"
|
||||||
#include "jni/emu_window/emu_window.h"
|
#include "jni/emu_window/emu_window.h"
|
||||||
#include "video_core/rasterizer_interface.h"
|
#include "video_core/rasterizer_interface.h"
|
||||||
@ -29,7 +30,6 @@ public:
|
|||||||
void SetNativeWindow(ANativeWindow* native_window);
|
void SetNativeWindow(ANativeWindow* native_window);
|
||||||
void SurfaceChanged();
|
void SurfaceChanged();
|
||||||
|
|
||||||
int InstallFileToNand(std::string filename, std::string file_extension);
|
|
||||||
void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir,
|
void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir,
|
||||||
const std::string& custom_driver_name,
|
const std::string& custom_driver_name,
|
||||||
const std::string& file_redirect_dir);
|
const std::string& file_redirect_dir);
|
||||||
|
@ -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->RuntimeModfiable();
|
return setting->RuntimeModifiable();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@
|
|||||||
android:layout_marginHorizontal="20dp" />
|
android:layout_marginHorizontal="20dp" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/button_build_hash"
|
android:id="@+id/button_version_name"
|
||||||
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_build_hash"
|
android:id="@+id/text_version_name"
|
||||||
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"
|
||||||
|
@ -1,8 +1,30 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<com.google.android.material.progressindicator.LinearProgressIndicator xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/progress_bar"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="24dp"
|
android:orientation="vertical">
|
||||||
app:trackCornerRadius="4dp" />
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/message"
|
||||||
|
style="@style/TextAppearance.Material3.BodyMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginBottom="6dp"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
|
android:requiresFadingEdge="horizontal"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
|
android:id="@+id/progress_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="24dp"
|
||||||
|
app:trackCornerRadius="4dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
@ -148,7 +148,7 @@
|
|||||||
android:layout_marginHorizontal="20dp" />
|
android:layout_marginHorizontal="20dp" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/button_build_hash"
|
android:id="@+id/button_version_name"
|
||||||
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_build_hash"
|
android:id="@+id/text_version_name"
|
||||||
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"
|
||||||
|
@ -14,12 +14,11 @@
|
|||||||
android:id="@+id/text_container"
|
android:id="@+id/text_container"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/addon_switch"
|
android:layout_marginEnd="16dp"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/addon_switch"
|
app:layout_constraintEnd_toStartOf="@+id/addon_checkbox"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="@+id/addon_switch">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/title"
|
android:id="@+id/title"
|
||||||
@ -42,16 +41,29 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<com.google.android.material.materialswitch.MaterialSwitch
|
<com.google.android.material.checkbox.MaterialCheckBox
|
||||||
android:id="@+id/addon_switch"
|
android:id="@+id/addon_checkbox"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:nextFocusLeft="@id/addon_container"
|
android:layout_marginEnd="8dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintTop_toTopOf="@+id/text_container"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/text_container"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/button_delete" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_delete"
|
||||||
|
style="@style/Widget.Material3.Button.IconButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:contentDescription="@string/delete"
|
||||||
|
android:tooltipText="@string/delete"
|
||||||
|
app:icon="@drawable/ic_delete"
|
||||||
|
app:iconTint="?attr/colorControlNormal"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@id/text_container"
|
app:layout_constraintTop_toTopOf="@+id/addon_checkbox"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintBottom_toBottomOf="@+id/addon_checkbox" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
@ -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/langauge_german</item>
|
<item>@string/language_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>@string/gamepad_left_stick</item>
|
|
||||||
<item>@string/gamepad_right_stick</item>
|
|
||||||
<item>L3</item>
|
<item>L3</item>
|
||||||
<item>R3</item>
|
<item>R3</item>
|
||||||
|
<item>@string/gamepad_left_stick</item>
|
||||||
|
<item>@string/gamepad_right_stick</item>
|
||||||
<item>@string/gamepad_d_pad</item>
|
<item>@string/gamepad_d_pad</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
@ -286,6 +286,7 @@
|
|||||||
<string name="custom">Custom</string>
|
<string name="custom">Custom</string>
|
||||||
<string name="notice">Notice</string>
|
<string name="notice">Notice</string>
|
||||||
<string name="import_complete">Import complete</string>
|
<string name="import_complete">Import complete</string>
|
||||||
|
<string name="more_options">More options</string>
|
||||||
|
|
||||||
<!-- GPU driver installation -->
|
<!-- GPU driver installation -->
|
||||||
<string name="select_gpu_driver">Select GPU driver</string>
|
<string name="select_gpu_driver">Select GPU driver</string>
|
||||||
@ -348,6 +349,8 @@
|
|||||||
<string name="verifying_content">Verifying content…</string>
|
<string name="verifying_content">Verifying content…</string>
|
||||||
<string name="content_install_notice">Content install notice</string>
|
<string name="content_install_notice">Content install notice</string>
|
||||||
<string name="content_install_notice_description">The content that you selected does not match this game.\nInstall anyway?</string>
|
<string name="content_install_notice_description">The content that you selected does not match this game.\nInstall anyway?</string>
|
||||||
|
<string name="confirm_uninstall">Confirm uninstall</string>
|
||||||
|
<string name="confirm_uninstall_description">Are you sure you want to uninstall this addon?</string>
|
||||||
|
|
||||||
<!-- ROM loading errors -->
|
<!-- ROM loading errors -->
|
||||||
<string name="loader_error_encrypted">Your ROM is encrypted</string>
|
<string name="loader_error_encrypted">Your ROM is encrypted</string>
|
||||||
@ -410,7 +413,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="langauge_german" translatable="false">Deutsch</string>
|
<string name="language_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>
|
||||||
|
@ -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, "OpusDeocder failed to initialize.");
|
LOG_ERROR(Service_Audio, "OpusDecoder failed to initialize.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
#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;
|
||||||
@ -26,7 +28,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_, u32 handle_,
|
u16 channel_count_, size_t session_id_, Kernel::KProcess* handle_,
|
||||||
u64 applet_resource_user_id_, Sink::StreamType type_) {
|
u64 applet_resource_user_id_, Sink::StreamType type_) {
|
||||||
if (stream) {
|
if (stream) {
|
||||||
Finalize();
|
Finalize();
|
||||||
@ -37,6 +39,7 @@ 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) {
|
||||||
@ -55,6 +58,11 @@ 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() {
|
||||||
@ -92,7 +100,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(
|
||||||
system.ApplicationMemory(), buffer.samples, buffer.size / sizeof(s16));
|
handle->GetMemory(), buffer.samples, buffer.size / sizeof(s16));
|
||||||
stream->AppendBuffer(new_buffer, samples);
|
stream->AppendBuffer(new_buffer, samples);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,7 +109,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))};
|
||||||
system.ApplicationMemory().WriteBlockUnsafe(buffer.samples, samples.data(), buffer.size);
|
handle->GetMemory().WriteBlockUnsafe(buffer.samples, samples.data(), buffer.size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,10 @@ struct EventType;
|
|||||||
} // namespace Timing
|
} // namespace Timing
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
class KProcess;
|
||||||
|
} // namespace Kernel
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
|
||||||
namespace Sink {
|
namespace Sink {
|
||||||
@ -44,13 +48,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 - Handle for this device session (unused).
|
* @param handle - Process handle for this device session.
|
||||||
* @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, u32 handle, u64 applet_resource_user_id,
|
size_t session_id, Kernel::KProcess* handle, u64 applet_resource_user_id,
|
||||||
Sink::StreamType type);
|
Sink::StreamType type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -137,8 +141,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{};
|
||||||
/// Handle of this device session
|
/// Process handle of device memory owner
|
||||||
u32 handle{};
|
Kernel::KProcess* 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
|
||||||
|
@ -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,
|
||||||
const u32 handle_, const u64 applet_resource_user_id_) {
|
Kernel::KProcess* 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;
|
||||||
|
@ -19,7 +19,8 @@ class System;
|
|||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
class KEvent;
|
class KEvent;
|
||||||
}
|
class KProcess;
|
||||||
|
} // namespace Kernel
|
||||||
|
|
||||||
namespace AudioCore::AudioIn {
|
namespace AudioCore::AudioIn {
|
||||||
|
|
||||||
@ -93,12 +94,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 - Unused.
|
* @param handle - Process handle.
|
||||||
* @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, u32 handle,
|
Result Initialize(std::string device_name, const AudioInParameter& in_params,
|
||||||
u64 applet_resource_user_id);
|
Kernel::KProcess* handle, u64 applet_resource_user_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start this system.
|
* Start this system.
|
||||||
@ -244,8 +245,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
/// Core system
|
/// Core system
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
/// (Unused)
|
/// Process handle
|
||||||
u32 handle{};
|
Kernel::KProcess* 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
|
||||||
|
@ -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, u32 handle_,
|
Result System::Initialize(std::string device_name, const AudioOutParameter& in_params,
|
||||||
u64 applet_resource_user_id_) {
|
Kernel::KProcess* handle_, 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;
|
||||||
|
@ -19,7 +19,8 @@ class System;
|
|||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
class KEvent;
|
class KEvent;
|
||||||
}
|
class KProcess;
|
||||||
|
} // namespace Kernel
|
||||||
|
|
||||||
namespace AudioCore::AudioOut {
|
namespace AudioCore::AudioOut {
|
||||||
|
|
||||||
@ -84,12 +85,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 - Unused.
|
* @param handle - Process handle.
|
||||||
* @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, u32 handle,
|
Result Initialize(std::string device_name, const AudioOutParameter& in_params,
|
||||||
u64 applet_resource_user_id);
|
Kernel::KProcess* handle, u64 applet_resource_user_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start this system.
|
* Start this system.
|
||||||
@ -228,8 +229,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
/// Core system
|
/// Core system
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
/// (Unused)
|
/// Process handle
|
||||||
u32 handle{};
|
Kernel::KProcess* 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
|
||||||
|
@ -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.GetDesintationData(voice_info.splitter_id, 0)};
|
auto destination{splitter_context.GetDestinationData(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.GetDesintationData(voice_info.splitter_id, dest_id);
|
destination = splitter_context.GetDestinationData(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.GetDesintationData(voice_info.splitter_id, i)};
|
auto destination{splitter_context.GetDestinationData(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.GetDesintationData(voice_info.splitter_id, i);
|
destination = splitter_context.GetDestinationData(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.GetDesintationData(mix_info.dst_splitter_id, dest_id)};
|
splitter_context.GetDestinationData(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.GetDesintationData(mix_info.dst_splitter_id, dest_id);
|
splitter_context.GetDestinationData(mix_info.dst_splitter_id, dest_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -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.GetDesintationData(in_params.dest_splitter_id, i)};
|
splitter_context.GetDestinationData(in_params.dest_splitter_id, i)};
|
||||||
|
|
||||||
if (destination) {
|
if (destination) {
|
||||||
const auto destination_id{destination->GetMixId()};
|
const auto destination_id{destination->GetMixId()};
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
namespace AudioCore::Renderer {
|
namespace AudioCore::Renderer {
|
||||||
|
|
||||||
SplitterDestinationData* SplitterContext::GetDesintationData(const s32 splitter_id,
|
SplitterDestinationData* SplitterContext::GetDestinationData(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);
|
||||||
}
|
}
|
||||||
|
@ -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* GetDesintationData(s32 splitter_id, s32 destination_id);
|
SplitterDestinationData* GetDestinationData(s32 splitter_id, s32 destination_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a splitter from the given index.
|
* Get a splitter from the given index.
|
||||||
|
@ -35,7 +35,7 @@ bool BasicSetting::Save() const {
|
|||||||
return save;
|
return save;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BasicSetting::RuntimeModfiable() const {
|
bool BasicSetting::RuntimeModifiable() const {
|
||||||
return runtime_modifiable;
|
return runtime_modifiable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 RuntimeModfiable() const;
|
[[nodiscard]] bool RuntimeModifiable() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns A unique number corresponding to the setting.
|
* @returns A unique number corresponding to the setting.
|
||||||
|
@ -716,22 +716,23 @@ add_library(core STATIC
|
|||||||
hle/service/server_manager.h
|
hle/service/server_manager.h
|
||||||
hle/service/service.cpp
|
hle/service/service.cpp
|
||||||
hle/service/service.h
|
hle/service/service.h
|
||||||
hle/service/set/appln_settings.cpp
|
hle/service/set/setting_formats/appln_settings.cpp
|
||||||
hle/service/set/appln_settings.h
|
hle/service/set/setting_formats/appln_settings.h
|
||||||
hle/service/set/device_settings.cpp
|
hle/service/set/setting_formats/device_settings.cpp
|
||||||
hle/service/set/device_settings.h
|
hle/service/set/setting_formats/device_settings.h
|
||||||
|
hle/service/set/setting_formats/system_settings.cpp
|
||||||
|
hle/service/set/setting_formats/system_settings.h
|
||||||
|
hle/service/set/setting_formats/private_settings.cpp
|
||||||
|
hle/service/set/setting_formats/private_settings.h
|
||||||
hle/service/set/factory_settings_server.cpp
|
hle/service/set/factory_settings_server.cpp
|
||||||
hle/service/set/factory_settings_server.h
|
hle/service/set/factory_settings_server.h
|
||||||
hle/service/set/firmware_debug_settings_server.cpp
|
hle/service/set/firmware_debug_settings_server.cpp
|
||||||
hle/service/set/firmware_debug_settings_server.h
|
hle/service/set/firmware_debug_settings_server.h
|
||||||
hle/service/set/private_settings.cpp
|
|
||||||
hle/service/set/private_settings.h
|
|
||||||
hle/service/set/settings.cpp
|
hle/service/set/settings.cpp
|
||||||
hle/service/set/settings.h
|
hle/service/set/settings.h
|
||||||
hle/service/set/settings_server.cpp
|
hle/service/set/settings_server.cpp
|
||||||
hle/service/set/settings_server.h
|
hle/service/set/settings_server.h
|
||||||
hle/service/set/system_settings.cpp
|
hle/service/set/settings_types.h
|
||||||
hle/service/set/system_settings.h
|
|
||||||
hle/service/set/system_settings_server.cpp
|
hle/service/set/system_settings_server.cpp
|
||||||
hle/service/set/system_settings_server.h
|
hle/service/set/system_settings_server.h
|
||||||
hle/service/sm/sm.cpp
|
hle/service/sm/sm.cpp
|
||||||
|
@ -22,14 +22,10 @@ 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
|
||||||
Patcher::~Patcher() = default;
|
// module.
|
||||||
|
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);
|
||||||
@ -38,6 +34,25 @@ void Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
|
|||||||
// 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);
|
||||||
@ -94,16 +109,17 @@ void Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (auto exclusive = Exclusive{inst}; exclusive.Verify()) {
|
if (auto exclusive = Exclusive{inst}; exclusive.Verify()) {
|
||||||
m_exclusives.push_back(i);
|
curr_patch->m_exclusives.push_back(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine patching mode for the final relocation step
|
// Determine patching mode for the final relocation step
|
||||||
const size_t image_size = program_image.size();
|
total_program_size += image_size;
|
||||||
this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData;
|
this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
|
bool 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) {
|
||||||
@ -120,7 +136,7 @@ void 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(image_size - rel.module_offset + rel.patch_offset);
|
rc.B(total_program_size - rel.module_offset + rel.patch_offset);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -129,7 +145,7 @@ void 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 - image_size - rel.patch_offset);
|
rc.B(rel.module_offset - total_program_size - rel.patch_offset);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -137,7 +153,7 @@ void 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) + image_size + patch_offset;
|
return GetInteger(load_base) + total_program_size + patch_offset;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -150,39 +166,50 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
|
|||||||
};
|
};
|
||||||
|
|
||||||
// We are now ready to relocate!
|
// We are now ready to relocate!
|
||||||
for (const Relocation& rel : m_branch_to_patch_relocations) {
|
auto& patch = modules[m_relocate_module_index++];
|
||||||
|
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 : m_branch_to_module_relocations) {
|
for (const Relocation& rel : patch.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 : m_write_module_pc_relocations) {
|
for (const Relocation& rel : patch.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 : m_trampolines) {
|
for (const Trampoline& rel : patch.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 : m_exclusives) {
|
for (const ModuleTextAddress i : patch.m_exclusives) {
|
||||||
auto exclusive = Exclusive{text_words[i]};
|
auto exclusive = Exclusive{text_words[i]};
|
||||||
text_words[i] = exclusive.AsOrdered();
|
text_words[i] = exclusive.AsOrdered();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy to program image
|
// Remove the patched module size from the total. This is done so total_program_size
|
||||||
if (this->mode == PatchMode::PreText) {
|
// always represents the distance from the currently patched module to the patch section.
|
||||||
std::memcpy(program_image.data(), m_patch_instructions.data(),
|
total_program_size -= image_size;
|
||||||
m_patch_instructions.size() * sizeof(u32));
|
|
||||||
} else {
|
// Only copy to the program image of the last module
|
||||||
program_image.resize(image_size + patch_size);
|
if (m_relocate_module_index == modules.size()) {
|
||||||
std::memcpy(program_image.data() + image_size, m_patch_instructions.data(),
|
if (this->mode == PatchMode::PreText) {
|
||||||
m_patch_instructions.size() * sizeof(u32));
|
ASSERT(image_size == total_program_size);
|
||||||
|
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 {
|
||||||
@ -322,7 +349,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.
|
||||||
m_trampolines.push_back({c.offset(), module_dest});
|
curr_patch->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.
|
||||||
|
@ -31,9 +31,9 @@ public:
|
|||||||
explicit Patcher();
|
explicit Patcher();
|
||||||
~Patcher();
|
~Patcher();
|
||||||
|
|
||||||
void PatchText(const Kernel::PhysicalMemory& program_image,
|
bool PatchText(const Kernel::PhysicalMemory& program_image,
|
||||||
const Kernel::CodeSet::Segment& code);
|
const Kernel::CodeSet::Segment& code);
|
||||||
void RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code,
|
bool 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) {
|
||||||
m_branch_to_patch_relocations.push_back({c.offset(), module_dest});
|
curr_patch->m_branch_to_patch_relocations.push_back({c.offset(), module_dest});
|
||||||
}
|
}
|
||||||
|
|
||||||
void BranchToModule(uintptr_t module_dest) {
|
void BranchToModule(uintptr_t module_dest) {
|
||||||
m_branch_to_module_relocations.push_back({c.offset(), module_dest});
|
curr_patch->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) {
|
||||||
m_write_module_pc_relocations.push_back({c.offset(), module_dest});
|
curr_patch->m_write_module_pc_relocations.push_back({c.offset(), module_dest});
|
||||||
c.dx(0);
|
c.dx(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,15 +84,22 @@ 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
|
||||||
|
@ -28,6 +28,10 @@ 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;
|
||||||
@ -43,15 +47,14 @@ 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, size_t process_id,
|
void Map(DAddr address, VAddr virtual_address, size_t size, Asid asid, bool track = false);
|
||||||
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, size_t process_id);
|
void TrackContinuityImpl(DAddr address, VAddr virtual_address, size_t size, Asid asid);
|
||||||
void TrackContinuity(DAddr address, VAddr virtual_address, size_t size, size_t process_id) {
|
void TrackContinuity(DAddr address, VAddr virtual_address, size_t size, Asid asid) {
|
||||||
std::scoped_lock lk(mapping_guard);
|
std::scoped_lock lk(mapping_guard);
|
||||||
TrackContinuityImpl(address, virtual_address, size, process_id);
|
TrackContinuityImpl(address, virtual_address, size, asid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write / Read
|
// Write / Read
|
||||||
@ -105,8 +108,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);
|
||||||
|
|
||||||
size_t RegisterProcess(Memory::Memory* memory);
|
Asid RegisterProcess(Memory::Memory* memory);
|
||||||
void UnregisterProcess(size_t id);
|
void UnregisterProcess(Asid id);
|
||||||
|
|
||||||
void UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta);
|
void UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta);
|
||||||
|
|
||||||
@ -163,17 +166,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 process_id_start_bit = guest_max_as_bits;
|
static constexpr size_t asid_start_bit = guest_max_as_bits;
|
||||||
|
|
||||||
std::pair<size_t, VAddr> ExtractCPUBacking(size_t page_index) {
|
std::pair<Asid, 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 size_t process_id = static_cast<size_t>(content >> process_id_start_bit);
|
const Asid asid{static_cast<size_t>(content >> asid_start_bit)};
|
||||||
return std::make_pair(process_id, address);
|
return std::make_pair(asid, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InsertCPUBacking(size_t page_index, VAddr address, size_t process_id) {
|
void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) {
|
||||||
cpu_backing_address[page_index] = address | (process_id << process_id_start_bit);
|
cpu_backing_address[page_index] = address | (asid.id << asid_start_bit);
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::VirtualBuffer<VAddr> cpu_backing_address;
|
Common::VirtualBuffer<VAddr> cpu_backing_address;
|
||||||
@ -205,4 +208,4 @@ private:
|
|||||||
std::mutex mapping_guard;
|
std::mutex mapping_guard;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
@ -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,
|
||||||
size_t process_id, bool track) {
|
Asid asid, bool track) {
|
||||||
Core::Memory::Memory* process_memory = registered_processes[process_id];
|
Core::Memory::Memory* process_memory = registered_processes[asid.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, process_id);
|
InsertCPUBacking(start_page_d + i, new_vaddress, asid);
|
||||||
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
|
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
|
||||||
const u32 new_dev = static_cast<u32>(start_page_d + i);
|
const u32 new_dev = static_cast<u32>(start_page_d + i);
|
||||||
if (base_dev == 0) [[likely]] {
|
if (base_dev == 0) [[likely]] {
|
||||||
@ -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, process_id);
|
TrackContinuityImpl(address, virtual_address, size, asid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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, size_t process_id) {
|
size_t size, Asid asid) {
|
||||||
Core::Memory::Memory* process_memory = registered_processes[process_id];
|
Core::Memory::Memory* process_memory = registered_processes[asid.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>
|
||||||
size_t DeviceMemoryManager<Traits>::RegisterProcess(Memory::Memory* memory_device_inter) {
|
Asid 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,29 +498,23 @@ size_t DeviceMemoryManager<Traits>::RegisterProcess(Memory::Memory* memory_devic
|
|||||||
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 new_id;
|
return Asid{new_id};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
void DeviceMemoryManager<Traits>::UnregisterProcess(size_t id) {
|
void DeviceMemoryManager<Traits>::UnregisterProcess(Asid asid) {
|
||||||
registered_processes[id] = nullptr;
|
registered_processes[asid.id] = nullptr;
|
||||||
id_pool.push_front(id);
|
id_pool.push_front(asid.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) {
|
||||||
bool locked = false;
|
std::unique_lock<std::mutex> lk(counter_guard, std::defer_lock);
|
||||||
auto lock = [&] {
|
const auto Lock = [&] {
|
||||||
if (!locked) {
|
if (!lk) {
|
||||||
counter_guard.lock();
|
lk.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;
|
||||||
@ -530,9 +524,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 [process_id, base_vaddress] = ExtractCPUBacking(page);
|
auto [asid, 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[process_id];
|
auto* memory_device_inter = registered_processes[asid.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);
|
||||||
|
|
||||||
@ -555,7 +549,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;
|
||||||
@ -566,7 +560,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;
|
||||||
@ -574,12 +568,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);
|
||||||
}
|
}
|
||||||
|
@ -466,12 +466,12 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
|
|||||||
return romfs;
|
return romfs;
|
||||||
}
|
}
|
||||||
|
|
||||||
PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile update_raw) const {
|
std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
||||||
if (title_id == 0) {
|
if (title_id == 0) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string, std::less<>> out;
|
std::vector<Patch> out;
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||||
|
|
||||||
// Game Updates
|
// Game Updates
|
||||||
@ -482,20 +482,28 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
|
|||||||
|
|
||||||
const auto update_disabled =
|
const auto update_disabled =
|
||||||
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
|
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
|
||||||
const auto update_label = update_disabled ? "[D] Update" : "Update";
|
Patch update_patch = {.enabled = !update_disabled,
|
||||||
|
.name = "Update",
|
||||||
|
.version = "",
|
||||||
|
.type = PatchType::Update,
|
||||||
|
.program_id = title_id,
|
||||||
|
.title_id = title_id};
|
||||||
|
|
||||||
if (nacp != nullptr) {
|
if (nacp != nullptr) {
|
||||||
out.insert_or_assign(update_label, nacp->GetVersionString());
|
update_patch.version = nacp->GetVersionString();
|
||||||
|
out.push_back(update_patch);
|
||||||
} else {
|
} else {
|
||||||
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
|
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
|
||||||
const auto meta_ver = content_provider.GetEntryVersion(update_tid);
|
const auto meta_ver = content_provider.GetEntryVersion(update_tid);
|
||||||
if (meta_ver.value_or(0) == 0) {
|
if (meta_ver.value_or(0) == 0) {
|
||||||
out.insert_or_assign(update_label, "");
|
out.push_back(update_patch);
|
||||||
} else {
|
} else {
|
||||||
out.insert_or_assign(update_label, FormatTitleVersion(*meta_ver));
|
update_patch.version = FormatTitleVersion(*meta_ver);
|
||||||
|
out.push_back(update_patch);
|
||||||
}
|
}
|
||||||
} else if (update_raw != nullptr) {
|
} else if (update_raw != nullptr) {
|
||||||
out.insert_or_assign(update_label, "PACKED");
|
update_patch.version = "PACKED";
|
||||||
|
out.push_back(update_patch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -539,7 +547,12 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
|
|||||||
|
|
||||||
const auto mod_disabled =
|
const auto mod_disabled =
|
||||||
std::find(disabled.begin(), disabled.end(), mod->GetName()) != disabled.end();
|
std::find(disabled.begin(), disabled.end(), mod->GetName()) != disabled.end();
|
||||||
out.insert_or_assign(mod_disabled ? "[D] " + mod->GetName() : mod->GetName(), types);
|
out.push_back({.enabled = !mod_disabled,
|
||||||
|
.name = mod->GetName(),
|
||||||
|
.version = types,
|
||||||
|
.type = PatchType::Mod,
|
||||||
|
.program_id = title_id,
|
||||||
|
.title_id = title_id});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,7 +570,12 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
|
|||||||
if (!types.empty()) {
|
if (!types.empty()) {
|
||||||
const auto mod_disabled =
|
const auto mod_disabled =
|
||||||
std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end();
|
std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end();
|
||||||
out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", types);
|
out.push_back({.enabled = !mod_disabled,
|
||||||
|
.name = "SDMC",
|
||||||
|
.version = types,
|
||||||
|
.type = PatchType::Mod,
|
||||||
|
.program_id = title_id,
|
||||||
|
.title_id = title_id});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -584,7 +602,12 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
|
|||||||
|
|
||||||
const auto dlc_disabled =
|
const auto dlc_disabled =
|
||||||
std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end();
|
std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end();
|
||||||
out.insert_or_assign(dlc_disabled ? "[D] DLC" : "DLC", std::move(list));
|
out.push_back({.enabled = !dlc_disabled,
|
||||||
|
.name = "DLC",
|
||||||
|
.version = std::move(list),
|
||||||
|
.type = PatchType::DLC,
|
||||||
|
.program_id = title_id,
|
||||||
|
.title_id = dlc_match.back().title_id});
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
@ -26,12 +26,22 @@ class ContentProvider;
|
|||||||
class NCA;
|
class NCA;
|
||||||
class NACP;
|
class NACP;
|
||||||
|
|
||||||
|
enum class PatchType { Update, DLC, Mod };
|
||||||
|
|
||||||
|
struct Patch {
|
||||||
|
bool enabled;
|
||||||
|
std::string name;
|
||||||
|
std::string version;
|
||||||
|
PatchType type;
|
||||||
|
u64 program_id;
|
||||||
|
u64 title_id;
|
||||||
|
};
|
||||||
|
|
||||||
// A centralized class to manage patches to games.
|
// A centralized class to manage patches to games.
|
||||||
class PatchManager {
|
class PatchManager {
|
||||||
public:
|
public:
|
||||||
using BuildID = std::array<u8, 0x20>;
|
using BuildID = std::array<u8, 0x20>;
|
||||||
using Metadata = std::pair<std::unique_ptr<NACP>, VirtualFile>;
|
using Metadata = std::pair<std::unique_ptr<NACP>, VirtualFile>;
|
||||||
using PatchVersionNames = std::map<std::string, std::string, std::less<>>;
|
|
||||||
|
|
||||||
explicit PatchManager(u64 title_id_,
|
explicit PatchManager(u64 title_id_,
|
||||||
const Service::FileSystem::FileSystemController& fs_controller_,
|
const Service::FileSystem::FileSystemController& fs_controller_,
|
||||||
@ -66,9 +76,8 @@ public:
|
|||||||
VirtualFile packed_update_raw = nullptr,
|
VirtualFile packed_update_raw = nullptr,
|
||||||
bool apply_layeredfs = true) const;
|
bool apply_layeredfs = true) const;
|
||||||
|
|
||||||
// Returns a vector of pairs between patch names and patch versions.
|
// Returns a vector of patches
|
||||||
// i.e. Update 3.2.2 will return {"Update", "3.2.2"}
|
[[nodiscard]] std::vector<Patch> GetPatches(VirtualFile update_raw = nullptr) const;
|
||||||
[[nodiscard]] PatchVersionNames GetPatchVersionNames(VirtualFile update_raw = nullptr) const;
|
|
||||||
|
|
||||||
// If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails,
|
// If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails,
|
||||||
// it will fallback to the Meta-type NCA of the base game. If that fails, the result will be
|
// it will fallback to the Meta-type NCA of the base game. If that fails, the result will be
|
||||||
|
@ -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::ProController);
|
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
|
||||||
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);
|
||||||
|
@ -28,14 +28,14 @@ Result KMemoryBlockManager::Initialize(KProcessAddress st, KProcessAddress nd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void KMemoryBlockManager::Finalize(KMemoryBlockSlabManager* slab_manager,
|
void KMemoryBlockManager::Finalize(KMemoryBlockSlabManager* slab_manager,
|
||||||
HostUnmapCallback&& host_unmap_callback) {
|
BlockCallback&& block_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());
|
||||||
|
@ -85,11 +85,11 @@ public:
|
|||||||
public:
|
public:
|
||||||
KMemoryBlockManager();
|
KMemoryBlockManager();
|
||||||
|
|
||||||
using HostUnmapCallback = std::function<void(Common::ProcessAddress, u64)>;
|
using BlockCallback = 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, HostUnmapCallback&& host_unmap_callback);
|
void Finalize(KMemoryBlockSlabManager* slab_manager, BlockCallback&& block_callback);
|
||||||
|
|
||||||
iterator end() {
|
iterator end() {
|
||||||
return m_memory_block_tree.end();
|
return m_memory_block_tree.end();
|
||||||
|
@ -435,69 +435,14 @@ Result KPageTableBase::FinalizeProcess() {
|
|||||||
// Only process tables should be finalized.
|
// Only process tables should be finalized.
|
||||||
ASSERT(!this->IsKernel());
|
ASSERT(!this->IsKernel());
|
||||||
|
|
||||||
// HLE processes don't have memory mapped.
|
|
||||||
R_SUCCEED_IF(m_impl == nullptr);
|
|
||||||
|
|
||||||
// NOTE: Here Nintendo calls an unknown OnFinalize function.
|
// NOTE: Here Nintendo calls an unknown OnFinalize function.
|
||||||
// this->OnFinalize();
|
// this->OnFinalize();
|
||||||
|
|
||||||
// NOTE: Here Nintendo calls a second unknown OnFinalize function.
|
// NOTE: Here Nintendo calls a second unknown OnFinalize function.
|
||||||
// this->OnFinalize2();
|
// this->OnFinalize2();
|
||||||
|
|
||||||
// Get implementation objects.
|
// NOTE: Here Nintendo does a page table walk to discover heap pages to free.
|
||||||
auto& impl = this->GetImpl();
|
// We will use the block manager finalization below to free them.
|
||||||
auto& mm = m_kernel.MemoryManager();
|
|
||||||
|
|
||||||
// Traverse, freeing all pages.
|
|
||||||
{
|
|
||||||
// Get the address space size.
|
|
||||||
const size_t as_size = this->GetAddressSpaceSize();
|
|
||||||
|
|
||||||
// Begin the traversal.
|
|
||||||
TraversalContext context;
|
|
||||||
TraversalEntry cur_entry = {
|
|
||||||
.phys_addr = 0,
|
|
||||||
.block_size = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool cur_valid = false;
|
|
||||||
TraversalEntry next_entry;
|
|
||||||
bool next_valid;
|
|
||||||
size_t tot_size = 0;
|
|
||||||
|
|
||||||
next_valid = impl.BeginTraversal(std::addressof(next_entry), std::addressof(context),
|
|
||||||
this->GetAddressSpaceStart());
|
|
||||||
|
|
||||||
// Iterate over entries.
|
|
||||||
while (true) {
|
|
||||||
if ((!next_valid && !cur_valid) ||
|
|
||||||
(next_valid && cur_valid &&
|
|
||||||
next_entry.phys_addr == cur_entry.phys_addr + cur_entry.block_size)) {
|
|
||||||
cur_entry.block_size += next_entry.block_size;
|
|
||||||
} else {
|
|
||||||
if (cur_valid && IsHeapPhysicalAddressForFinalize(cur_entry.phys_addr)) {
|
|
||||||
mm.Close(cur_entry.phys_addr, cur_entry.block_size / PageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update tracking variables.
|
|
||||||
tot_size += cur_entry.block_size;
|
|
||||||
cur_entry = next_entry;
|
|
||||||
cur_valid = next_valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cur_entry.block_size + tot_size >= as_size) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
next_valid =
|
|
||||||
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the last block.
|
|
||||||
if (cur_valid && IsHeapPhysicalAddressForFinalize(cur_entry.phys_addr)) {
|
|
||||||
mm.Close(cur_entry.phys_addr, cur_entry.block_size / PageSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
@ -505,14 +450,24 @@ Result KPageTableBase::FinalizeProcess() {
|
|||||||
void KPageTableBase::Finalize() {
|
void KPageTableBase::Finalize() {
|
||||||
this->FinalizeProcess();
|
this->FinalizeProcess();
|
||||||
|
|
||||||
auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) {
|
auto BlockCallback = [&](KProcessAddress addr, u64 size) {
|
||||||
if (m_impl->fastmem_arena) {
|
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) {
|
||||||
|
@ -1239,10 +1239,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
|
||||||
if (this->IsApplication() && Settings::IsNceEnabled()) {
|
const auto& patch = code_set.PatchSegment();
|
||||||
|
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,
|
||||||
|
@ -112,7 +112,14 @@ 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;
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "common/fs/path_util.h"
|
#include "common/fs/path_util.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include "common/polyfill_ranges.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
#include "core/hle/service/acc/profile_manager.h"
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
|
|
||||||
namespace Service::Account {
|
namespace Service::Account {
|
||||||
@ -61,9 +62,7 @@ ProfileManager::ProfileManager() {
|
|||||||
OpenUser(*GetUser(current));
|
OpenUser(*GetUser(current));
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfileManager::~ProfileManager() {
|
ProfileManager::~ProfileManager() = default;
|
||||||
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
|
||||||
@ -113,6 +112,8 @@ 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,
|
||||||
@ -164,6 +165,22 @@ std::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user)
|
|||||||
return GetUserIndex(user.user_uuid);
|
return GetUserIndex(user.user_uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the first user profile seen based on username (which does not enforce uniqueness)
|
||||||
|
std::optional<std::size_t> ProfileManager::GetUserIndex(const std::string& username) const {
|
||||||
|
const auto iter =
|
||||||
|
std::find_if(profiles.begin(), profiles.end(), [&username](const ProfileInfo& p) {
|
||||||
|
const std::string profile_username = Common::StringFromFixedZeroTerminatedBuffer(
|
||||||
|
reinterpret_cast<const char*>(p.username.data()), p.username.size());
|
||||||
|
|
||||||
|
return username.compare(profile_username) == 0;
|
||||||
|
});
|
||||||
|
if (iter == profiles.end()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<std::size_t>(std::distance(profiles.begin(), iter));
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
|
/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
|
||||||
bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const {
|
bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const {
|
||||||
if (!index || index >= MAX_USERS) {
|
if (!index || index >= MAX_USERS) {
|
||||||
@ -326,6 +343,9 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,6 +360,8 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,6 +370,7 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,6 +414,10 @@ 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) {
|
||||||
@ -423,7 +450,10 @@ 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
|
||||||
|
@ -70,6 +70,7 @@ public:
|
|||||||
std::optional<Common::UUID> GetUser(std::size_t index) const;
|
std::optional<Common::UUID> GetUser(std::size_t index) const;
|
||||||
std::optional<std::size_t> GetUserIndex(const Common::UUID& uuid) const;
|
std::optional<std::size_t> GetUserIndex(const Common::UUID& uuid) const;
|
||||||
std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
|
std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
|
||||||
|
std::optional<std::size_t> GetUserIndex(const std::string& username) const;
|
||||||
bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const;
|
bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const;
|
||||||
bool GetProfileBase(Common::UUID uuid, ProfileBase& profile) const;
|
bool GetProfileBase(Common::UUID uuid, ProfileBase& profile) const;
|
||||||
bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
|
bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
|
||||||
@ -103,6 +104,7 @@ 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{};
|
||||||
|
@ -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, u32 handle,
|
const std::string& device_name, const AudioInParameter& in_params,
|
||||||
u64 applet_resource_user_id)
|
Kernel::KProcess* handle, 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")},
|
||||||
impl{std::make_shared<In>(system_, manager, event, session_id)} {
|
process{handle}, 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,6 +45,8 @@ 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()) {
|
||||||
@ -55,6 +57,7 @@ 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() {
|
||||||
@ -196,6 +199,7 @@ 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;
|
||||||
};
|
};
|
||||||
@ -267,6 +271,14 @@ 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()) {
|
||||||
@ -287,8 +299,9 @@ 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 = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name,
|
auto audio_in =
|
||||||
in_params, handle, applet_resource_user_id);
|
std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, in_params,
|
||||||
|
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;
|
||||||
|
|
||||||
@ -318,6 +331,14 @@ 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()) {
|
||||||
@ -338,8 +359,9 @@ 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 = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name,
|
auto audio_in =
|
||||||
in_params, handle, applet_resource_user_id);
|
std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, in_params,
|
||||||
|
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;
|
||||||
|
|
||||||
|
@ -26,9 +26,10 @@ 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, u32 handle, u64 applet_resource_user_id)
|
const AudioOutParameter& in_params, Kernel::KProcess* handle,
|
||||||
|
u64 applet_resource_user_id)
|
||||||
: ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"},
|
: ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"},
|
||||||
event{service_context.CreateEvent("AudioOutEvent")},
|
event{service_context.CreateEvent("AudioOutEvent")}, process{handle},
|
||||||
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
|
||||||
@ -50,11 +51,14 @@ 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() {
|
||||||
@ -206,6 +210,7 @@ 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;
|
||||||
};
|
};
|
||||||
@ -257,6 +262,14 @@ 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");
|
||||||
@ -276,10 +289,11 @@ 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 = std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name,
|
auto audio_out =
|
||||||
in_params, handle, applet_resource_user_id);
|
std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name, in_params,
|
||||||
result = audio_out->GetImpl()->GetSystem().Initialize(device_name, in_params, handle,
|
process.GetPointerUnsafe(), applet_resource_user_id);
|
||||||
applet_resource_user_id);
|
result = audio_out->GetImpl()->GetSystem().Initialize(
|
||||||
|
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};
|
||||||
|
@ -40,7 +40,7 @@ Result SaveDataController::CreateSaveData(FileSys::VirtualDir* out_save_data,
|
|||||||
FileSys::SaveDataSpaceId space,
|
FileSys::SaveDataSpaceId space,
|
||||||
const FileSys::SaveDataAttribute& attribute) {
|
const FileSys::SaveDataAttribute& attribute) {
|
||||||
LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space,
|
LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space,
|
||||||
save_struct.DebugInfo());
|
attribute.DebugInfo());
|
||||||
|
|
||||||
auto save_data = factory->Create(space, attribute);
|
auto save_data = factory->Create(space, attribute);
|
||||||
if (save_data == nullptr) {
|
if (save_data == nullptr) {
|
||||||
|
@ -20,12 +20,13 @@ void LoopProcess(Core::System& system) {
|
|||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
std::shared_ptr<ResourceManager> resource_manager = std::make_shared<ResourceManager>(system);
|
std::shared_ptr<ResourceManager> resource_manager = std::make_shared<ResourceManager>(system);
|
||||||
std::shared_ptr<HidFirmwareSettings> firmware_settings =
|
std::shared_ptr<HidFirmwareSettings> firmware_settings =
|
||||||
std::make_shared<HidFirmwareSettings>();
|
std::make_shared<HidFirmwareSettings>(system);
|
||||||
|
|
||||||
// TODO: Remove this hack when am is emulated properly.
|
// TODO: Remove this hack when am is emulated properly.
|
||||||
resource_manager->Initialize();
|
resource_manager->Initialize();
|
||||||
resource_manager->RegisterAppletResourceUserId(system.ApplicationProcess()->GetProcessId(),
|
resource_manager->RegisterAppletResourceUserId(system.ApplicationProcess()->GetProcessId(),
|
||||||
true);
|
true);
|
||||||
|
resource_manager->SetAruidValidForVibration(system.ApplicationProcess()->GetProcessId(), 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));
|
||||||
|
@ -22,12 +22,16 @@
|
|||||||
#include "hid_core/resources/mouse/mouse.h"
|
#include "hid_core/resources/mouse/mouse.h"
|
||||||
#include "hid_core/resources/npad/npad.h"
|
#include "hid_core/resources/npad/npad.h"
|
||||||
#include "hid_core/resources/npad/npad_types.h"
|
#include "hid_core/resources/npad/npad_types.h"
|
||||||
|
#include "hid_core/resources/npad/npad_vibration.h"
|
||||||
#include "hid_core/resources/palma/palma.h"
|
#include "hid_core/resources/palma/palma.h"
|
||||||
#include "hid_core/resources/six_axis/console_six_axis.h"
|
#include "hid_core/resources/six_axis/console_six_axis.h"
|
||||||
#include "hid_core/resources/six_axis/seven_six_axis.h"
|
#include "hid_core/resources/six_axis/seven_six_axis.h"
|
||||||
#include "hid_core/resources/six_axis/six_axis.h"
|
#include "hid_core/resources/six_axis/six_axis.h"
|
||||||
#include "hid_core/resources/touch_screen/gesture.h"
|
#include "hid_core/resources/touch_screen/gesture.h"
|
||||||
#include "hid_core/resources/touch_screen/touch_screen.h"
|
#include "hid_core/resources/touch_screen/touch_screen.h"
|
||||||
|
#include "hid_core/resources/vibration/gc_vibration_device.h"
|
||||||
|
#include "hid_core/resources/vibration/n64_vibration_device.h"
|
||||||
|
#include "hid_core/resources/vibration/vibration_device.h"
|
||||||
|
|
||||||
namespace Service::HID {
|
namespace Service::HID {
|
||||||
|
|
||||||
@ -38,7 +42,7 @@ public:
|
|||||||
: ServiceFramework{system_, "IActiveVibrationDeviceList"}, resource_manager(resource) {
|
: ServiceFramework{system_, "IActiveVibrationDeviceList"}, resource_manager(resource) {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"},
|
{0, &IActiveVibrationDeviceList::ActivateVibrationDevice, "ActivateVibrationDevice"},
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
@ -46,22 +50,49 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void InitializeVibrationDevice(HLERequestContext& ctx) {
|
void ActivateVibrationDevice(HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
|
const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
|
||||||
|
|
||||||
if (resource_manager != nullptr && resource_manager->GetNpad()) {
|
|
||||||
resource_manager->GetNpad()->InitializeVibrationDevice(vibration_device_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}",
|
LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}",
|
||||||
vibration_device_handle.npad_type, vibration_device_handle.npad_id,
|
vibration_device_handle.npad_type, vibration_device_handle.npad_id,
|
||||||
vibration_device_handle.device_index);
|
vibration_device_handle.device_index);
|
||||||
|
|
||||||
|
const auto result = ActivateVibrationDeviceImpl(vibration_device_handle);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result ActivateVibrationDeviceImpl(const Core::HID::VibrationDeviceHandle& handle) {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
|
||||||
|
const Result is_valid = IsVibrationHandleValid(handle);
|
||||||
|
if (is_valid.IsError()) {
|
||||||
|
return is_valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < list_size; i++) {
|
||||||
|
if (handle.device_index == vibration_device_list[i].device_index &&
|
||||||
|
handle.npad_id == vibration_device_list[i].npad_id &&
|
||||||
|
handle.npad_type == vibration_device_list[i].npad_type) {
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (list_size == vibration_device_list.size()) {
|
||||||
|
return ResultVibrationDeviceIndexOutOfRange;
|
||||||
|
}
|
||||||
|
const Result result = resource_manager->GetVibrationDevice(handle)->Activate();
|
||||||
|
if (result.IsError()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
vibration_device_list[list_size++] = handle;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutable std::mutex mutex;
|
||||||
|
std::size_t list_size{};
|
||||||
|
std::array<Core::HID::VibrationDeviceHandle, 0x100> vibration_device_list{};
|
||||||
std::shared_ptr<ResourceManager> resource_manager;
|
std::shared_ptr<ResourceManager> resource_manager;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -153,7 +184,7 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r
|
|||||||
{209, &IHidServer::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
|
{209, &IHidServer::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
|
||||||
{210, &IHidServer::EndPermitVibrationSession, "EndPermitVibrationSession"},
|
{210, &IHidServer::EndPermitVibrationSession, "EndPermitVibrationSession"},
|
||||||
{211, &IHidServer::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"},
|
{211, &IHidServer::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"},
|
||||||
{212, nullptr, "SendVibrationValueInBool"},
|
{212, &IHidServer::SendVibrationValueInBool, "SendVibrationValueInBool"},
|
||||||
{300, &IHidServer::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
|
{300, &IHidServer::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
|
||||||
{301, &IHidServer::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
|
{301, &IHidServer::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
|
||||||
{302, &IHidServer::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"},
|
{302, &IHidServer::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"},
|
||||||
@ -1492,59 +1523,13 @@ void IHidServer::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) {
|
|||||||
void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) {
|
void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
|
const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
|
||||||
const auto controller = GetResourceManager()->GetNpad();
|
|
||||||
|
|
||||||
Core::HID::VibrationDeviceInfo vibration_device_info;
|
Core::HID::VibrationDeviceInfo vibration_device_info{};
|
||||||
bool check_device_index = false;
|
const auto result = GetResourceManager()->GetVibrationDeviceInfo(vibration_device_info,
|
||||||
|
vibration_device_handle);
|
||||||
switch (vibration_device_handle.npad_type) {
|
|
||||||
case Core::HID::NpadStyleIndex::ProController:
|
|
||||||
case Core::HID::NpadStyleIndex::Handheld:
|
|
||||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
|
||||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
|
||||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
|
||||||
vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
|
|
||||||
check_device_index = true;
|
|
||||||
break;
|
|
||||||
case Core::HID::NpadStyleIndex::GameCube:
|
|
||||||
vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm;
|
|
||||||
break;
|
|
||||||
case Core::HID::NpadStyleIndex::N64:
|
|
||||||
vibration_device_info.type = Core::HID::VibrationDeviceType::N64;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
|
|
||||||
if (check_device_index) {
|
|
||||||
switch (vibration_device_handle.device_index) {
|
|
||||||
case Core::HID::DeviceIndex::Left:
|
|
||||||
vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
|
|
||||||
break;
|
|
||||||
case Core::HID::DeviceIndex::Right:
|
|
||||||
vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
|
|
||||||
break;
|
|
||||||
case Core::HID::DeviceIndex::None:
|
|
||||||
default:
|
|
||||||
ASSERT_MSG(false, "DeviceIndex should never be None!");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}",
|
|
||||||
vibration_device_info.type, vibration_device_info.position);
|
|
||||||
|
|
||||||
const auto result = IsVibrationHandleValid(vibration_device_handle);
|
|
||||||
if (result.IsError()) {
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
rb.PushRaw(vibration_device_info);
|
rb.PushRaw(vibration_device_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1560,16 +1545,16 @@ void IHidServer::SendVibrationValue(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
const auto parameters{rp.PopRaw<Parameters>()};
|
const auto parameters{rp.PopRaw<Parameters>()};
|
||||||
|
|
||||||
GetResourceManager()->GetNpad()->VibrateController(parameters.applet_resource_user_id,
|
|
||||||
parameters.vibration_device_handle,
|
|
||||||
parameters.vibration_value);
|
|
||||||
|
|
||||||
LOG_DEBUG(Service_HID,
|
LOG_DEBUG(Service_HID,
|
||||||
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
|
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
|
||||||
parameters.vibration_device_handle.npad_type,
|
parameters.vibration_device_handle.npad_type,
|
||||||
parameters.vibration_device_handle.npad_id,
|
parameters.vibration_device_handle.npad_id,
|
||||||
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
|
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
GetResourceManager()->SendVibrationValue(parameters.applet_resource_user_id,
|
||||||
|
parameters.vibration_device_handle,
|
||||||
|
parameters.vibration_value);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
@ -1591,10 +1576,28 @@ void IHidServer::GetActualVibrationValue(HLERequestContext& ctx) {
|
|||||||
parameters.vibration_device_handle.npad_id,
|
parameters.vibration_device_handle.npad_id,
|
||||||
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
|
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
bool has_active_aruid{};
|
||||||
|
NpadVibrationDevice* device{nullptr};
|
||||||
|
Core::HID::VibrationValue vibration_value{};
|
||||||
|
Result result = GetResourceManager()->IsVibrationAruidActive(parameters.applet_resource_user_id,
|
||||||
|
has_active_aruid);
|
||||||
|
|
||||||
|
if (result.IsSuccess() && has_active_aruid) {
|
||||||
|
result = IsVibrationHandleValid(parameters.vibration_device_handle);
|
||||||
|
}
|
||||||
|
if (result.IsSuccess() && has_active_aruid) {
|
||||||
|
device = GetResourceManager()->GetNSVibrationDevice(parameters.vibration_device_handle);
|
||||||
|
}
|
||||||
|
if (device != nullptr) {
|
||||||
|
result = device->GetActualVibrationValue(vibration_value);
|
||||||
|
}
|
||||||
|
if (result.IsError()) {
|
||||||
|
vibration_value = Core::HID::DEFAULT_VIBRATION_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 6};
|
IPC::ResponseBuilder rb{ctx, 6};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.PushRaw(GetResourceManager()->GetNpad()->GetLastVibration(
|
rb.PushRaw(vibration_value);
|
||||||
parameters.applet_resource_user_id, parameters.vibration_device_handle));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) {
|
void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) {
|
||||||
@ -1609,25 +1612,27 @@ void IHidServer::PermitVibration(HLERequestContext& ctx) {
|
|||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto can_vibrate{rp.Pop<bool>()};
|
const auto can_vibrate{rp.Pop<bool>()};
|
||||||
|
|
||||||
// nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value
|
|
||||||
// by converting it to a bool
|
|
||||||
Settings::values.vibration_enabled.SetValue(can_vibrate);
|
|
||||||
|
|
||||||
LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
|
LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
|
||||||
|
|
||||||
|
const auto result =
|
||||||
|
GetResourceManager()->GetNpad()->GetVibrationHandler()->SetVibrationMasterVolume(
|
||||||
|
can_vibrate ? 1.0f : 0.0f);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IHidServer::IsVibrationPermitted(HLERequestContext& ctx) {
|
void IHidServer::IsVibrationPermitted(HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_HID, "called");
|
LOG_DEBUG(Service_HID, "called");
|
||||||
|
|
||||||
// nnSDK checks if a float is greater than zero. We return the bool we stored earlier
|
f32 master_volume{};
|
||||||
const auto is_enabled = Settings::values.vibration_enabled.GetValue();
|
const auto result =
|
||||||
|
GetResourceManager()->GetNpad()->GetVibrationHandler()->GetVibrationMasterVolume(
|
||||||
|
master_volume);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
rb.Push(is_enabled);
|
rb.Push(master_volume > 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IHidServer::SendVibrationValues(HLERequestContext& ctx) {
|
void IHidServer::SendVibrationValues(HLERequestContext& ctx) {
|
||||||
@ -1645,13 +1650,22 @@ void IHidServer::SendVibrationValues(HLERequestContext& ctx) {
|
|||||||
auto vibration_values = std::span(
|
auto vibration_values = std::span(
|
||||||
reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count);
|
reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count);
|
||||||
|
|
||||||
GetResourceManager()->GetNpad()->VibrateControllers(applet_resource_user_id,
|
|
||||||
vibration_device_handles, vibration_values);
|
|
||||||
|
|
||||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||||
|
|
||||||
|
Result result = ResultSuccess;
|
||||||
|
if (handle_count != vibration_count) {
|
||||||
|
result = ResultVibrationArraySizeMismatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < handle_count; i++) {
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
result = GetResourceManager()->SendVibrationValue(
|
||||||
|
applet_resource_user_id, vibration_device_handles[i], vibration_values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) {
|
void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) {
|
||||||
@ -1666,43 +1680,6 @@ void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
const auto parameters{rp.PopRaw<Parameters>()};
|
const auto parameters{rp.PopRaw<Parameters>()};
|
||||||
|
|
||||||
/**
|
|
||||||
* Note: This uses yuzu-specific behavior such that the StopHard command produces
|
|
||||||
* vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below,
|
|
||||||
* in order to differentiate between Stop and StopHard commands.
|
|
||||||
* This is done to reuse the controller vibration functions made for regular controllers.
|
|
||||||
*/
|
|
||||||
const auto vibration_value = [parameters] {
|
|
||||||
switch (parameters.gc_erm_command) {
|
|
||||||
case Core::HID::VibrationGcErmCommand::Stop:
|
|
||||||
return Core::HID::VibrationValue{
|
|
||||||
.low_amplitude = 0.0f,
|
|
||||||
.low_frequency = 160.0f,
|
|
||||||
.high_amplitude = 0.0f,
|
|
||||||
.high_frequency = 320.0f,
|
|
||||||
};
|
|
||||||
case Core::HID::VibrationGcErmCommand::Start:
|
|
||||||
return Core::HID::VibrationValue{
|
|
||||||
.low_amplitude = 1.0f,
|
|
||||||
.low_frequency = 160.0f,
|
|
||||||
.high_amplitude = 1.0f,
|
|
||||||
.high_frequency = 320.0f,
|
|
||||||
};
|
|
||||||
case Core::HID::VibrationGcErmCommand::StopHard:
|
|
||||||
return Core::HID::VibrationValue{
|
|
||||||
.low_amplitude = 0.0f,
|
|
||||||
.low_frequency = 0.0f,
|
|
||||||
.high_amplitude = 0.0f,
|
|
||||||
.high_frequency = 0.0f,
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
return Core::HID::DEFAULT_VIBRATION_VALUE;
|
|
||||||
}
|
|
||||||
}();
|
|
||||||
|
|
||||||
GetResourceManager()->GetNpad()->VibrateController(
|
|
||||||
parameters.applet_resource_user_id, parameters.vibration_device_handle, vibration_value);
|
|
||||||
|
|
||||||
LOG_DEBUG(Service_HID,
|
LOG_DEBUG(Service_HID,
|
||||||
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
|
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
|
||||||
"gc_erm_command={}",
|
"gc_erm_command={}",
|
||||||
@ -1711,8 +1688,23 @@ void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) {
|
|||||||
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id,
|
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id,
|
||||||
parameters.gc_erm_command);
|
parameters.gc_erm_command);
|
||||||
|
|
||||||
|
bool has_active_aruid{};
|
||||||
|
NpadGcVibrationDevice* gc_device{nullptr};
|
||||||
|
Result result = GetResourceManager()->IsVibrationAruidActive(parameters.applet_resource_user_id,
|
||||||
|
has_active_aruid);
|
||||||
|
|
||||||
|
if (result.IsSuccess() && has_active_aruid) {
|
||||||
|
result = IsVibrationHandleValid(parameters.vibration_device_handle);
|
||||||
|
}
|
||||||
|
if (result.IsSuccess() && has_active_aruid) {
|
||||||
|
gc_device = GetResourceManager()->GetGcVibrationDevice(parameters.vibration_device_handle);
|
||||||
|
}
|
||||||
|
if (gc_device != nullptr) {
|
||||||
|
result = gc_device->SendVibrationGcErmCommand(parameters.gc_erm_command);
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) {
|
void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) {
|
||||||
@ -1725,33 +1717,31 @@ void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
const auto parameters{rp.PopRaw<Parameters>()};
|
const auto parameters{rp.PopRaw<Parameters>()};
|
||||||
|
|
||||||
const auto last_vibration = GetResourceManager()->GetNpad()->GetLastVibration(
|
|
||||||
parameters.applet_resource_user_id, parameters.vibration_device_handle);
|
|
||||||
|
|
||||||
const auto gc_erm_command = [last_vibration] {
|
|
||||||
if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) {
|
|
||||||
return Core::HID::VibrationGcErmCommand::Start;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Note: This uses yuzu-specific behavior such that the StopHard command produces
|
|
||||||
* vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function
|
|
||||||
* SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands.
|
|
||||||
* This is done to reuse the controller vibration functions made for regular controllers.
|
|
||||||
*/
|
|
||||||
if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) {
|
|
||||||
return Core::HID::VibrationGcErmCommand::StopHard;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Core::HID::VibrationGcErmCommand::Stop;
|
|
||||||
}();
|
|
||||||
|
|
||||||
LOG_DEBUG(Service_HID,
|
LOG_DEBUG(Service_HID,
|
||||||
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
|
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
|
||||||
parameters.vibration_device_handle.npad_type,
|
parameters.vibration_device_handle.npad_type,
|
||||||
parameters.vibration_device_handle.npad_id,
|
parameters.vibration_device_handle.npad_id,
|
||||||
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
|
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
bool has_active_aruid{};
|
||||||
|
NpadGcVibrationDevice* gc_device{nullptr};
|
||||||
|
Core::HID::VibrationGcErmCommand gc_erm_command{};
|
||||||
|
Result result = GetResourceManager()->IsVibrationAruidActive(parameters.applet_resource_user_id,
|
||||||
|
has_active_aruid);
|
||||||
|
|
||||||
|
if (result.IsSuccess() && has_active_aruid) {
|
||||||
|
result = IsVibrationHandleValid(parameters.vibration_device_handle);
|
||||||
|
}
|
||||||
|
if (result.IsSuccess() && has_active_aruid) {
|
||||||
|
gc_device = GetResourceManager()->GetGcVibrationDevice(parameters.vibration_device_handle);
|
||||||
|
}
|
||||||
|
if (gc_device != nullptr) {
|
||||||
|
result = gc_device->GetActualVibrationGcErmCommand(gc_erm_command);
|
||||||
|
}
|
||||||
|
if (result.IsError()) {
|
||||||
|
gc_erm_command = Core::HID::VibrationGcErmCommand::Stop;
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.PushEnum(gc_erm_command);
|
rb.PushEnum(gc_erm_command);
|
||||||
@ -1761,21 +1751,24 @@ void IHidServer::BeginPermitVibrationSession(HLERequestContext& ctx) {
|
|||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
GetResourceManager()->GetNpad()->SetPermitVibrationSession(true);
|
|
||||||
|
|
||||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||||
|
|
||||||
|
const auto result =
|
||||||
|
GetResourceManager()->GetNpad()->GetVibrationHandler()->BeginPermitVibrationSession(
|
||||||
|
applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IHidServer::EndPermitVibrationSession(HLERequestContext& ctx) {
|
void IHidServer::EndPermitVibrationSession(HLERequestContext& ctx) {
|
||||||
GetResourceManager()->GetNpad()->SetPermitVibrationSession(false);
|
|
||||||
|
|
||||||
LOG_DEBUG(Service_HID, "called");
|
LOG_DEBUG(Service_HID, "called");
|
||||||
|
|
||||||
|
const auto result =
|
||||||
|
GetResourceManager()->GetNpad()->GetVibrationHandler()->EndPermitVibrationSession();
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) {
|
void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) {
|
||||||
@ -1795,10 +1788,61 @@ void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) {
|
|||||||
parameters.vibration_device_handle.npad_id,
|
parameters.vibration_device_handle.npad_id,
|
||||||
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
|
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
bool is_mounted{};
|
||||||
|
NpadVibrationBase* device{nullptr};
|
||||||
|
Result result = IsVibrationHandleValid(parameters.vibration_device_handle);
|
||||||
|
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
device = GetResourceManager()->GetVibrationDevice(parameters.vibration_device_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device != nullptr) {
|
||||||
|
is_mounted = device->IsVibrationMounted();
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
rb.Push(GetResourceManager()->GetNpad()->IsVibrationDeviceMounted(
|
rb.Push(is_mounted);
|
||||||
parameters.applet_resource_user_id, parameters.vibration_device_handle));
|
}
|
||||||
|
|
||||||
|
void IHidServer::SendVibrationValueInBool(HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
struct Parameters {
|
||||||
|
Core::HID::VibrationDeviceHandle vibration_device_handle;
|
||||||
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
|
u64 applet_resource_user_id;
|
||||||
|
bool is_vibrating;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
|
||||||
|
|
||||||
|
const auto parameters{rp.PopRaw<Parameters>()};
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_HID,
|
||||||
|
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
|
||||||
|
"is_vibrating={}",
|
||||||
|
parameters.vibration_device_handle.npad_type,
|
||||||
|
parameters.vibration_device_handle.npad_id,
|
||||||
|
parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id,
|
||||||
|
parameters.is_vibrating);
|
||||||
|
|
||||||
|
bool has_active_aruid{};
|
||||||
|
NpadN64VibrationDevice* n64_device{nullptr};
|
||||||
|
Result result = GetResourceManager()->IsVibrationAruidActive(parameters.applet_resource_user_id,
|
||||||
|
has_active_aruid);
|
||||||
|
|
||||||
|
if (result.IsSuccess() && has_active_aruid) {
|
||||||
|
result = IsVibrationHandleValid(parameters.vibration_device_handle);
|
||||||
|
}
|
||||||
|
if (result.IsSuccess() && has_active_aruid) {
|
||||||
|
n64_device =
|
||||||
|
GetResourceManager()->GetN64VibrationDevice(parameters.vibration_device_handle);
|
||||||
|
}
|
||||||
|
if (n64_device != nullptr) {
|
||||||
|
result = n64_device->SendValueInBool(parameters.is_vibrating);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) {
|
void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) {
|
||||||
|
@ -97,6 +97,7 @@ private:
|
|||||||
void BeginPermitVibrationSession(HLERequestContext& ctx);
|
void BeginPermitVibrationSession(HLERequestContext& ctx);
|
||||||
void EndPermitVibrationSession(HLERequestContext& ctx);
|
void EndPermitVibrationSession(HLERequestContext& ctx);
|
||||||
void IsVibrationDeviceMounted(HLERequestContext& ctx);
|
void IsVibrationDeviceMounted(HLERequestContext& ctx);
|
||||||
|
void SendVibrationValueInBool(HLERequestContext& ctx);
|
||||||
void ActivateConsoleSixAxisSensor(HLERequestContext& ctx);
|
void ActivateConsoleSixAxisSensor(HLERequestContext& ctx);
|
||||||
void StartConsoleSixAxisSensor(HLERequestContext& ctx);
|
void StartConsoleSixAxisSensor(HLERequestContext& ctx);
|
||||||
void StopConsoleSixAxisSensor(HLERequestContext& ctx);
|
void StopConsoleSixAxisSensor(HLERequestContext& ctx);
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "hid_core/resource_manager.h"
|
#include "hid_core/resource_manager.h"
|
||||||
#include "hid_core/resources/npad/npad.h"
|
#include "hid_core/resources/npad/npad.h"
|
||||||
#include "hid_core/resources/npad/npad_types.h"
|
#include "hid_core/resources/npad/npad_types.h"
|
||||||
|
#include "hid_core/resources/npad/npad_vibration.h"
|
||||||
#include "hid_core/resources/palma/palma.h"
|
#include "hid_core/resources/palma/palma.h"
|
||||||
#include "hid_core/resources/touch_screen/touch_screen.h"
|
#include "hid_core/resources/touch_screen/touch_screen.h"
|
||||||
|
|
||||||
@ -67,14 +68,14 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
|
|||||||
{501, &IHidSystemServer::RegisterAppletResourceUserId, "RegisterAppletResourceUserId"},
|
{501, &IHidSystemServer::RegisterAppletResourceUserId, "RegisterAppletResourceUserId"},
|
||||||
{502, &IHidSystemServer::UnregisterAppletResourceUserId, "UnregisterAppletResourceUserId"},
|
{502, &IHidSystemServer::UnregisterAppletResourceUserId, "UnregisterAppletResourceUserId"},
|
||||||
{503, &IHidSystemServer::EnableAppletToGetInput, "EnableAppletToGetInput"},
|
{503, &IHidSystemServer::EnableAppletToGetInput, "EnableAppletToGetInput"},
|
||||||
{504, nullptr, "SetAruidValidForVibration"},
|
{504, &IHidSystemServer::SetAruidValidForVibration, "SetAruidValidForVibration"},
|
||||||
{505, &IHidSystemServer::EnableAppletToGetSixAxisSensor, "EnableAppletToGetSixAxisSensor"},
|
{505, &IHidSystemServer::EnableAppletToGetSixAxisSensor, "EnableAppletToGetSixAxisSensor"},
|
||||||
{506, &IHidSystemServer::EnableAppletToGetPadInput, "EnableAppletToGetPadInput"},
|
{506, &IHidSystemServer::EnableAppletToGetPadInput, "EnableAppletToGetPadInput"},
|
||||||
{507, &IHidSystemServer::EnableAppletToGetTouchScreen, "EnableAppletToGetTouchScreen"},
|
{507, &IHidSystemServer::EnableAppletToGetTouchScreen, "EnableAppletToGetTouchScreen"},
|
||||||
{510, nullptr, "SetVibrationMasterVolume"},
|
{510, &IHidSystemServer::SetVibrationMasterVolume, "SetVibrationMasterVolume"},
|
||||||
{511, nullptr, "GetVibrationMasterVolume"},
|
{511, &IHidSystemServer::GetVibrationMasterVolume, "GetVibrationMasterVolume"},
|
||||||
{512, nullptr, "BeginPermitVibrationSession"},
|
{512, &IHidSystemServer::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
|
||||||
{513, nullptr, "EndPermitVibrationSession"},
|
{513, &IHidSystemServer::EndPermitVibrationSession, "EndPermitVibrationSession"},
|
||||||
{514, nullptr, "Unknown514"},
|
{514, nullptr, "Unknown514"},
|
||||||
{520, nullptr, "EnableHandheldHids"},
|
{520, nullptr, "EnableHandheldHids"},
|
||||||
{521, nullptr, "DisableHandheldHids"},
|
{521, nullptr, "DisableHandheldHids"},
|
||||||
@ -156,7 +157,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
|
|||||||
{1152, nullptr, "SetTouchScreenDefaultConfiguration"},
|
{1152, nullptr, "SetTouchScreenDefaultConfiguration"},
|
||||||
{1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
|
{1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
|
||||||
{1154, nullptr, "IsFirmwareAvailableForNotification"},
|
{1154, nullptr, "IsFirmwareAvailableForNotification"},
|
||||||
{1155, nullptr, "SetForceHandheldStyleVibration"},
|
{1155, &IHidSystemServer::SetForceHandheldStyleVibration, "SetForceHandheldStyleVibration"},
|
||||||
{1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
|
{1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
|
||||||
{1157, nullptr, "CancelConnectionTrigger"},
|
{1157, nullptr, "CancelConnectionTrigger"},
|
||||||
{1200, nullptr, "IsButtonConfigSupported"},
|
{1200, nullptr, "IsButtonConfigSupported"},
|
||||||
@ -538,6 +539,27 @@ void IHidSystemServer::EnableAppletToGetInput(HLERequestContext& ctx) {
|
|||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IHidSystemServer::SetAruidValidForVibration(HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
struct Parameters {
|
||||||
|
bool is_enabled;
|
||||||
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
|
u64 applet_resource_user_id;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
|
||||||
|
|
||||||
|
const auto parameters{rp.PopRaw<Parameters>()};
|
||||||
|
|
||||||
|
LOG_INFO(Service_HID, "called, is_enabled={}, applet_resource_user_id={}",
|
||||||
|
parameters.is_enabled, parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
GetResourceManager()->SetAruidValidForVibration(parameters.applet_resource_user_id,
|
||||||
|
parameters.is_enabled);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
void IHidSystemServer::EnableAppletToGetSixAxisSensor(HLERequestContext& ctx) {
|
void IHidSystemServer::EnableAppletToGetSixAxisSensor(HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
@ -601,6 +623,57 @@ void IHidSystemServer::EnableAppletToGetTouchScreen(HLERequestContext& ctx) {
|
|||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IHidSystemServer::SetVibrationMasterVolume(HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto master_volume{rp.Pop<f32>()};
|
||||||
|
|
||||||
|
LOG_INFO(Service_HID, "called, volume={}", master_volume);
|
||||||
|
|
||||||
|
const auto result =
|
||||||
|
GetResourceManager()->GetNpad()->GetVibrationHandler()->SetVibrationMasterVolume(
|
||||||
|
master_volume);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IHidSystemServer::GetVibrationMasterVolume(HLERequestContext& ctx) {
|
||||||
|
f32 master_volume{};
|
||||||
|
const auto result =
|
||||||
|
GetResourceManager()->GetNpad()->GetVibrationHandler()->GetVibrationMasterVolume(
|
||||||
|
master_volume);
|
||||||
|
|
||||||
|
LOG_INFO(Service_HID, "called, volume={}", master_volume);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(result);
|
||||||
|
rb.Push(master_volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IHidSystemServer::BeginPermitVibrationSession(HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
|
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
||||||
|
|
||||||
|
const auto result =
|
||||||
|
GetResourceManager()->GetNpad()->GetVibrationHandler()->BeginPermitVibrationSession(
|
||||||
|
applet_resource_user_id);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IHidSystemServer::EndPermitVibrationSession(HLERequestContext& ctx) {
|
||||||
|
LOG_INFO(Service_HID, "called");
|
||||||
|
|
||||||
|
const auto result =
|
||||||
|
GetResourceManager()->GetNpad()->GetVibrationHandler()->EndPermitVibrationSession();
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result);
|
||||||
|
}
|
||||||
|
|
||||||
void IHidSystemServer::IsJoyConAttachedOnAllRail(HLERequestContext& ctx) {
|
void IHidSystemServer::IsJoyConAttachedOnAllRail(HLERequestContext& ctx) {
|
||||||
const bool is_attached = true;
|
const bool is_attached = true;
|
||||||
|
|
||||||
@ -749,6 +822,19 @@ void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx
|
|||||||
rb.PushRaw(touchscreen_config);
|
rb.PushRaw(touchscreen_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IHidSystemServer::SetForceHandheldStyleVibration(HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto is_forced{rp.Pop<bool>()};
|
||||||
|
|
||||||
|
LOG_INFO(Service_HID, "called, is_forced={}", is_forced);
|
||||||
|
|
||||||
|
GetResourceManager()->SetForceHandheldStyleVibration(is_forced);
|
||||||
|
GetResourceManager()->GetNpad()->UpdateHandheldAbstractState();
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
void IHidSystemServer::IsUsingCustomButtonConfig(HLERequestContext& ctx) {
|
void IHidSystemServer::IsUsingCustomButtonConfig(HLERequestContext& ctx) {
|
||||||
const bool is_enabled = false;
|
const bool is_enabled = false;
|
||||||
|
|
||||||
|
@ -42,9 +42,14 @@ private:
|
|||||||
void RegisterAppletResourceUserId(HLERequestContext& ctx);
|
void RegisterAppletResourceUserId(HLERequestContext& ctx);
|
||||||
void UnregisterAppletResourceUserId(HLERequestContext& ctx);
|
void UnregisterAppletResourceUserId(HLERequestContext& ctx);
|
||||||
void EnableAppletToGetInput(HLERequestContext& ctx);
|
void EnableAppletToGetInput(HLERequestContext& ctx);
|
||||||
|
void SetAruidValidForVibration(HLERequestContext& ctx);
|
||||||
void EnableAppletToGetSixAxisSensor(HLERequestContext& ctx);
|
void EnableAppletToGetSixAxisSensor(HLERequestContext& ctx);
|
||||||
void EnableAppletToGetPadInput(HLERequestContext& ctx);
|
void EnableAppletToGetPadInput(HLERequestContext& ctx);
|
||||||
void EnableAppletToGetTouchScreen(HLERequestContext& ctx);
|
void EnableAppletToGetTouchScreen(HLERequestContext& ctx);
|
||||||
|
void SetVibrationMasterVolume(HLERequestContext& ctx);
|
||||||
|
void GetVibrationMasterVolume(HLERequestContext& ctx);
|
||||||
|
void BeginPermitVibrationSession(HLERequestContext& ctx);
|
||||||
|
void EndPermitVibrationSession(HLERequestContext& ctx);
|
||||||
void IsJoyConAttachedOnAllRail(HLERequestContext& ctx);
|
void IsJoyConAttachedOnAllRail(HLERequestContext& ctx);
|
||||||
void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx);
|
void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx);
|
||||||
void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx);
|
void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx);
|
||||||
@ -61,6 +66,7 @@ private:
|
|||||||
void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx);
|
void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx);
|
||||||
void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx);
|
void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx);
|
||||||
void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
|
void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
|
||||||
|
void SetForceHandheldStyleVibration(HLERequestContext& ctx);
|
||||||
void IsUsingCustomButtonConfig(HLERequestContext& ctx);
|
void IsUsingCustomButtonConfig(HLERequestContext& ctx);
|
||||||
|
|
||||||
std::shared_ptr<ResourceManager> GetResourceManager();
|
std::shared_ptr<ResourceManager> GetResourceManager();
|
||||||
|
@ -24,19 +24,6 @@
|
|||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
|
||||||
namespace {
|
|
||||||
static thread_local std::array read_buffer_data_a{
|
|
||||||
Common::ScratchBuffer<u8>(),
|
|
||||||
Common::ScratchBuffer<u8>(),
|
|
||||||
Common::ScratchBuffer<u8>(),
|
|
||||||
};
|
|
||||||
static thread_local std::array read_buffer_data_x{
|
|
||||||
Common::ScratchBuffer<u8>(),
|
|
||||||
Common::ScratchBuffer<u8>(),
|
|
||||||
Common::ScratchBuffer<u8>(),
|
|
||||||
};
|
|
||||||
} // Anonymous namespace
|
|
||||||
|
|
||||||
namespace Service {
|
namespace Service {
|
||||||
|
|
||||||
SessionRequestHandler::SessionRequestHandler(Kernel::KernelCore& kernel_, const char* service_name_)
|
SessionRequestHandler::SessionRequestHandler(Kernel::KernelCore& kernel_, const char* service_name_)
|
||||||
@ -344,48 +331,27 @@ std::vector<u8> HLERequestContext::ReadBufferCopy(std::size_t buffer_index) cons
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::span<const u8> HLERequestContext::ReadBufferA(std::size_t buffer_index) const {
|
std::span<const u8> HLERequestContext::ReadBufferA(std::size_t buffer_index) const {
|
||||||
static thread_local std::array read_buffer_a{
|
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> gm(memory, 0, 0);
|
||||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
|
|
||||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
|
|
||||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
|
|
||||||
};
|
|
||||||
|
|
||||||
ASSERT_OR_EXECUTE_MSG(
|
ASSERT_OR_EXECUTE_MSG(
|
||||||
BufferDescriptorA().size() > buffer_index, { return {}; },
|
BufferDescriptorA().size() > buffer_index, { return {}; },
|
||||||
"BufferDescriptorA invalid buffer_index {}", buffer_index);
|
"BufferDescriptorA invalid buffer_index {}", buffer_index);
|
||||||
auto& read_buffer = read_buffer_a[buffer_index];
|
return gm.Read(BufferDescriptorA()[buffer_index].Address(),
|
||||||
return read_buffer.Read(BufferDescriptorA()[buffer_index].Address(),
|
BufferDescriptorA()[buffer_index].Size(), &read_buffer_data_a[buffer_index]);
|
||||||
BufferDescriptorA()[buffer_index].Size(),
|
|
||||||
&read_buffer_data_a[buffer_index]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::span<const u8> HLERequestContext::ReadBufferX(std::size_t buffer_index) const {
|
std::span<const u8> HLERequestContext::ReadBufferX(std::size_t buffer_index) const {
|
||||||
static thread_local std::array read_buffer_x{
|
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> gm(memory, 0, 0);
|
||||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
|
|
||||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
|
|
||||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
|
|
||||||
};
|
|
||||||
|
|
||||||
ASSERT_OR_EXECUTE_MSG(
|
ASSERT_OR_EXECUTE_MSG(
|
||||||
BufferDescriptorX().size() > buffer_index, { return {}; },
|
BufferDescriptorX().size() > buffer_index, { return {}; },
|
||||||
"BufferDescriptorX invalid buffer_index {}", buffer_index);
|
"BufferDescriptorX invalid buffer_index {}", buffer_index);
|
||||||
auto& read_buffer = read_buffer_x[buffer_index];
|
return gm.Read(BufferDescriptorX()[buffer_index].Address(),
|
||||||
return read_buffer.Read(BufferDescriptorX()[buffer_index].Address(),
|
BufferDescriptorX()[buffer_index].Size(), &read_buffer_data_x[buffer_index]);
|
||||||
BufferDescriptorX()[buffer_index].Size(),
|
|
||||||
&read_buffer_data_x[buffer_index]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
|
std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
|
||||||
static thread_local std::array read_buffer_a{
|
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> gm(memory, 0, 0);
|
||||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
|
|
||||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
|
|
||||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
|
|
||||||
};
|
|
||||||
static thread_local std::array read_buffer_x{
|
|
||||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
|
|
||||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
|
|
||||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead>(memory, 0, 0),
|
|
||||||
};
|
|
||||||
|
|
||||||
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
|
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
|
||||||
BufferDescriptorA()[buffer_index].Size()};
|
BufferDescriptorA()[buffer_index].Size()};
|
||||||
@ -402,18 +368,14 @@ std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) cons
|
|||||||
ASSERT_OR_EXECUTE_MSG(
|
ASSERT_OR_EXECUTE_MSG(
|
||||||
BufferDescriptorA().size() > buffer_index, { return {}; },
|
BufferDescriptorA().size() > buffer_index, { return {}; },
|
||||||
"BufferDescriptorA invalid buffer_index {}", buffer_index);
|
"BufferDescriptorA invalid buffer_index {}", buffer_index);
|
||||||
auto& read_buffer = read_buffer_a[buffer_index];
|
return gm.Read(BufferDescriptorA()[buffer_index].Address(),
|
||||||
return read_buffer.Read(BufferDescriptorA()[buffer_index].Address(),
|
BufferDescriptorA()[buffer_index].Size(), &read_buffer_data_a[buffer_index]);
|
||||||
BufferDescriptorA()[buffer_index].Size(),
|
|
||||||
&read_buffer_data_a[buffer_index]);
|
|
||||||
} else {
|
} else {
|
||||||
ASSERT_OR_EXECUTE_MSG(
|
ASSERT_OR_EXECUTE_MSG(
|
||||||
BufferDescriptorX().size() > buffer_index, { return {}; },
|
BufferDescriptorX().size() > buffer_index, { return {}; },
|
||||||
"BufferDescriptorX invalid buffer_index {}", buffer_index);
|
"BufferDescriptorX invalid buffer_index {}", buffer_index);
|
||||||
auto& read_buffer = read_buffer_x[buffer_index];
|
return gm.Read(BufferDescriptorX()[buffer_index].Address(),
|
||||||
return read_buffer.Read(BufferDescriptorX()[buffer_index].Address(),
|
BufferDescriptorX()[buffer_index].Size(), &read_buffer_data_x[buffer_index]);
|
||||||
BufferDescriptorX()[buffer_index].Size(),
|
|
||||||
&read_buffer_data_x[buffer_index]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,6 +426,9 @@ private:
|
|||||||
|
|
||||||
Kernel::KernelCore& kernel;
|
Kernel::KernelCore& kernel;
|
||||||
Core::Memory::Memory& memory;
|
Core::Memory::Memory& memory;
|
||||||
|
|
||||||
|
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_a{};
|
||||||
|
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_x{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service
|
} // namespace Service
|
||||||
|
@ -441,7 +441,10 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target
|
|||||||
device_state = DeviceState::TagMounted;
|
device_state = DeviceState::TagMounted;
|
||||||
mount_target = mount_target_;
|
mount_target = mount_target_;
|
||||||
|
|
||||||
if (!is_corrupted && mount_target != NFP::MountTarget::Rom) {
|
const bool create_backup =
|
||||||
|
mount_target == NFP::MountTarget::All || mount_target == NFP::MountTarget::Ram ||
|
||||||
|
(mount_target == NFP::MountTarget::Rom && HasBackup(encrypted_tag_data.uuid).IsError());
|
||||||
|
if (!is_corrupted && create_backup) {
|
||||||
std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File));
|
std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File));
|
||||||
memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
|
memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
|
||||||
WriteBackupData(encrypted_tag_data.uuid, data);
|
WriteBackupData(encrypted_tag_data.uuid, data);
|
||||||
|
@ -16,9 +16,8 @@
|
|||||||
|
|
||||||
namespace Service::Nvidia::NvCore {
|
namespace Service::Nvidia::NvCore {
|
||||||
|
|
||||||
Session::Session(size_t id_, Kernel::KProcess* process_, size_t smmu_id_)
|
Session::Session(SessionId id_, Kernel::KProcess* process_, Core::Asid asid_)
|
||||||
: id{id_}, process{process_}, smmu_id{smmu_id_},
|
: id{id_}, process{process_}, asid{asid_}, has_preallocated_area{}, mapper{}, is_active{} {}
|
||||||
has_preallocated_area{}, mapper{}, is_active{} {}
|
|
||||||
|
|
||||||
Session::~Session() = default;
|
Session::~Session() = default;
|
||||||
|
|
||||||
@ -41,7 +40,9 @@ Container::Container(Tegra::Host1x::Host1x& host1x_) {
|
|||||||
|
|
||||||
Container::~Container() = default;
|
Container::~Container() = default;
|
||||||
|
|
||||||
size_t Container::OpenSession(Kernel::KProcess* process) {
|
SessionId 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) {
|
||||||
@ -54,14 +55,14 @@ size_t 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 smmu_id = smmu.RegisterProcess(memory_interface);
|
auto asid = 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{new_id, process, smmu_id};
|
impl->sessions[new_id] = Session{SessionId{new_id}, process, asid};
|
||||||
} else {
|
} else {
|
||||||
new_id = impl->new_ids++;
|
new_id = impl->new_ids++;
|
||||||
impl->sessions.emplace_back(new_id, process, smmu_id);
|
impl->sessions.emplace_back(SessionId{new_id}, process, asid);
|
||||||
}
|
}
|
||||||
auto& session = impl->sessions[new_id];
|
auto& session = impl->sessions[new_id];
|
||||||
session.is_active = true;
|
session.is_active = true;
|
||||||
@ -80,7 +81,7 @@ size_t 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;
|
||||||
@ -97,21 +98,21 @@ size_t 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 >> 15) >= 1024 ? smmu.Allocate(region_size) : 0;
|
auto start_region = region_size >= 32_MiB ? 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,
|
||||||
smmu_id, impl->host1x);
|
asid, impl->host1x);
|
||||||
smmu.TrackContinuity(start_region, region_start, region_size, smmu_id);
|
smmu.TrackContinuity(start_region, region_start, region_size, asid);
|
||||||
session.has_preallocated_area = true;
|
session.has_preallocated_area = true;
|
||||||
LOG_CRITICAL(Debug, "Preallocation created!");
|
LOG_DEBUG(Debug, "Preallocation created!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new_id;
|
return SessionId{new_id};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Container::CloseSession(size_t id) {
|
void Container::CloseSession(SessionId session_id) {
|
||||||
std::scoped_lock lk(impl->session_guard);
|
std::scoped_lock lk(impl->session_guard);
|
||||||
auto& session = impl->sessions[id];
|
auto& session = impl->sessions[session_id.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();
|
||||||
@ -121,13 +122,13 @@ void Container::CloseSession(size_t id) {
|
|||||||
session.has_preallocated_area = false;
|
session.has_preallocated_area = false;
|
||||||
}
|
}
|
||||||
session.is_active = false;
|
session.is_active = false;
|
||||||
smmu.UnregisterProcess(impl->sessions[id].smmu_id);
|
smmu.UnregisterProcess(impl->sessions[session_id.id].asid);
|
||||||
impl->id_pool.emplace_front(id);
|
impl->id_pool.emplace_front(session_id.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Session* Container::GetSession(size_t id) {
|
Session* Container::GetSession(SessionId session_id) {
|
||||||
std::atomic_thread_fence(std::memory_order_acquire);
|
std::atomic_thread_fence(std::memory_order_acquire);
|
||||||
return &impl->sessions[id];
|
return &impl->sessions[session_id.id];
|
||||||
}
|
}
|
||||||
|
|
||||||
NvMap& Container::GetNvMapFile() {
|
NvMap& Container::GetNvMapFile() {
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#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 {
|
||||||
@ -26,8 +27,12 @@ class SyncpointManager;
|
|||||||
|
|
||||||
struct ContainerImpl;
|
struct ContainerImpl;
|
||||||
|
|
||||||
|
struct SessionId {
|
||||||
|
size_t id;
|
||||||
|
};
|
||||||
|
|
||||||
struct Session {
|
struct Session {
|
||||||
Session(size_t id_, Kernel::KProcess* process_, size_t smmu_id_);
|
Session(SessionId id_, Kernel::KProcess* process_, Core::Asid asid_);
|
||||||
~Session();
|
~Session();
|
||||||
|
|
||||||
Session(const Session&) = delete;
|
Session(const Session&) = delete;
|
||||||
@ -35,9 +40,9 @@ struct Session {
|
|||||||
Session(Session&&) = default;
|
Session(Session&&) = default;
|
||||||
Session& operator=(Session&&) = default;
|
Session& operator=(Session&&) = default;
|
||||||
|
|
||||||
size_t id;
|
SessionId id;
|
||||||
Kernel::KProcess* process;
|
Kernel::KProcess* process;
|
||||||
size_t smmu_id;
|
Core::Asid asid;
|
||||||
bool has_preallocated_area{};
|
bool has_preallocated_area{};
|
||||||
std::unique_ptr<HeapMapper> mapper{};
|
std::unique_ptr<HeapMapper> mapper{};
|
||||||
bool is_active{};
|
bool is_active{};
|
||||||
@ -48,10 +53,10 @@ public:
|
|||||||
explicit Container(Tegra::Host1x::Host1x& host1x);
|
explicit Container(Tegra::Host1x::Host1x& host1x);
|
||||||
~Container();
|
~Container();
|
||||||
|
|
||||||
size_t OpenSession(Kernel::KProcess* process);
|
SessionId OpenSession(Kernel::KProcess* process);
|
||||||
void CloseSession(size_t id);
|
void CloseSession(SessionId id);
|
||||||
|
|
||||||
Session* GetSession(size_t id);
|
Session* GetSession(SessionId id);
|
||||||
|
|
||||||
NvMap& GetNvMapFile();
|
NvMap& GetNvMapFile();
|
||||||
|
|
||||||
|
@ -109,9 +109,9 @@ struct HeapMapper::HeapMapperInternal {
|
|||||||
std::mutex guard;
|
std::mutex guard;
|
||||||
};
|
};
|
||||||
|
|
||||||
HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, size_t smmu_id,
|
HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid,
|
||||||
Tegra::Host1x::Host1x& host1x)
|
Tegra::Host1x::Host1x& host1x)
|
||||||
: m_vaddress{start_vaddress}, m_daddress{start_daddress}, m_size{size}, m_smmu_id{smmu_id} {
|
: m_vaddress{start_vaddress}, m_daddress{start_daddress}, m_size{size}, m_asid{asid} {
|
||||||
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_smmu_id);
|
m_asid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_internal->mapping_overlaps += std::make_pair(interval, 1);
|
m_internal->mapping_overlaps += std::make_pair(interval, 1);
|
||||||
@ -172,4 +172,4 @@ void HeapMapper::Unmap(VAddr start, size_t size) {
|
|||||||
m_internal->base_set.clear();
|
m_internal->base_set.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::Nvidia::NvCore
|
} // namespace Service::Nvidia::NvCore
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#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;
|
||||||
@ -15,7 +16,7 @@ namespace Service::Nvidia::NvCore {
|
|||||||
|
|
||||||
class HeapMapper {
|
class HeapMapper {
|
||||||
public:
|
public:
|
||||||
HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, size_t smmu_id,
|
HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid,
|
||||||
Tegra::Host1x::Host1x& host1x);
|
Tegra::Host1x::Host1x& host1x);
|
||||||
~HeapMapper();
|
~HeapMapper();
|
||||||
|
|
||||||
@ -41,8 +42,8 @@ private:
|
|||||||
VAddr m_vaddress;
|
VAddr m_vaddress;
|
||||||
DAddr m_daddress;
|
DAddr m_daddress;
|
||||||
size_t m_size;
|
size_t m_size;
|
||||||
size_t m_smmu_id;
|
Core::Asid m_asid;
|
||||||
std::unique_ptr<HeapMapperInternal> m_internal;
|
std::unique_ptr<HeapMapperInternal> m_internal;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::Nvidia::NvCore
|
} // namespace Service::Nvidia::NvCore
|
||||||
|
@ -22,7 +22,8 @@ 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, size_t pSessionId) {
|
NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress,
|
||||||
|
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) {
|
||||||
@ -223,7 +224,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->smmu_id, true);
|
smmu.Map(address, vaddress, map_size, session->asid, true);
|
||||||
handle_description->in_heap = false;
|
handle_description->in_heap = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#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 {
|
||||||
@ -71,7 +72,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{};
|
||||||
size_t session_id{};
|
NvCore::SessionId 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
|
||||||
@ -83,7 +84,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,
|
||||||
size_t pSessionId);
|
NvCore::SessionId 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
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#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 {
|
||||||
@ -62,7 +63,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(size_t session_id, DeviceFD fd) = 0;
|
virtual void OnOpen(NvCore::SessionId session_id, DeviceFD fd) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called once a device is closed
|
* Called once a device is closed
|
||||||
|
@ -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(size_t session_id, DeviceFD fd) {}
|
void nvdisp_disp0::OnOpen(NvCore::SessionId 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,
|
||||||
|
@ -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(size_t session_id, DeviceFD fd) override;
|
void OnOpen(NvCore::SessionId 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.
|
||||||
|
@ -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(size_t session_id, DeviceFD fd) {}
|
void nvhost_as_gpu::OnOpen(NvCore::SessionId 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) {
|
||||||
|
@ -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(size_t session_id, DeviceFD fd) override;
|
void OnOpen(NvCore::SessionId 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;
|
||||||
|
@ -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(size_t session_id, DeviceFD fd) {}
|
void nvhost_ctrl::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {}
|
||||||
|
|
||||||
void nvhost_ctrl::OnClose(DeviceFD fd) {}
|
void nvhost_ctrl::OnClose(DeviceFD fd) {}
|
||||||
|
|
||||||
|
@ -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(size_t session_id, DeviceFD fd) override;
|
void OnOpen(NvCore::SessionId 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;
|
||||||
|
@ -82,7 +82,7 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8>
|
|||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nvhost_ctrl_gpu::OnOpen(size_t session_id, DeviceFD fd) {}
|
void nvhost_ctrl_gpu::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {}
|
||||||
void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {}
|
void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {}
|
||||||
|
|
||||||
NvResult nvhost_ctrl_gpu::GetCharacteristics1(IoctlCharacteristics& params) {
|
NvResult nvhost_ctrl_gpu::GetCharacteristics1(IoctlCharacteristics& params) {
|
||||||
|
@ -28,7 +28,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(size_t session_id, DeviceFD fd) override;
|
void OnOpen(NvCore::SessionId 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;
|
||||||
|
@ -120,7 +120,7 @@ NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu
|
|||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nvhost_gpu::OnOpen(size_t session_id, DeviceFD fd) {}
|
void nvhost_gpu::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {}
|
||||||
void nvhost_gpu::OnClose(DeviceFD fd) {}
|
void nvhost_gpu::OnClose(DeviceFD fd) {}
|
||||||
|
|
||||||
NvResult nvhost_gpu::SetNVMAPfd(IoctlSetNvmapFD& params) {
|
NvResult nvhost_gpu::SetNVMAPfd(IoctlSetNvmapFD& params) {
|
||||||
|
@ -47,7 +47,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(size_t session_id, DeviceFD fd) override;
|
void OnOpen(NvCore::SessionId 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;
|
||||||
|
@ -68,7 +68,7 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
|
|||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nvhost_nvdec::OnOpen(size_t session_id, DeviceFD fd) {
|
void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
|
||||||
LOG_INFO(Service_NVDRV, "NVDEC video stream started");
|
LOG_INFO(Service_NVDRV, "NVDEC video stream started");
|
||||||
system.SetNVDECActive(true);
|
system.SetNVDECActive(true);
|
||||||
sessions[fd] = session_id;
|
sessions[fd] = session_id;
|
||||||
|
@ -20,7 +20,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(size_t session_id, DeviceFD fd) override;
|
void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override;
|
||||||
void OnClose(DeviceFD fd) override;
|
void OnClose(DeviceFD fd) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ protected:
|
|||||||
NvCore::NvMap& nvmap;
|
NvCore::NvMap& nvmap;
|
||||||
NvCore::ChannelType channel_type;
|
NvCore::ChannelType channel_type;
|
||||||
std::array<u32, MaxSyncPoints> device_syncpoints{};
|
std::array<u32, MaxSyncPoints> device_syncpoints{};
|
||||||
std::unordered_map<DeviceFD, size_t> sessions;
|
std::unordered_map<DeviceFD, NvCore::SessionId> sessions;
|
||||||
};
|
};
|
||||||
}; // namespace Devices
|
}; // namespace Devices
|
||||||
} // namespace Service::Nvidia
|
} // namespace Service::Nvidia
|
||||||
|
@ -44,7 +44,7 @@ NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
|
|||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nvhost_nvjpg::OnOpen(size_t session_id, DeviceFD fd) {}
|
void nvhost_nvjpg::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {}
|
||||||
void nvhost_nvjpg::OnClose(DeviceFD fd) {}
|
void nvhost_nvjpg::OnClose(DeviceFD fd) {}
|
||||||
|
|
||||||
NvResult nvhost_nvjpg::SetNVMAPfd(IoctlSetNvmapFD& params) {
|
NvResult nvhost_nvjpg::SetNVMAPfd(IoctlSetNvmapFD& params) {
|
||||||
|
@ -22,7 +22,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(size_t session_id, DeviceFD fd) override;
|
void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override;
|
||||||
void OnClose(DeviceFD fd) override;
|
void OnClose(DeviceFD fd) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -68,7 +68,7 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu
|
|||||||
return NvResult::NotImplemented;
|
return NvResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nvhost_vic::OnOpen(size_t session_id, DeviceFD fd) {
|
void nvhost_vic::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
|
||||||
sessions[fd] = session_id;
|
sessions[fd] = session_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,10 +78,7 @@ void nvhost_vic::OnClose(DeviceFD fd) {
|
|||||||
if (iter != host1x_file.fd_to_id.end()) {
|
if (iter != host1x_file.fd_to_id.end()) {
|
||||||
system.GPU().ClearCdmaInstance(iter->second);
|
system.GPU().ClearCdmaInstance(iter->second);
|
||||||
}
|
}
|
||||||
auto it = sessions.find(fd);
|
sessions.erase(fd);
|
||||||
if (it != sessions.end()) {
|
|
||||||
sessions.erase(it);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::Nvidia::Devices
|
} // namespace Service::Nvidia::Devices
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user